| /* Copyright (c) 2015, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation, nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| #include <LocThread.h> |
| #include <string.h> |
| #include <pthread.h> |
| #include <platform_lib_macros.h> |
| |
| class LocThreadDelegate { |
| LocRunnable* mRunnable; |
| bool mJoinable; |
| pthread_t mThandle; |
| pthread_mutex_t mMutex; |
| int mRefCount; |
| ~LocThreadDelegate(); |
| LocThreadDelegate(LocThread::tCreate creator, const char* threadName, |
| LocRunnable* runnable, bool joinable); |
| void destroy(); |
| public: |
| static LocThreadDelegate* create(LocThread::tCreate creator, |
| const char* threadName, LocRunnable* runnable, bool joinable); |
| void stop(); |
| // bye() is for the parent thread to go away. if joinable, |
| // parent must stop the spawned thread, join, and then |
| // destroy(); if detached, the parent can go straight |
| // ahead to destroy() |
| inline void bye() { mJoinable ? stop() : destroy(); } |
| inline bool isRunning() { return (NULL != mRunnable); } |
| static void* threadMain(void* arg); |
| }; |
| |
| // it is important to note that internal members must be |
| // initialized to values as if pthread_create succeeds. |
| // This is to avoid the race condition between the threads, |
| // once the thread is created, some of these values will |
| // be check in the spawned thread, and must set correctly |
| // then and there. |
| // However, upon pthread_create failure, the data members |
| // must be set to indicate failure, e.g. mRunnable, and |
| // threashold approprietly for destroy(), e.g. mRefCount. |
| LocThreadDelegate::LocThreadDelegate(LocThread::tCreate creator, |
| const char* threadName, LocRunnable* runnable, bool joinable) : |
| mRunnable(runnable), mJoinable(joinable), mThandle(NULL), |
| mMutex(PTHREAD_MUTEX_INITIALIZER), mRefCount(2) { |
| |
| // set up thread name, if nothing is passed in |
| if (!threadName) { |
| threadName = "LocThread"; |
| } |
| |
| // create the thread here, then if successful |
| // and a name is given, we set the thread name |
| if (creator) { |
| mThandle = creator(threadName, threadMain, this); |
| } else if (pthread_create(&mThandle, NULL, threadMain, this)) { |
| // pthread_create() failed |
| mThandle = NULL; |
| } |
| |
| if (mThandle) { |
| // set thread name |
| char lname[16]; |
| int len = (sizeof(lname)>sizeof(threadName)) ? |
| (sizeof(threadName) -1):(sizeof(lname) - 1); |
| memcpy(lname, threadName, len); |
| lname[len] = 0; |
| // set the thread name here |
| pthread_setname_np(mThandle, lname); |
| |
| // detach, if not joinable |
| if (!joinable) { |
| pthread_detach(mThandle); |
| } |
| } else { |
| // must set these values upon failure |
| mRunnable = NULL; |
| mJoinable = false; |
| mRefCount = 1; |
| } |
| } |
| |
| inline |
| LocThreadDelegate::~LocThreadDelegate() { |
| // at this point nothing should need done any more |
| } |
| |
| // factory method so that we could return NULL upon failure |
| LocThreadDelegate* LocThreadDelegate::create(LocThread::tCreate creator, |
| const char* threadName, LocRunnable* runnable, bool joinable) { |
| LocThreadDelegate* thread = NULL; |
| if (runnable) { |
| thread = new LocThreadDelegate(creator, threadName, runnable, joinable); |
| if (thread && !thread->isRunning()) { |
| thread->destroy(); |
| thread = NULL; |
| } |
| } |
| |
| return thread; |
| } |
| |
| // The order is importang |
| // NULLing mRunnalbe stops the while loop in threadMain() |
| // join() if mJoinble must come before destroy() call, as |
| // the obj must remain alive at this time so that mThandle |
| // remains valud. |
| void LocThreadDelegate::stop() { |
| // mRunnable and mJoinable are reset on different triggers. |
| // mRunnable may get nulled on the spawned thread's way out; |
| // or here. |
| // mJouinable (if ever been true) gets falsed when client |
| // thread triggers stop, with either a stop() |
| // call or the client releases thread obj handle. |
| if (mRunnable) { |
| mRunnable = NULL; |
| } |
| if (mJoinable) { |
| mJoinable = false; |
| pthread_join(mThandle, NULL); |
| } |
| // call destroy() to possibly delete the obj |
| destroy(); |
| } |
| |
| // method for clients to call to release the obj |
| // when it is a detached thread, the client thread |
| // and the spawned thread can both try to destroy() |
| // asynchronously. And we delete this obj when |
| // mRefCount becomes 0. |
| void LocThreadDelegate::destroy() { |
| // else case shouldn't happen, unless there is a |
| // leaking obj. But only our code here has such |
| // obj, so if we test our code well, else case |
| // will never happen |
| if (mRefCount > 0) { |
| // we need a flag on the stack |
| bool callDelete = false; |
| |
| // critical section between threads |
| pthread_mutex_lock(&mMutex); |
| // last destroy() call |
| callDelete = (1 == mRefCount--); |
| pthread_mutex_unlock(&mMutex); |
| |
| // upon last destroy() call we delete this obj |
| if (callDelete) { |
| delete this; |
| } |
| } |
| } |
| |
| void* LocThreadDelegate::threadMain(void* arg) { |
| LocThreadDelegate* locThread = (LocThreadDelegate*)(arg); |
| |
| if (locThread) { |
| LocRunnable* runnable = locThread->mRunnable; |
| |
| if (runnable) { |
| if (locThread->isRunning()) { |
| runnable->prerun(); |
| } |
| |
| while (locThread->isRunning() && runnable->run()); |
| |
| if (locThread->isRunning()) { |
| runnable->postrun(); |
| } |
| |
| // at this time, locThread->mRunnable may or may not be NULL |
| // NULL it just to be safe and clean, as we want the field |
| // in the released memory slot to be NULL. |
| locThread->mRunnable = NULL; |
| delete runnable; |
| } |
| locThread->destroy(); |
| } |
| |
| return NULL; |
| } |
| |
| LocThread::~LocThread() { |
| if (mThread) { |
| mThread->bye(); |
| mThread = NULL; |
| } |
| } |
| |
| bool LocThread::start(tCreate creator, const char* threadName, LocRunnable* runnable, bool joinable) { |
| bool success = false; |
| if (!mThread) { |
| mThread = LocThreadDelegate::create(creator, threadName, runnable, joinable); |
| // true only if thread is created successfully |
| success = (NULL != mThread); |
| } |
| return success; |
| } |
| |
| void LocThread::stop() { |
| if (mThread) { |
| mThread->stop(); |
| mThread = NULL; |
| } |
| } |
| |
| #ifdef __LOC_DEBUG__ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| class LocRunnableTest1 : public LocRunnable { |
| int mID; |
| public: |
| LocRunnableTest1(int id) : LocRunnable(), mID(id) {} |
| virtual bool run() { |
| printf("LocRunnableTest1: %d\n", mID++); |
| sleep(1); |
| return true; |
| } |
| }; |
| |
| // on linux command line: |
| // compile: g++ -D__LOC_HOST_DEBUG__ -D__LOC_DEBUG__ -g -std=c++0x -I. -I../../../../vendor/qcom/proprietary/gps-internal/unit-tests/fakes_for_host -I../../../../system/core/include -lpthread LocThread.cpp |
| // test detached thread: valgrind ./a.out 0 |
| // test joinable thread: valgrind ./a.out 1 |
| int main(int argc, char** argv) { |
| LocRunnableTest1 test(10); |
| |
| LocThread thread; |
| thread.start("LocThreadTest", test, atoi(argv[1])); |
| |
| sleep(10); |
| |
| thread.stop(); |
| |
| sleep(5); |
| |
| return 0; |
| } |
| |
| #endif |