diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-09-23 15:10:02 -0400 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-09-23 15:10:02 -0400 |
commit | d154954f02691618fdf73a5a97336e64cad37af0 (patch) | |
tree | d2f1db7cc41fe703e011ea93f6c2f21d7cf20816 | |
parent | 4a05d12cf52a9a40759f11f28543bd75715c8f00 (diff) | |
parent | 3f56b7f65adb9ee35cd0f878ca00b92011eec427 (diff) | |
download | bionic-d154954f02691618fdf73a5a97336e64cad37af0.zip bionic-d154954f02691618fdf73a5a97336e64cad37af0.tar.gz bionic-d154954f02691618fdf73a5a97336e64cad37af0.tar.bz2 |
Merge change 26419 into eclair
* changes:
Add pthread_mutex_lock_timeout_np
-rw-r--r-- | libc/bionic/pthread.c | 141 | ||||
-rw-r--r-- | libc/include/pthread.h | 7 |
2 files changed, 138 insertions, 10 deletions
diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index d8a3166..ccc91d0 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -441,7 +441,7 @@ int pthread_attr_setstackaddr(pthread_attr_t * attr, void * stack_addr) int pthread_attr_getstackaddr(pthread_attr_t const * attr, void ** stack_addr) { - *stack_addr = attr->stack_base + attr->stack_size; + *stack_addr = (char*)attr->stack_base + attr->stack_size; return 0; } @@ -1125,6 +1125,135 @@ int pthread_mutex_trylock(pthread_mutex_t *mutex) } +/* initialize 'ts' with the difference between 'abstime' and the current time + * according to 'clock'. Returns -1 if abstime already expired, or 0 otherwise. + */ +static int +__timespec_to_absolute(struct timespec* ts, const struct timespec* abstime, clockid_t clock) +{ + clock_gettime(clock, ts); + ts->tv_sec = abstime->tv_sec - ts->tv_sec; + ts->tv_nsec = abstime->tv_nsec - ts->tv_nsec; + if (ts->tv_nsec < 0) { + ts->tv_sec--; + ts->tv_nsec += 1000000000; + } + if ((ts->tv_nsec < 0) || (ts->tv_nsec < 0)) + return -1; + + return 0; +} + +/* initialize 'abstime' to the current time according to 'clock' plus 'msecs' + * milliseconds. + */ +static void +__timespec_to_relative_msec(struct timespec* abstime, unsigned msecs, clockid_t clock) +{ + clock_gettime(clock, abstime); + abstime->tv_sec += msecs/1000; + abstime->tv_nsec += (msecs%1000)*1000000; + if (abstime->tv_nsec >= 1000000000) { + abstime->tv_sec++; + abstime->tv_nsec -= 1000000000; + } +} + +int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs) +{ + clockid_t clock = CLOCK_MONOTONIC; + struct timespec abstime; + struct timespec ts; + + /* compute absolute expiration time */ + __timespec_to_relative_msec(&abstime, msecs, clock); + + if (__likely(mutex != NULL)) + { + int mtype = (mutex->value & MUTEX_TYPE_MASK); + + if ( __likely(mtype == MUTEX_TYPE_NORMAL) ) + { + /* fast path for unconteded lock */ + if (__atomic_cmpxchg(0, 1, &mutex->value) == 0) + return 0; + + /* loop while needed */ + while (__atomic_swap(2, &mutex->value) != 0) { + if (__timespec_to_absolute(&ts, &abstime, clock) < 0) + return EBUSY; + + __futex_wait(&mutex->value, 2, &ts); + } + return 0; + } + else + { + int tid = __get_thread()->kernel_id; + int oldv; + + if ( tid == MUTEX_OWNER(mutex) ) + { + int oldv, counter; + + if (mtype == MUTEX_TYPE_ERRORCHECK) { + /* already locked by ourselves */ + return EDEADLK; + } + + _recursive_lock(); + oldv = mutex->value; + counter = (oldv + (1 << MUTEX_COUNTER_SHIFT)) & MUTEX_COUNTER_MASK; + mutex->value = (oldv & ~MUTEX_COUNTER_MASK) | counter; + _recursive_unlock(); + return 0; + } + else + { + /* + * If the new lock is available immediately, we grab it in + * the "uncontended" state. + */ + int new_lock_type = 1; + + for (;;) { + int oldv; + struct timespec ts; + + _recursive_lock(); + oldv = mutex->value; + if (oldv == mtype) { /* uncontended released lock => 1 or 2 */ + mutex->value = ((tid << 16) | mtype | new_lock_type); + } else if ((oldv & 3) == 1) { /* locked state 1 => state 2 */ + oldv ^= 3; + mutex->value = oldv; + } + _recursive_unlock(); + + if (oldv == mtype) + break; + + /* + * The lock was held, possibly contended by others. From + * now on, if we manage to acquire the lock, we have to + * assume that others are still contending for it so that + * we'll wake them when we unlock it. + */ + new_lock_type = 2; + + if (__timespec_to_absolute(&ts, &abstime, clock) < 0) + return EBUSY; + + __futex_wait( &mutex->value, oldv, &ts ); + } + return 0; + } + } + } + return EINVAL; +} + + /* XXX *technically* there is a race condition that could allow * XXX a signal to be missed. If thread A is preempted in _wait() * XXX after unlocking the mutex and before waiting, and if other @@ -1189,16 +1318,8 @@ int __pthread_cond_timedwait(pthread_cond_t *cond, struct timespec * tsp; if (abstime != NULL) { - clock_gettime(clock, &ts); - ts.tv_sec = abstime->tv_sec - ts.tv_sec; - ts.tv_nsec = abstime->tv_nsec - ts.tv_nsec; - if (ts.tv_nsec < 0) { - ts.tv_sec--; - ts.tv_nsec += 1000000000; - } - if((ts.tv_nsec < 0) || (ts.tv_sec < 0)) { + if (__timespec_to_absolute(&ts, abstime, clock) < 0) return ETIMEDOUT; - } tsp = &ts; } else { tsp = NULL; diff --git a/libc/include/pthread.h b/libc/include/pthread.h index ae7b758..6603b3f 100644 --- a/libc/include/pthread.h +++ b/libc/include/pthread.h @@ -207,6 +207,13 @@ int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t * mutex, unsigned msecs); +/* same as pthread_mutex_lock(), but will wait up to 'msecs' milli-seconds + * before returning. same return values than pthread_mutex_trylock though, i.e. + * returns EBUSY if the lock could not be acquired after the timeout + * expired. + */ +int pthread_mutex_lock_timeout_np(pthread_mutex_t *mutex, unsigned msecs); + int pthread_key_create(pthread_key_t *key, void (*destructor_function)(void *)); int pthread_key_delete (pthread_key_t); int pthread_setspecific(pthread_key_t key, const void *value); |