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