xref: /freebsd/tools/regression/posixsem/posixsem.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
1 /*-
2  * Copyright (c) 2008 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <sys/_semaphore.h>
37 #include <sys/sysctl.h>
38 #include <sys/time.h>
39 #include <sys/user.h>
40 #include <sys/wait.h>
41 
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <kvm.h>
45 #include <limits.h>
46 #include <semaphore.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <time.h>
52 #include <unistd.h>
53 
54 #include "test.h"
55 
56 /* Cut and pasted from kernel header, bah! */
57 
58 /* Operations on timespecs */
59 #define	timespecclear(tvp)	((tvp)->tv_sec = (tvp)->tv_nsec = 0)
60 #define	timespecisset(tvp)	((tvp)->tv_sec || (tvp)->tv_nsec)
61 #define	timespeccmp(tvp, uvp, cmp)					\
62 	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
63 	    ((tvp)->tv_nsec cmp (uvp)->tv_nsec) :			\
64 	    ((tvp)->tv_sec cmp (uvp)->tv_sec))
65 #define timespecadd(vvp, uvp)						\
66 	do {								\
67 		(vvp)->tv_sec += (uvp)->tv_sec;				\
68 		(vvp)->tv_nsec += (uvp)->tv_nsec;			\
69 		if ((vvp)->tv_nsec >= 1000000000) {			\
70 			(vvp)->tv_sec++;				\
71 			(vvp)->tv_nsec -= 1000000000;			\
72 		}							\
73 	} while (0)
74 #define timespecsub(vvp, uvp)						\
75 	do {								\
76 		(vvp)->tv_sec -= (uvp)->tv_sec;				\
77 		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
78 		if ((vvp)->tv_nsec < 0) {				\
79 			(vvp)->tv_sec--;				\
80 			(vvp)->tv_nsec += 1000000000;			\
81 		}							\
82 	} while (0)
83 
84 
85 #define	TEST_PATH	"/tmp/posixsem_regression_test"
86 
87 #define	ELAPSED(elapsed, limit)		(abs((elapsed) - (limit)) < 100)
88 
89 /* Macros for passing child status to parent over a pipe. */
90 #define	CSTAT(class, error)		((class) << 16 | (error))
91 #define	CSTAT_CLASS(stat)		((stat) >> 16)
92 #define	CSTAT_ERROR(stat)		((stat) & 0xffff)
93 
94 /*
95  * Helper routine for tests that use a child process.  This routine
96  * creates a pipe and forks a child process.  The child process runs
97  * the 'func' routine which returns a status integer.  The status
98  * integer gets written over the pipe to the parent and returned in
99  * '*stat'.  If there is an error in pipe(), fork(), or wait() this
100  * returns -1 and fails the test.
101  */
102 static int
103 child_worker(int (*func)(void *arg), void *arg, int *stat)
104 {
105 	pid_t pid;
106 	int pfd[2], cstat;
107 
108 	if (pipe(pfd) < 0) {
109 		fail_errno("pipe");
110 		return (-1);
111 	}
112 
113 	pid = fork();
114 	switch (pid) {
115 	case -1:
116 		/* Error. */
117 		fail_errno("fork");
118 		close(pfd[0]);
119 		close(pfd[1]);
120 		return (-1);
121 	case 0:
122 		/* Child. */
123 		cstat = func(arg);
124 		write(pfd[1], &cstat, sizeof(cstat));
125 		exit(0);
126 	}
127 
128 	if (read(pfd[0], stat, sizeof(*stat)) < 0) {
129 		fail_errno("read(pipe)");
130 		close(pfd[0]);
131 		close(pfd[1]);
132 		return (-1);
133 	}
134 	if (waitpid(pid, NULL, 0) < 0) {
135 		fail_errno("wait");
136 		close(pfd[0]);
137 		close(pfd[1]);
138 		return (-1);
139 	}
140 	close(pfd[0]);
141 	close(pfd[1]);
142 	return (0);
143 }
144 
145 /*
146  * Attempt a ksem_open() that should fail with an expected error of
147  * 'error'.
148  */
149 static void
150 ksem_open_should_fail(const char *path, int flags, mode_t mode, unsigned int
151     value, int error)
152 {
153 	semid_t id;
154 
155 	if (ksem_open(&id, path, flags, mode, value) >= 0) {
156 		fail_err("ksem_open() didn't fail");
157 		ksem_close(id);
158 		return;
159 	}
160 	if (errno != error) {
161 		fail_errno("ksem_open");
162 		return;
163 	}
164 	pass();
165 }
166 
167 /*
168  * Attempt a ksem_unlink() that should fail with an expected error of
169  * 'error'.
170  */
171 static void
172 ksem_unlink_should_fail(const char *path, int error)
173 {
174 
175 	if (ksem_unlink(path) >= 0) {
176 		fail_err("ksem_unlink() didn't fail");
177 		return;
178 	}
179 	if (errno != error) {
180 		fail_errno("ksem_unlink");
181 		return;
182 	}
183 	pass();
184 }
185 
186 /*
187  * Attempt a ksem_close() that should fail with an expected error of
188  * 'error'.
189  */
190 static void
191 ksem_close_should_fail(semid_t id, int error)
192 {
193 
194 	if (ksem_close(id) >= 0) {
195 		fail_err("ksem_close() didn't fail");
196 		return;
197 	}
198 	if (errno != error) {
199 		fail_errno("ksem_close");
200 		return;
201 	}
202 	pass();
203 }
204 
205 /*
206  * Attempt a ksem_init() that should fail with an expected error of
207  * 'error'.
208  */
209 static void
210 ksem_init_should_fail(unsigned int value, int error)
211 {
212 	semid_t id;
213 
214 	if (ksem_init(&id, value) >= 0) {
215 		fail_err("ksem_init() didn't fail");
216 		ksem_destroy(id);
217 		return;
218 	}
219 	if (errno != error) {
220 		fail_errno("ksem_init");
221 		return;
222 	}
223 	pass();
224 }
225 
226 /*
227  * Attempt a ksem_destroy() that should fail with an expected error of
228  * 'error'.
229  */
230 static void
231 ksem_destroy_should_fail(semid_t id, int error)
232 {
233 
234 	if (ksem_destroy(id) >= 0) {
235 		fail_err("ksem_destroy() didn't fail");
236 		return;
237 	}
238 	if (errno != error) {
239 		fail_errno("ksem_destroy");
240 		return;
241 	}
242 	pass();
243 }
244 
245 /*
246  * Attempt a ksem_post() that should fail with an expected error of
247  * 'error'.
248  */
249 static void
250 ksem_post_should_fail(semid_t id, int error)
251 {
252 
253 	if (ksem_post(id) >= 0) {
254 		fail_err("ksem_post() didn't fail");
255 		return;
256 	}
257 	if (errno != error) {
258 		fail_errno("ksem_post");
259 		return;
260 	}
261 	pass();
262 }
263 
264 static void
265 open_after_unlink(void)
266 {
267 	semid_t id;
268 
269 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
270 		fail_errno("ksem_open(1)");
271 		return;
272 	}
273 	ksem_close(id);
274 
275 	if (ksem_unlink(TEST_PATH) < 0) {
276 		fail_errno("ksem_unlink");
277 		return;
278 	}
279 
280 	ksem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
281 }
282 TEST(open_after_unlink, "open after unlink");
283 
284 static void
285 open_invalid_path(void)
286 {
287 
288 	ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
289 }
290 TEST(open_invalid_path, "open invalid path");
291 
292 static void
293 open_extra_flags(void)
294 {
295 
296 	ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
297 }
298 TEST(open_extra_flags, "open with extra flags");
299 
300 static void
301 open_bad_value(void)
302 {
303 
304 	(void)ksem_unlink(TEST_PATH);
305 
306 	ksem_open_should_fail(TEST_PATH, O_CREAT, 0777, UINT_MAX, EINVAL);
307 }
308 TEST(open_bad_value, "open with invalid initial value");
309 
310 static void
311 open_bad_path_pointer(void)
312 {
313 
314 	ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
315 }
316 TEST(open_bad_path_pointer, "open bad path pointer");
317 
318 static void
319 open_path_too_long(void)
320 {
321 	char *page;
322 
323 	page = malloc(MAXPATHLEN + 1);
324 	memset(page, 'a', MAXPATHLEN);
325 	page[MAXPATHLEN] = '\0';
326 	ksem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
327 	free(page);
328 }
329 TEST(open_path_too_long, "open pathname too long");
330 
331 static void
332 open_nonexisting_semaphore(void)
333 {
334 
335 	ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
336 }
337 TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
338 
339 static void
340 exclusive_create_existing_semaphore(void)
341 {
342 	semid_t id;
343 
344 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
345 		fail_errno("ksem_open(O_CREAT)");
346 		return;
347 	}
348 	ksem_close(id);
349 
350 	ksem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
351 
352 	ksem_unlink(TEST_PATH);
353 }
354 TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
355 
356 static void
357 init_bad_value(void)
358 {
359 
360 	ksem_init_should_fail(UINT_MAX, EINVAL);
361 }
362 TEST(init_bad_value, "init with invalid initial value");
363 
364 static void
365 unlink_bad_path_pointer(void)
366 {
367 
368 	ksem_unlink_should_fail((char *)1024, EFAULT);
369 }
370 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
371 
372 static void
373 unlink_path_too_long(void)
374 {
375 	char *page;
376 
377 	page = malloc(MAXPATHLEN + 1);
378 	memset(page, 'a', MAXPATHLEN);
379 	page[MAXPATHLEN] = '\0';
380 	ksem_unlink_should_fail(page, ENAMETOOLONG);
381 	free(page);
382 }
383 TEST(unlink_path_too_long, "unlink pathname too long");
384 
385 static void
386 destroy_named_semaphore(void)
387 {
388 	semid_t id;
389 
390 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
391 		fail_errno("ksem_open(O_CREAT)");
392 		return;
393 	}
394 
395 	ksem_destroy_should_fail(id, EINVAL);
396 
397 	ksem_close(id);
398 	ksem_unlink(TEST_PATH);
399 }
400 TEST(destroy_named_semaphore, "destroy named semaphore");
401 
402 static void
403 close_unnamed_semaphore(void)
404 {
405 	semid_t id;
406 
407 	if (ksem_init(&id, 1) < 0) {
408 		fail_errno("ksem_init");
409 		return;
410 	}
411 
412 	ksem_close_should_fail(id, EINVAL);
413 
414 	ksem_destroy(id);
415 }
416 TEST(close_unnamed_semaphore, "close unnamed semaphore");
417 
418 static void
419 destroy_invalid_fd(void)
420 {
421 
422 	ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
423 }
424 TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
425 
426 static void
427 close_invalid_fd(void)
428 {
429 
430 	ksem_close_should_fail(STDERR_FILENO, EINVAL);
431 }
432 TEST(close_invalid_fd, "close non-semaphore file descriptor");
433 
434 static void
435 create_unnamed_semaphore(void)
436 {
437 	semid_t id;
438 
439 	if (ksem_init(&id, 1) < 0) {
440 		fail_errno("ksem_init");
441 		return;
442 	}
443 
444 	if (ksem_destroy(id) < 0) {
445 		fail_errno("ksem_destroy");
446 		return;
447 	}
448 	pass();
449 }
450 TEST(create_unnamed_semaphore, "create unnamed semaphore");
451 
452 static void
453 open_named_semaphore(void)
454 {
455 	semid_t id;
456 
457 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
458 		fail_errno("ksem_open(O_CREAT)");
459 		return;
460 	}
461 
462 	if (ksem_close(id) < 0) {
463 		fail_errno("ksem_close");
464 		return;
465 	}
466 
467 	if (ksem_unlink(TEST_PATH) < 0) {
468 		fail_errno("ksem_unlink");
469 		return;
470 	}
471 	pass();
472 }
473 TEST(open_named_semaphore, "create named semaphore");
474 
475 static void
476 getvalue_invalid_semaphore(void)
477 {
478 	int val;
479 
480 	if (ksem_getvalue(STDERR_FILENO, &val) >= 0) {
481 		fail_err("ksem_getvalue() didn't fail");
482 		return;
483 	}
484 	if (errno != EINVAL) {
485 		fail_errno("ksem_getvalue");
486 		return;
487 	}
488 	pass();
489 }
490 TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
491 
492 static void
493 post_invalid_semaphore(void)
494 {
495 
496 	ksem_post_should_fail(STDERR_FILENO, EINVAL);
497 }
498 TEST(post_invalid_semaphore, "post of invalid semaphore");
499 
500 static void
501 wait_invalid_semaphore(void)
502 {
503 
504 	if (ksem_wait(STDERR_FILENO) >= 0) {
505 		fail_err("ksem_wait() didn't fail");
506 		return;
507 	}
508 	if (errno != EINVAL) {
509 		fail_errno("ksem_wait");
510 		return;
511 	}
512 	pass();
513 }
514 TEST(wait_invalid_semaphore, "wait for invalid semaphore");
515 
516 static void
517 trywait_invalid_semaphore(void)
518 {
519 
520 	if (ksem_trywait(STDERR_FILENO) >= 0) {
521 		fail_err("ksem_trywait() didn't fail");
522 		return;
523 	}
524 	if (errno != EINVAL) {
525 		fail_errno("ksem_trywait");
526 		return;
527 	}
528 	pass();
529 }
530 TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
531 
532 static void
533 timedwait_invalid_semaphore(void)
534 {
535 
536 	if (ksem_timedwait(STDERR_FILENO, NULL) >= 0) {
537 		fail_err("ksem_timedwait() didn't fail");
538 		return;
539 	}
540 	if (errno != EINVAL) {
541 		fail_errno("ksem_timedwait");
542 		return;
543 	}
544 	pass();
545 }
546 TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
547 
548 static int
549 checkvalue(semid_t id, int expected)
550 {
551 	int val;
552 
553 	if (ksem_getvalue(id, &val) < 0) {
554 		fail_errno("ksem_getvalue");
555 		return (-1);
556 	}
557 	if (val != expected) {
558 		fail_err("sem value should be %d instead of %d", expected, val);
559 		return (-1);
560 	}
561 	return (0);
562 }
563 
564 static void
565 post_test(void)
566 {
567 	semid_t id;
568 
569 	if (ksem_init(&id, 1) < 0) {
570 		fail_errno("ksem_init");
571 		return;
572 	}
573 	if (checkvalue(id, 1) < 0) {
574 		ksem_destroy(id);
575 		return;
576 	}
577 	if (ksem_post(id) < 0) {
578 		fail_errno("ksem_post");
579 		ksem_destroy(id);
580 		return;
581 	}
582 	if (checkvalue(id, 2) < 0) {
583 		ksem_destroy(id);
584 		return;
585 	}
586 	if (ksem_destroy(id) < 0) {
587 		fail_errno("ksem_destroy");
588 		return;
589 	}
590 	pass();
591 }
592 TEST(post_test, "simple post");
593 
594 static void
595 use_after_unlink_test(void)
596 {
597 	semid_t id;
598 
599 	/*
600 	 * Create named semaphore with value of 1 and then unlink it
601 	 * while still retaining the initial reference.
602 	 */
603 	if (ksem_open(&id, TEST_PATH, O_CREAT | O_EXCL, 0777, 1) < 0) {
604 		fail_errno("ksem_open(O_CREAT | O_EXCL)");
605 		return;
606 	}
607 	if (ksem_unlink(TEST_PATH) < 0) {
608 		fail_errno("ksem_unlink");
609 		ksem_close(id);
610 		return;
611 	}
612 	if (checkvalue(id, 1) < 0) {
613 		ksem_close(id);
614 		return;
615 	}
616 
617 	/* Post the semaphore to set its value to 2. */
618 	if (ksem_post(id) < 0) {
619 		fail_errno("ksem_post");
620 		ksem_close(id);
621 		return;
622 	}
623 	if (checkvalue(id, 2) < 0) {
624 		ksem_close(id);
625 		return;
626 	}
627 
628 	/* Wait on the semaphore which should set its value to 1. */
629 	if (ksem_wait(id) < 0) {
630 		fail_errno("ksem_wait");
631 		ksem_close(id);
632 		return;
633 	}
634 	if (checkvalue(id, 1) < 0) {
635 		ksem_close(id);
636 		return;
637 	}
638 
639 	if (ksem_close(id) < 0) {
640 		fail_errno("ksem_close");
641 		return;
642 	}
643 	pass();
644 }
645 TEST(use_after_unlink_test, "use named semaphore after unlink");
646 
647 static void
648 unlocked_trywait(void)
649 {
650 	semid_t id;
651 
652 	if (ksem_init(&id, 1) < 0) {
653 		fail_errno("ksem_init");
654 		return;
655 	}
656 
657 	/* This should succeed and decrement the value to 0. */
658 	if (ksem_trywait(id) < 0) {
659 		fail_errno("ksem_trywait()");
660 		ksem_destroy(id);
661 		return;
662 	}
663 	if (checkvalue(id, 0) < 0) {
664 		ksem_destroy(id);
665 		return;
666 	}
667 
668 	if (ksem_destroy(id) < 0) {
669 		fail_errno("ksem_destroy");
670 		return;
671 	}
672 	pass();
673 }
674 TEST(unlocked_trywait, "unlocked trywait");
675 
676 static void
677 locked_trywait(void)
678 {
679 	semid_t id;
680 
681 	if (ksem_init(&id, 0) < 0) {
682 		fail_errno("ksem_init");
683 		return;
684 	}
685 
686 	/* This should fail with EAGAIN and leave the value at 0. */
687 	if (ksem_trywait(id) >= 0) {
688 		fail_err("ksem_trywait() didn't fail");
689 		ksem_destroy(id);
690 		return;
691 	}
692 	if (errno != EAGAIN) {
693 		fail_errno("wrong error from ksem_trywait()");
694 		ksem_destroy(id);
695 		return;
696 	}
697 	if (checkvalue(id, 0) < 0) {
698 		ksem_destroy(id);
699 		return;
700 	}
701 
702 	if (ksem_destroy(id) < 0) {
703 		fail_errno("ksem_destroy");
704 		return;
705 	}
706 	pass();
707 }
708 TEST(locked_trywait, "locked trywait");
709 
710 /*
711  * Use a timer to post a specific semaphore after a timeout.  A timer
712  * is scheduled via schedule_post().  check_alarm() must be called
713  * afterwards to clean up and check for errors.
714  */
715 static semid_t alarm_id = -1;
716 static int alarm_errno;
717 static int alarm_handler_installed;
718 
719 static void
720 alarm_handler(int signo)
721 {
722 
723 	if (ksem_post(alarm_id) < 0)
724 		alarm_errno = errno;
725 }
726 
727 static int
728 check_alarm(int just_clear)
729 {
730 	struct itimerval it;
731 
732 	bzero(&it, sizeof(it));
733 	if (just_clear) {
734 		setitimer(ITIMER_REAL, &it, NULL);
735 		alarm_errno = 0;
736 		alarm_id = -1;
737 		return (0);
738 	}
739 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
740 		fail_errno("setitimer");
741 		return (-1);
742 	}
743 	if (alarm_errno != 0 && !just_clear) {
744 		errno = alarm_errno;
745 		fail_errno("ksem_post() (via timeout)");
746 		alarm_errno = 0;
747 		return (-1);
748 	}
749 	alarm_id = -1;
750 
751 	return (0);
752 }
753 
754 static int
755 schedule_post(semid_t id, u_int msec)
756 {
757 	struct itimerval it;
758 
759 	if (!alarm_handler_installed) {
760 		if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
761 			fail_errno("signal(SIGALRM)");
762 			return (-1);
763 		}
764 		alarm_handler_installed = 1;
765 	}
766 	if (alarm_id != -1) {
767 		fail_err("ksem_post() already scheduled");
768 		return (-1);
769 	}
770 	alarm_id = id;
771 	bzero(&it, sizeof(it));
772 	it.it_value.tv_sec = msec / 1000;
773 	it.it_value.tv_usec = (msec % 1000) * 1000;
774 	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
775 		fail_errno("setitimer");
776 		return (-1);
777 	}
778 	return (0);
779 }
780 
781 static int
782 timedwait(semid_t id, u_int msec, u_int *delta, int error)
783 {
784 	struct timespec start, end;
785 
786 	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
787 		fail_errno("clock_gettime(CLOCK_REALTIME)");
788 		return (-1);
789 	}
790 	end.tv_sec = msec / 1000;
791 	end.tv_nsec = msec % 1000 * 1000000;
792 	timespecadd(&end, &start);
793 	if (ksem_timedwait(id, &end) < 0) {
794 		if (errno != error) {
795 			fail_errno("ksem_timedwait");
796 			return (-1);
797 		}
798 	} else if (error != 0) {
799 		fail_err("ksem_timedwait() didn't fail");
800 		return (-1);
801 	}
802 	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
803 		fail_errno("clock_gettime(CLOCK_REALTIME)");
804 		return (-1);
805 	}
806 	timespecsub(&end, &start);
807 	*delta = end.tv_nsec / 1000000;
808 	*delta += end.tv_sec * 1000;
809 	return (0);
810 }
811 
812 static void
813 unlocked_timedwait(void)
814 {
815 	semid_t id;
816 	u_int elapsed;
817 
818 	if (ksem_init(&id, 1) < 0) {
819 		fail_errno("ksem_init");
820 		return;
821 	}
822 
823 	/* This should succeed right away and set the value to 0. */
824 	if (timedwait(id, 5000, &elapsed, 0) < 0) {
825 		ksem_destroy(id);
826 		return;
827 	}
828 	if (!ELAPSED(elapsed, 0)) {
829 		fail_err("ksem_timedwait() of unlocked sem took %ums", elapsed);
830 		ksem_destroy(id);
831 		return;
832 	}
833 	if (checkvalue(id, 0) < 0) {
834 		ksem_destroy(id);
835 		return;
836 	}
837 
838 	if (ksem_destroy(id) < 0) {
839 		fail_errno("ksem_destroy");
840 		return;
841 	}
842 	pass();
843 }
844 TEST(unlocked_timedwait, "unlocked timedwait");
845 
846 static void
847 expired_timedwait(void)
848 {
849 	semid_t id;
850 	u_int elapsed;
851 
852 	if (ksem_init(&id, 0) < 0) {
853 		fail_errno("ksem_init");
854 		return;
855 	}
856 
857 	/* This should fail with a timeout and leave the value at 0. */
858 	if (timedwait(id, 2500, &elapsed, ETIMEDOUT) < 0) {
859 		ksem_destroy(id);
860 		return;
861 	}
862 	if (!ELAPSED(elapsed, 2500)) {
863 		fail_err(
864 	    "ksem_timedwait() of locked sem took %ums instead of 2500ms",
865 		    elapsed);
866 		ksem_destroy(id);
867 		return;
868 	}
869 	if (checkvalue(id, 0) < 0) {
870 		ksem_destroy(id);
871 		return;
872 	}
873 
874 	if (ksem_destroy(id) < 0) {
875 		fail_errno("ksem_destroy");
876 		return;
877 	}
878 	pass();
879 }
880 TEST(expired_timedwait, "locked timedwait timeout");
881 
882 static void
883 locked_timedwait(void)
884 {
885 	semid_t id;
886 	u_int elapsed;
887 
888 	if (ksem_init(&id, 0) < 0) {
889 		fail_errno("ksem_init");
890 		return;
891 	}
892 
893 	/*
894 	 * Schedule a post to trigger after 1000 ms.  The subsequent
895 	 * timedwait should succeed after 1000 ms as a result w/o
896 	 * timing out.
897 	 */
898 	if (schedule_post(id, 1000) < 0) {
899 		ksem_destroy(id);
900 		return;
901 	}
902 	if (timedwait(id, 2000, &elapsed, 0) < 0) {
903 		check_alarm(1);
904 		ksem_destroy(id);
905 		return;
906 	}
907 	if (!ELAPSED(elapsed, 1000)) {
908 		fail_err(
909 	    "ksem_timedwait() with delayed post took %ums instead of 1000ms",
910 		    elapsed);
911 		check_alarm(1);
912 		ksem_destroy(id);
913 		return;
914 	}
915 	if (check_alarm(0) < 0) {
916 		ksem_destroy(id);
917 		return;
918 	}
919 
920 	if (ksem_destroy(id) < 0) {
921 		fail_errno("ksem_destroy");
922 		return;
923 	}
924 	pass();
925 }
926 TEST(locked_timedwait, "locked timedwait");
927 
928 static int
929 testwait(semid_t id, u_int *delta)
930 {
931 	struct timespec start, end;
932 
933 	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
934 		fail_errno("clock_gettime(CLOCK_REALTIME)");
935 		return (-1);
936 	}
937 	if (ksem_wait(id) < 0) {
938 		fail_errno("ksem_wait");
939 		return (-1);
940 	}
941 	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
942 		fail_errno("clock_gettime(CLOCK_REALTIME)");
943 		return (-1);
944 	}
945 	timespecsub(&end, &start);
946 	*delta = end.tv_nsec / 1000000;
947 	*delta += end.tv_sec * 1000;
948 	return (0);
949 }
950 
951 static void
952 unlocked_wait(void)
953 {
954 	semid_t id;
955 	u_int elapsed;
956 
957 	if (ksem_init(&id, 1) < 0) {
958 		fail_errno("ksem_init");
959 		return;
960 	}
961 
962 	/* This should succeed right away and set the value to 0. */
963 	if (testwait(id, &elapsed) < 0) {
964 		ksem_destroy(id);
965 		return;
966 	}
967 	if (!ELAPSED(elapsed, 0)) {
968 		fail_err("ksem_wait() of unlocked sem took %ums", elapsed);
969 		ksem_destroy(id);
970 		return;
971 	}
972 	if (checkvalue(id, 0) < 0) {
973 		ksem_destroy(id);
974 		return;
975 	}
976 
977 	if (ksem_destroy(id) < 0) {
978 		fail_errno("ksem_destroy");
979 		return;
980 	}
981 	pass();
982 }
983 TEST(unlocked_wait, "unlocked wait");
984 
985 static void
986 locked_wait(void)
987 {
988 	semid_t id;
989 	u_int elapsed;
990 
991 	if (ksem_init(&id, 0) < 0) {
992 		fail_errno("ksem_init");
993 		return;
994 	}
995 
996 	/*
997 	 * Schedule a post to trigger after 1000 ms.  The subsequent
998 	 * wait should succeed after 1000 ms as a result.
999 	 */
1000 	if (schedule_post(id, 1000) < 0) {
1001 		ksem_destroy(id);
1002 		return;
1003 	}
1004 	if (testwait(id, &elapsed) < 0) {
1005 		check_alarm(1);
1006 		ksem_destroy(id);
1007 		return;
1008 	}
1009 	if (!ELAPSED(elapsed, 1000)) {
1010 		fail_err(
1011 	    "ksem_wait() with delayed post took %ums instead of 1000ms",
1012 		    elapsed);
1013 		check_alarm(1);
1014 		ksem_destroy(id);
1015 		return;
1016 	}
1017 	if (check_alarm(0) < 0) {
1018 		ksem_destroy(id);
1019 		return;
1020 	}
1021 
1022 	if (ksem_destroy(id) < 0) {
1023 		fail_errno("ksem_destroy");
1024 		return;
1025 	}
1026 	pass();
1027 }
1028 TEST(locked_wait, "locked wait");
1029 
1030 /*
1031  * Fork off a child process.  The child will open the semaphore via
1032  * the same name.  The child will then block on the semaphore waiting
1033  * for the parent to post it.
1034  */
1035 static int
1036 wait_twoproc_child(void *arg)
1037 {
1038 	semid_t id;
1039 
1040 	if (ksem_open(&id, TEST_PATH, 0, 0, 0) < 0)
1041 		return (CSTAT(1, errno));
1042 	if (ksem_wait(id) < 0)
1043 		return (CSTAT(2, errno));
1044 	if (ksem_close(id) < 0)
1045 		return (CSTAT(3, errno));
1046 	return (CSTAT(0, 0));
1047 }
1048 
1049 static void
1050 wait_twoproc_test(void)
1051 {
1052 	semid_t id;
1053 	int stat;
1054 
1055 	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 0)) {
1056 		fail_errno("ksem_open");
1057 		return;
1058 	}
1059 
1060 	if (schedule_post(id, 500) < 0) {
1061 		ksem_close(id);
1062 		ksem_unlink(TEST_PATH);
1063 		return;
1064 	}
1065 
1066 	if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
1067 		check_alarm(1);
1068 		ksem_close(id);
1069 		ksem_unlink(TEST_PATH);
1070 		return;
1071 	}
1072 
1073 	errno = CSTAT_ERROR(stat);
1074 	switch (CSTAT_CLASS(stat)) {
1075 	case 0:
1076 		pass();
1077 		break;
1078 	case 1:
1079 		fail_errno("child ksem_open()");
1080 		break;
1081 	case 2:
1082 		fail_errno("child ksem_wait()");
1083 		break;
1084 	case 3:
1085 		fail_errno("child ksem_close()");
1086 		break;
1087 	default:
1088 		fail_err("bad child state %#x", stat);
1089 		break;
1090 	}
1091 
1092 	check_alarm(1);
1093 	ksem_close(id);
1094 	ksem_unlink(TEST_PATH);
1095 }
1096 TEST(wait_twoproc_test, "two proc wait");
1097 
1098 static void
1099 maxvalue_test(void)
1100 {
1101 	semid_t id;
1102 	int val;
1103 
1104 	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1105 		fail_errno("ksem_init");
1106 		return;
1107 	}
1108 	if (ksem_getvalue(id, &val) < 0) {
1109 		fail_errno("ksem_getvalue");
1110 		ksem_destroy(id);
1111 		return;
1112 	}
1113 	if (val != SEM_VALUE_MAX) {
1114 		fail_err("value %d != SEM_VALUE_MAX");
1115 		ksem_destroy(id);
1116 		return;
1117 	}
1118 	if (val < 0) {
1119 		fail_err("value < 0");
1120 		ksem_destroy(id);
1121 		return;
1122 	}
1123 	if (ksem_destroy(id) < 0) {
1124 		fail_errno("ksem_destroy");
1125 		return;
1126 	}
1127 	pass();
1128 }
1129 TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1130 
1131 static void
1132 maxvalue_post_test(void)
1133 {
1134 	semid_t id;
1135 
1136 	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1137 		fail_errno("ksem_init");
1138 		return;
1139 	}
1140 
1141 	ksem_post_should_fail(id, EOVERFLOW);
1142 
1143 	ksem_destroy(id);
1144 }
1145 TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1146 
1147 static void
1148 busy_destroy_test(void)
1149 {
1150 	char errbuf[_POSIX2_LINE_MAX];
1151 	struct kinfo_proc *kp;
1152 	semid_t id;
1153 	pid_t pid;
1154 	kvm_t *kd;
1155 	int count;
1156 
1157 	kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
1158 	if (kd == NULL) {
1159 		fail_err("kvm_openfiles: %s", errbuf);
1160 		return;
1161 	}
1162 
1163 	if (ksem_init(&id, 0) < 0) {
1164 		fail_errno("ksem_init");
1165 		kvm_close(kd);
1166 		return;
1167 	}
1168 
1169 	pid = fork();
1170 	switch (pid) {
1171 	case -1:
1172 		/* Error. */
1173 		fail_errno("fork");
1174 		ksem_destroy(id);
1175 		kvm_close(kd);
1176 		return;
1177 	case 0:
1178 		/* Child. */
1179 		ksem_wait(id);
1180 		exit(0);
1181 	}
1182 
1183 	/*
1184 	 * Wait for the child process to block on the semaphore.  This
1185 	 * is a bit gross.
1186 	 */
1187 	for (;;) {
1188 		kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
1189 		if (kp == NULL) {
1190 			fail_err("kvm_getprocs: %s", kvm_geterr(kd));
1191 			kvm_close(kd);
1192 			ksem_destroy(id);
1193 			return;
1194 		}
1195 		if (kp->ki_stat == SSLEEP &&
1196 		    (strcmp(kp->ki_wmesg, "sem") == 0 ||
1197 		    strcmp(kp->ki_wmesg, "ksem") == 0))
1198 			break;
1199 		usleep(1000);
1200 	}
1201 	kvm_close(kd);
1202 
1203 	ksem_destroy_should_fail(id, EBUSY);
1204 
1205 	/* Cleanup. */
1206 	ksem_post(id);
1207 	waitpid(pid, NULL, 0);
1208 	ksem_destroy(id);
1209 }
1210 TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1211 
1212 static int
1213 exhaust_unnamed_child(void *arg)
1214 {
1215 	semid_t id;
1216 	int i, max;
1217 
1218 	max = (intptr_t)arg;
1219 	for (i = 0; i < max + 1; i++) {
1220 		if (ksem_init(&id, 1) < 0) {
1221 			if (errno == ENOSPC)
1222 				return (CSTAT(0, 0));
1223 			return (CSTAT(1, errno));
1224 		}
1225 	}
1226 	return (CSTAT(2, 0));
1227 }
1228 
1229 static void
1230 exhaust_unnamed_sems(void)
1231 {
1232 	size_t len;
1233 	int nsems_max, stat;
1234 
1235 	len = sizeof(nsems_max);
1236 	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1237 	    0) {
1238 		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1239 		return;
1240 	}
1241 
1242 	if (child_worker(exhaust_unnamed_child, (void *)(uintptr_t)nsems_max,
1243 	    &stat))
1244 		return;
1245 	errno = CSTAT_ERROR(stat);
1246 	switch (CSTAT_CLASS(stat)) {
1247 	case 0:
1248 		pass();
1249 		break;
1250 	case 1:
1251 		fail_errno("ksem_init");
1252 		break;
1253 	case 2:
1254 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1255 		break;
1256 	default:
1257 		fail_err("bad child state %#x", stat);
1258 		break;
1259 	}
1260 }
1261 TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1262 
1263 static int
1264 exhaust_named_child(void *arg)
1265 {
1266 	char buffer[64];
1267 	semid_t id;
1268 	int i, max;
1269 
1270 	max = (intptr_t)arg;
1271 	for (i = 0; i < max + 1; i++) {
1272 		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1273 		if (ksem_open(&id, buffer, O_CREAT, 0777, 1) < 0) {
1274 			if (errno == ENOSPC || errno == EMFILE ||
1275 			    errno == ENFILE)
1276 				return (CSTAT(0, 0));
1277 			return (CSTAT(1, errno));
1278 		}
1279 	}
1280 	return (CSTAT(2, errno));
1281 }
1282 
1283 static void
1284 exhaust_named_sems(void)
1285 {
1286 	char buffer[64];
1287 	size_t len;
1288 	int i, nsems_max, stat;
1289 
1290 	len = sizeof(nsems_max);
1291 	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1292 	    0) {
1293 		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1294 		return;
1295 	}
1296 
1297 	if (child_worker(exhaust_named_child, (void *)(uintptr_t)nsems_max,
1298 	    &stat) < 0)
1299 		return;
1300 	errno = CSTAT_ERROR(stat);
1301 	switch (CSTAT_CLASS(stat)) {
1302 	case 0:
1303 		pass();
1304 		break;
1305 	case 1:
1306 		fail_errno("ksem_open");
1307 		break;
1308 	case 2:
1309 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1310 		break;
1311 	default:
1312 		fail_err("bad child state %#x", stat);
1313 		break;
1314 	}
1315 
1316 	/* Cleanup any semaphores created by the child. */
1317 	for (i = 0; i < nsems_max + 1; i++) {
1318 		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1319 		ksem_unlink(buffer);
1320 	}
1321 }
1322 TEST(exhaust_named_sems, "exhaust named semaphores (1)");
1323 
1324 static int
1325 fdlimit_set(void *arg)
1326 {
1327 	struct rlimit rlim;
1328 	int max;
1329 
1330 	max = (intptr_t)arg;
1331 	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
1332 		return (CSTAT(3, errno));
1333 	rlim.rlim_cur = max;
1334 	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
1335 		return (CSTAT(4, errno));
1336 	return (0);
1337 }
1338 
1339 static int
1340 fdlimit_unnamed_child(void *arg)
1341 {
1342 	int stat;
1343 
1344 	stat = fdlimit_set(arg);
1345 	if (stat == 0)
1346 		stat = exhaust_unnamed_child(arg);
1347 	return (stat);
1348 }
1349 
1350 static void
1351 fdlimit_unnamed_sems(void)
1352 {
1353 	int nsems_max, stat;
1354 
1355 	nsems_max = 10;
1356 	if (child_worker(fdlimit_unnamed_child, (void *)(uintptr_t)nsems_max,
1357 	    &stat))
1358 		return;
1359 	errno = CSTAT_ERROR(stat);
1360 	switch (CSTAT_CLASS(stat)) {
1361 	case 0:
1362 		pass();
1363 		break;
1364 	case 1:
1365 		fail_errno("ksem_init");
1366 		break;
1367 	case 2:
1368 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1369 		break;
1370 	case 3:
1371 		fail_errno("getrlimit");
1372 		break;
1373 	case 4:
1374 		fail_errno("getrlimit");
1375 		break;
1376 	default:
1377 		fail_err("bad child state %#x", stat);
1378 		break;
1379 	}
1380 }
1381 TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1382 
1383 static int
1384 fdlimit_named_child(void *arg)
1385 {
1386 	int stat;
1387 
1388 	stat = fdlimit_set(arg);
1389 	if (stat == 0)
1390 		stat = exhaust_named_child(arg);
1391 	return (stat);
1392 }
1393 
1394 static void
1395 fdlimit_named_sems(void)
1396 {
1397 	char buffer[64];
1398 	int i, nsems_max, stat;
1399 
1400 	nsems_max = 10;
1401 	if (child_worker(fdlimit_named_child, (void *)(uintptr_t)nsems_max,
1402 	    &stat) < 0)
1403 		return;
1404 	errno = CSTAT_ERROR(stat);
1405 	switch (CSTAT_CLASS(stat)) {
1406 	case 0:
1407 		pass();
1408 		break;
1409 	case 1:
1410 		fail_errno("ksem_open");
1411 		break;
1412 	case 2:
1413 		fail_err("Limit of %d semaphores not enforced", nsems_max);
1414 		break;
1415 	case 3:
1416 		fail_errno("getrlimit");
1417 		break;
1418 	case 4:
1419 		fail_errno("getrlimit");
1420 		break;
1421 	default:
1422 		fail_err("bad child state %#x", stat);
1423 		break;
1424 	}
1425 
1426 	/* Cleanup any semaphores created by the child. */
1427 	for (i = 0; i < nsems_max + 1; i++) {
1428 		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1429 		ksem_unlink(buffer);
1430 	}
1431 }
1432 TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1433 
1434 int
1435 main(int argc, char *argv[])
1436 {
1437 
1438 	signal(SIGSYS, SIG_IGN);
1439 	run_tests();
1440 	return (0);
1441 }
1442