diff options
Diffstat (limited to 'media/omx-plugin/include/ics/utils/threads.h')
-rw-r--r-- | media/omx-plugin/include/ics/utils/threads.h | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/media/omx-plugin/include/ics/utils/threads.h b/media/omx-plugin/include/ics/utils/threads.h new file mode 100644 index 000000000..ab3e8cdb6 --- /dev/null +++ b/media/omx-plugin/include/ics/utils/threads.h @@ -0,0 +1,564 @@ +/* + * 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 |