sem.c revision 314817
1/*	$NetBSD: sem.c,v 1.11 2017/01/13 21:30:42 christos Exp $	*/
2
3/*
4 * Common code for semaphore tests.  This can be included both into
5 * programs using librt and libpthread.
6 */
7
8#include <sys/types.h>
9
10#include <rump/rump.h>
11#include <rump/rump_syscalls.h>
12
13#include <atf-c.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <pthread.h>
17#include <semaphore.h>
18#include <sched.h>
19#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23
24#include "h_macros.h"
25
26ATF_TC(postwait);
27ATF_TC_HEAD(postwait, tc)
28{
29
30	atf_tc_set_md_var(tc, "descr", "tests post and wait from a "
31	    "single thread (%s)", LIBNAME);
32}
33
34ATF_TC_BODY(postwait, tc)
35{
36	sem_t sem;
37	int rv;
38
39	rump_init();
40
41	ATF_REQUIRE_EQ(sem_init(&sem, 1, 0), 0);
42
43	sem_post(&sem);
44	sem_post(&sem);
45
46	sem_wait(&sem);
47	sem_wait(&sem);
48	rv = sem_trywait(&sem);
49	ATF_REQUIRE(errno == EAGAIN);
50	ATF_REQUIRE(rv == -1);
51}
52
53ATF_TC(initvalue);
54ATF_TC_HEAD(initvalue, tc)
55{
56
57	atf_tc_set_md_var(tc, "descr", "tests initialization with a non-zero "
58	    "value (%s)", LIBNAME);
59}
60
61ATF_TC_BODY(initvalue, tc)
62{
63	sem_t sem;
64
65	rump_init();
66	sem_init(&sem, 1, 4);
67
68	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
69	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
70	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
71	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
72	ATF_REQUIRE_EQ(sem_trywait(&sem), -1);
73}
74
75ATF_TC(destroy);
76ATF_TC_HEAD(destroy, tc)
77{
78
79	atf_tc_set_md_var(tc, "descr", "tests sem_destroy works (%s)", LIBNAME);
80}
81
82ATF_TC_BODY(destroy, tc)
83{
84	sem_t sem;
85	int rv, i;
86
87	rump_init();
88	for (i = 0; i < 2; i++) {
89		sem_init(&sem, 1, 1);
90
91		ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
92		ATF_REQUIRE_EQ(sem_trywait(&sem), -1);
93		ATF_REQUIRE_EQ(sem_destroy(&sem), 0);
94		rv = sem_trywait(&sem);
95		ATF_REQUIRE_EQ(errno, EINVAL);
96		ATF_REQUIRE_EQ(rv, -1);
97	}
98}
99
100ATF_TC(busydestroy);
101ATF_TC_HEAD(busydestroy, tc)
102{
103
104	atf_tc_set_md_var(tc, "descr", "tests sem_destroy report EBUSY for "
105	    "a busy semaphore (%s)", LIBNAME);
106}
107
108static void *
109hthread(void *arg)
110{
111	sem_t *semmarit = arg;
112
113	for (;;) {
114		sem_post(&semmarit[2]);
115		sem_wait(&semmarit[1]);
116		sem_wait(&semmarit[0]);
117	}
118
119	return NULL;
120}
121
122ATF_TC_BODY(busydestroy, tc)
123{
124	sem_t semmarit[3];
125	pthread_t pt;
126	int i;
127
128	/* use a unicpu rump kernel.  this means less chance for race */
129	setenv("RUMP_NCPU", "1", 1);
130
131	rump_init();
132	sem_init(&semmarit[0], 1, 0);
133	sem_init(&semmarit[1], 1, 0);
134	sem_init(&semmarit[2], 1, 0);
135
136	pthread_create(&pt, NULL, hthread, semmarit);
137
138	/*
139	 * Make a best-effort to catch the other thread with its pants down.
140	 * We can't do this for sure, can we?  Although, we could reach
141	 * inside the rump kernel and inquire about the thread's sleep
142	 * status.
143	 */
144	for (i = 0; i < 1000; i++) {
145		sem_wait(&semmarit[2]);
146		usleep(1);
147		if (sem_destroy(&semmarit[1]) == -1)
148			if (errno == EBUSY)
149				break;
150
151		/*
152		 * Didn't catch it?  ok, recreate and post to make the
153		 * other thread run
154		 */
155		sem_init(&semmarit[1], 1, 0);
156		sem_post(&semmarit[0]);
157		sem_post(&semmarit[1]);
158
159	}
160	if (i == 1000)
161		atf_tc_fail("sem destroy not reporting EBUSY");
162
163	pthread_cancel(pt);
164	pthread_join(pt, NULL);
165}
166
167ATF_TC(blockwait);
168ATF_TC_HEAD(blockwait, tc)
169{
170
171	atf_tc_set_md_var(tc, "descr", "tests sem_wait can handle blocking "
172	    "(%s)", LIBNAME);
173	atf_tc_set_md_var(tc, "timeout", "2");
174}
175
176ATF_TC_BODY(blockwait, tc)
177{
178	sem_t semmarit[3];
179	pthread_t pt;
180	int i;
181
182	rump_init();
183	sem_init(&semmarit[0], 1, 0);
184	sem_init(&semmarit[1], 1, 0);
185	sem_init(&semmarit[2], 1, 0);
186
187	pthread_create(&pt, NULL, hthread, semmarit);
188
189	/*
190	 * Make a best-effort.  Unless we're extremely unlucky, we should
191	 * at least one blocking wait.
192	 */
193	for (i = 0; i < 10; i++) {
194		sem_wait(&semmarit[2]);
195		usleep(1);
196		sem_post(&semmarit[0]);
197		sem_post(&semmarit[1]);
198
199	}
200
201	pthread_cancel(pt);
202	pthread_join(pt, NULL);
203}
204
205ATF_TC(blocktimedwait);
206ATF_TC_HEAD(blocktimedwait, tc)
207{
208
209	atf_tc_set_md_var(tc, "descr", "tests sem_timedwait can handle blocking"
210	    " (%s)", LIBNAME);
211	atf_tc_set_md_var(tc, "timeout", "2");
212}
213
214ATF_TC_BODY(blocktimedwait, tc)
215{
216	sem_t semid;
217	struct timespec tp;
218
219	rump_init();
220
221	clock_gettime(CLOCK_REALTIME, &tp);
222	tp.tv_nsec += 50000000;
223	tp.tv_sec += tp.tv_nsec / 1000000000;
224	tp.tv_nsec %= 1000000000;
225
226	ATF_REQUIRE_EQ(sem_init(&semid, 1, 0), 0);
227	ATF_REQUIRE_ERRNO(ETIMEDOUT, sem_timedwait(&semid, &tp) == -1);
228}
229
230ATF_TC(named);
231ATF_TC_HEAD(named, tc)
232{
233
234	atf_tc_set_md_var(tc, "descr", "tests named semaphores (%s)", LIBNAME);
235}
236
237/*
238 * Wow, easy naming rules.  it's these times i'm really happy i can
239 * single-step into the kernel.
240 */
241#define SEM1 "/precious_sem"
242#define SEM2 "/justsem"
243ATF_TC_BODY(named, tc)
244{
245	sem_t *sem1, *sem2;
246	void *rv;
247
248	rump_init();
249	sem1 = sem_open(SEM1, 0);
250	ATF_REQUIRE_EQ(errno, ENOENT);
251	ATF_REQUIRE_EQ(sem1, NULL);
252
253	sem1 = sem_open(SEM1, O_CREAT, 0444, 1);
254	if (sem1 == NULL)
255		atf_tc_fail_errno("sem_open O_CREAT");
256
257	rv = sem_open(SEM1, O_CREAT | O_EXCL);
258	ATF_REQUIRE_EQ(errno, EEXIST);
259	ATF_REQUIRE_EQ(rv, NULL);
260
261	sem2 = sem_open(SEM2, O_CREAT, 0444, 0);
262	if (sem2 == NULL)
263		atf_tc_fail_errno("sem_open O_CREAT");
264
265	/* check that semaphores are independent */
266	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
267	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
268	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
269
270	/* check that unlinked remains valid */
271	sem_unlink(SEM2);
272	ATF_REQUIRE_EQ(sem_post(sem2), 0);
273	ATF_REQUIRE_EQ(sem_trywait(sem2), 0);
274	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
275	ATF_REQUIRE_EQ(errno, EAGAIN);
276
277#if 0 /* see unlink */
278	/* close it and check that it's gone */
279	if (sem_close(sem2) != 0)
280		atf_tc_fail_errno("sem close");
281	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
282	ATF_REQUIRE_EQ(errno, EINVAL);
283#endif
284
285	/* check that we still have sem1 */
286	sem_post(sem1);
287	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
288	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
289	ATF_REQUIRE_EQ(errno, EAGAIN);
290}
291
292ATF_TC(unlink);
293ATF_TC_HEAD(unlink, tc)
294{
295
296	/* this is currently broken.  i'll append the PR number soon */
297	atf_tc_set_md_var(tc, "descr", "tests unlinked semaphores can be "
298	    "closed (%s)", LIBNAME);
299}
300
301#define SEM "/thesem"
302ATF_TC_BODY(unlink, tc)
303{
304	sem_t *sem;
305
306	rump_init();
307	sem = sem_open(SEM, O_CREAT, 0444, 0);
308	ATF_REQUIRE(sem);
309
310	if (sem_unlink(SEM) == -1)
311		atf_tc_fail_errno("unlink");
312	if (sem_close(sem) == -1)
313		atf_tc_fail_errno("close unlinked semaphore");
314}
315
316/* use rump calls for libpthread _ksem_foo() calls */
317#define F1(name, a) int _ksem_##name(a); \
318int _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);}
319#define F2(name, a, b) int _ksem_##name(a, b); \
320int _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);}
321F2(init, unsigned int, intptr_t *);
322F1(close, intptr_t);
323F1(destroy, intptr_t);
324F1(post, intptr_t);
325F1(unlink, const char *);
326F1(trywait, intptr_t);
327F1(wait, intptr_t);
328F2(getvalue, intptr_t, unsigned int *);
329F2(timedwait, intptr_t, const struct timespec *);
330int _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *);
331int _ksem_open(const char *a, int b, mode_t c, unsigned int d, intptr_t *e)
332    {return rump_sys__ksem_open(a,b,c,d,e);}
333