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 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 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); \ 318 int _ksem_##name(a v1) {return rump_sys__ksem_##name(v1);} 319 #define F2(name, a, b) int _ksem_##name(a, b); \ 320 int _ksem_##name(a v1, b v2) {return rump_sys__ksem_##name(v1, v2);} 321 F2(init, unsigned int, intptr_t *); 322 F1(close, intptr_t); 323 F1(destroy, intptr_t); 324 F1(post, intptr_t); 325 F1(unlink, const char *); 326 F1(trywait, intptr_t); 327 F1(wait, intptr_t); 328 F2(getvalue, intptr_t, unsigned int *); 329 F2(timedwait, intptr_t, const struct timespec *); 330 int _ksem_open(const char *, int, mode_t, unsigned int, intptr_t *); 331 int _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