xref: /netbsd-src/tests/lib/semaphore/sem.c (revision 5c46dd73a9bcb28b2994504ea090f64066b17a77)
1 /*	$NetBSD: sem.c,v 1.3 2010/06/12 22:59:59 pooka 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 
26 ATF_TC(postwait);
27 ATF_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 
34 ATF_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 
53 ATF_TC(initvalue);
54 ATF_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 
61 ATF_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 
75 ATF_TC(destroy);
76 ATF_TC_HEAD(destroy, tc)
77 {
78 
79 	atf_tc_set_md_var(tc, "descr", "tests sem_destroy works (%s)", LIBNAME);
80 }
81 
82 ATF_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 
100 ATF_TC(busydestroy);
101 ATF_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 
108 static void *
109 hthread(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 
122 ATF_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 
164 ATF_TC(blockwait);
165 ATF_TC_HEAD(blockwait, tc)
166 {
167 
168 	atf_tc_set_md_var(tc, "descr", "tests sem_wait can handle blocking "
169 	    "(%s)", LIBNAME);
170 }
171 
172 ATF_TC_BODY(blockwait, tc)
173 {
174 	sem_t semmarit[3];
175 	pthread_t pt;
176 	int i;
177 
178 	rump_init();
179 	sem_init(&semmarit[0], 1, 0);
180 	sem_init(&semmarit[1], 1, 0);
181 	sem_init(&semmarit[2], 1, 0);
182 
183 	pthread_create(&pt, NULL, hthread, semmarit);
184 
185 	/*
186 	 * Make a best-effort.  Unless we're extremely unlucky, we should
187 	 * at least one blocking wait.
188 	 */
189 	for (i = 0; i < 10; i++) {
190 		sem_wait(&semmarit[2]);
191 		usleep(1);
192 		sem_post(&semmarit[0]);
193 		sem_post(&semmarit[1]);
194 
195 	}
196 	if (i == 1000)
197 		atf_tc_fail("sem destroy not reporting EBUSY");
198 }
199 
200 ATF_TC(named);
201 ATF_TC_HEAD(named, tc)
202 {
203 
204 	atf_tc_set_md_var(tc, "descr", "tests named semaphores (%s)", LIBNAME);
205 }
206 
207 /*
208  * Wow, easy naming rules.  it's these times i'm really happy i can
209  * single-step into the kernel.
210  */
211 #define SEM1 "/my_precious_sem"
212 #define SEM2 "/justsem"
213 ATF_TC_BODY(named, tc)
214 {
215 	sem_t *sem1, *sem2;
216 	void *rv;
217 
218 	rump_init();
219 	sem1 = sem_open(SEM1, 0);
220 	ATF_REQUIRE_EQ(errno, ENOENT);
221 	ATF_REQUIRE_EQ(sem1, NULL);
222 
223 	sem1 = sem_open(SEM1, O_CREAT, 0444, 1);
224 	if (sem1 == NULL)
225 		atf_tc_fail_errno("sem_open O_CREAT");
226 
227 	rv = sem_open(SEM1, O_CREAT | O_EXCL);
228 	ATF_REQUIRE_EQ(errno, EEXIST);
229 	ATF_REQUIRE_EQ(rv, NULL);
230 
231 	sem2 = sem_open(SEM2, O_CREAT, 0444, 0);
232 	if (sem2 == NULL)
233 		atf_tc_fail_errno("sem_open O_CREAT");
234 
235 	/* check that semaphores are independent */
236 	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
237 	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
238 	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
239 
240 	/* check that unlinked remains valid */
241 	sem_unlink(SEM2);
242 	ATF_REQUIRE_EQ(sem_post(sem2), 0);
243 	ATF_REQUIRE_EQ(sem_trywait(sem2), 0);
244 	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
245 	ATF_REQUIRE_EQ(errno, EAGAIN);
246 
247 #if 0 /* see unlink */
248 	/* close it and check that it's gone */
249 	if (sem_close(sem2) != 0)
250 		atf_tc_fail_errno("sem close");
251 	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
252 	ATF_REQUIRE_EQ(errno, EINVAL);
253 #endif
254 
255 	/* check that we still have sem1 */
256 	sem_post(sem1);
257 	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
258 	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
259 	ATF_REQUIRE_EQ(errno, EAGAIN);
260 }
261 
262 ATF_TC(unlink);
263 ATF_TC_HEAD(unlink, tc)
264 {
265 
266 	/* this is currently broken.  i'll append the PR number soon */
267 	atf_tc_set_md_var(tc, "descr", "tests unlinked semaphores can be "
268 	    "closed, PR kern/43452 (%s)", LIBNAME);
269 }
270 
271 #define SEM "/thesem"
272 ATF_TC_BODY(unlink, tc)
273 {
274 	sem_t *sem;
275 
276 	rump_init();
277 	sem = sem_open(SEM, O_CREAT, 0444, 0);
278 	ATF_REQUIRE(sem);
279 
280 	if (sem_unlink(SEM) == -1)
281 		atf_tc_fail_errno("unlink");
282 	if (sem_close(sem) == -1)
283 		atf_tc_fail_errno("close unlinked semaphore");
284 }
285 
286 /* use rump calls for libpthread _ksem_foo() calls */
287 #define F1(name, a) int _ksem_##name(a); \
288 int _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);}
289 #define F2(name, a, b) int _ksem_##name(a, b); \
290 int _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);}
291 F2(init, unsigned int, intptr_t *);
292 F1(close, intptr_t);
293 F1(destroy, intptr_t);
294 F1(post, intptr_t);
295 F1(unlink, const char *);
296 F1(trywait, intptr_t);
297 F1(wait, intptr_t);
298 F2(getvalue, intptr_t, unsigned int *);
299 int _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *);
300 int _ksem_open(const char *a, int b, mode_t c, unsigned int d, intptr_t *e)
301     {return rump_sys__ksem_open(a,b,c,d,e);}
302