summaryrefslogtreecommitdiff
path: root/utils/threads.h
diff options
context:
space:
mode:
Diffstat (limited to 'utils/threads.h')
-rw-r--r--utils/threads.h546
1 files changed, 546 insertions, 0 deletions
diff --git a/utils/threads.h b/utils/threads.h
new file mode 100644
index 0000000..1bcfaed
--- /dev/null
+++ b/utils/threads.h
@@ -0,0 +1,546 @@
+/*
+ * 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>
+
+#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" 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 <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();
+
+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<Thread> mHoldSelf;
+#if HAVE_ANDROID_OS
+ int mTid;
+#endif
+};
+
+
+}; // namespace android
+
+#endif // __cplusplus
+
+#endif // _LIBS_UTILS_THREADS_H