summaryrefslogtreecommitdiffstats
path: root/contrib/netbsd-tests/lib/semaphore/sem.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/netbsd-tests/lib/semaphore/sem.c')
-rw-r--r--contrib/netbsd-tests/lib/semaphore/sem.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/contrib/netbsd-tests/lib/semaphore/sem.c b/contrib/netbsd-tests/lib/semaphore/sem.c
new file mode 100644
index 0000000..5967b88
--- /dev/null
+++ b/contrib/netbsd-tests/lib/semaphore/sem.c
@@ -0,0 +1,332 @@
+/* $NetBSD: sem.c,v 1.10 2012/03/09 14:25:34 joerg Exp $ */
+
+/*
+ * Common code for semaphore tests. This can be included both into
+ * programs using librt and libpthread.
+ */
+
+#include <sys/types.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sched.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#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);}
OpenPOWER on IntegriCloud