Home | History | Annotate | Line # | Download | only in semaphore
sem.c revision 1.9
      1 /*	$NetBSD: sem.c,v 1.9 2012/03/08 21:59:32 joerg Exp $	*/
      2 
      3 /*
      4  * Common code for semaphore tests.  This can be included both into
      5  * programs using librt and libpthread.
      6  */
      7 
      8 #include <sys/types.h>
      9 
     10 #include <rump/rump.h>
     11 #include <rump/rump_syscalls.h>
     12 
     13 #include <atf-c.h>
     14 #include <errno.h>
     15 #include <fcntl.h>
     16 #include <pthread.h>
     17 #include <semaphore.h>
     18 #include <sched.h>
     19 #include <stdint.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <unistd.h>
     23 
     24 #include "../../h_macros.h"
     25 
     26 ATF_TC(postwait);
     27 ATF_TC_HEAD(postwait, tc)
     28 {
     29 
     30 	atf_tc_set_md_var(tc, "descr", "tests post and wait from a "
     31 	    "single thread (%s)", LIBNAME);
     32 }
     33 
     34 ATF_TC_BODY(postwait, tc)
     35 {
     36 	sem_t sem;
     37 	int rv;
     38 
     39 	rump_init();
     40 
     41 	ATF_REQUIRE_EQ(sem_init(&sem, 1, 0), 0);
     42 
     43 	sem_post(&sem);
     44 	sem_post(&sem);
     45 
     46 	sem_wait(&sem);
     47 	sem_wait(&sem);
     48 	rv = sem_trywait(&sem);
     49 	ATF_REQUIRE(errno == EAGAIN);
     50 	ATF_REQUIRE(rv == -1);
     51 }
     52 
     53 ATF_TC(initvalue);
     54 ATF_TC_HEAD(initvalue, tc)
     55 {
     56 
     57 	atf_tc_set_md_var(tc, "descr", "tests initialization with a non-zero "
     58 	    "value (%s)", LIBNAME);
     59 }
     60 
     61 ATF_TC_BODY(initvalue, tc)
     62 {
     63 	sem_t sem;
     64 
     65 	rump_init();
     66 	sem_init(&sem, 1, 4);
     67 
     68 	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
     69 	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
     70 	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
     71 	ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
     72 	ATF_REQUIRE_EQ(sem_trywait(&sem), -1);
     73 }
     74 
     75 ATF_TC(destroy);
     76 ATF_TC_HEAD(destroy, tc)
     77 {
     78 
     79 	atf_tc_set_md_var(tc, "descr", "tests sem_destroy works (%s)", LIBNAME);
     80 }
     81 
     82 ATF_TC_BODY(destroy, tc)
     83 {
     84 	sem_t sem;
     85 	int rv, i;
     86 
     87 	rump_init();
     88 	for (i = 0; i < 2; i++) {
     89 		sem_init(&sem, 1, 1);
     90 
     91 		ATF_REQUIRE_EQ(sem_trywait(&sem), 0);
     92 		ATF_REQUIRE_EQ(sem_trywait(&sem), -1);
     93 		ATF_REQUIRE_EQ(sem_destroy(&sem), 0);
     94 		rv = sem_trywait(&sem);
     95 		ATF_REQUIRE_EQ(errno, EINVAL);
     96 		ATF_REQUIRE_EQ(rv, -1);
     97 	}
     98 }
     99 
    100 ATF_TC(busydestroy);
    101 ATF_TC_HEAD(busydestroy, tc)
    102 {
    103 
    104 	atf_tc_set_md_var(tc, "descr", "tests sem_destroy report EBUSY for "
    105 	    "a busy semaphore (%s)", LIBNAME);
    106 }
    107 
    108 static void *
    109 hthread(void *arg)
    110 {
    111 	sem_t *semmarit = arg;
    112 
    113 	for (;;) {
    114 		sem_post(&semmarit[2]);
    115 		sem_wait(&semmarit[1]);
    116 		sem_wait(&semmarit[0]);
    117 	}
    118 
    119 	return NULL;
    120 }
    121 
    122 ATF_TC_BODY(busydestroy, tc)
    123 {
    124 	sem_t semmarit[3];
    125 	pthread_t pt;
    126 	int i;
    127 
    128 	/* use a unicpu rump kernel.  this means less chance for race */
    129 	setenv("RUMP_NCPU", "1", 1);
    130 
    131 	rump_init();
    132 	sem_init(&semmarit[0], 1, 0);
    133 	sem_init(&semmarit[1], 1, 0);
    134 	sem_init(&semmarit[2], 1, 0);
    135 
    136 	pthread_create(&pt, NULL, hthread, semmarit);
    137 
    138 	/*
    139 	 * Make a best-effort to catch the other thread with its pants down.
    140 	 * We can't do this for sure, can we?  Although, we could reach
    141 	 * inside the rump kernel and inquire about the thread's sleep
    142 	 * status.
    143 	 */
    144 	for (i = 0; i < 1000; i++) {
    145 		sem_wait(&semmarit[2]);
    146 		usleep(1);
    147 		if (sem_destroy(&semmarit[1]) == -1)
    148 			if (errno == EBUSY)
    149 				break;
    150 
    151 		/*
    152 		 * Didn't catch it?  ok, recreate and post to make the
    153 		 * other thread run
    154 		 */
    155 		sem_init(&semmarit[1], 1, 0);
    156 		sem_post(&semmarit[0]);
    157 		sem_post(&semmarit[1]);
    158 
    159 	}
    160 	if (i == 1000)
    161 		atf_tc_fail("sem destroy not reporting EBUSY");
    162 
    163 	pthread_cancel(pt);
    164 	pthread_join(pt, NULL);
    165 }
    166 
    167 ATF_TC(blockwait);
    168 ATF_TC_HEAD(blockwait, tc)
    169 {
    170 
    171 	atf_tc_set_md_var(tc, "descr", "tests sem_wait can handle blocking "
    172 	    "(%s)", LIBNAME);
    173 	atf_tc_set_md_var(tc, "timeout", "2");
    174 }
    175 
    176 ATF_TC_BODY(blockwait, tc)
    177 {
    178 	sem_t semmarit[3];
    179 	pthread_t pt;
    180 	int i;
    181 
    182 	rump_init();
    183 	sem_init(&semmarit[0], 1, 0);
    184 	sem_init(&semmarit[1], 1, 0);
    185 	sem_init(&semmarit[2], 1, 0);
    186 
    187 	pthread_create(&pt, NULL, hthread, semmarit);
    188 
    189 	/*
    190 	 * Make a best-effort.  Unless we're extremely unlucky, we should
    191 	 * at least one blocking wait.
    192 	 */
    193 	for (i = 0; i < 10; i++) {
    194 		sem_wait(&semmarit[2]);
    195 		usleep(1);
    196 		sem_post(&semmarit[0]);
    197 		sem_post(&semmarit[1]);
    198 
    199 	}
    200 
    201 	pthread_cancel(pt);
    202 	pthread_join(pt, NULL);
    203 }
    204 
    205 ATF_TC(blocktimedwait);
    206 ATF_TC_HEAD(blocktimedwait, tc)
    207 {
    208 
    209 	atf_tc_set_md_var(tc, "descr", "tests sem_timedwait can handle blocking"
    210 	    " (%s)", LIBNAME);
    211 	atf_tc_set_md_var(tc, "timeout", "2");
    212 }
    213 
    214 ATF_TC_BODY(blocktimedwait, tc)
    215 {
    216 	sem_t semid;
    217 	struct timespec tp;
    218 
    219 	rump_init();
    220 
    221 	clock_gettime(CLOCK_REALTIME, &tp);
    222 	tp.tv_nsec += 50000000;
    223 	tp.tv_sec += tp.tv_nsec / 1000000000;
    224 	tp.tv_nsec %= 1000000000;
    225 
    226 	ATF_REQUIRE_EQ(sem_init(&semid, 1, 0), 0);
    227 	ATF_REQUIRE_ERRNO(ETIMEDOUT, sem_timedwait(&semid, &tp) == -1);
    228 }
    229 
    230 ATF_TC(named);
    231 ATF_TC_HEAD(named, tc)
    232 {
    233 
    234 	atf_tc_set_md_var(tc, "descr", "tests named semaphores (%s)", LIBNAME);
    235 }
    236 
    237 /*
    238  * Wow, easy naming rules.  it's these times i'm really happy i can
    239  * single-step into the kernel.
    240  */
    241 #define SEM1 "/precious_sem"
    242 #define SEM2 "/justsem"
    243 ATF_TC_BODY(named, tc)
    244 {
    245 	sem_t *sem1, *sem2;
    246 	void *rv;
    247 
    248 	rump_init();
    249 	sem1 = sem_open(SEM1, 0);
    250 	ATF_REQUIRE_EQ(errno, ENOENT);
    251 	ATF_REQUIRE_EQ(sem1, NULL);
    252 
    253 	sem1 = sem_open(SEM1, O_CREAT, 0444, 1);
    254 	if (sem1 == NULL)
    255 		atf_tc_fail_errno("sem_open O_CREAT");
    256 
    257 	rv = sem_open(SEM1, O_CREAT | O_EXCL);
    258 	ATF_REQUIRE_EQ(errno, EEXIST);
    259 	ATF_REQUIRE_EQ(rv, NULL);
    260 
    261 	sem2 = sem_open(SEM2, O_CREAT, 0444, 0);
    262 	if (sem2 == NULL)
    263 		atf_tc_fail_errno("sem_open O_CREAT");
    264 
    265 	/* check that semaphores are independent */
    266 	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
    267 	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
    268 	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
    269 
    270 	/* check that unlinked remains valid */
    271 	sem_unlink(SEM2);
    272 	ATF_REQUIRE_EQ(sem_post(sem2), 0);
    273 	ATF_REQUIRE_EQ(sem_trywait(sem2), 0);
    274 	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
    275 	ATF_REQUIRE_EQ(errno, EAGAIN);
    276 
    277 #if 0 /* see unlink */
    278 	/* close it and check that it's gone */
    279 	if (sem_close(sem2) != 0)
    280 		atf_tc_fail_errno("sem close");
    281 	ATF_REQUIRE_EQ(sem_trywait(sem2), -1);
    282 	ATF_REQUIRE_EQ(errno, EINVAL);
    283 #endif
    284 
    285 	/* check that we still have sem1 */
    286 	sem_post(sem1);
    287 	ATF_REQUIRE_EQ(sem_trywait(sem1), 0);
    288 	ATF_REQUIRE_EQ(sem_trywait(sem1), -1);
    289 	ATF_REQUIRE_EQ(errno, EAGAIN);
    290 }
    291 
    292 ATF_TC(unlink);
    293 ATF_TC_HEAD(unlink, tc)
    294 {
    295 
    296 	/* this is currently broken.  i'll append the PR number soon */
    297 	atf_tc_set_md_var(tc, "descr", "tests unlinked semaphores can be "
    298 	    "closed (%s)", LIBNAME);
    299 }
    300 
    301 #define SEM "/thesem"
    302 ATF_TC_BODY(unlink, tc)
    303 {
    304 	sem_t *sem;
    305 
    306 	rump_init();
    307 	sem = sem_open(SEM, O_CREAT, 0444, 0);
    308 	ATF_REQUIRE(sem);
    309 
    310 	if (sem_unlink(SEM) == -1)
    311 		atf_tc_fail_errno("unlink");
    312 	atf_tc_expect_fail("PR kern/43452");
    313 	if (sem_close(sem) == -1)
    314 		atf_tc_fail_errno("close unlinked semaphore");
    315 }
    316 
    317 /* use rump calls for libpthread _ksem_foo() calls */
    318 #define F1(name, a) int _ksem_##name(a); \
    319 int _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);}
    320 #define F2(name, a, b) int _ksem_##name(a, b); \
    321 int _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);}
    322 F2(init, unsigned int, intptr_t *);
    323 F1(close, intptr_t);
    324 F1(destroy, intptr_t);
    325 F1(post, intptr_t);
    326 F1(unlink, const char *);
    327 F1(trywait, intptr_t);
    328 F1(wait, intptr_t);
    329 F2(getvalue, intptr_t, unsigned int *);
    330 F2(timedwait, intptr_t, const struct timespec *);
    331 int _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *);
    332 int _ksem_open(const char *a, int b, mode_t c, unsigned int d, intptr_t *e)
    333     {return rump_sys__ksem_open(a,b,c,d,e);}
    334