1 /*	$OpenBSD: sem_timedwait.c,v 1.2 2014/01/22 04:31:45 guenther Exp $	*/
2 /*
3  * Martin Pieuchot <mpi@openbsd.org>, 2011. Public Domain.
4  */
5 
6 #include <err.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <semaphore.h>
10 #include <signal.h>
11 #include <pthread.h>
12 #include "test.h"
13 
14 
15 void *waiter(void *arg);
16 
17 void
18 handler(int sig)
19 {
20 	static char message[] = "got sig\n";
21 
22 	write(STDERR_FILENO, message, sizeof(message) - 1);
23 }
24 
25 sem_t sem;
26 volatile int posted = 0, eintr_ok = 0;
27 
28 int
29 main(int argc, char **argv)
30 {
31 	pthread_t th;
32 	struct sigaction sa;
33 	struct timespec ts, ts2;
34 
35 	CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
36 	ts.tv_sec += 3;
37 	CHECKn(sem_timedwait(&sem, &ts));
38 	ASSERT(errno == EINVAL);
39 
40 	CHECKr(sem_init(&sem, 0, 0));
41 
42 	CHECKr(pthread_create(&th, NULL, waiter, &sem));
43 
44 	sleep(1);
45 
46 	printf("expect: sem_destroy on semaphore with waiters!\n");
47 	CHECKn(sem_destroy(&sem));
48 	ASSERT(errno == EBUSY);
49 
50 	posted = 1;
51 	CHECKr(sem_post(&sem));
52 	CHECKr(pthread_join(th, NULL));
53 
54 	/* test that sem_timedwait() resumes after handling a signal */
55 	memset(&sa, 0, sizeof sa);
56 	sa.sa_handler = &handler;
57 	sigemptyset(&sa.sa_mask);
58 	sa.sa_flags = 0;
59 	if (sigaction(SIGUSR1, &sa, NULL))
60 		err(1, "sigaction");
61 	posted = 0;
62 	CHECKr(pthread_create(&th, NULL, waiter, &sem));
63 	sleep(1);
64 	fprintf(stderr, "sending sig\n");
65 	eintr_ok = 1;
66 	pthread_kill(th, SIGUSR1);
67 	sleep(1);
68 	fprintf(stderr, "posting\n");
69 	posted = 1;
70 	eintr_ok = 0;
71 	CHECKr(sem_post(&sem));
72 	CHECKr(pthread_join(th, NULL));
73 
74 	CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
75 	ts.tv_sec += 2;
76 	CHECKn(sem_timedwait(&sem, &ts));
77 	ASSERT(errno == ETIMEDOUT);
78 	CHECKr(clock_gettime(CLOCK_REALTIME, &ts2));
79 	if (timespeccmp(&ts, &ts2, < ))
80 		timespecsub(&ts2, &ts, &ts);
81 	else
82 		timespecsub(&ts, &ts2, &ts);
83 	CHECKr(clock_getres(CLOCK_REALTIME, &ts2));
84 	timespecadd(&ts2, &ts2, &ts2);	/* 2 * resolution slop */
85 	ASSERT(timespeccmp(&ts, &ts2, < ));
86 
87 	CHECKe(sem_destroy(&sem));
88 
89 	SUCCEED;
90 }
91 
92 void *
93 waiter(void *arg)
94 {
95 	sem_t *semp = arg;
96 	struct timespec ts;
97 	int value;
98 	int r;
99 
100 	CHECKr(clock_gettime(CLOCK_REALTIME, &ts));
101 	ts.tv_sec += 3;
102 	r = sem_timedwait(semp, &ts);
103 	CHECKr(sem_getvalue(semp, &value));
104 	if (r == 0) {
105 		ASSERT(value == 0);
106 		ASSERT(posted != 0);
107 	} else {
108 		ASSERT(r == -1);
109 		ASSERT(errno == EINTR);
110 		ASSERT(eintr_ok);
111 		if (posted)
112 			ASSERT(value == 1);
113 		else
114 			ASSERT(value == 0);
115 	}
116 
117 	return (NULL);
118 }
119