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