/* * Copyright (C) 2010 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <pthread.h> #include <errno.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> /* Posix states that EDEADLK should be returned in case a deadlock condition * is detected with a PTHREAD_MUTEX_ERRORCHECK lock() or trylock(), but * GLibc returns EBUSY instead. */ #ifdef HOST # define ERRNO_PTHREAD_EDEADLK EBUSY #else # define ERRNO_PTHREAD_EDEADLK EDEADLK #endif static void __attribute__((noreturn)) panic(const char* func, const char* format, ...) { va_list args; fprintf(stderr, "%s: ", func); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); exit(1); } #define PANIC(...) panic(__FUNCTION__,__VA_ARGS__) static void __attribute__((noreturn)) error(int errcode, const char* func, const char* format, ...) { va_list args; fprintf(stderr, "%s: ", func); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, " error=%d: %s\n", errcode, strerror(errcode)); exit(1); } /* return current time in seconds as floating point value */ static double time_now(void) { struct timespec ts[1]; clock_gettime(CLOCK_MONOTONIC, ts); return (double)ts->tv_sec + ts->tv_nsec/1e9; } static void time_sleep(double delay) { struct timespec ts; int ret; ts.tv_sec = (time_t)delay; ts.tv_nsec = (long)((delay - ts.tv_sec)*1e9); do { ret = nanosleep(&ts, &ts); } while (ret < 0 && errno == EINTR); } #define ERROR(errcode,...) error((errcode),__FUNCTION__,__VA_ARGS__) #define TZERO(cond) \ { int _ret = (cond); if (_ret != 0) ERROR(_ret,"%d:%s", __LINE__, #cond); } #define TTRUE(cond) \ { if (!(cond)) PANIC("%d:%s", __LINE__, #cond); } #define TFALSE(cond) \ { if (!!(cond)) PANIC("%d:%s", __LINE__, #cond); } #define TEXPECT_INT(cond,val) \ { int _ret = (cond); if (_ret != (val)) PANIC("%d:%s returned %d (%d expected)", __LINE__, #cond, _ret, (val)); } /* perform a simple init/lock/unlock/destroy test on a mutex of given attributes */ static void do_test_mutex_1(pthread_mutexattr_t *attr) { int ret; pthread_mutex_t lock[1]; TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_lock(lock)); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); } static void set_mutexattr_type(pthread_mutexattr_t *attr, int type) { int newtype; TZERO(pthread_mutexattr_settype(attr, type)); newtype = ~type; TZERO(pthread_mutexattr_gettype(attr, &newtype)); TEXPECT_INT(newtype,type); } /* simple init/lock/unlock/destroy on all mutex types */ static void do_test_1(void) { int ret, type; pthread_mutexattr_t attr[1]; do_test_mutex_1(NULL); /* non-shared version */ TZERO(pthread_mutexattr_init(attr)); set_mutexattr_type(attr, PTHREAD_MUTEX_NORMAL); do_test_mutex_1(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_RECURSIVE); do_test_mutex_1(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_ERRORCHECK); do_test_mutex_1(attr); TZERO(pthread_mutexattr_destroy(attr)); /* shared version */ TZERO(pthread_mutexattr_init(attr)); TZERO(pthread_mutexattr_setpshared(attr, PTHREAD_PROCESS_SHARED)); set_mutexattr_type(attr, PTHREAD_MUTEX_NORMAL); do_test_mutex_1(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_RECURSIVE); do_test_mutex_1(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_ERRORCHECK); do_test_mutex_1(attr); TZERO(pthread_mutexattr_destroy(attr)); } /* perform init/trylock/unlock/destroy then init/lock/trylock/destroy */ static void do_test_mutex_2(pthread_mutexattr_t *attr) { pthread_mutex_t lock[1]; TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_trylock(lock)); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_trylock(lock)); TEXPECT_INT(pthread_mutex_trylock(lock),EBUSY); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); } static void do_test_mutex_2_rec(pthread_mutexattr_t *attr) { pthread_mutex_t lock[1]; TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_trylock(lock)); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_trylock(lock)); TZERO(pthread_mutex_trylock(lock)); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); } static void do_test_mutex_2_chk(pthread_mutexattr_t *attr) { pthread_mutex_t lock[1]; TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_trylock(lock)); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); TZERO(pthread_mutex_init(lock, attr)); TZERO(pthread_mutex_trylock(lock)); TEXPECT_INT(pthread_mutex_trylock(lock),ERRNO_PTHREAD_EDEADLK); TZERO(pthread_mutex_unlock(lock)); TZERO(pthread_mutex_destroy(lock)); } static void do_test_2(void) { pthread_mutexattr_t attr[1]; do_test_mutex_2(NULL); /* non-shared version */ TZERO(pthread_mutexattr_init(attr)); set_mutexattr_type(attr, PTHREAD_MUTEX_NORMAL); do_test_mutex_2(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_RECURSIVE); do_test_mutex_2_rec(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_ERRORCHECK); do_test_mutex_2_chk(attr); TZERO(pthread_mutexattr_destroy(attr)); /* shared version */ TZERO(pthread_mutexattr_init(attr)); TZERO(pthread_mutexattr_setpshared(attr, PTHREAD_PROCESS_SHARED)); set_mutexattr_type(attr, PTHREAD_MUTEX_NORMAL); do_test_mutex_2(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_RECURSIVE); do_test_mutex_2_rec(attr); set_mutexattr_type(attr, PTHREAD_MUTEX_ERRORCHECK); do_test_mutex_2_chk(attr); TZERO(pthread_mutexattr_destroy(attr)); } /* This is more complex example to test contention of mutexes. * Essentially, what happens is this: * * - main thread creates a mutex and locks it * - it then creates thread 1 and thread 2 * * - it then record the current time, sleep for a specific 'waitDelay' * then unlock the mutex. * * - thread 1 locks() the mutex. It shall be stopped for a least 'waitDelay' * seconds. It then unlocks the mutex. * * - thread 2 trylocks() the mutex. In case of failure (EBUSY), it waits * for a small amount of time (see 'spinDelay') and tries again, until * it succeeds. It then unlocks the mutex. * * The goal of this test is to verify that thread 1 has been stopped * for a sufficiently long time, and that thread 2 has been spinning for * the same minimum period. There is no guarantee as to which thread is * going to acquire the mutex first. */ typedef struct { pthread_mutex_t mutex[1]; double t0; double waitDelay; double spinDelay; } Test3State; static void* do_mutex_test_3_t1(void* arg) { Test3State *s = arg; double t1; TZERO(pthread_mutex_lock(s->mutex)); t1 = time_now(); //DEBUG ONLY: printf("t1-s->t0=%g waitDelay=%g\n", t1-s->t0, s->waitDelay); TTRUE((t1-s->t0) >= s->waitDelay); TZERO(pthread_mutex_unlock(s->mutex)); return NULL; } static void* do_mutex_test_3_t2(void* arg) { Test3State *s = arg; double t1; for (;;) { int ret = pthread_mutex_trylock(s->mutex); if (ret == 0) break; if (ret == EBUSY) { time_sleep(s->spinDelay); continue; } } t1 = time_now(); TTRUE((t1-s->t0) >= s->waitDelay); TZERO(pthread_mutex_unlock(s->mutex)); return NULL; } static void do_test_mutex_3(pthread_mutexattr_t *attr, double delay) { Test3State s[1]; pthread_t th1, th2; void* dummy; TZERO(pthread_mutex_init(s->mutex, attr)); s->waitDelay = delay; s->spinDelay = delay/20.; TZERO(pthread_mutex_lock(s->mutex)); pthread_create(&th1, NULL, do_mutex_test_3_t1, s); pthread_create(&th2, NULL, do_mutex_test_3_t2, s); s->t0 = time_now(); time_sleep(delay); TZERO(pthread_mutex_unlock(s->mutex)); TZERO(pthread_join(th1, &dummy)); TZERO(pthread_join(th2, &dummy)); } static void do_test_3(double delay) { pthread_mutexattr_t attr[1]; do_test_mutex_3(NULL, delay); /* non-shared version */ TZERO(pthread_mutexattr_init(attr)); set_mutexattr_type(attr, PTHREAD_MUTEX_NORMAL); do_test_mutex_3(attr, delay); set_mutexattr_type(attr, PTHREAD_MUTEX_RECURSIVE); do_test_mutex_3(attr, delay); set_mutexattr_type(attr, PTHREAD_MUTEX_ERRORCHECK); do_test_mutex_3(attr, delay); TZERO(pthread_mutexattr_destroy(attr)); /* shared version */ TZERO(pthread_mutexattr_init(attr)); TZERO(pthread_mutexattr_setpshared(attr, PTHREAD_PROCESS_SHARED)); set_mutexattr_type(attr, PTHREAD_MUTEX_NORMAL); do_test_mutex_3(attr, delay); set_mutexattr_type(attr, PTHREAD_MUTEX_RECURSIVE); do_test_mutex_3(attr, delay); set_mutexattr_type(attr, PTHREAD_MUTEX_ERRORCHECK); do_test_mutex_3(attr, delay); TZERO(pthread_mutexattr_destroy(attr)); } int main(void) { do_test_1(); do_test_2(); do_test_3(0.1); return 0; }