diff options
| author | christos <christos@NetBSD.org> | 2016-07-03 14:24:58 +0000 |
|---|---|---|
| committer | christos <christos@NetBSD.org> | 2016-07-03 14:24:58 +0000 |
| commit | 774674fd4710ea607eef47c793bd0cf3d4eb5184 (patch) | |
| tree | 05cc5703bc1498308ea98c1fe07c25fdaee74f76 /lib/libpthread/pthread_mutex.c | |
| parent | 3abdd8b82ad97d2929a64342f1bbc596ee3b1efa (diff) | |
GSoC 2016 Charles Cui: Implement thread priority protection based on work
by Andy Doran. Also document the get/set pshared thread calls as not
implemented, and add a skeleton implementation that is disabled.
XXX: document _sched_protect(2).
Diffstat (limited to 'lib/libpthread/pthread_mutex.c')
| -rw-r--r-- | lib/libpthread/pthread_mutex.c | 197 |
1 files changed, 184 insertions, 13 deletions
diff --git a/lib/libpthread/pthread_mutex.c b/lib/libpthread/pthread_mutex.c index 2d8eed5f144..d8879eea523 100644 --- a/lib/libpthread/pthread_mutex.c +++ b/lib/libpthread/pthread_mutex.c @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_mutex.c,v 1.59 2014/02/03 15:51:01 rmind Exp $ */ +/* $NetBSD: pthread_mutex.c,v 1.60 2016/07/03 14:24:58 christos Exp $ */ /*- * Copyright (c) 2001, 2003, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -47,10 +47,11 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_mutex.c,v 1.59 2014/02/03 15:51:01 rmind Exp $"); +__RCSID("$NetBSD: pthread_mutex.c,v 1.60 2016/07/03 14:24:58 christos Exp $"); #include <sys/types.h> #include <sys/lwpctl.h> +#include <sys/sched.h> #include <sys/lock.h> #include <errno.h> @@ -67,12 +68,27 @@ __RCSID("$NetBSD: pthread_mutex.c,v 1.59 2014/02/03 15:51:01 rmind Exp $"); #define MUTEX_WAITERS_BIT ((uintptr_t)0x01) #define MUTEX_RECURSIVE_BIT ((uintptr_t)0x02) #define MUTEX_DEFERRED_BIT ((uintptr_t)0x04) -#define MUTEX_THREAD ((uintptr_t)-16L) +#define MUTEX_PROTECT_BIT ((uintptr_t)0x08) +#define MUTEX_THREAD ((uintptr_t)~0x0f) #define MUTEX_HAS_WAITERS(x) ((uintptr_t)(x) & MUTEX_WAITERS_BIT) #define MUTEX_RECURSIVE(x) ((uintptr_t)(x) & MUTEX_RECURSIVE_BIT) +#define MUTEX_PROTECT(x) ((uintptr_t)(x) & MUTEX_PROTECT_BIT) #define MUTEX_OWNER(x) ((uintptr_t)(x) & MUTEX_THREAD) +#define MUTEX_GET_TYPE(x) \ + ((int)(((uintptr_t)(x) & 0x000000ff) >> 0)) +#define MUTEX_SET_TYPE(x, t) \ + (x) = (void *)(((uintptr_t)(x) & ~0x000000ff) | ((t) << 0)) +#define MUTEX_GET_PROTOCOL(x) \ + ((int)(((uintptr_t)(x) & 0x0000ff00) >> 8)) +#define MUTEX_SET_PROTOCOL(x, p) \ + (x) = (void *)(((uintptr_t)(x) & ~0x0000ff00) | ((p) << 8)) +#define MUTEX_GET_CEILING(x) \ + ((int)(((uintptr_t)(x) & 0x00ff0000) >> 16)) +#define MUTEX_SET_CEILING(x, c) \ + (x) = (void *)(((uintptr_t)(x) & ~0x00ff0000) | ((c) << 16)) + #if __GNUC_PREREQ__(3, 0) #define NOINLINE __attribute ((noinline)) #else @@ -80,7 +96,8 @@ __RCSID("$NetBSD: pthread_mutex.c,v 1.59 2014/02/03 15:51:01 rmind Exp $"); #endif static void pthread__mutex_wakeup(pthread_t, pthread_mutex_t *); -static int pthread__mutex_lock_slow(pthread_mutex_t *); +static int pthread__mutex_lock_slow(pthread_mutex_t *, + const struct timespec *); static int pthread__mutex_unlock_slow(pthread_mutex_t *); static void pthread__mutex_pause(void); @@ -103,16 +120,22 @@ __strong_alias(__libc_mutexattr_settype,pthread_mutexattr_settype) int pthread_mutex_init(pthread_mutex_t *ptm, const pthread_mutexattr_t *attr) { - intptr_t type; + uintptr_t type, proto, val, ceil; if (__predict_false(__uselibcstub)) return __libc_mutex_init_stub(ptm, attr); - if (attr == NULL) + if (attr == NULL) { type = PTHREAD_MUTEX_NORMAL; - else - type = (intptr_t)attr->ptma_private; + proto = PTHREAD_PRIO_NONE; + ceil = 0; + } else { + val = (uintptr_t)attr->ptma_private; + type = MUTEX_GET_TYPE(val); + proto = MUTEX_GET_PROTOCOL(val); + ceil = MUTEX_GET_CEILING(val); + } switch (type) { case PTHREAD_MUTEX_ERRORCHECK: __cpu_simple_lock_set(&ptm->ptm_errorcheck); @@ -127,10 +150,18 @@ pthread_mutex_init(pthread_mutex_t *ptm, const pthread_mutexattr_t *attr) ptm->ptm_owner = NULL; break; } + switch (proto) { + case PTHREAD_PRIO_PROTECT: + val = (uintptr_t)ptm->ptm_owner; + val |= MUTEX_PROTECT_BIT; + ptm->ptm_owner = (void *)val; + break; + } ptm->ptm_magic = _PT_MUTEX_MAGIC; ptm->ptm_waiters = NULL; ptm->ptm_recursed = 0; + ptm->ptm_ceiling = (unsigned char)ceil; return 0; } @@ -168,7 +199,24 @@ pthread_mutex_lock(pthread_mutex_t *ptm) #endif return 0; } - return pthread__mutex_lock_slow(ptm); + return pthread__mutex_lock_slow(ptm, NULL); +} + +int +pthread_mutex_timedlock(pthread_mutex_t* ptm, const struct timespec *ts) +{ + pthread_t self; + void *val; + + self = pthread__self(); + val = atomic_cas_ptr(&ptm->ptm_owner, NULL, self); + if (__predict_true(val == NULL)) { +#ifndef PTHREAD__ATOMIC_IS_MEMBAR + membar_enter(); +#endif + return 0; + } + return pthread__mutex_lock_slow(ptm, ts); } /* We want function call overhead. */ @@ -258,11 +306,12 @@ again: } NOINLINE static int -pthread__mutex_lock_slow(pthread_mutex_t *ptm) +pthread__mutex_lock_slow(pthread_mutex_t *ptm, const struct timespec *ts) { void *waiters, *new, *owner, *next; pthread_t self; int serrno; + int error; pthread__error(EINVAL, "Invalid mutex", ptm->ptm_magic == _PT_MUTEX_MAGIC); @@ -282,6 +331,10 @@ pthread__mutex_lock_slow(pthread_mutex_t *ptm) return EDEADLK; } + /* priority protect */ + if (MUTEX_PROTECT(owner) && _sched_protect(ptm->ptm_ceiling) == -1) { + return errno; + } serrno = errno; for (;; owner = ptm->ptm_owner) { /* Spin while the owner is running. */ @@ -339,12 +392,23 @@ pthread__mutex_lock_slow(pthread_mutex_t *ptm) */ while (self->pt_mutexwait) { self->pt_blocking++; - (void)_lwp_park(CLOCK_REALTIME, TIMER_ABSTIME, NULL, + error = _lwp_park(CLOCK_REALTIME, TIMER_ABSTIME, ts, self->pt_unpark, __UNVOLATILE(&ptm->ptm_waiters), __UNVOLATILE(&ptm->ptm_waiters)); self->pt_unpark = 0; self->pt_blocking--; membar_sync(); + if (__predict_true(error != -1)) { + continue; + } + if (errno == ETIMEDOUT && self->pt_mutexwait) { + /*Remove self from waiters list*/ + pthread__mutex_wakeup(self, ptm); + /*priority protect*/ + if (MUTEX_PROTECT(owner)) + (void)_sched_protect(-1); + return ETIMEDOUT; + } } } } @@ -460,6 +524,10 @@ pthread__mutex_unlock_slow(pthread_mutex_t *ptm) */ if (new != owner) { owner = atomic_swap_ptr(&ptm->ptm_owner, new); + if (__predict_false(MUTEX_PROTECT(owner))) { + /* restore elevated priority */ + (void)_sched_protect(-1); + } if (MUTEX_HAS_WAITERS(owner) != 0) { pthread__mutex_wakeup(self, ptm); return 0; @@ -591,16 +659,18 @@ pthread_mutexattr_destroy(pthread_mutexattr_t *attr) int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *typep) { + pthread__error(EINVAL, "Invalid mutex attribute", attr->ptma_magic == _PT_MUTEXATTR_MAGIC); - *typep = (int)(intptr_t)attr->ptma_private; + *typep = MUTEX_GET_TYPE(attr->ptma_private); return 0; } int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { + if (__predict_false(__uselibcstub)) return __libc_mutexattr_settype_stub(attr, type); @@ -611,13 +681,92 @@ pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) case PTHREAD_MUTEX_NORMAL: case PTHREAD_MUTEX_ERRORCHECK: case PTHREAD_MUTEX_RECURSIVE: - attr->ptma_private = (void *)(intptr_t)type; + MUTEX_SET_TYPE(attr->ptma_private, type); return 0; default: return EINVAL; } } +int +pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int*proto) +{ + + pthread__error(EINVAL, "Invalid mutex attribute", + attr->ptma_magic == _PT_MUTEXATTR_MAGIC); + + *proto = MUTEX_GET_PROTOCOL(attr->ptma_private); + return 0; +} + +int +pthread_mutexattr_setprotocol(pthread_mutexattr_t* attr, int proto) +{ + + pthread__error(EINVAL, "Invalid mutex attribute", + attr->ptma_magic == _PT_MUTEXATTR_MAGIC); + + switch (proto) { + case PTHREAD_PRIO_NONE: + case PTHREAD_PRIO_PROTECT: + MUTEX_SET_PROTOCOL(attr->ptma_private, proto); + return 0; + case PTHREAD_PRIO_INHERIT: + return ENOTSUP; + default: + return EINVAL; + } +} + +int +pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *ceil) +{ + + pthread__error(EINVAL, "Invalid mutex attribute", + attr->ptma_magic == _PT_MUTEXATTR_MAGIC); + + *ceil = MUTEX_GET_CEILING(attr->ptma_private); + return 0; +} + +int +pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int ceil) +{ + + pthread__error(EINVAL, "Invalid mutex attribute", + attr->ptma_magic == _PT_MUTEXATTR_MAGIC); + + if (ceil & ~0xff) + return EINVAL; + + MUTEX_SET_CEILING(attr->ptma_private, ceil); + return 0; +} + +#ifdef _PTHREAD_PSHARED +int +pthread_mutexattr_getpshared(const pthread_mutexattr_t * __restrict attr, + int * __restrict pshared) +{ + + *pshared = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +int +pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) +{ + + switch(pshared) { + case PTHREAD_PROCESS_PRIVATE: + return 0; + case PTHREAD_PROCESS_SHARED: + return ENOSYS; + } + return EINVAL; +} +#endif + /* * pthread__mutex_deferwake: try to defer unparking threads in self->pt_waiters * @@ -645,6 +794,28 @@ pthread__mutex_deferwake(pthread_t self, pthread_mutex_t *ptm) } int +pthread_mutex_getprioceiling(const pthread_mutex_t *ptm, int*ceil) +{ + *ceil = (unsigned int)ptm->ptm_ceiling; + return 0; +} + +int +pthread_mutex_setprioceiling(pthread_mutex_t *ptm, int ceil, int *old_ceil) +{ + int error; + + error = pthread_mutex_lock(ptm); + if (error == 0) { + *old_ceil = (unsigned int)ptm->ptm_ceiling; + /*check range*/ + ptm->ptm_ceiling = (unsigned char)ceil; + pthread_mutex_unlock(ptm); + } + return error; +} + +int _pthread_mutex_held_np(pthread_mutex_t *ptm) { |
