1272343Sngie/*	$NetBSD: sem.c,v 1.10 2012/03/09 14:25:34 joerg Exp $	*/
2272343Sngie
3272343Sngie/*
4272343Sngie * Common code for semaphore tests.  This can be included both into
5272343Sngie * programs using librt and libpthread.
6272343Sngie */
7272343Sngie
8272343Sngie#include <sys/types.h>
9272343Sngie
10272343Sngie#include <rump/rump.h>
11272343Sngie#include <rump/rump_syscalls.h>
12272343Sngie
13272343Sngie#include <atf-c.h>
14272343Sngie#include <errno.h>
15272343Sngie#include <fcntl.h>
16272343Sngie#include <pthread.h>
17272343Sngie#include <semaphore.h>
18272343Sngie#include <sched.h>
19272343Sngie#include <stdint.h>
20272343Sngie#include <stdio.h>
21272343Sngie#include <stdlib.h>
22272343Sngie#include <unistd.h>
23272343Sngie
24272343Sngie#include "../../h_macros.h"
25272343Sngie
26272343SngieATF_TC(postwait);
27272343SngieATF_TC_HEAD(postwait, tc)
28272343Sngie{
29272343Sngie
30272343Sngie	atf_tc_set_md_var(tc, "descr", "tests post and wait from a "
31272343Sngie	    "single thread (%s)", LIBNAME);
32272343Sngie}
33272343Sngie
34272343SngieATF_TC_BODY(postwait, tc)
35272343Sngie{
36272343Sngie	sem_t sem;
37272343Sngie	int rv;
38272343Sngie
39272343Sngie	rump_init();
40272343Sngie
41272343Sngie	ATF_REQUIRE_EQ(sem_init(&sem, 1, 0), 0);
42272343Sngie
43272343Sngie	sem_post(&sem);
44272343Sngie	sem_post(&sem);
45272343Sngie
46272343Sngie	sem_wait(&sem);
47272343Sngie	sem_wait(&sem);
48272343Sngie	rv = sem_trywait(&sem);
49272343Sngie	ATF_REQUIRE(errno == EAGAIN);
50272343Sngie	ATF_REQUIRE(rv == -1);
51272343Sngie}
52272343Sngie
53272343SngieATF_TC(initvalue);
54272343SngieATF_TC_HEAD(initvalue, tc)
55272343Sngie{
56272343Sngie
57272343Sngie	atf_tc_set_md_var(tc, "descr", "tests initialization with a non-zero "
58272343Sngie	    "value (%s)", LIBNAME);
59272343Sngie}
60272343Sngie
61272343SngieATF_TC_BODY(initvalue, tc)
62272343Sngie{
63272343Sngie	sem_t sem;
64272343Sngie
65272343Sngie	rump_init();
66272343Sngie	sem_init(&sem, 1, 4);
67272343Sngie
68272343Sngie	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
69272343Sngie	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
70272343Sngie	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
71272343Sngie	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
72272343Sngie	ATF_REQUIRE_EQ(sem_trywait(&sem), -1);
73272343Sngie}
74272343Sngie
75272343SngieATF_TC(destroy);
76272343SngieATF_TC_HEAD(destroy, tc)
77272343Sngie{
78272343Sngie
79272343Sngie	atf_tc_set_md_var(tc, "descr", "tests sem_destroy works (%s)", LIBNAME);
80272343Sngie}
81272343Sngie
82272343SngieATF_TC_BODY(destroy, tc)
83272343Sngie{
84272343Sngie	sem_t sem;
85272343Sngie	int rv, i;
86272343Sngie
87272343Sngie	rump_init();
88272343Sngie	for (i = 0; i < 2; i++) {
89272343Sngie		sem_init(&sem, 1, 1);
90272343Sngie
91272343Sngie		ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
92272343Sngie		ATF_REQUIRE_EQ(sem_trywait(&sem), -1);
93272343Sngie		ATF_REQUIRE_EQ(sem_destroy(&sem), 0);
94272343Sngie		rv = sem_trywait(&sem);
95272343Sngie		ATF_REQUIRE_EQ(errno, EINVAL);
96272343Sngie		ATF_REQUIRE_EQ(rv, -1);
97272343Sngie	}
98272343Sngie}
99272343Sngie
100272343SngieATF_TC(busydestroy);
101272343SngieATF_TC_HEAD(busydestroy, tc)
102272343Sngie{
103272343Sngie
104272343Sngie	atf_tc_set_md_var(tc, "descr", "tests sem_destroy report EBUSY for "
105272343Sngie	    "a busy semaphore (%s)", LIBNAME);
106272343Sngie}
107272343Sngie
108272343Sngiestatic void *
109272343Sngiehthread(void *arg)
110272343Sngie{
111272343Sngie	sem_t *semmarit = arg;
112272343Sngie
113272343Sngie	for (;;) {
114272343Sngie		sem_post(&semmarit[2]);
115272343Sngie		sem_wait(&semmarit[1]);
116272343Sngie		sem_wait(&semmarit[0]);
117272343Sngie	}
118272343Sngie
119272343Sngie	return NULL;
120272343Sngie}
121272343Sngie
122272343SngieATF_TC_BODY(busydestroy, tc)
123272343Sngie{
124272343Sngie	sem_t semmarit[3];
125272343Sngie	pthread_t pt;
126272343Sngie	int i;
127272343Sngie
128272343Sngie	/* use a unicpu rump kernel.  this means less chance for race */
129272343Sngie	setenv("RUMP_NCPU", "1", 1);
130272343Sngie
131272343Sngie	rump_init();
132272343Sngie	sem_init(&semmarit[0], 1, 0);
133272343Sngie	sem_init(&semmarit[1], 1, 0);
134272343Sngie	sem_init(&semmarit[2], 1, 0);
135272343Sngie
136272343Sngie	pthread_create(&pt, NULL, hthread, semmarit);
137272343Sngie
138272343Sngie	/*
139272343Sngie	 * Make a best-effort to catch the other thread with its pants down.
140272343Sngie	 * We can't do this for sure, can we?  Although, we could reach
141272343Sngie	 * inside the rump kernel and inquire about the thread's sleep
142272343Sngie	 * status.
143272343Sngie	 */
144272343Sngie	for (i = 0; i < 1000; i++) {
145272343Sngie		sem_wait(&semmarit[2]);
146272343Sngie		usleep(1);
147272343Sngie		if (sem_destroy(&semmarit[1]) == -1)
148272343Sngie			if (errno == EBUSY)
149272343Sngie				break;
150272343Sngie
151272343Sngie		/*
152272343Sngie		 * Didn't catch it?  ok, recreate and post to make the
153272343Sngie		 * other thread run
154272343Sngie		 */
155272343Sngie		sem_init(&semmarit[1], 1, 0);
156272343Sngie		sem_post(&semmarit[0]);
157272343Sngie		sem_post(&semmarit[1]);
158272343Sngie
159272343Sngie	}
160272343Sngie	if (i == 1000)
161272343Sngie		atf_tc_fail("sem destroy not reporting EBUSY");
162272343Sngie
163272343Sngie	pthread_cancel(pt);
164272343Sngie	pthread_join(pt, NULL);
165272343Sngie}
166272343Sngie
167272343SngieATF_TC(blockwait);
168272343SngieATF_TC_HEAD(blockwait, tc)
169272343Sngie{
170272343Sngie
171272343Sngie	atf_tc_set_md_var(tc, "descr", "tests sem_wait can handle blocking "
172272343Sngie	    "(%s)", LIBNAME);
173272343Sngie	atf_tc_set_md_var(tc, "timeout", "2");
174272343Sngie}
175272343Sngie
176272343SngieATF_TC_BODY(blockwait, tc)
177272343Sngie{
178272343Sngie	sem_t semmarit[3];
179272343Sngie	pthread_t pt;
180272343Sngie	int i;
181272343Sngie
182272343Sngie	rump_init();
183272343Sngie	sem_init(&semmarit[0], 1, 0);
184272343Sngie	sem_init(&semmarit[1], 1, 0);
185272343Sngie	sem_init(&semmarit[2], 1, 0);
186272343Sngie
187272343Sngie	pthread_create(&pt, NULL, hthread, semmarit);
188272343Sngie
189272343Sngie	/*
190272343Sngie	 * Make a best-effort.  Unless we're extremely unlucky, we should
191272343Sngie	 * at least one blocking wait.
192272343Sngie	 */
193272343Sngie	for (i = 0; i < 10; i++) {
194272343Sngie		sem_wait(&semmarit[2]);
195272343Sngie		usleep(1);
196272343Sngie		sem_post(&semmarit[0]);
197272343Sngie		sem_post(&semmarit[1]);
198272343Sngie
199272343Sngie	}
200272343Sngie
201272343Sngie	pthread_cancel(pt);
202272343Sngie	pthread_join(pt, NULL);
203272343Sngie}
204272343Sngie
205272343SngieATF_TC(blocktimedwait);
206272343SngieATF_TC_HEAD(blocktimedwait, tc)
207272343Sngie{
208272343Sngie
209272343Sngie	atf_tc_set_md_var(tc, "descr", "tests sem_timedwait can handle blocking"
210272343Sngie	    " (%s)", LIBNAME);
211272343Sngie	atf_tc_set_md_var(tc, "timeout", "2");
212272343Sngie}
213272343Sngie
214272343SngieATF_TC_BODY(blocktimedwait, tc)
215272343Sngie{
216272343Sngie	sem_t semid;
217272343Sngie	struct timespec tp;
218272343Sngie
219272343Sngie	rump_init();
220272343Sngie
221272343Sngie	clock_gettime(CLOCK_REALTIME, &tp);
222272343Sngie	tp.tv_nsec += 50000000;
223272343Sngie	tp.tv_sec += tp.tv_nsec / 1000000000;
224272343Sngie	tp.tv_nsec %= 1000000000;
225272343Sngie
226272343Sngie	ATF_REQUIRE_EQ(sem_init(&semid, 1, 0), 0);
227272343Sngie	ATF_REQUIRE_ERRNO(ETIMEDOUT, sem_timedwait(&semid, &tp) == -1);
228272343Sngie}
229272343Sngie
230272343SngieATF_TC(named);
231272343SngieATF_TC_HEAD(named, tc)
232272343Sngie{
233272343Sngie
234272343Sngie	atf_tc_set_md_var(tc, "descr", "tests named semaphores (%s)", LIBNAME);
235272343Sngie}
236272343Sngie
237272343Sngie/*
238272343Sngie * Wow, easy naming rules.  it's these times i'm really happy i can
239272343Sngie * single-step into the kernel.
240272343Sngie */
241272343Sngie#define SEM1 "/precious_sem"
242272343Sngie#define SEM2 "/justsem"
243272343SngieATF_TC_BODY(named, tc)
244272343Sngie{
245272343Sngie	sem_t *sem1, *sem2;
246272343Sngie	void *rv;
247272343Sngie
248272343Sngie	rump_init();
249272343Sngie	sem1 = sem_open(SEM1, 0);
250272343Sngie	ATF_REQUIRE_EQ(errno, ENOENT);
251272343Sngie	ATF_REQUIRE_EQ(sem1, NULL);
252272343Sngie
253272343Sngie	sem1 = sem_open(SEM1, O_CREAT, 0444, 1);
254272343Sngie	if (sem1 == NULL)
255272343Sngie		atf_tc_fail_errno("sem_open O_CREAT");
256272343Sngie
257272343Sngie	rv = sem_open(SEM1, O_CREAT | O_EXCL);
258272343Sngie	ATF_REQUIRE_EQ(errno, EEXIST);
259272343Sngie	ATF_REQUIRE_EQ(rv, NULL);
260272343Sngie
261272343Sngie	sem2 = sem_open(SEM2, O_CREAT, 0444, 0);
262272343Sngie	if (sem2 == NULL)
263272343Sngie		atf_tc_fail_errno("sem_open O_CREAT");
264272343Sngie
265272343Sngie	/* check that semaphores are independent */
266272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
267272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
268272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
269272343Sngie
270272343Sngie	/* check that unlinked remains valid */
271272343Sngie	sem_unlink(SEM2);
272272343Sngie	ATF_REQUIRE_EQ(sem_post(sem2), 0);
273272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem2), 0);
274272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
275272343Sngie	ATF_REQUIRE_EQ(errno, EAGAIN);
276272343Sngie
277272343Sngie#if 0 /* see unlink */
278272343Sngie	/* close it and check that it's gone */
279272343Sngie	if (sem_close(sem2) != 0)
280272343Sngie		atf_tc_fail_errno("sem close");
281272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
282272343Sngie	ATF_REQUIRE_EQ(errno, EINVAL);
283272343Sngie#endif
284272343Sngie
285272343Sngie	/* check that we still have sem1 */
286272343Sngie	sem_post(sem1);
287272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
288272343Sngie	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
289272343Sngie	ATF_REQUIRE_EQ(errno, EAGAIN);
290272343Sngie}
291272343Sngie
292272343SngieATF_TC(unlink);
293272343SngieATF_TC_HEAD(unlink, tc)
294272343Sngie{
295272343Sngie
296272343Sngie	/* this is currently broken.  i'll append the PR number soon */
297272343Sngie	atf_tc_set_md_var(tc, "descr", "tests unlinked semaphores can be "
298272343Sngie	    "closed (%s)", LIBNAME);
299272343Sngie}
300272343Sngie
301272343Sngie#define SEM "/thesem"
302272343SngieATF_TC_BODY(unlink, tc)
303272343Sngie{
304272343Sngie	sem_t *sem;
305272343Sngie
306272343Sngie	rump_init();
307272343Sngie	sem = sem_open(SEM, O_CREAT, 0444, 0);
308272343Sngie	ATF_REQUIRE(sem);
309272343Sngie
310272343Sngie	if (sem_unlink(SEM) == -1)
311272343Sngie		atf_tc_fail_errno("unlink");
312272343Sngie	if (sem_close(sem) == -1)
313272343Sngie		atf_tc_fail_errno("close unlinked semaphore");
314272343Sngie}
315272343Sngie
316272343Sngie/* use rump calls for libpthread _ksem_foo() calls */
317272343Sngie#define F1(name, a) int _ksem_##name(a); \
318272343Sngieint _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);}
319272343Sngie#define F2(name, a, b) int _ksem_##name(a, b); \
320272343Sngieint _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);}
321272343SngieF2(init, unsigned int, intptr_t *);
322272343SngieF1(close, intptr_t);
323272343SngieF1(destroy, intptr_t);
324272343SngieF1(post, intptr_t);
325272343SngieF1(unlink, const char *);
326272343SngieF1(trywait, intptr_t);
327272343SngieF1(wait, intptr_t);
328272343SngieF2(getvalue, intptr_t, unsigned int *);
329272343SngieF2(timedwait, intptr_t, const struct timespec *);
330272343Sngieint _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *);
331272343Sngieint _ksem_open(const char *a, int b, mode_t c, unsigned int d, intptr_t *e)
332272343Sngie    {return rump_sys__ksem_open(a,b,c,d,e);}
333