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