/* $NetBSD: sem.c,v 1.11 2017/01/13 21:30:42 christos Exp $ */ /* * Common code for semaphore tests. This can be included both into * programs using librt and libpthread. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "h_macros.h" ATF_TC(postwait); ATF_TC_HEAD(postwait, tc) { atf_tc_set_md_var(tc, "descr", "tests post and wait from a " "single thread (%s)", LIBNAME); } ATF_TC_BODY(postwait, tc) { sem_t sem; int rv; rump_init(); ATF_REQUIRE_EQ(sem_init(&sem, 1, 0), 0); sem_post(&sem); sem_post(&sem); sem_wait(&sem); sem_wait(&sem); rv = sem_trywait(&sem); ATF_REQUIRE(errno == EAGAIN); ATF_REQUIRE(rv == -1); } ATF_TC(initvalue); ATF_TC_HEAD(initvalue, tc) { atf_tc_set_md_var(tc, "descr", "tests initialization with a non-zero " "value (%s)", LIBNAME); } ATF_TC_BODY(initvalue, tc) { sem_t sem; rump_init(); sem_init(&sem, 1, 4); ATF_REQUIRE_EQ(sem_trywait(&sem), 0); ATF_REQUIRE_EQ(sem_trywait(&sem), 0); ATF_REQUIRE_EQ(sem_trywait(&sem), 0); ATF_REQUIRE_EQ(sem_trywait(&sem), 0); ATF_REQUIRE_EQ(sem_trywait(&sem), -1); } ATF_TC(destroy); ATF_TC_HEAD(destroy, tc) { atf_tc_set_md_var(tc, "descr", "tests sem_destroy works (%s)", LIBNAME); } ATF_TC_BODY(destroy, tc) { sem_t sem; int rv, i; rump_init(); for (i = 0; i < 2; i++) { sem_init(&sem, 1, 1); ATF_REQUIRE_EQ(sem_trywait(&sem), 0); ATF_REQUIRE_EQ(sem_trywait(&sem), -1); ATF_REQUIRE_EQ(sem_destroy(&sem), 0); rv = sem_trywait(&sem); ATF_REQUIRE_EQ(errno, EINVAL); ATF_REQUIRE_EQ(rv, -1); } } ATF_TC(busydestroy); ATF_TC_HEAD(busydestroy, tc) { atf_tc_set_md_var(tc, "descr", "tests sem_destroy report EBUSY for " "a busy semaphore (%s)", LIBNAME); } static void * hthread(void *arg) { sem_t *semmarit = arg; for (;;) { sem_post(&semmarit[2]); sem_wait(&semmarit[1]); sem_wait(&semmarit[0]); } return NULL; } ATF_TC_BODY(busydestroy, tc) { sem_t semmarit[3]; pthread_t pt; int i; /* use a unicpu rump kernel. this means less chance for race */ setenv("RUMP_NCPU", "1", 1); rump_init(); sem_init(&semmarit[0], 1, 0); sem_init(&semmarit[1], 1, 0); sem_init(&semmarit[2], 1, 0); pthread_create(&pt, NULL, hthread, semmarit); /* * Make a best-effort to catch the other thread with its pants down. * We can't do this for sure, can we? Although, we could reach * inside the rump kernel and inquire about the thread's sleep * status. */ for (i = 0; i < 1000; i++) { sem_wait(&semmarit[2]); usleep(1); if (sem_destroy(&semmarit[1]) == -1) if (errno == EBUSY) break; /* * Didn't catch it? ok, recreate and post to make the * other thread run */ sem_init(&semmarit[1], 1, 0); sem_post(&semmarit[0]); sem_post(&semmarit[1]); } if (i == 1000) atf_tc_fail("sem destroy not reporting EBUSY"); pthread_cancel(pt); pthread_join(pt, NULL); } ATF_TC(blockwait); ATF_TC_HEAD(blockwait, tc) { atf_tc_set_md_var(tc, "descr", "tests sem_wait can handle blocking " "(%s)", LIBNAME); atf_tc_set_md_var(tc, "timeout", "2"); } ATF_TC_BODY(blockwait, tc) { sem_t semmarit[3]; pthread_t pt; int i; rump_init(); sem_init(&semmarit[0], 1, 0); sem_init(&semmarit[1], 1, 0); sem_init(&semmarit[2], 1, 0); pthread_create(&pt, NULL, hthread, semmarit); /* * Make a best-effort. Unless we're extremely unlucky, we should * at least one blocking wait. */ for (i = 0; i < 10; i++) { sem_wait(&semmarit[2]); usleep(1); sem_post(&semmarit[0]); sem_post(&semmarit[1]); } pthread_cancel(pt); pthread_join(pt, NULL); } ATF_TC(blocktimedwait); ATF_TC_HEAD(blocktimedwait, tc) { atf_tc_set_md_var(tc, "descr", "tests sem_timedwait can handle blocking" " (%s)", LIBNAME); atf_tc_set_md_var(tc, "timeout", "2"); } ATF_TC_BODY(blocktimedwait, tc) { sem_t semid; struct timespec tp; rump_init(); clock_gettime(CLOCK_REALTIME, &tp); tp.tv_nsec += 50000000; tp.tv_sec += tp.tv_nsec / 1000000000; tp.tv_nsec %= 1000000000; ATF_REQUIRE_EQ(sem_init(&semid, 1, 0), 0); ATF_REQUIRE_ERRNO(ETIMEDOUT, sem_timedwait(&semid, &tp) == -1); } ATF_TC(named); ATF_TC_HEAD(named, tc) { atf_tc_set_md_var(tc, "descr", "tests named semaphores (%s)", LIBNAME); } /* * Wow, easy naming rules. it's these times i'm really happy i can * single-step into the kernel. */ #define SEM1 "/precious_sem" #define SEM2 "/justsem" ATF_TC_BODY(named, tc) { sem_t *sem1, *sem2; void *rv; rump_init(); sem1 = sem_open(SEM1, 0); ATF_REQUIRE_EQ(errno, ENOENT); ATF_REQUIRE_EQ(sem1, NULL); sem1 = sem_open(SEM1, O_CREAT, 0444, 1); if (sem1 == NULL) atf_tc_fail_errno("sem_open O_CREAT"); rv = sem_open(SEM1, O_CREAT | O_EXCL); ATF_REQUIRE_EQ(errno, EEXIST); ATF_REQUIRE_EQ(rv, NULL); sem2 = sem_open(SEM2, O_CREAT, 0444, 0); if (sem2 == NULL) atf_tc_fail_errno("sem_open O_CREAT"); /* check that semaphores are independent */ ATF_REQUIRE_EQ(sem_trywait(sem2), -1); ATF_REQUIRE_EQ(sem_trywait(sem1), 0); ATF_REQUIRE_EQ(sem_trywait(sem1), -1); /* check that unlinked remains valid */ sem_unlink(SEM2); ATF_REQUIRE_EQ(sem_post(sem2), 0); ATF_REQUIRE_EQ(sem_trywait(sem2), 0); ATF_REQUIRE_EQ(sem_trywait(sem2), -1); ATF_REQUIRE_EQ(errno, EAGAIN); #if 0 /* see unlink */ /* close it and check that it's gone */ if (sem_close(sem2) != 0) atf_tc_fail_errno("sem close"); ATF_REQUIRE_EQ(sem_trywait(sem2), -1); ATF_REQUIRE_EQ(errno, EINVAL); #endif /* check that we still have sem1 */ sem_post(sem1); ATF_REQUIRE_EQ(sem_trywait(sem1), 0); ATF_REQUIRE_EQ(sem_trywait(sem1), -1); ATF_REQUIRE_EQ(errno, EAGAIN); } ATF_TC(unlink); ATF_TC_HEAD(unlink, tc) { /* this is currently broken. i'll append the PR number soon */ atf_tc_set_md_var(tc, "descr", "tests unlinked semaphores can be " "closed (%s)", LIBNAME); } #define SEM "/thesem" ATF_TC_BODY(unlink, tc) { sem_t *sem; rump_init(); sem = sem_open(SEM, O_CREAT, 0444, 0); ATF_REQUIRE(sem); if (sem_unlink(SEM) == -1) atf_tc_fail_errno("unlink"); if (sem_close(sem) == -1) atf_tc_fail_errno("close unlinked semaphore"); } /* use rump calls for libpthread _ksem_foo() calls */ #define F1(name, a) int _ksem_##name(a); \ int _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);} #define F2(name, a, b) int _ksem_##name(a, b); \ int _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);} F2(init, unsigned int, intptr_t *); F1(close, intptr_t); F1(destroy, intptr_t); F1(post, intptr_t); F1(unlink, const char *); F1(trywait, intptr_t); F1(wait, intptr_t); F2(getvalue, intptr_t, unsigned int *); F2(timedwait, intptr_t, const struct timespec *); int _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *); int _ksem_open(const char *a, int b, mode_t c, unsigned int d, intptr_t *e) {return rump_sys__ksem_open(a,b,c,d,e);}