]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/regression/posixsem/posixsem.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / tools / regression / posixsem / posixsem.c
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 }