1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28#include <pthread.h> 29#include <errno.h> 30#include <string.h> 31#include <stdarg.h> 32#include <stdio.h> 33#include <stdlib.h> 34 35/* Posix states that EDEADLK should be returned in case a deadlock condition 36 * is detected with a PTHREAD_MUTEX_ERRORCHECK lock() or trylock(), but 37 * GLibc returns EBUSY instead. 38 */ 39#ifdef HOST 40# define ERRNO_PTHREAD_EDEADLK EBUSY 41#else 42# define ERRNO_PTHREAD_EDEADLK EDEADLK 43#endif 44 45static void __attribute__((noreturn)) 46panic(const char* func, const char* format, ...) 47{ 48 va_list args; 49 fprintf(stderr, "%s: ", func); 50 va_start(args, format); 51 vfprintf(stderr, format, args); 52 va_end(args); 53 fprintf(stderr, "\n"); 54 exit(1); 55} 56 57#define PANIC(...) panic(__FUNCTION__,__VA_ARGS__) 58 59static void __attribute__((noreturn)) 60error(int errcode, const char* func, const char* format, ...) 61{ 62 va_list args; 63 fprintf(stderr, "%s: ", func); 64 va_start(args, format); 65 vfprintf(stderr, format, args); 66 va_end(args); 67 fprintf(stderr, " error=%d: %s\n", errcode, strerror(errcode)); 68 exit(1); 69} 70 71/* return current time in seconds as floating point value */ 72static double 73time_now(void) 74{ 75 struct timespec ts[1]; 76 77 clock_gettime(CLOCK_MONOTONIC, ts); 78 return (double)ts->tv_sec + ts->tv_nsec/1e9; 79} 80 81static void 82time_sleep(double delay) 83{ 84 struct timespec ts; 85 int ret; 86 87 ts.tv_sec = (time_t)delay; 88 ts.tv_nsec = (long)((delay - ts.tv_sec)*1e9); 89 90 do { 91 ret = nanosleep(&ts, &ts); 92 } while (ret < 0 && errno == EINTR); 93} 94 95#define ERROR(errcode,...) error((errcode),__FUNCTION__,__VA_ARGS__) 96 97#define TZERO(cond) \ 98 { int _ret = (cond); if (_ret != 0) ERROR(_ret,"%d:%s", __LINE__, #cond); } 99 100#define TTRUE(cond) \ 101 { if (!(cond)) PANIC("%d:%s", __LINE__, #cond); } 102 103#define TFALSE(cond) \ 104 { if (!!(cond)) PANIC("%d:%s", __LINE__, #cond); } 105 106#define TEXPECT_INT(cond,val) \ 107 { int _ret = (cond); if (_ret != (val)) PANIC("%d:%s returned %d (%d expected)", __LINE__, #cond, _ret, (val)); } 108 109/* perform a simple init/lock/unlock/destroy test on a rwlock of given attributes */ 110static void do_test_rwlock_rd1(pthread_rwlockattr_t *attr) 111{ 112 int ret; 113 pthread_rwlock_t lock[1]; 114 115 TZERO(pthread_rwlock_init(lock, attr)); 116 TZERO(pthread_rwlock_rdlock(lock)); 117 TZERO(pthread_rwlock_unlock(lock)); 118 TZERO(pthread_rwlock_destroy(lock)); 119} 120 121static void do_test_rwlock_wr1(pthread_rwlockattr_t *attr) 122{ 123 int ret; 124 pthread_rwlock_t lock[1]; 125 126 TZERO(pthread_rwlock_init(lock, attr)); 127 TZERO(pthread_rwlock_wrlock(lock)); 128 TZERO(pthread_rwlock_unlock(lock)); 129 TZERO(pthread_rwlock_destroy(lock)); 130} 131 132static void set_rwlockattr_shared(pthread_rwlockattr_t *attr, int shared) 133{ 134 int newshared; 135 TZERO(pthread_rwlockattr_setpshared(attr, shared)); 136 newshared = ~shared; 137 TZERO(pthread_rwlockattr_getpshared(attr, &newshared)); 138 TEXPECT_INT(newshared,shared); 139} 140 141/* simple init/lock/unlock/destroy on all rwlock types */ 142static void do_test_1(void) 143{ 144 int ret, type; 145 pthread_rwlockattr_t attr[1]; 146 147 do_test_rwlock_rd1(NULL); 148 do_test_rwlock_wr1(NULL); 149 150 /* non-shared version */ 151 152 TZERO(pthread_rwlockattr_init(attr)); 153 154 set_rwlockattr_shared(attr, PTHREAD_PROCESS_PRIVATE); 155 do_test_rwlock_rd1(attr); 156 do_test_rwlock_wr1(attr); 157 158 set_rwlockattr_shared(attr, PTHREAD_PROCESS_SHARED); 159 do_test_rwlock_rd1(attr); 160 do_test_rwlock_wr1(attr); 161 162 TZERO(pthread_rwlockattr_destroy(attr)); 163} 164 165static void do_test_rwlock_rd2_rec(pthread_rwlockattr_t *attr) 166{ 167 pthread_rwlock_t lock[1]; 168 169 TZERO(pthread_rwlock_init(lock, attr)); 170 TZERO(pthread_rwlock_tryrdlock(lock)); 171 TZERO(pthread_rwlock_unlock(lock)); 172 TZERO(pthread_rwlock_destroy(lock)); 173 174 TZERO(pthread_rwlock_init(lock, attr)); 175 TZERO(pthread_rwlock_tryrdlock(lock)); 176 TZERO(pthread_rwlock_tryrdlock(lock)); 177 TZERO(pthread_rwlock_unlock(lock)); 178 TZERO(pthread_rwlock_unlock(lock)); 179 TZERO(pthread_rwlock_destroy(lock)); 180} 181 182static void do_test_rwlock_wr2_rec(pthread_rwlockattr_t *attr) 183{ 184 pthread_rwlock_t lock[1]; 185 186 TZERO(pthread_rwlock_init(lock, attr)); 187 TZERO(pthread_rwlock_trywrlock(lock)); 188 TZERO(pthread_rwlock_unlock(lock)); 189 TZERO(pthread_rwlock_destroy(lock)); 190 191 TZERO(pthread_rwlock_init(lock, attr)); 192 TZERO(pthread_rwlock_trywrlock(lock)); 193#ifdef HOST 194 /* The host implementation (GLibc) does not support recursive 195 * write locks */ 196 TEXPECT_INT(pthread_rwlock_trywrlock(lock),EBUSY); 197#else 198 /* Our implementation supports recursive write locks ! */ 199 TZERO(pthread_rwlock_trywrlock(lock)); 200 TZERO(pthread_rwlock_unlock(lock)); 201#endif 202 TZERO(pthread_rwlock_unlock(lock)); 203 TZERO(pthread_rwlock_destroy(lock)); 204} 205 206static void do_test_2(void) 207{ 208 pthread_rwlockattr_t attr[1]; 209 210 do_test_rwlock_rd2_rec(NULL); 211 do_test_rwlock_wr2_rec(NULL); 212 213 /* non-shared version */ 214 215 TZERO(pthread_rwlockattr_init(attr)); 216 217 set_rwlockattr_shared(attr, PTHREAD_PROCESS_PRIVATE); 218 do_test_rwlock_rd2_rec(attr); 219 do_test_rwlock_wr2_rec(attr); 220 221 set_rwlockattr_shared(attr, PTHREAD_PROCESS_SHARED); 222 do_test_rwlock_rd2_rec(attr); 223 do_test_rwlock_wr2_rec(attr); 224 225 TZERO(pthread_rwlockattr_destroy(attr)); 226} 227 228/* This is more complex example to test contention of rwlockes. 229 * Essentially, what happens is this: 230 * 231 * - main thread creates a rwlock and rdlocks it 232 * - it then creates thread 1 and thread 2 233 * 234 * - it then record the current time, sleep for a specific 'waitDelay' 235 * then unlock the rwlock. 236 * 237 * - thread 1 tryrdlocks() the rwlock. It shall acquire the lock 238 * immediately, then release it, then wrlock(). 239 * 240 * - thread 2 trywrlocks() the rwlock. In case of failure (EBUSY), it waits 241 * for a small amount of time (see 'spinDelay') and tries again, until 242 * it succeeds. It then unlocks the rwlock. 243 * 244 * The goal of this test is to verify that thread 1 has been stopped 245 * for a sufficiently long time (in the wrlock), and that thread 2 has 246 * been spinning for the same minimum period. There is no guarantee as 247 * to which thread is going to acquire the rwlock first. 248 */ 249typedef struct { 250 pthread_rwlock_t rwlock[1]; 251 double t0; 252 double waitDelay; 253 double spinDelay; 254} Test3State; 255 256static void* do_rwlock_test_rd3_t1(void* arg) 257{ 258 Test3State *s = arg; 259 double t1; 260 261 /* try-acquire the lock, should succeed immediately */ 262 TZERO(pthread_rwlock_tryrdlock(s->rwlock)); 263 TZERO(pthread_rwlock_unlock(s->rwlock)); 264 265 /* wrlock() the lock, now */ 266 TZERO(pthread_rwlock_wrlock(s->rwlock)); 267 268 t1 = time_now(); 269 //DEBUG ONLY: printf("t1-s->t0=%g waitDelay=%g\n", t1-s->t0, s->waitDelay); 270 TTRUE((t1-s->t0) >= s->waitDelay); 271 TZERO(pthread_rwlock_unlock(s->rwlock)); 272 return NULL; 273} 274 275static void* do_rwlock_test_rd3_t2(void* arg) 276{ 277 Test3State *s = arg; 278 double t1; 279 280 for (;;) { 281 int ret = pthread_rwlock_trywrlock(s->rwlock); 282 if (ret == 0) 283 break; 284 if (ret == EBUSY) { 285 time_sleep(s->spinDelay); 286 continue; 287 } 288 } 289 t1 = time_now(); 290 TTRUE((t1-s->t0) >= s->waitDelay); 291 TZERO(pthread_rwlock_unlock(s->rwlock)); 292 return NULL; 293} 294 295 296static void do_test_rwlock_rd3(pthread_rwlockattr_t *attr, double delay) 297{ 298 Test3State s[1]; 299 pthread_t th1, th2; 300 void* dummy; 301 302 TZERO(pthread_rwlock_init(s->rwlock, attr)); 303 s->waitDelay = delay; 304 s->spinDelay = delay/20.; 305 306 TZERO(pthread_rwlock_rdlock(s->rwlock)); 307 308 pthread_create(&th1, NULL, do_rwlock_test_rd3_t1, s); 309 pthread_create(&th2, NULL, do_rwlock_test_rd3_t2, s); 310 311 s->t0 = time_now(); 312 time_sleep(delay); 313 314 TZERO(pthread_rwlock_unlock(s->rwlock)); 315 316 TZERO(pthread_join(th1, &dummy)); 317 TZERO(pthread_join(th2, &dummy)); 318} 319 320static void do_test_3(double delay) 321{ 322 pthread_rwlockattr_t attr[1]; 323 324 do_test_rwlock_rd3(NULL, delay); 325 326 /* non-shared version */ 327 328 TZERO(pthread_rwlockattr_init(attr)); 329 330 set_rwlockattr_shared(attr, PTHREAD_PROCESS_PRIVATE); 331 do_test_rwlock_rd3(attr, delay); 332 333 set_rwlockattr_shared(attr, PTHREAD_PROCESS_SHARED); 334 do_test_rwlock_rd3(attr, delay); 335 336 TZERO(pthread_rwlockattr_destroy(attr)); 337} 338 339 340int main(void) 341{ 342 do_test_1(); 343 do_test_2(); 344 do_test_3(0.1); 345 return 0; 346} 347