/* * 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 #include #include #if defined(HAVE_PTHREADS) # include #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" priorites we use in Android. * A thread priority should be chosen inverse-proportinally 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 = -8, /* 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. 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. extern int androidSetThreadPriority(pid_t tid, int prio); #ifdef __cplusplus } #endif // ------------------------------------------------------------------ // C++ API #ifdef __cplusplus #include #include #include 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(); 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; thread_id_t mThread; Mutex mLock; Condition mThreadExitedCondition; status_t mStatus; volatile bool mExitPending; volatile bool mRunning; sp mHoldSelf; #if HAVE_ANDROID_OS int mTid; #endif }; }; // namespace android #endif // __cplusplus #endif // _LIBS_UTILS_THREADS_H