You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
8.6 KiB
266 lines
8.6 KiB
/* 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
|
|
|