1 /*	$OpenBSD: sem_timedwait.c,v 1.4 2019/12/20 23:39:01 cheloha 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
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
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 	ASSERT(timespeccmp(&ts2, &ts, <=));
102 
103 	CHECKe(sem_destroy(&sem));
104 
105 	SUCCEED;
106 }
107 
108 void *
109 waiter(void *arg)
110 {
111 	sem_t *semp = arg;
112 	struct timespec ts;
113 	int value;
114 	int r;
115 
116 	CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
117 	ts.tv_sec += 3;
118 	r = sem_timedwait(semp, &ts);
119 	CHECKr(sem_getvalue(semp, &value));
120 	if (r == 0) {
121 		ASSERT(value == 0);
122 		ASSERT(posted != 0);
123 	} else {
124 		ASSERT(r == -1);
125 		ASSERT(errno == EINTR);
126 		ASSERT(eintr_ok);
127 		if (posted)
128 			ASSERT(value == 1);
129 		else
130 			ASSERT(value == 0);
131 	}
132 
133 	return (NULL);
134 }
135