1/* $NetBSD: sem.c,v 1.11 2017/01/13 21:30:42 christos 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 26ATF_TC(postwait); 27ATF_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 34ATF_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 53ATF_TC(initvalue); 54ATF_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 61ATF_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 75ATF_TC(destroy); 76ATF_TC_HEAD(destroy, tc) 77{ 78 79 atf_tc_set_md_var(tc, "descr", "tests sem_destroy works (%s)", LIBNAME); 80} 81 82ATF_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 100ATF_TC(busydestroy); 101ATF_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 108static void * 109hthread(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 122ATF_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 167ATF_TC(blockwait); 168ATF_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 176ATF_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 205ATF_TC(blocktimedwait); 206ATF_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 214ATF_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 230ATF_TC(named); 231ATF_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" 243ATF_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 292ATF_TC(unlink); 293ATF_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" 302ATF_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 if (sem_close(sem) == -1) 313 atf_tc_fail_errno("close unlinked semaphore"); 314} 315 316/* use rump calls for libpthread _ksem_foo() calls */ 317#define F1(name, a) int _ksem_##name(a); \ 318int _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);} 319#define F2(name, a, b) int _ksem_##name(a, b); \ 320int _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);} 321F2(init, unsigned int, intptr_t *); 322F1(close, intptr_t); 323F1(destroy, intptr_t); 324F1(post, intptr_t); 325F1(unlink, const char *); 326F1(trywait, intptr_t); 327F1(wait, intptr_t); 328F2(getvalue, intptr_t, unsigned int *); 329F2(timedwait, intptr_t, const struct timespec *); 330int _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *); 331int _ksem_open(const char *a, int b, mode_t c, unsigned int d, intptr_t *e) 332 {return rump_sys__ksem_open(a,b,c,d,e);} 333