/* 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> 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) - 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