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