Merge change 6898

* changes:
  implement Mutex and Condition with pthread instead of calling futex directly.
This commit is contained in:
Android (Google) Code Review
2009-07-13 21:27:38 -07:00
6 changed files with 96 additions and 539 deletions

View File

@@ -1,60 +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.
*/
#ifndef _FUTEX_SYNCHRO_H
#define _FUTEX_SYNCHRO_H
#ifndef HAVE_FUTEX
#error "HAVE_FUTEX not defined"
#endif
#define FUTEX_WAIT_INFINITE (0)
typedef struct futex_mutex_t futex_mutex_t;
struct futex_mutex_t
{
volatile int value;
};
typedef struct futex_cond_t futex_cond_t;
struct futex_cond_t
{
volatile int value;
};
#if __cplusplus
extern "C" {
#endif
void futex_mutex_init(futex_mutex_t *m);
int futex_mutex_lock(futex_mutex_t *m, unsigned msec);
void futex_mutex_unlock(futex_mutex_t *m);
int futex_mutex_trylock(futex_mutex_t *m);
void futex_cond_init(futex_cond_t *c);
int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec);
void futex_cond_signal(futex_cond_t *c);
void futex_cond_broadcast(futex_cond_t *c);
#if __cplusplus
} // extern "C"
#endif
#endif // _FUTEX_SYNCHRO_H

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

@@ -111,7 +111,10 @@ MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
if (nextEventTime > 0) {
// we're about to wait, flush the binder command buffer
IPCThreadState::self()->flushCommands();
mCondition.wait(mLock, nextEventTime);
const nsecs_t reltime = nextEventTime - systemTime();
if (reltime > 0) {
mCondition.waitRelative(mLock, reltime);
}
}
} else {
//LOGD("going to wait");

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);
}