зеркало из https://github.com/mozilla/gecko-dev.git
565 строки
18 KiB
C++
565 строки
18 KiB
C++
/*
|
|
* Copyright (C) 2007 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 _LIBS_UTILS_THREADS_H
|
|
#define _LIBS_UTILS_THREADS_H
|
|
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <system/graphics.h>
|
|
|
|
#if defined(HAVE_PTHREADS)
|
|
# include <pthread.h>
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------
|
|
// C API
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef void* android_thread_id_t;
|
|
|
|
typedef int (*android_thread_func_t)(void*);
|
|
|
|
enum {
|
|
/*
|
|
* ***********************************************
|
|
* ** Keep in sync with android.os.Process.java **
|
|
* ***********************************************
|
|
*
|
|
* This maps directly to the "nice" priorities we use in Android.
|
|
* A thread priority should be chosen inverse-proportionally to
|
|
* the amount of work the thread is expected to do. The more work
|
|
* a thread will do, the less favorable priority it should get so that
|
|
* it doesn't starve the system. Threads not behaving properly might
|
|
* be "punished" by the kernel.
|
|
* Use the levels below when appropriate. Intermediate values are
|
|
* acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below.
|
|
*/
|
|
ANDROID_PRIORITY_LOWEST = 19,
|
|
|
|
/* use for background tasks */
|
|
ANDROID_PRIORITY_BACKGROUND = 10,
|
|
|
|
/* most threads run at normal priority */
|
|
ANDROID_PRIORITY_NORMAL = 0,
|
|
|
|
/* threads currently running a UI that the user is interacting with */
|
|
ANDROID_PRIORITY_FOREGROUND = -2,
|
|
|
|
/* the main UI thread has a slightly more favorable priority */
|
|
ANDROID_PRIORITY_DISPLAY = -4,
|
|
|
|
/* ui service treads might want to run at a urgent display (uncommon) */
|
|
ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY,
|
|
|
|
/* all normal audio threads */
|
|
ANDROID_PRIORITY_AUDIO = -16,
|
|
|
|
/* service audio threads (uncommon) */
|
|
ANDROID_PRIORITY_URGENT_AUDIO = -19,
|
|
|
|
/* should never be used in practice. regular process might not
|
|
* be allowed to use this level */
|
|
ANDROID_PRIORITY_HIGHEST = -20,
|
|
|
|
ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL,
|
|
ANDROID_PRIORITY_MORE_FAVORABLE = -1,
|
|
ANDROID_PRIORITY_LESS_FAVORABLE = +1,
|
|
};
|
|
|
|
enum {
|
|
ANDROID_TGROUP_DEFAULT = 0,
|
|
ANDROID_TGROUP_BG_NONINTERACT = 1,
|
|
ANDROID_TGROUP_FG_BOOST = 2,
|
|
ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST,
|
|
};
|
|
|
|
// Create and run a new thread.
|
|
extern int androidCreateThread(android_thread_func_t, void *);
|
|
|
|
// Create thread with lots of parameters
|
|
extern int androidCreateThreadEtc(android_thread_func_t entryFunction,
|
|
void *userData,
|
|
const char* threadName,
|
|
int32_t threadPriority,
|
|
size_t threadStackSize,
|
|
android_thread_id_t *threadId);
|
|
|
|
// Get some sort of unique identifier for the current thread.
|
|
extern android_thread_id_t androidGetThreadId();
|
|
|
|
// Low-level thread creation -- never creates threads that can
|
|
// interact with the Java VM.
|
|
extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
|
|
void *userData,
|
|
const char* threadName,
|
|
int32_t threadPriority,
|
|
size_t threadStackSize,
|
|
android_thread_id_t *threadId);
|
|
|
|
// Used by the Java Runtime to control how threads are created, so that
|
|
// they can be proper and lovely Java threads.
|
|
typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction,
|
|
void *userData,
|
|
const char* threadName,
|
|
int32_t threadPriority,
|
|
size_t threadStackSize,
|
|
android_thread_id_t *threadId);
|
|
|
|
extern void androidSetCreateThreadFunc(android_create_thread_fn func);
|
|
|
|
// ------------------------------------------------------------------
|
|
// Extra functions working with raw pids.
|
|
|
|
// Get pid for the current thread.
|
|
extern pid_t androidGetTid();
|
|
|
|
// Change the scheduling group of a particular thread. The group
|
|
// should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if
|
|
// grp is out of range, else another non-zero value with errno set if
|
|
// the operation failed. Thread ID zero means current thread.
|
|
extern int androidSetThreadSchedulingGroup(pid_t tid, int grp);
|
|
|
|
// Change the priority AND scheduling group of a particular thread. The priority
|
|
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
|
|
// if the priority set failed, else another value if just the group set failed;
|
|
// in either case errno is set. Thread ID zero means current thread.
|
|
extern int androidSetThreadPriority(pid_t tid, int prio);
|
|
|
|
// Get the current priority of a particular thread. Returns one of the
|
|
// ANDROID_PRIORITY constants or a negative result in case of error.
|
|
extern int androidGetThreadPriority(pid_t tid);
|
|
|
|
// Get the current scheduling group of a particular thread. Normally returns
|
|
// one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT.
|
|
// Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if
|
|
// scheduling groups are disabled. Returns INVALID_OPERATION if unexpected error.
|
|
// Thread ID zero means current thread.
|
|
extern int androidGetThreadSchedulingGroup(pid_t tid);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------
|
|
// C++ API
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <utils/Errors.h>
|
|
#include <utils/RefBase.h>
|
|
#include <utils/Timers.h>
|
|
|
|
namespace android {
|
|
|
|
typedef android_thread_id_t thread_id_t;
|
|
|
|
typedef android_thread_func_t thread_func_t;
|
|
|
|
enum {
|
|
PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST,
|
|
PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND,
|
|
PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL,
|
|
PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND,
|
|
PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY,
|
|
PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY,
|
|
PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO,
|
|
PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO,
|
|
PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST,
|
|
PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT,
|
|
PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE,
|
|
PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE,
|
|
};
|
|
|
|
// Create and run a new thread.
|
|
inline bool createThread(thread_func_t f, void *a) {
|
|
return androidCreateThread(f, a) ? true : false;
|
|
}
|
|
|
|
// Create thread with lots of parameters
|
|
inline bool createThreadEtc(thread_func_t entryFunction,
|
|
void *userData,
|
|
const char* threadName = "android:unnamed_thread",
|
|
int32_t threadPriority = PRIORITY_DEFAULT,
|
|
size_t threadStackSize = 0,
|
|
thread_id_t *threadId = 0)
|
|
{
|
|
return androidCreateThreadEtc(entryFunction, userData, threadName,
|
|
threadPriority, threadStackSize, threadId) ? true : false;
|
|
}
|
|
|
|
// Get some sort of unique identifier for the current thread.
|
|
inline thread_id_t getThreadId() {
|
|
return androidGetThreadId();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Simple mutex class. The implementation is system-dependent.
|
|
*
|
|
* The mutex must be unlocked by the thread that locked it. They are not
|
|
* recursive, i.e. the same thread can't lock it multiple times.
|
|
*/
|
|
class Mutex {
|
|
public:
|
|
enum {
|
|
PRIVATE = 0,
|
|
SHARED = 1
|
|
};
|
|
|
|
Mutex();
|
|
Mutex(const char* name);
|
|
Mutex(int type, const char* name = NULL);
|
|
~Mutex();
|
|
|
|
// lock or unlock the mutex
|
|
status_t lock();
|
|
void unlock();
|
|
|
|
// lock if possible; returns 0 on success, error otherwise
|
|
status_t tryLock();
|
|
|
|
// Manages the mutex automatically. It'll be locked when Autolock is
|
|
// constructed and released when Autolock goes out of scope.
|
|
class Autolock {
|
|
public:
|
|
inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
|
|
inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
|
|
inline ~Autolock() { mLock.unlock(); }
|
|
private:
|
|
Mutex& mLock;
|
|
};
|
|
|
|
private:
|
|
friend class Condition;
|
|
|
|
// A mutex cannot be copied
|
|
Mutex(const Mutex&);
|
|
Mutex& operator = (const Mutex&);
|
|
|
|
#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(int type, const char* name) {
|
|
if (type == SHARED) {
|
|
pthread_mutexattr_t attr;
|
|
pthread_mutexattr_init(&attr);
|
|
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
pthread_mutex_init(&mMutex, &attr);
|
|
pthread_mutexattr_destroy(&attr);
|
|
} else {
|
|
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
|
|
* mutex.
|
|
*/
|
|
|
|
typedef Mutex::Autolock AutoMutex;
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if defined(HAVE_PTHREADS)
|
|
|
|
/*
|
|
* Simple mutex class. The implementation is system-dependent.
|
|
*
|
|
* The mutex must be unlocked by the thread that locked it. They are not
|
|
* recursive, i.e. the same thread can't lock it multiple times.
|
|
*/
|
|
class RWLock {
|
|
public:
|
|
enum {
|
|
PRIVATE = 0,
|
|
SHARED = 1
|
|
};
|
|
|
|
RWLock();
|
|
RWLock(const char* name);
|
|
RWLock(int type, const char* name = NULL);
|
|
~RWLock();
|
|
|
|
status_t readLock();
|
|
status_t tryReadLock();
|
|
status_t writeLock();
|
|
status_t tryWriteLock();
|
|
void unlock();
|
|
|
|
class AutoRLock {
|
|
public:
|
|
inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); }
|
|
inline ~AutoRLock() { mLock.unlock(); }
|
|
private:
|
|
RWLock& mLock;
|
|
};
|
|
|
|
class AutoWLock {
|
|
public:
|
|
inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); }
|
|
inline ~AutoWLock() { mLock.unlock(); }
|
|
private:
|
|
RWLock& mLock;
|
|
};
|
|
|
|
private:
|
|
// A RWLock cannot be copied
|
|
RWLock(const RWLock&);
|
|
RWLock& operator = (const RWLock&);
|
|
|
|
pthread_rwlock_t mRWLock;
|
|
};
|
|
|
|
inline RWLock::RWLock() {
|
|
pthread_rwlock_init(&mRWLock, NULL);
|
|
}
|
|
inline RWLock::RWLock(const char* name) {
|
|
pthread_rwlock_init(&mRWLock, NULL);
|
|
}
|
|
inline RWLock::RWLock(int type, const char* name) {
|
|
if (type == SHARED) {
|
|
pthread_rwlockattr_t attr;
|
|
pthread_rwlockattr_init(&attr);
|
|
pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
pthread_rwlock_init(&mRWLock, &attr);
|
|
pthread_rwlockattr_destroy(&attr);
|
|
} else {
|
|
pthread_rwlock_init(&mRWLock, NULL);
|
|
}
|
|
}
|
|
inline RWLock::~RWLock() {
|
|
pthread_rwlock_destroy(&mRWLock);
|
|
}
|
|
inline status_t RWLock::readLock() {
|
|
return -pthread_rwlock_rdlock(&mRWLock);
|
|
}
|
|
inline status_t RWLock::tryReadLock() {
|
|
return -pthread_rwlock_tryrdlock(&mRWLock);
|
|
}
|
|
inline status_t RWLock::writeLock() {
|
|
return -pthread_rwlock_wrlock(&mRWLock);
|
|
}
|
|
inline status_t RWLock::tryWriteLock() {
|
|
return -pthread_rwlock_trywrlock(&mRWLock);
|
|
}
|
|
inline void RWLock::unlock() {
|
|
pthread_rwlock_unlock(&mRWLock);
|
|
}
|
|
|
|
#endif // HAVE_PTHREADS
|
|
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* Condition variable class. The implementation is system-dependent.
|
|
*
|
|
* Condition variables are paired up with mutexes. Lock the mutex,
|
|
* call wait(), then either re-wait() if things aren't quite what you want,
|
|
* or unlock the mutex and continue. All threads calling wait() must
|
|
* use the same mutex for a given Condition.
|
|
*/
|
|
class Condition {
|
|
public:
|
|
enum {
|
|
PRIVATE = 0,
|
|
SHARED = 1
|
|
};
|
|
|
|
Condition();
|
|
Condition(int type);
|
|
~Condition();
|
|
// Wait on the condition variable. Lock the mutex before calling.
|
|
status_t wait(Mutex& mutex);
|
|
// same with relative timeout
|
|
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
|
|
// Signal the condition variable, allowing one thread to continue.
|
|
void signal();
|
|
// Signal the condition variable, allowing all threads to continue.
|
|
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(int type) {
|
|
if (type == SHARED) {
|
|
pthread_condattr_t attr;
|
|
pthread_condattr_init(&attr);
|
|
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
pthread_cond_init(&mCond, &attr);
|
|
pthread_condattr_destroy(&attr);
|
|
} else {
|
|
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!
|
|
*/
|
|
|
|
class Thread : virtual public RefBase
|
|
{
|
|
public:
|
|
// Create a Thread object, but doesn't create or start the associated
|
|
// thread. See the run() method.
|
|
Thread(bool canCallJava = true);
|
|
virtual ~Thread();
|
|
|
|
// Start the thread in threadLoop() which needs to be implemented.
|
|
virtual status_t run( const char* name = 0,
|
|
int32_t priority = PRIORITY_DEFAULT,
|
|
size_t stack = 0);
|
|
|
|
// Ask this object's thread to exit. This function is asynchronous, when the
|
|
// function returns the thread might still be running. Of course, this
|
|
// function can be called from a different thread.
|
|
virtual void requestExit();
|
|
|
|
// Good place to do one-time initializations
|
|
virtual status_t readyToRun();
|
|
|
|
// Call requestExit() and wait until this object's thread exits.
|
|
// BE VERY CAREFUL of deadlocks. In particular, it would be silly to call
|
|
// this function from this object's thread. Will return WOULD_BLOCK in
|
|
// that case.
|
|
status_t requestExitAndWait();
|
|
|
|
// Wait until this object's thread exits. Returns immediately if not yet running.
|
|
// Do not call from this object's thread; will return WOULD_BLOCK in that case.
|
|
status_t join();
|
|
|
|
protected:
|
|
// exitPending() returns true if requestExit() has been called.
|
|
bool exitPending() const;
|
|
|
|
private:
|
|
// Derived class must implement threadLoop(). The thread starts its life
|
|
// here. There are two ways of using the Thread object:
|
|
// 1) loop: if threadLoop() returns true, it will be called again if
|
|
// requestExit() wasn't called.
|
|
// 2) once: if threadLoop() returns false, the thread will exit upon return.
|
|
virtual bool threadLoop() = 0;
|
|
|
|
private:
|
|
Thread& operator=(const Thread&);
|
|
static int _threadLoop(void* user);
|
|
const bool mCanCallJava;
|
|
// always hold mLock when reading or writing
|
|
thread_id_t mThread;
|
|
mutable Mutex mLock;
|
|
Condition mThreadExitedCondition;
|
|
status_t mStatus;
|
|
// note that all accesses of mExitPending and mRunning need to hold mLock
|
|
volatile bool mExitPending;
|
|
volatile bool mRunning;
|
|
sp<Thread> mHoldSelf;
|
|
#if HAVE_ANDROID_OS
|
|
int mTid;
|
|
#endif
|
|
};
|
|
|
|
|
|
}; // namespace android
|
|
|
|
#endif // __cplusplus
|
|
|
|
#endif // _LIBS_UTILS_THREADS_H
|