Module: xenomai-3 Branch: next Commit: bf959d29e1699abdcb3ff1cecda8f996562b2d7a URL: http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=bf959d29e1699abdcb3ff1cecda8f996562b2d7a
Author: Philippe Gerum <r...@xenomai.org> Date: Wed Feb 17 09:21:27 2016 +0100 testsuite/smokey: mutex: simplify, introduce PP tests At this chance, the lock stealing test is also fixed. --- testsuite/smokey/posix-mutex/posix-mutex.c | 1357 ++++++++++++++-------------- 1 file changed, 670 insertions(+), 687 deletions(-) diff --git a/testsuite/smokey/posix-mutex/posix-mutex.c b/testsuite/smokey/posix-mutex/posix-mutex.c index ac71b31..ae82fc1 100644 --- a/testsuite/smokey/posix-mutex/posix-mutex.c +++ b/testsuite/smokey/posix-mutex/posix-mutex.c @@ -4,6 +4,7 @@ * Copyright (C) Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>, * Marion Deveaud <marion.deve...@siemens.com>, * Jan Kiszka <jan.kis...@siemens.com> + * Philippe Gerum <r...@xenomai.org> * * Released under the terms of GPLv2. */ @@ -17,8 +18,6 @@ #include <signal.h> #include <pthread.h> #include <cobalt/sys/cobalt.h> -#include <cobalt/uapi/syscall.h> -#include "lib/cobalt/current.h" #include <smokey/smokey.h> smokey_test_plugin(posix_mutex, @@ -26,886 +25,870 @@ smokey_test_plugin(posix_mutex, "Check POSIX mutex services" ); -#define MUTEX_CREATE 1 -#define MUTEX_LOCK 2 -#define MUTEX_TRYLOCK 3 -#define MUTEX_TIMED_LOCK 4 -#define MUTEX_UNLOCK 5 -#define MUTEX_DESTROY 6 -#define COND_CREATE 7 -#define COND_SIGNAL 8 -#define COND_WAIT 9 -#define COND_DESTROY 10 -#define THREAD_DETACH 11 -#define THREAD_CREATE 12 -#define THREAD_JOIN 13 -#define THREAD_RENICE 14 - -#define NS_PER_MS 1000000 - static const char *reason_str[] = { - [SIGDEBUG_UNDEFINED] = "undefined", + [SIGDEBUG_UNDEFINED] = "received SIGDEBUG for unknown reason", [SIGDEBUG_MIGRATE_SIGNAL] = "received signal", [SIGDEBUG_MIGRATE_SYSCALL] = "invoked syscall", [SIGDEBUG_MIGRATE_FAULT] = "triggered fault", [SIGDEBUG_MIGRATE_PRIOINV] = "affected by priority inversion", - [SIGDEBUG_NOMLOCK] = "missing mlockall", - [SIGDEBUG_WATCHDOG] = "runaway thread", + [SIGDEBUG_NOMLOCK] = "process memory not locked", + [SIGDEBUG_WATCHDOG] = "watchdog triggered (period too short?)", + [SIGDEBUG_LOCK_BREAK] = "scheduler lock break", }; static void sigdebug(int sig, siginfo_t *si, void *context) { + const char fmt[] = "%s, this is unexpected.\n" + "(enabling CONFIG_XENO_OPT_DEBUG_TRACE_RELAX may help)\n"; unsigned int reason = sigdebug_reason(si); + int n __attribute__ ((unused)); + static char buffer[256]; - smokey_trace("\nSIGDEBUG received, reason %d: %s\n", reason, - reason <= SIGDEBUG_WATCHDOG ? reason_str[reason] : "<unknown>"); -} + if (reason > SIGDEBUG_WATCHDOG) + reason = SIGDEBUG_UNDEFINED; -static inline unsigned long long timer_get_tsc(void) -{ - return clockobj_get_tsc(); + n = snprintf(buffer, sizeof(buffer), fmt, reason_str[reason]); + n = write(STDERR_FILENO, buffer, n); } -static inline unsigned long long timer_tsc2ns(unsigned long long tsc) -{ - return clockobj_tsc_to_ns(tsc); -} +#define THREAD_PRIO_WEAK 0 +#define THREAD_PRIO_LOW 1 +#define THREAD_PRIO_MEDIUM 2 +#define THREAD_PRIO_HIGH 3 +#define THREAD_PRIO_VERY_HIGH 4 -static void add_timespec(struct timespec *ts, unsigned long long value) -{ - ts->tv_sec += value / 1000000000; - ts->tv_nsec += value % 1000000000; - if (ts->tv_nsec > 1000000000) { - ts->tv_sec++; - ts->tv_nsec -= 1000000000; - } -} +#define MAX_100_MS 100000000ULL + +struct locker_context { + pthread_mutex_t *mutex; + struct smokey_barrier *barrier; + int lock_acquired; +}; -static void ms_sleep(int time) +static void sleep_ms(unsigned int ms) /* < 1000 */ { struct timespec ts; - + ts.tv_sec = 0; - ts.tv_nsec = time*NS_PER_MS; - - nanosleep(&ts, NULL); + ts.tv_nsec = ms * 1000000; + clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); } -static void check_current_prio(int expected_prio) +static int get_effective_prio(void) { struct cobalt_threadstat stat; int ret; ret = cobalt_thread_stat(0, &stat); - if (ret) { - fprintf(stderr, - "FAILURE: cobalt_threadstat (%s)\n", strerror(-ret)); - exit(EXIT_FAILURE); - } + if (ret) + return ret; - if (stat.cprio != expected_prio) { - fprintf(stderr, - "FAILURE: current prio (%d) != expected prio (%d)\n", - stat.cprio, expected_prio); - exit(EXIT_FAILURE); - } + return stat.cprio; } -static void __check_current_mode(const char *file, int line, - int mask, int expected_value) +static int create_thread(pthread_t *tid, int policy, int prio, + void *(*thread)(void *), void *arg) { - int current_mode; + struct sched_param param; + pthread_attr_t thattr; + int ret; - /* This is a unit test, and in this circonstance, we are allowed to - call cobalt_get_current_mode. But please do not do that in your - own code. */ - current_mode = cobalt_get_current_mode() & mask; + pthread_attr_init(&thattr); + param.sched_priority = prio; + pthread_attr_setschedpolicy(&thattr, policy); + pthread_attr_setschedparam(&thattr, ¶m); + pthread_attr_setinheritsched(&thattr, PTHREAD_EXPLICIT_SCHED); - if (current_mode != expected_value) { - fprintf(stderr, - "FAILURE at %s:%d: current mode (%x) != expected mode (%x)\n", - file, line, current_mode, expected_value); - exit(EXIT_FAILURE); - } -} + if (!__T(ret, pthread_create(tid, &thattr, thread, arg))) + return ret; -#define check_current_mode(mask, expected_value) \ - __check_current_mode(__FILE__, __LINE__, (mask), (expected_value)) + return 0; +} -static int dispatch(const char *service_name, - int service_type, int check, int expected, ...) +static int do_init_mutexattr(pthread_mutexattr_t *mattr, int type, int protocol) { - unsigned long long timeout; - pthread_t *thread; - pthread_cond_t *cond; - void *handler; - va_list ap; - int status, protocol, type; - pthread_mutex_t *mutex; - struct sched_param param; - pthread_attr_t threadattr; - pthread_mutexattr_t mutexattr; - struct timespec ts; + int ret; - va_start(ap, expected); - switch (service_type) { - case MUTEX_CREATE: - mutex = va_arg(ap, pthread_mutex_t *); - pthread_mutexattr_init(&mutexattr); - protocol = va_arg(ap, int); - /* May fail if unsupported, that's ok. */ - pthread_mutexattr_setprotocol(&mutexattr, protocol); - type = va_arg(ap, int); - pthread_mutexattr_settype(&mutexattr, type); - status = pthread_mutex_init(mutex, &mutexattr); - break; - - case MUTEX_LOCK: - status = pthread_mutex_lock(va_arg(ap, pthread_mutex_t *)); - break; - - case MUTEX_TRYLOCK: - status = pthread_mutex_trylock(va_arg(ap, pthread_mutex_t *)); - break; - - case MUTEX_TIMED_LOCK: - mutex = va_arg(ap, pthread_mutex_t *); - timeout = va_arg(ap, unsigned long long); - clock_gettime(CLOCK_REALTIME, &ts); - add_timespec(&ts, timeout); - status = pthread_mutex_timedlock(mutex, &ts); - break; - - case MUTEX_UNLOCK: - status = pthread_mutex_unlock(va_arg(ap, pthread_mutex_t *)); - break; - - case MUTEX_DESTROY: - status = pthread_mutex_destroy(va_arg(ap, pthread_mutex_t *)); - break; - - case COND_CREATE: - status = pthread_cond_init(va_arg(ap, pthread_cond_t *), NULL); - break; - - case COND_SIGNAL: - status = pthread_cond_signal(va_arg(ap, pthread_cond_t *)); - break; - - case COND_WAIT: - cond = va_arg(ap, pthread_cond_t *); - status = - pthread_cond_wait(cond, va_arg(ap, pthread_mutex_t *)); - break; - - case COND_DESTROY: - status = pthread_cond_destroy(va_arg(ap, pthread_cond_t *)); - break; - - case THREAD_DETACH: - status = pthread_detach(pthread_self()); - break; - - case THREAD_CREATE: - thread = va_arg(ap, pthread_t *); - pthread_attr_init(&threadattr); - param.sched_priority = va_arg(ap, int); - if (param.sched_priority) - pthread_attr_setschedpolicy(&threadattr, SCHED_FIFO); - else - pthread_attr_setschedpolicy(&threadattr, SCHED_OTHER); - pthread_attr_setschedparam(&threadattr, ¶m); - pthread_attr_setinheritsched(&threadattr, - PTHREAD_EXPLICIT_SCHED); - handler = va_arg(ap, void *); - status = pthread_create(thread, &threadattr, handler, - va_arg(ap, void *)); - break; - - case THREAD_JOIN: - thread = va_arg(ap, pthread_t *); - status = pthread_join(*thread, NULL); - break; - - case THREAD_RENICE: - param.sched_priority = va_arg(ap, int); - if (param.sched_priority) - status = pthread_setschedparam(pthread_self(), - SCHED_FIFO, ¶m); - else - status = pthread_setschedparam(pthread_self(), - SCHED_OTHER, ¶m); - break; - - default: - fprintf(stderr, "Unknown service %i.\n", service_type); - exit(EXIT_FAILURE); - } - va_end(ap); + if (!__T(ret, pthread_mutexattr_init(mattr))) + return ret; + + if (!__T(ret, pthread_mutexattr_settype(mattr, type))) + return ret; + + if (!__T(ret, pthread_mutexattr_setprotocol(mattr, protocol))) + return ret; + + if (!__T(ret, pthread_mutexattr_setpshared(mattr, PTHREAD_PROCESS_PRIVATE))) + return ret; - if (check && status != expected) { - fprintf(stderr, "FAILURE: %s: %i (%s) instead of %i\n", - service_name, status, strerror(status), expected); - exit(EXIT_FAILURE); - } - return status; + return 0; } -static void *waiter(void *cookie) +static int do_init_mutex(pthread_mutex_t *mutex, int type, int protocol) { - pthread_mutex_t *mutex = (pthread_mutex_t *) cookie; - unsigned long long start, diff; + pthread_mutexattr_t mattr; + int ret; - dispatch("waiter pthread_detach", THREAD_DETACH, 1, 0); - start = timer_get_tsc(); - dispatch("waiter mutex_lock", MUTEX_LOCK, 1, 0, mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: waiter, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - ms_sleep(11); - dispatch("waiter mutex_unlock", MUTEX_UNLOCK, 1, 0, mutex); + ret = do_init_mutexattr(&mattr, type, protocol); + if (ret) + return ret; + + if (!__T(ret, pthread_mutex_init(mutex, &mattr))) + return ret; - return cookie; + if (!__T(ret, pthread_mutexattr_destroy(&mattr))) + return ret; + + return 0; } -static void autoinit_simple_wait(void) +static int do_init_mutex_ceiling(pthread_mutex_t *mutex, int type, int prio) { - unsigned long long start, diff; - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_t waiter_tid; - - smokey_trace("%s", __func__); - - dispatch("simple mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); - dispatch("simple thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 2, - waiter, &mutex); - ms_sleep(11); - dispatch("simple mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); - - start = timer_get_tsc(); - dispatch("simple mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } + pthread_mutexattr_t mattr; + int ret; + + ret = do_init_mutexattr(&mattr, type, PTHREAD_PRIO_PROTECT); + if (ret) + return ret; + + if (!__T(ret, pthread_mutexattr_setprioceiling(&mattr, prio))) + return ret; - dispatch("simple mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("simple mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_init(mutex, &mattr))) + return ret; + + if (!__T(ret, pthread_mutexattr_destroy(&mattr))) + return ret; + + return 0; } -static void simple_wait(void) +static void *mutex_timed_locker(void *arg) { - unsigned long long start, diff; - pthread_mutex_t mutex; - pthread_t waiter_tid; - - smokey_trace("%s", __func__); - - dispatch("simple mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_NONE, PTHREAD_MUTEX_NORMAL); - dispatch("simple mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); - dispatch("simple thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 2, - waiter, &mutex); - ms_sleep(11); - dispatch("simple mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); - - start = timer_get_tsc(); - dispatch("simple mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } + struct locker_context *p = arg; + struct timespec now, ts; + int ret; + + clock_gettime(CLOCK_REALTIME, &now); + timespec_adds(&ts, &now, 5000000); /* 5ms from now */ + + if (p->barrier) + smokey_barrier_release(p->barrier); + + if (__F(ret, pthread_mutex_timedlock(p->mutex, &ts)) && + __Tassert(ret == -ETIMEDOUT)) + return (void *)1; - dispatch("simple mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("simple mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + return NULL; } -static void autoinit_recursive_wait(void) +static int do_timed_contend(pthread_mutex_t *mutex, int prio) { - unsigned long long start, diff; - pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; - pthread_t waiter_tid; + struct locker_context args = { .barrier = NULL }; + pthread_t tid; + void *status; + int ret; - smokey_trace("%s", __func__); + if (!__T(ret, pthread_mutex_lock(mutex))) + return ret; - dispatch("rec mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); - dispatch("rec mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); + args.mutex = mutex; + ret = create_thread(&tid, SCHED_FIFO, prio, + mutex_timed_locker, &args); + if (ret) + return ret; + + if (!__T(ret, pthread_join(tid, &status))) + return ret; - dispatch("rec thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 2, - waiter, &mutex); + if (!__T(ret, pthread_mutex_unlock(mutex))) + return ret; - dispatch("rec mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); - ms_sleep(11); - dispatch("rec mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); + if (!__Fassert(status == NULL)) + return -EINVAL; - start = timer_get_tsc(); - dispatch("rec mutex_lock 3", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); + if (!__T(ret, pthread_mutex_destroy(mutex))) + return ret; - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("rec mutex_unlock 3", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("rec mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + return 0; } -static void recursive_wait(void) +static void *mutex_locker(void *arg) { - unsigned long long start, diff; - pthread_mutex_t mutex; - pthread_t waiter_tid; - - smokey_trace("%s", __func__); + struct locker_context *p = arg; + int ret; - dispatch("rec mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_NONE, PTHREAD_MUTEX_RECURSIVE); - dispatch("rec mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); - dispatch("rec mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_lock(p->mutex))) + return (void *)(long)ret; - dispatch("rec thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 2, - waiter, &mutex); + p->lock_acquired = 1; - dispatch("rec mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); - ms_sleep(11); - dispatch("rec mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); + if (!__T(ret, pthread_mutex_unlock(p->mutex))) + return (void *)(long)ret; - start = timer_get_tsc(); - dispatch("rec mutex_lock 3", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); + smokey_barrier_release(p->barrier); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("rec mutex_unlock 3", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("rec mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + return NULL; } -static void autoinit_errorcheck_wait(void) +static int do_contend(pthread_mutex_t *mutex, int type) { - unsigned long long start, diff; - pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; - pthread_t waiter_tid; - int err; - - smokey_trace("%s", __func__); + struct smokey_barrier barrier; + struct locker_context args; + pthread_t tid; + void *status; + int ret; - dispatch("errorcheck mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_lock(mutex))) + return ret; - err = pthread_mutex_lock(&mutex); - if (err != EDEADLK) { - fprintf(stderr, "FAILURE: errorcheck mutex_lock 2: %s\n", - strerror(err)); - exit(EXIT_FAILURE); + if (type == PTHREAD_MUTEX_RECURSIVE) { + if (!__T(ret, pthread_mutex_lock(mutex))) + return ret; + } else if (type == PTHREAD_MUTEX_ERRORCHECK) { + if (!__F(ret, pthread_mutex_lock(mutex)) || + !__Tassert(ret == -EDEADLK)) + return -EINVAL; } - dispatch("errorcheck thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 2, - waiter, &mutex); - ms_sleep(11); - dispatch("errorcheck mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); - err = pthread_mutex_unlock(&mutex); - if (err != EPERM) { - fprintf(stderr, "FAILURE: errorcheck mutex_unlock 2: %s\n", - strerror(err)); - exit(EXIT_FAILURE); + args.mutex = mutex; + smokey_barrier_init(&barrier); + args.barrier = &barrier; + args.lock_acquired = 0; + ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_MEDIUM, + mutex_locker, &args); + if (ret) + return ret; + + if (!__T(ret, pthread_mutex_unlock(mutex))) + return ret; + + if (type == PTHREAD_MUTEX_RECURSIVE) { + if (!__T(ret, pthread_mutex_unlock(mutex))) + return ret; + } else if (type == PTHREAD_MUTEX_ERRORCHECK) { + if (!__F(ret, pthread_mutex_unlock(mutex)) || + !__Tassert(ret == -EPERM)) + return -EINVAL; } - start = timer_get_tsc(); - dispatch("errorcheck mutex_lock 3", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("errorcheck mutex_unlock 3", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("errorcheck mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + /* Wait until locker runs through. */ + if (!__T(ret, smokey_barrier_wait(&barrier))) + return ret; + + if (!__T(ret, pthread_mutex_lock(mutex))) + return ret; + + if (!__T(ret, pthread_mutex_unlock(mutex))) + return ret; + + if (!__T(ret, pthread_mutex_destroy(mutex))) + return ret; + + if (!__T(ret, pthread_join(tid, &status))) + return ret; + + if (!__Tassert(status == NULL)) + return -EINVAL; + + return 0; } -static void errorcheck_wait(void) +static int static_init_normal_contend(void) { - unsigned long long start, diff; - pthread_mutex_t mutex; - pthread_t waiter_tid; - int err; + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - smokey_trace("%s", __func__); + return do_contend(&mutex, PTHREAD_MUTEX_NORMAL); +} - dispatch("errorcheck mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_NONE, PTHREAD_MUTEX_ERRORCHECK); - dispatch("errorcheck mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); +static int __dynamic_init_contend(int type) +{ + pthread_mutex_t mutex; + int ret; - err = pthread_mutex_lock(&mutex); - if (err != EDEADLK) { - fprintf(stderr, "FAILURE: errorcheck mutex_lock 2: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } + ret = do_init_mutex(&mutex, type, PTHREAD_PRIO_NONE); + if (ret) + return ret; + + return do_contend(&mutex, type); +} - dispatch("errorcheck thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 2, - waiter, &mutex); - ms_sleep(11); - dispatch("errorcheck mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); - err = pthread_mutex_unlock(&mutex); - if (err != EPERM) { - fprintf(stderr, "FAILURE: errorcheck mutex_unlock 2: %s\n", - strerror(err)); - exit(EXIT_FAILURE); - } +static int dynamic_init_normal_contend(void) +{ + return __dynamic_init_contend(PTHREAD_MUTEX_NORMAL); +} - start = timer_get_tsc(); - dispatch("errorcheck mutex_lock 3", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("errorcheck mutex_unlock 3", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("errorcheck mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); +static int static_init_recursive_contend(void) +{ + pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + + return do_contend(&mutex, PTHREAD_MUTEX_RECURSIVE); } -static void *timed_waiter(void *cookie) +static int dynamic_init_recursive_contend(void) { - pthread_mutex_t *mutex = (pthread_mutex_t *) cookie; - unsigned long long start, diff; + return __dynamic_init_contend(PTHREAD_MUTEX_RECURSIVE); +} - dispatch("timed_waiter pthread_detach", THREAD_DETACH, 1, 0); +static int static_init_errorcheck_contend(void) +{ + pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; - start = timer_get_tsc(); - dispatch("timed_waiter mutex_timed_lock", MUTEX_TIMED_LOCK, 1, - ETIMEDOUT, mutex, 10000000ULL); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: timed_waiter, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } + return do_contend(&mutex, PTHREAD_MUTEX_ERRORCHECK); +} - return cookie; +static int dynamic_init_errorcheck_contend(void) +{ + return __dynamic_init_contend(PTHREAD_MUTEX_ERRORCHECK); } -static void timed_mutex(void) +static int timed_contend(void) { pthread_mutex_t mutex; - pthread_t waiter_tid; - - smokey_trace("%s", __func__); + int ret; - dispatch("timed_mutex mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_INHERIT, PTHREAD_MUTEX_NORMAL); - dispatch("timed_mutex mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); - dispatch("timed_mutex thread_create", THREAD_CREATE, 1, 0, &waiter_tid, - 2, timed_waiter, &mutex); - ms_sleep(20); - dispatch("timed_mutex mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - ms_sleep(11); - dispatch("timed_mutex mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL, + PTHREAD_PRIO_INHERIT); + if (ret) + return ret; + return do_timed_contend(&mutex, THREAD_PRIO_MEDIUM); } -static void mode_switch(void) +static int weak_mode_switch(void) { + struct sched_param old_param, param = { .sched_priority = 0 }; + int old_policy, ret, mode; pthread_mutex_t mutex; - /* Cause a switch to secondary mode */ - __real_sched_yield(); + ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL, + PTHREAD_PRIO_INHERIT); + if (ret) + return ret; - smokey_trace("%s", __func__); + /* Save old schedparams, then switch to weak mode. */ - dispatch("switch mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_INHERIT, PTHREAD_MUTEX_NORMAL); + if (!__T(ret, pthread_getschedparam(pthread_self(), + &old_policy, &old_param))) + return ret; - check_current_mode(XNRELAX, XNRELAX); + /* Assume we are running SCHED_FIFO. */ - dispatch("switch mutex_lock", MUTEX_LOCK, 1, 0, &mutex); + mode = cobalt_thread_mode(); + if (!__Fassert(mode & XNWEAK)) + return -EINVAL; - check_current_mode(XNRELAX, 0); + /* Enter SCHED_WEAK scheduling. */ + + if (!__T(ret, pthread_setschedparam(pthread_self(), + SCHED_OTHER, ¶m))) + return ret; - dispatch("switch mutex_unlock", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("switch mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); -} + mode = cobalt_thread_mode(); + if (!__Tassert((mode & (XNWEAK|XNRELAX)) == (XNWEAK|XNRELAX))) + return -EINVAL; -static void pi_wait(void) -{ - unsigned long long start, diff; - pthread_mutex_t mutex; - pthread_t waiter_tid; + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; -#ifndef HAVE_PTHREAD_MUTEXATTR_SETPROTOCOL - smokey_note("PTHREAD_PRIO_INHERIT not supported"); - return; -#endif - smokey_trace("%s", __func__); + /* + * Holding a mutex should have switched us out of relaxed + * mode despite being assigned to the SCHED_WEAK class. + */ + mode = cobalt_thread_mode(); + if (!__Tassert((mode & (XNWEAK|XNRELAX)) == XNWEAK)) + return -EINVAL; - dispatch("pi mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_INHERIT, PTHREAD_MUTEX_NORMAL); - dispatch("pi mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; - check_current_prio(2); + /* Dropped it, we should have relaxed in the same move. */ + + mode = cobalt_thread_mode(); + if (!__Tassert((mode & (XNWEAK|XNRELAX)) == (XNWEAK|XNRELAX))) + return -EINVAL; - /* Give waiter a higher priority than main thread */ - dispatch("pi thread_create", THREAD_CREATE, 1, 0, &waiter_tid, 3, waiter, - &mutex); - ms_sleep(11); + if (!__T(ret, pthread_mutex_destroy(&mutex))) + return ret; - check_current_prio(3); + /* Leaving the SCHED_WEAK class. */ - dispatch("pi mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); + if (!__T(ret, pthread_setschedparam(pthread_self(), + old_policy, &old_param))) + return ret; - check_current_prio(2); + mode = cobalt_thread_mode(); + if (!__Fassert(mode & XNWEAK)) + return -EINVAL; - start = timer_get_tsc(); - dispatch("pi mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("pi mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); - dispatch("pi mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + return 0; } -static void lock_stealing(void) +static int do_pi_contend(int prio) { + struct smokey_barrier barrier; + struct locker_context args; pthread_mutex_t mutex; - pthread_t lowprio_tid; - int trylock_result; + pthread_t tid; + void *status; + int ret; - /* Main thread acquires the mutex and starts a waiter with lower - priority. Then main thread releases the mutex, but locks it again - without giving the waiter a chance to get it beforehand. */ + ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL, + PTHREAD_PRIO_INHERIT); + if (ret) + return ret; - smokey_trace("%s", __func__); + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; - dispatch("lock_stealing mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_INHERIT, PTHREAD_MUTEX_NORMAL); - dispatch("lock_stealing mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); + args.mutex = &mutex; + smokey_barrier_init(&barrier); + args.barrier = &barrier; + ret = create_thread(&tid, SCHED_FIFO, prio, + mutex_timed_locker, &args); + if (ret) + return ret; - /* Main thread should have higher priority */ - dispatch("lock_stealing thread_create 1", THREAD_CREATE, 1, 0, - &lowprio_tid, 1, waiter, &mutex); + if (!__T(ret, smokey_barrier_wait(&barrier))) + return ret; - /* Give lowprio thread 1 more ms to block on the mutex */ - ms_sleep(6); + /* + * Back while mutex_timed_locker is waiting. We should have + * been boosted by now. + */ + if (!__Tassert(get_effective_prio() == prio)) + return -EINVAL; + + if (!__T(ret, pthread_join(tid, &status))) + return ret; - dispatch("lock_stealing mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; - /* Try to stealing the lock from low prio task */ - trylock_result = dispatch("lock_stealing mutex_trylock", - MUTEX_TRYLOCK, 0, 0, &mutex); - if (trylock_result == 0) { - ms_sleep(6); + if (!__Fassert(status == NULL)) + return -EINVAL; - dispatch("lock_stealing mutex_unlock 2", MUTEX_UNLOCK, 1, 0, - &mutex); + if (!__T(ret, pthread_mutex_destroy(&mutex))) + return ret; + + return 0; +} - /* Let waiter_lowprio a chance to run */ - ms_sleep(20); +static int pi_contend(void) +{ + return do_pi_contend(THREAD_PRIO_HIGH); +} - dispatch("lock_stealing mutex_lock 3", MUTEX_LOCK, 1, 0, &mutex); +static void *mutex_locker_steal(void *arg) +{ + struct locker_context *p = arg; + int ret; - /* Restart the waiter */ - dispatch("lock_stealing thread_create 2", THREAD_CREATE, 1, 0, - &lowprio_tid, 1, waiter, &mutex); + smokey_barrier_release(p->barrier); + + if (!__T(ret, pthread_mutex_lock(p->mutex))) + return (void *)(long)ret; - ms_sleep(6); + p->lock_acquired = 1; - dispatch("lock_stealing mutex_unlock 3", MUTEX_UNLOCK, 1, 0, &mutex); - } else if (trylock_result != EBUSY) { - fprintf(stderr, - "FAILURE: lock_stealing mutex_trylock: %i (%s)\n", - trylock_result, strerror(trylock_result)); - exit(EXIT_FAILURE); + if (!__T(ret, pthread_mutex_unlock(p->mutex))) + return (void *)(long)ret; + + return NULL; +} + +static int do_steal(int may_steal) +{ + struct smokey_barrier barrier; + struct locker_context args; + pthread_mutex_t mutex; + pthread_t tid; + void *status; + int ret; + + ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL, + PTHREAD_PRIO_NONE); + if (ret) + return ret; + + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; + + args.mutex = &mutex; + smokey_barrier_init(&barrier); + args.barrier = &barrier; + args.lock_acquired = 0; + ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW, + mutex_locker_steal, &args); + if (ret) + return ret; + + /* Make sure the locker thread emerges... */ + if (!__T(ret, smokey_barrier_wait(&barrier))) + return ret; + + /* ...and blocks waiting on the mutex. */ + sleep_ms(1); + + /* + * Back while mutex_locker should be blocking. + * + * If stealing is exercised, unlock then relock immediately: + * we should have kept the ownership of the mutex and the + * locker thread should not have grabbed it so far, because of + * our higher priority. + * + * If stealing should not happen, unlock, wait a moment then + * observe whether the locker thread was able to grab it as + * expected. + * + * CAUTION: don't use pthread_mutex_trylock() to re-grab the + * mutex, this is not going to do what you want, since there + * is no stealing from userland, so using a fast op which + * never enters the kernel won't help. + */ + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; + + if (may_steal) { + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; + + if (!__Fassert(args.lock_acquired)) + return -EINVAL; + } else { + sleep_ms(1); + + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; + + if (!__Tassert(args.lock_acquired)) + return -EINVAL; } - /* Stealing the lock (again) from low prio task */ - dispatch("lock_stealing mutex_lock 4", MUTEX_LOCK, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; - ms_sleep(6); + if (!__T(ret, pthread_join(tid, &status))) + return ret; - dispatch("lock_stealing mutex_unlock 4", MUTEX_UNLOCK, 1, 0, &mutex); + if (!__Tassert(status == NULL)) + return -EINVAL; - /* Let waiter_lowprio a chance to run */ - ms_sleep(20); + if (!__T(ret, pthread_mutex_destroy(&mutex))) + return ret; - dispatch("lock_stealing mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + return 0; +} - if (trylock_result != 0) - smokey_note("mutex_trylock not supported"); +static int steal(void) +{ + return do_steal(1); } -static void *victim(void *cookie) +static int no_steal(void) { - pthread_mutex_t *mutex = (pthread_mutex_t *) cookie; - unsigned long long start; + return do_steal(0); +} - dispatch("victim pthread_detach", THREAD_DETACH, 1, 0); - dispatch("victim mutex_lock", MUTEX_LOCK, 1, 0, mutex); +/* + * NOTE: Cobalt implements a lazy enforcement scheme for priority + * protection of threads running in primary mode, which only registers + * a pending boost at locking time, committing it eventually when/if + * the owner thread schedules away while holding it. Entering a short + * sleep (in primary mode) right after a mutex is grabbed makes sure + * the boost is actually applied. + */ +static int protect_raise(void) +{ + pthread_mutex_t mutex; + int ret; + + ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_HIGH); + if (ret) + return ret; - start = timer_get_tsc(); - while (timer_tsc2ns(timer_get_tsc() - start) < 110000000); + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; - dispatch("victim mutex_unlock", MUTEX_UNLOCK, 1, 0, mutex); + sleep_ms(1); /* Commit the pending PP request. */ - return cookie; + /* We should have been given a MEDIUM -> HIGH boost. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; + + if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM)) + return -EINVAL; + + return 0; } -static void deny_stealing(void) +static int protect_lower(void) { - unsigned long long start, diff; pthread_mutex_t mutex; - pthread_t lowprio_tid; + int ret; - smokey_trace("%s", __func__); + ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_LOW); + if (ret) + return ret; - dispatch("deny_stealing mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_INHERIT, PTHREAD_MUTEX_NORMAL); - dispatch("deny_stealing mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; - /* Main thread should have higher priority */ - dispatch("deny_stealing thread_create", THREAD_CREATE, 1, 0, - &lowprio_tid, 1, victim, &mutex); + sleep_ms(1); /* Commit the pending PP request. */ - /* Give lowprio thread 1 more ms to block on the mutex */ - ms_sleep(6); + /* No boost should be applied. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; - dispatch("deny_stealing mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); + if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM)) + return -EINVAL; + + return 0; +} - /* Steal the lock for a short while */ - dispatch("deny_stealing mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); - dispatch("deny_stealing mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); +static int protect_weak(void) +{ + struct sched_param old_param, weak_param; + pthread_mutex_t mutex; + int ret, old_policy; + + if (!__T(ret, pthread_getschedparam(pthread_self(), + &old_policy, &old_param))) + return ret; + + /* + * Switch to the SCHED_WEAK class if present. THREAD_PRIO_WEAK + * (0) is used to make this work even without SCHED_WEAK + * support. + */ + weak_param.sched_priority = THREAD_PRIO_WEAK; + if (!__T(ret, pthread_setschedparam(pthread_self(), + SCHED_WEAK, &weak_param))) + return ret; + + ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_HIGH); + if (ret) + return ret; + + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; + + sleep_ms(1); /* Commit the pending PP request. */ + + /* We should have been sent to SCHED_FIFO, THREAD_PRIO_HIGH. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; + + /* Back to SCHED_WEAK, THREAD_PRIO_WEAK. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_WEAK)) + return -EINVAL; + + if (!__T(ret, pthread_setschedparam(pthread_self(), + old_policy, &old_param))) + return ret; + + return 0; +} - /* Give lowprio thread a chance to run */ - ms_sleep(6); +static int protect_nesting_protect(void) +{ + pthread_mutex_t mutex_high, mutex_very_high; + int ret; - /* Try to reacquire the lock, but the lowprio thread should hold it */ - start = timer_get_tsc(); - dispatch("deny_stealing mutex_lock 3", MUTEX_LOCK, 1, 0, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } + ret = do_init_mutex_ceiling(&mutex_high, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_HIGH); + if (ret) + return ret; - dispatch("deny_stealing mutex_unlock 3", MUTEX_UNLOCK, 1, 0, &mutex); + ret = do_init_mutex_ceiling(&mutex_very_high, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_VERY_HIGH); + if (ret) + return ret; - /* Let waiter_lowprio a chance to run */ - ms_sleep(20); + if (!__T(ret, pthread_mutex_lock(&mutex_high))) + return ret; - dispatch("deny_stealing mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); -} + sleep_ms(1); /* Commit the pending PP request. */ -struct cond_mutex { - pthread_mutex_t *mutex; - pthread_cond_t *cond; -}; + if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH)) + return -EINVAL; -static void *cond_signaler(void *cookie) -{ - struct cond_mutex *cm = (struct cond_mutex *) cookie; - unsigned long long start, diff; + if (!__T(ret, pthread_mutex_lock(&mutex_very_high))) + return ret; - start = timer_get_tsc(); - dispatch("cond_signaler mutex_lock 1", MUTEX_LOCK, 1, 0, cm->mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); + sleep_ms(1); /* Commit the pending PP request. */ - if (diff < 10000000) { - fprintf(stderr, - "FAILURE: cond_signaler, mutex_lock waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - ms_sleep(11); - dispatch("cond_signaler cond_signal", COND_SIGNAL, 1, 0, cm->cond); - dispatch("cond_signaler mutex_unlock 2", MUTEX_UNLOCK, 1, 0, cm->mutex); - sched_yield(); - - start = timer_get_tsc(); - dispatch("cond_signaler mutex_lock 2", MUTEX_LOCK, 1, 0, cm->mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, - "FAILURE: cond_signaler, mutex_lock 2 waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("cond_signaler mutex_unlock 2", MUTEX_UNLOCK, 1, 0, cm->mutex); + if (!__Tassert(get_effective_prio() == THREAD_PRIO_VERY_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex_very_high))) + return ret; - return cookie; + if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex_high))) + return ret; + + if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM)) + return -EINVAL; + + return 0; } -static void simple_condwait(void) +static int protect_nesting_pi(void) { - unsigned long long start, diff; - pthread_mutex_t mutex; - pthread_cond_t cond; - struct cond_mutex cm = { - .mutex = &mutex, - .cond = &cond, - }; - pthread_t cond_signaler_tid; - - smokey_trace("%s", __func__); - - dispatch("simple_condwait mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_NONE, PTHREAD_MUTEX_NORMAL); - dispatch("simple_condwait cond_init", COND_CREATE, 1, 0, &cond); - dispatch("simple_condwait mutex_lock", MUTEX_LOCK, 1, 0, &mutex); - dispatch("simple_condwait thread_create", THREAD_CREATE, 1, 0, - &cond_signaler_tid, 2, cond_signaler, &cm); - - ms_sleep(11); - start = timer_get_tsc(); - dispatch("simple_condwait cond_wait", COND_WAIT, 1, 0, &cond, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - ms_sleep(11); - dispatch("simple_condwait mutex_unlock", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); - - dispatch("simple_condwait mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); - dispatch("simple_condwait cond_destroy", COND_DESTROY, 1, 0, &cond); + pthread_mutex_t mutex_pp; + int ret; - dispatch("simple_condwait join", THREAD_JOIN, 1, 0, &cond_signaler_tid); + ret = do_init_mutex_ceiling(&mutex_pp, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_HIGH); + if (ret) + return ret; + + if (!__T(ret, pthread_mutex_lock(&mutex_pp))) + return ret; + + sleep_ms(1); /* Commit the pending PP request. */ + + /* PP ceiling: MEDIUM -> HIGH */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH)) + return -EINVAL; + + /* PI boost expected: HIGH -> VERY_HIGH, then back to HIGH */ + ret = do_pi_contend(THREAD_PRIO_VERY_HIGH); + if (ret) + return ret; + + if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex_pp))) + return ret; + + /* PP boost just dropped: HIGH -> MEDIUM. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM)) + return -EINVAL; + + return 0; } -static void recursive_condwait(void) +static int protect_dynamic(void) { - unsigned long long start, diff; pthread_mutex_t mutex; - pthread_cond_t cond; - struct cond_mutex cm = { - .mutex = &mutex, - .cond = &cond, - }; - pthread_t cond_signaler_tid; - - smokey_trace("%s", __func__); - - dispatch("rec_condwait mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_NONE, PTHREAD_MUTEX_RECURSIVE); - dispatch("rec_condwait cond_init", COND_CREATE, 1, 0, &cond); - dispatch("rec_condwait mutex_lock 1", MUTEX_LOCK, 1, 0, &mutex); - dispatch("rec_condwait mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); - dispatch("rec_condwait thread_create", THREAD_CREATE, 1, 0, - &cond_signaler_tid, 2, cond_signaler, &cm); - - ms_sleep(11); - start = timer_get_tsc(); - dispatch("rec_condwait cond_wait", COND_WAIT, 1, 0, &cond, &mutex); - diff = timer_tsc2ns(timer_get_tsc() - start); - if (diff < 10000000) { - fprintf(stderr, "FAILURE: main, waited %Ld.%03u us\n", - diff / 1000, (unsigned) (diff % 1000)); - exit(EXIT_FAILURE); - } - dispatch("rec_condwait mutex_unlock 1", MUTEX_UNLOCK, 1, 0, &mutex); - ms_sleep(11); - dispatch("rec_condwait mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); - sched_yield(); + int ret, old_ceiling; - dispatch("rec_condwait mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); - dispatch("rec_condwait cond_destroy", COND_DESTROY, 1, 0, &cond); + ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL, + THREAD_PRIO_HIGH); + if (ret) + return ret; - dispatch("rec_condwait join", THREAD_JOIN, 1, 0, &cond_signaler_tid); -} + if (!__T(ret, pthread_mutex_setprioceiling(&mutex, + THREAD_PRIO_VERY_HIGH, &old_ceiling))) + return ret; -static void nrt_lock(void *cookie) -{ - pthread_mutex_t *mutex = cookie; + if (!__Tassert(old_ceiling == THREAD_PRIO_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_lock(&mutex))) + return ret; + + sleep_ms(1); /* Commit the pending PP request. */ - /* Check that XNWEAK flag gets cleared and set back when - changing priority */ - check_current_mode(XNRELAX | XNWEAK, XNRELAX | XNWEAK); - check_current_prio(0); - dispatch("auto_switchback renice 1", THREAD_RENICE, 1, 0, 1); - check_current_mode(XNWEAK, 0); - check_current_prio(1); - dispatch("auto_switchback renice 2", THREAD_RENICE, 1, 0, 0); - check_current_mode(XNRELAX | XNWEAK, XNRELAX | XNWEAK); - check_current_prio(0); + /* We should have been given a HIGH -> VERY_HIGH boost. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_VERY_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_unlock(&mutex))) + return ret; - /* Check mode changes for auto-switchback threads while using - mutexes with priority inheritance */ - dispatch("auto_switchback mutex_lock 1", MUTEX_LOCK, 1, 0, mutex); - check_current_mode(XNRELAX, 0); - ms_sleep(11); - check_current_prio(2); - dispatch("auto_switchback mutex_unlock 1", MUTEX_UNLOCK, 1, 0, mutex); - check_current_mode(XNRELAX | XNWEAK, XNRELAX | XNWEAK); + /* Drop the boost: VERY_HIGH -> MEDIUM. */ + if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_getprioceiling(&mutex, &old_ceiling))) + return ret; + + if (!__Tassert(old_ceiling == THREAD_PRIO_VERY_HIGH)) + return -EINVAL; + + if (!__T(ret, pthread_mutex_destroy(&mutex))) + return ret; + + return 0; } -static void auto_switchback(void) +/* Detect obviously wrong execution times. */ +static int check_time_limit(const struct timespec *start, + xnticks_t limit_ns) { - pthread_t nrt_lock_tid; - pthread_mutex_t mutex; - - smokey_trace("%s", __func__); + struct timespec stop, delta; - dispatch("auto_switchback mutex_init", MUTEX_CREATE, 1, 0, &mutex, - PTHREAD_PRIO_INHERIT, PTHREAD_MUTEX_RECURSIVE); - dispatch("auto_switchback nrt thread_create", THREAD_CREATE, 1, 0, - &nrt_lock_tid, 0, nrt_lock, &mutex); - ms_sleep(11); - dispatch("auto_switchback mutex_lock 2", MUTEX_LOCK, 1, 0, &mutex); - dispatch("auto_switchback mutex_unlock 2", MUTEX_UNLOCK, 1, 0, &mutex); + clock_gettime(CLOCK_MONOTONIC, &stop); + timespec_sub(&delta, &stop, start); - dispatch("auto_switchback join", THREAD_JOIN, 1, 0, &nrt_lock_tid); - dispatch("auto_switchback mutex_destroy", MUTEX_DESTROY, 1, 0, &mutex); + return timespec_scalar(&delta) <= limit_ns; } -int run_posix_mutex(struct smokey_test *t, int argc, char *const argv[]) +#define do_test(__fn, __limit_ns) \ + do { \ + struct timespec __start; \ + int __ret; \ + smokey_trace(".. " __stringify(__fn)); \ + clock_gettime(CLOCK_MONOTONIC, &__start); \ + __ret = __fn(); \ + if (__ret) \ + return __ret; \ + if (!__Tassert(check_time_limit(&__start, __limit_ns))) \ + return -ETIMEDOUT; \ + } while (0) + +static int run_posix_mutex(struct smokey_test *t, int argc, char *const argv[]) { - struct sched_param sparam; + struct sched_param param; struct sigaction sa; + int ret; sigemptyset(&sa.sa_mask); sa.sa_sigaction = sigdebug; sa.sa_flags = SA_SIGINFO; sigaction(SIGDEBUG, &sa, NULL); - /* Set scheduling parameters for the current process */ - sparam.sched_priority = 2; - pthread_setschedparam(pthread_self(), SCHED_FIFO, &sparam); - - /* Call test routines */ - autoinit_simple_wait(); - simple_wait(); - autoinit_recursive_wait(); - recursive_wait(); - autoinit_errorcheck_wait(); - errorcheck_wait(); - timed_mutex(); - mode_switch(); - pi_wait(); - lock_stealing(); - deny_stealing(); - simple_condwait(); - recursive_condwait(); - auto_switchback(); + param.sched_priority = THREAD_PRIO_MEDIUM; + if (!__T(ret, pthread_setschedparam(pthread_self(), + SCHED_FIFO, ¶m))) + return ret; + + do_test(static_init_normal_contend, MAX_100_MS); + do_test(dynamic_init_normal_contend, MAX_100_MS); + do_test(static_init_recursive_contend, MAX_100_MS); + do_test(dynamic_init_recursive_contend, MAX_100_MS); + do_test(static_init_errorcheck_contend, MAX_100_MS); + do_test(dynamic_init_errorcheck_contend, MAX_100_MS); + do_test(timed_contend, MAX_100_MS); + do_test(weak_mode_switch, MAX_100_MS); + do_test(pi_contend, MAX_100_MS); + do_test(steal, MAX_100_MS); + do_test(no_steal, MAX_100_MS); + do_test(protect_raise, MAX_100_MS); + do_test(protect_lower, MAX_100_MS); + do_test(protect_nesting_protect, MAX_100_MS); + do_test(protect_nesting_pi, MAX_100_MS); + do_test(protect_weak, MAX_100_MS); + do_test(protect_dynamic, MAX_100_MS); return 0; } _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org https://xenomai.org/mailman/listinfo/xenomai-git