1 /* $OpenBSD: sem_timedwait.c,v 1.5 2022/05/13 15:32:49 anton Exp $ */
2 /*
3 * Martin Pieuchot <mpi@openbsd.org>, 2011. Public Domain.
4 */
5
6 #include <sys/types.h>
7 #include <sys/sysctl.h>
8
9 #include <err.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <semaphore.h>
13 #include <signal.h>
14 #include <pthread.h>
15 #include "test.h"
16
17
18 void *waiter(void *arg);
19
20 void
handler(int sig)21 handler(int sig)
22 {
23 static char message[] = "got sig\n";
24
25 write(STDERR_FILENO, message, sizeof(message) - 1);
26 }
27
28 sem_t sem;
29 volatile int posted = 0, eintr_ok = 0;
30
31 int
main(int argc,char ** argv)32 main(int argc, char **argv)
33 {
34 pthread_t th;
35 struct clockinfo info;
36 struct sigaction sa;
37 struct timespec delay, ts, ts2;
38 size_t infosize = sizeof(info);
39 int mib[] = { CTL_KERN, KERN_CLOCKRATE };
40
41 CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
42 ts.tv_sec += 3;
43 CHECKn(sem_timedwait(&sem, &ts));
44 ASSERT(errno == EINVAL);
45
46 CHECKr(sem_init(&sem, 0, 0));
47
48 CHECKr(pthread_create(&th, NULL, waiter, &sem));
49
50 sleep(1);
51
52 printf("expect: sem_destroy on semaphore with waiters!\n");
53 CHECKn(sem_destroy(&sem));
54 ASSERT(errno == EBUSY);
55
56 posted = 1;
57 CHECKr(sem_post(&sem));
58 CHECKr(pthread_join(th, NULL));
59
60 /* test that sem_timedwait() resumes after handling a signal */
61 memset(&sa, 0, sizeof sa);
62 sa.sa_handler = &handler;
63 sigemptyset(&sa.sa_mask);
64 sa.sa_flags = SA_RESTART;
65 if (sigaction(SIGUSR1, &sa, NULL))
66 err(1, "sigaction");
67 posted = 0;
68 CHECKr(pthread_create(&th, NULL, waiter, &sem));
69 sleep(1);
70 fprintf(stderr, "sending sig\n");
71 eintr_ok = 1;
72 pthread_kill(th, SIGUSR1);
73 sleep(1);
74 fprintf(stderr, "posting\n");
75 posted = 1;
76 eintr_ok = 0;
77 CHECKr(sem_post(&sem));
78 CHECKr(pthread_join(th, NULL));
79
80 CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
81 ts.tv_sec += 2;
82 CHECKn(sem_timedwait(&sem, &ts));
83 ASSERT(errno == ETIMEDOUT);
84 CHECKr(clock_gettime(CLOCK_REALTIME, &ts2));
85
86 fprintf(stderr, "timeout: expected %lld.%09ld actual %lld.%09ld\n",
87 ts.tv_sec, ts.tv_nsec, ts2.tv_sec, ts2.tv_nsec);
88
89 /* Check that we don't return early. */
90 ASSERT(timespeccmp(&ts, &ts2, <=));
91
92 /*
93 * Check that we don't return unusually late. Something might be
94 * off if the wait returns more than two ticks after our timeout.
95 */
96 CHECKr(sysctl(mib, 2, &info, &infosize, NULL, 0));
97 delay.tv_sec = 0;
98 delay.tv_nsec = info.tick * 1000; /* usecs -> nsecs */
99 timespecadd(&delay, &delay, &delay); /* up to two ticks of delay */
100 timespecadd(&ts, &delay, &ts);
101 fprintf(stderr, "timeout: expected %lld.%09ld actual %lld.%09ld\n",
102 ts2.tv_sec, ts2.tv_nsec, ts.tv_sec, ts.tv_nsec);
103 ASSERT(timespeccmp(&ts2, &ts, <=));
104
105 CHECKe(sem_destroy(&sem));
106
107 SUCCEED;
108 }
109
110 void *
waiter(void * arg)111 waiter(void *arg)
112 {
113 sem_t *semp = arg;
114 struct timespec ts;
115 int value;
116 int r;
117
118 CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
119 ts.tv_sec += 3;
120 r = sem_timedwait(semp, &ts);
121 CHECKr(sem_getvalue(semp, &value));
122 if (r == 0) {
123 ASSERT(value == 0);
124 ASSERT(posted != 0);
125 } else {
126 ASSERT(r == -1);
127 ASSERT(errno == EINTR);
128 ASSERT(eintr_ok);
129 if (posted)
130 ASSERT(value == 1);
131 else
132 ASSERT(value == 0);
133 }
134
135 return (NULL);
136 }
137