implement Mutex and Condition with pthread instead of calling futex directly.

internally pthread uses futex. the implementation consists of simple inlines
there are no implementation files anymore.
This commit is contained in:
Mathias Agopian 2009-07-12 23:11:20 -07:00 committed by Alex Ray
parent d8cc052faf
commit 1555436473
4 changed files with 92 additions and 478 deletions

View File

@ -21,6 +21,10 @@
#include <sys/types.h>
#include <time.h>
#if defined(HAVE_PTHREADS)
# include <pthread.h>
#endif
// ------------------------------------------------------------------
// C API
@ -176,6 +180,8 @@ inline thread_id_t getThreadId() {
return androidGetThreadId();
}
/*****************************************************************************/
/*
* Simple mutex class. The implementation is system-dependent.
*
@ -212,11 +218,38 @@ private:
// A mutex cannot be copied
Mutex(const Mutex&);
Mutex& operator = (const Mutex&);
void _init();
#if defined(HAVE_PTHREADS)
pthread_mutex_t mMutex;
#else
void _init();
void* mState;
#endif
};
#if defined(HAVE_PTHREADS)
inline Mutex::Mutex() {
pthread_mutex_init(&mMutex, NULL);
}
inline Mutex::Mutex(const char* name) {
pthread_mutex_init(&mMutex, NULL);
}
inline Mutex::~Mutex() {
pthread_mutex_destroy(&mMutex);
}
inline status_t Mutex::lock() {
return -pthread_mutex_lock(&mMutex);
}
inline void Mutex::unlock() {
pthread_mutex_unlock(&mMutex);
}
inline status_t Mutex::tryLock() {
return -pthread_mutex_trylock(&mMutex);
}
#endif // HAVE_PTHREADS
/*
* Automatic mutex. Declare one of these at the top of a function.
* When the function returns, it will go out of scope, and release the
@ -225,6 +258,7 @@ private:
typedef Mutex::Autolock AutoMutex;
/*****************************************************************************/
/*
* Condition variable class. The implementation is system-dependent.
@ -240,9 +274,6 @@ public:
~Condition();
// Wait on the condition variable. Lock the mutex before calling.
status_t wait(Mutex& mutex);
// Wait on the condition variable until the given time. Lock the mutex
// before calling.
status_t wait(Mutex& mutex, nsecs_t abstime);
// same with relative timeout
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
// Signal the condition variable, allowing one thread to continue.
@ -251,9 +282,60 @@ public:
void broadcast();
private:
#if defined(HAVE_PTHREADS)
pthread_cond_t mCond;
#else
void* mState;
#endif
};
#if defined(HAVE_PTHREADS)
inline Condition::Condition() {
pthread_cond_init(&mCond, NULL);
}
inline Condition::~Condition() {
pthread_cond_destroy(&mCond);
}
inline status_t Condition::wait(Mutex& mutex) {
return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
struct timespec ts;
ts.tv_sec = reltime/1000000000;
ts.tv_nsec = reltime%1000000000;
return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
struct timespec ts;
#if defined(HAVE_POSIX_CLOCKS)
clock_gettime(CLOCK_REALTIME, &ts);
#else // HAVE_POSIX_CLOCKS
// we don't support the clocks here.
struct timeval t;
gettimeofday(&t, NULL);
ts.tv_sec = t.tv_sec;
ts.tv_nsec= t.tv_usec*1000;
#endif // HAVE_POSIX_CLOCKS
ts.tv_sec += reltime/1000000000;
ts.tv_nsec+= reltime%1000000000;
if (ts.tv_nsec >= 1000000000) {
ts.tv_nsec -= 1000000000;
ts.tv_sec += 1;
}
return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
}
inline void Condition::signal() {
pthread_cond_signal(&mCond);
}
inline void Condition::broadcast() {
pthread_cond_broadcast(&mCond);
}
#endif // HAVE_PTHREADS
/*****************************************************************************/
/*
* This is our spiffy thread object!

View File

@ -51,13 +51,6 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= $(commonSources)
ifeq ($(HOST_OS),linux)
# Use the futex based mutex and condition variable
# implementation from android-arm because it's shared mem safe
LOCAL_SRC_FILES += \
futex_synchro.c
endif
LOCAL_MODULE:= libutils
LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS)
@ -87,15 +80,13 @@ LOCAL_SRC_FILES:= \
BackupHelpers.cpp
ifeq ($(TARGET_OS),linux)
# Use the futex based mutex and condition variable
# implementation from android-arm because it's shared mem safe
LOCAL_SRC_FILES += futex_synchro.c
LOCAL_LDLIBS += -lrt -ldl
endif
LOCAL_C_INCLUDES += \
external/zlib \
external/icu4c/common
LOCAL_LDLIBS += -lpthread
LOCAL_SHARED_LIBRARIES := \
@ -106,8 +97,7 @@ LOCAL_SHARED_LIBRARIES := \
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86)
# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp
LOCAL_SHARED_LIBRARIES += \
libdl
LOCAL_SHARED_LIBRARIES += libdl
endif # linux-x86
endif # sim

View File

@ -38,10 +38,6 @@
# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW
#endif
#if defined(HAVE_FUTEX)
#include <private/utils/futex_synchro.h>
#endif
#if defined(HAVE_PRCTL)
#include <sys/prctl.h>
#endif
@ -56,10 +52,6 @@ using namespace android;
// ----------------------------------------------------------------------------
#if defined(HAVE_PTHREADS)
#if 0
#pragma mark -
#pragma mark PTHREAD
#endif
// ----------------------------------------------------------------------------
/*
@ -163,10 +155,6 @@ android_thread_id_t androidGetThreadId()
// ----------------------------------------------------------------------------
#elif defined(HAVE_WIN32_THREADS)
#if 0
#pragma mark -
#pragma mark WIN32_THREADS
#endif
// ----------------------------------------------------------------------------
/*
@ -252,11 +240,6 @@ android_thread_id_t androidGetThreadId()
// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#pragma mark Common Thread functions
#endif
int androidCreateThread(android_thread_func_t fn, void* arg)
{
return createThreadEtc(fn, arg);
@ -294,109 +277,9 @@ namespace android {
* ===========================================================================
*/
#if 0
#pragma mark -
#pragma mark Mutex
#endif
#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX)
/*
* Simple pthread wrapper.
*/
Mutex::Mutex()
{
_init();
}
Mutex::Mutex(const char* name)
{
// XXX: name not used for now
_init();
}
void Mutex::_init()
{
pthread_mutex_t* pMutex = new pthread_mutex_t;
pthread_mutex_init(pMutex, NULL);
mState = pMutex;
}
Mutex::~Mutex()
{
delete (pthread_mutex_t*) mState;
}
status_t Mutex::lock()
{
int res;
while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ;
return -res;
}
void Mutex::unlock()
{
pthread_mutex_unlock((pthread_mutex_t*) mState);
}
status_t Mutex::tryLock()
{
int res;
while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ;
return -res;
}
#elif defined(HAVE_FUTEX)
#if 0
#pragma mark -
#endif
#define STATE ((futex_mutex_t*) (&mState))
Mutex::Mutex()
{
_init();
}
Mutex::Mutex(const char* name)
{
_init();
}
void
Mutex::_init()
{
futex_mutex_init(STATE);
}
Mutex::~Mutex()
{
}
status_t Mutex::lock()
{
int res;
while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ;
return -res;
}
void Mutex::unlock()
{
futex_mutex_unlock(STATE);
}
status_t Mutex::tryLock()
{
int res;
while ((res=futex_mutex_trylock(STATE)) == EINTR) ;
return -res;
}
#undef STATE
#if defined(HAVE_PTHREADS)
// implemented as inlines in threads.h
#elif defined(HAVE_WIN32_THREADS)
#if 0
#pragma mark -
#endif
Mutex::Mutex()
{
@ -456,161 +339,9 @@ status_t Mutex::tryLock()
* ===========================================================================
*/
#if 0
#pragma mark -
#pragma mark Condition
#endif
#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX)
/*
* Constructor. This is a simple pthread wrapper.
*/
Condition::Condition()
{
pthread_cond_t* pCond = new pthread_cond_t;
pthread_cond_init(pCond, NULL);
mState = pCond;
}
/*
* Destructor.
*/
Condition::~Condition()
{
pthread_cond_destroy((pthread_cond_t*) mState);
delete (pthread_cond_t*) mState;
}
/*
* Wait on a condition variable. Lock the mutex before calling.
*/
status_t Condition::wait(Mutex& mutex)
{
assert(mutex.mState != NULL);
int cc;
while ((cc = pthread_cond_wait((pthread_cond_t*)mState,
(pthread_mutex_t*) mutex.mState)) == EINTR) ;
return -cc;
}
status_t Condition::wait(Mutex& mutex, nsecs_t abstime)
{
assert(mutex.mState != NULL);
struct timespec ts;
ts.tv_sec = abstime/1000000000;
ts.tv_nsec = abstime-(ts.tv_sec*1000000000);
int cc;
while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState,
(pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ;
return -cc;
}
status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
{
return wait(mutex, systemTime()+reltime);
}
/*
* Signal the condition variable, allowing one thread to continue.
*/
void Condition::signal()
{
pthread_cond_signal((pthread_cond_t*) mState);
}
/*
* Signal the condition variable, allowing all threads to continue.
*/
void Condition::broadcast()
{
pthread_cond_broadcast((pthread_cond_t*) mState);
}
#elif defined(HAVE_FUTEX)
#if 0
#pragma mark -
#endif
#define STATE ((futex_cond_t*) (&mState))
/*
* Constructor. This is a simple pthread wrapper.
*/
Condition::Condition()
{
futex_cond_init(STATE);
}
/*
* Destructor.
*/
Condition::~Condition()
{
}
/*
* Wait on a condition variable. Lock the mutex before calling.
*/
status_t Condition::wait(Mutex& mutex)
{
assert(mutex.mState != NULL);
int res;
while ((res = futex_cond_wait(STATE,
(futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ;
return -res;
}
status_t Condition::wait(Mutex& mutex, nsecs_t abstime)
{
nsecs_t reltime = abstime - systemTime();
if (reltime <= 0) return true;
return waitRelative(mutex, reltime);
}
status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
{
assert(mutex.mState != NULL);
int res;
unsigned msec = ns2ms(reltime);
if(msec == 0)
return true;
// This code will not time out at the correct time if interrupted by signals
while ((res = futex_cond_wait(STATE,
(futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ;
return res;
}
/*
* Signal the condition variable, allowing one thread to continue.
*/
void Condition::signal()
{
futex_cond_signal(STATE);
}
/*
* Signal the condition variable, allowing all threads to continue.
*/
void Condition::broadcast()
{
futex_cond_broadcast(STATE);
}
#undef STATE
#if defined(HAVE_PTHREADS)
// implemented as inlines in threads.h
#elif defined(HAVE_WIN32_THREADS)
#if 0
#pragma mark -
#endif
/*
* Windows doesn't have a condition variable solution. It's possible
@ -753,14 +484,6 @@ status_t Condition::wait(Mutex& mutex)
return ((WinCondition*)mState)->wait(condState, hMutex, NULL);
}
status_t Condition::wait(Mutex& mutex, nsecs_t abstime)
{
WinCondition* condState = (WinCondition*) mState;
HANDLE hMutex = (HANDLE) mutex.mState;
return ((WinCondition*)mState)->wait(condState, hMutex, &abstime);
}
status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
{
return wait(mutex, systemTime()+reltime);
@ -841,11 +564,6 @@ void Condition::broadcast()
// ----------------------------------------------------------------------------
#if 0
#pragma mark -
#pragma mark Thread::Thread
#endif
/*
* This is our thread object!
*/

View File

@ -1,176 +0,0 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <limits.h>
#include <sys/time.h>
#include <sched.h>
#include <errno.h>
#include <private/utils/futex_synchro.h>
// This futex glue code is need on desktop linux, but is already part of bionic.
#if !defined(HAVE_FUTEX_WRAPPERS)
#include <unistd.h>
#include <sys/syscall.h>
typedef unsigned int u32;
#define asmlinkage
#define __user
#include <linux/futex.h>
#include <utils/Atomic.h>
int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3)
{
int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
return err == 0 ? 0 : -errno;
}
int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout)
{
return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0);
}
int __futex_wake(volatile void *ftx, int count)
{
return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0);
}
int __atomic_cmpxchg(int old, int _new, volatile int *ptr)
{
return android_atomic_cmpxchg(old, _new, ptr);
}
int __atomic_swap(int _new, volatile int *ptr)
{
return android_atomic_swap(_new, ptr);
}
int __atomic_dec(volatile int *ptr)
{
return android_atomic_dec(ptr);
}
#else // !defined(__arm__)
int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
int __futex_wake(volatile void *ftx, int count);
int __atomic_cmpxchg(int old, int _new, volatile int *ptr);
int __atomic_swap(int _new, volatile int *ptr);
int __atomic_dec(volatile int *ptr);
#endif // !defined(HAVE_FUTEX_WRAPPERS)
// lock states
//
// 0: unlocked
// 1: locked, no waiters
// 2: locked, maybe waiters
void futex_mutex_init(futex_mutex_t *m)
{
m->value = 0;
}
int futex_mutex_lock(futex_mutex_t *m, unsigned msec)
{
if(__atomic_cmpxchg(0, 1, &m->value) == 0) {
return 0;
}
if(msec == FUTEX_WAIT_INFINITE) {
while(__atomic_swap(2, &m->value) != 0) {
__futex_wait(&m->value, 2, 0);
}
} else {
struct timespec ts;
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
while(__atomic_swap(2, &m->value) != 0) {
if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) {
return -1;
}
}
}
return 0;
}
int futex_mutex_trylock(futex_mutex_t *m)
{
if(__atomic_cmpxchg(0, 1, &m->value) == 0) {
return 0;
}
return -1;
}
void futex_mutex_unlock(futex_mutex_t *m)
{
if(__atomic_dec(&m->value) != 1) {
m->value = 0;
__futex_wake(&m->value, 1);
}
}
/* 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
* XXX threads call signal or broadcast UINT_MAX times (exactly),
* XXX before thread A is scheduled again and calls futex_wait(),
* XXX then the signal will be lost.
*/
void futex_cond_init(futex_cond_t *c)
{
c->value = 0;
}
int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec)
{
if(msec == FUTEX_WAIT_INFINITE){
int oldvalue = c->value;
futex_mutex_unlock(m);
__futex_wait(&c->value, oldvalue, 0);
futex_mutex_lock(m, FUTEX_WAIT_INFINITE);
return 0;
} else {
int oldvalue = c->value;
struct timespec ts;
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
futex_mutex_unlock(m);
const int err = __futex_wait(&c->value, oldvalue, &ts);
futex_mutex_lock(m, FUTEX_WAIT_INFINITE);
return err;
}
}
void futex_cond_signal(futex_cond_t *c)
{
__atomic_dec(&c->value);
__futex_wake(&c->value, 1);
}
void futex_cond_broadcast(futex_cond_t *c)
{
__atomic_dec(&c->value);
__futex_wake(&c->value, INT_MAX);
}