2 * Copyright (c) 2008 Yahoo!, Inc.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/cdefs.h>
33 #include <sys/queue.h>
34 #include <sys/sysctl.h>
36 #include <sys/types.h>
50 * Use a timer to post a specific semaphore after a timeout. A timer
51 * is scheduled via schedule_post(). check_alarm() must be called
52 * afterwards to clean up and check for errors.
54 sem_t
*alarm_id
= SEM_FAILED
;
56 int alarm_handler_installed
;
59 checkvalue(sem_t
*id
, int expected
)
63 if (sem_getvalue(id
, &val
) < 0) {
64 perror("sem_getvalue");
67 if (val
!= expected
) {
68 fprintf(stderr
, "sem value should be %d instead of %d",
76 construct_shared_unnamed_sem(unsigned int count
)
78 sem_t
*id
= mmap(NULL
, sizeof(sem_t
), PROT_READ
|PROT_WRITE
,
79 MAP_SHARED
|MAP_ANON
, -1, 0);
80 if (id
== MAP_FAILED
) {
85 if (sem_init(id
, 1, count
) < 0) {
87 munmap(id
, sizeof(sem_t
));
95 destruct_shared_unnamed_sem(sem_t
*id
)
97 if (sem_destroy(id
) < 0)
98 perror("sem_destroy");
100 if (munmap(id
, sizeof(sem_t
)) < 0)
105 testwait(sem_t
*id
, u_int
*delta
)
107 struct timespec start
, end
;
109 if (clock_gettime(CLOCK_REALTIME
, &start
) < 0) {
110 perror("clock_gettime(CLOCK_REALTIME)");
113 if (sem_wait(id
) < 0) {
117 if (clock_gettime(CLOCK_REALTIME
, &end
) < 0) {
118 perror("clock_gettime(CLOCK_REALTIME)");
121 timespecsub(&end
, &start
, &end
);
122 *delta
= end
.tv_nsec
/ 1000000;
123 *delta
+= end
.tv_sec
* 1000;
128 alarm_handler(int signo
)
131 if (sem_post(alarm_id
) < 0)
136 schedule_post(sem_t
*id
, u_int msec
)
140 if (!alarm_handler_installed
) {
141 if (signal(SIGALRM
, alarm_handler
) == SIG_ERR
) {
142 perror("signal(SIGALRM)");
145 alarm_handler_installed
= 1;
147 if (alarm_id
!= SEM_FAILED
) {
148 fprintf(stderr
, "sem_post() already scheduled");
152 bzero(&it
, sizeof(it
));
153 it
.it_value
.tv_sec
= msec
/ 1000;
154 it
.it_value
.tv_usec
= (msec
% 1000) * 1000;
155 if (setitimer(ITIMER_REAL
, &it
, NULL
) < 0) {
163 check_alarm(int just_clear
)
167 bzero(&it
, sizeof(it
));
169 setitimer(ITIMER_REAL
, &it
, NULL
);
171 alarm_id
= SEM_FAILED
;
174 if (setitimer(ITIMER_REAL
, &it
, NULL
) < 0) {
178 if (alarm_errno
!= 0 && !just_clear
) {
180 perror("sem_post() (via timeout)");
184 alarm_id
= SEM_FAILED
;
190 * Helper routine for tests that use a child process. This routine
191 * creates a pipe and forks a child process. The child process runs
192 * the 'func' routine which returns a status integer. The status
193 * integer gets written over the pipe to the parent and returned in
194 * '*stat'. If there is an error in pipe(), fork(), or wait() this
195 * returns -1 and fails the test.
198 child_worker(int (*func
)(void *arg
), void *arg
, int *stat
)
219 write(pfd
[1], &cstat
, sizeof(cstat
));
223 if (read(pfd
[0], stat
, sizeof(*stat
)) < 0) {
224 perror("read(pipe)");
229 if (waitpid(pid
, NULL
, 0) < 0) {
241 * Fork off a child process. The child will open the semaphore via
242 * the same name. The child will then block on the semaphore waiting
243 * for the parent to post it.
246 wait_twoproc_child(void *arg
)
250 id
= sem_open(TEST_PATH
, 0, 0, 0);
251 if (id
== SEM_FAILED
)
252 return (CSTAT(1, errno
));
253 if (sem_wait(id
) < 0)
254 return (CSTAT(2, errno
));
255 if (sem_close(id
) < 0)
256 return (CSTAT(3, errno
));
257 return (CSTAT(0, 0));
261 timedwait(sem_t
*id
, u_int msec
, u_int
*delta
, int error
)
263 struct timespec start
, end
;
265 if (clock_gettime(CLOCK_REALTIME
, &start
) < 0) {
266 perror("clock_gettime(CLOCK_REALTIME)");
269 end
.tv_sec
= msec
/ 1000;
270 end
.tv_nsec
= msec
% 1000 * 1000000;
271 timespecadd(&end
, &start
, &end
);
272 if (sem_timedwait(id
, &end
) < 0) {
273 if (errno
!= error
) {
274 perror("sem_timedwait");
277 } else if (error
!= 0) {
280 if (clock_gettime(CLOCK_REALTIME
, &end
) < 0) {
281 perror("clock_gettime(CLOCK_REALTIME)");
284 timespecsub(&end
, &start
, &end
);
285 *delta
= end
.tv_nsec
/ 1000000;
286 *delta
+= end
.tv_sec
* 1000;
291 * Attempt a sem_open() that should fail with an expected error of
295 sem_open_should_fail(const char *path
, int flags
, mode_t mode
,
296 unsigned int value
, int error
)
301 id
= sem_open(path
, flags
, mode
, value
);
302 if (id
!= SEM_FAILED
) {
306 if (errno
!= error
) {
307 fprintf(stderr
, "sem_open: %s\n", strerror(errno
));
314 * Attempt a sem_init() that should fail with an expected error of
318 sem_init_should_fail(unsigned int value
, int error
)
322 if (sem_init(&id
, 0, value
) >= 0) {
326 if (errno
!= error
) {
335 * Attempt a sem_unlink() that should fail with an expected error of
339 sem_unlink_should_fail(const char *path
, int error
)
342 if (sem_unlink(path
) >= 0) {
345 if (errno
!= error
) {
346 perror("sem_unlink");
353 * Attempt a sem_destroy() that should fail with an expected error of
357 sem_destroy_should_fail(sem_t
*id
, int error
)
359 if (sem_destroy(id
) >= 0) {
362 if (errno
!= error
) {
363 perror("sem_destroy");
370 * Attempt a sem_close() that should fail with an expected error of
374 sem_close_should_fail(sem_t
*id
, int error
)
377 if (sem_close(id
) >= 0) {
380 if (errno
!= error
) {