1 /* Copyright (c) 2015, The Linux Foundation. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are 5 * met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer in the documentation and/or other materials provided 11 * with the distribution. 12 * * Neither the name of The Linux Foundation, nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 */ 29 #include <LocThread.h> 30 #include <string.h> 31 #include <pthread.h> 32 33 class LocThreadDelegate { 34 LocRunnable* mRunnable; 35 bool mJoinable; 36 pthread_t mThandle; 37 pthread_mutex_t mMutex; 38 int mRefCount; 39 ~LocThreadDelegate(); 40 LocThreadDelegate(LocThread::tCreate creator, const char* threadName, 41 LocRunnable* runnable, bool joinable); 42 void destroy(); 43 public: 44 static LocThreadDelegate* create(LocThread::tCreate creator, 45 const char* threadName, LocRunnable* runnable, bool joinable); 46 void stop(); 47 // bye() is for the parent thread to go away. if joinable, 48 // parent must stop the spawned thread, join, and then 49 // destroy(); if detached, the parent can go straight 50 // ahead to destroy() 51 inline void bye() { mJoinable ? stop() : destroy(); } 52 inline bool isRunning() { return (NULL != mRunnable); } 53 static void* threadMain(void* arg); 54 }; 55 56 // it is important to note that internal members must be 57 // initialized to values as if pthread_create succeeds. 58 // This is to avoid the race condition between the threads, 59 // once the thread is created, some of these values will 60 // be check in the spawned thread, and must set correctly 61 // then and there. 62 // However, upon pthread_create failure, the data members 63 // must be set to indicate failure, e.g. mRunnable, and 64 // threashold approprietly for destroy(), e.g. mRefCount. 65 LocThreadDelegate::LocThreadDelegate(LocThread::tCreate creator, 66 const char* threadName, LocRunnable* runnable, bool joinable) : 67 mRunnable(runnable), mJoinable(joinable), mThandle(NULL), 68 mMutex(PTHREAD_MUTEX_INITIALIZER), mRefCount(2) { 69 70 // set up thread name, if nothing is passed in 71 if (!threadName) { 72 threadName = "LocThread"; 73 } 74 75 // create the thread here, then if successful 76 // and a name is given, we set the thread name 77 if (creator) { 78 mThandle = creator(threadName, threadMain, this); 79 } else if (pthread_create(&mThandle, NULL, threadMain, this)) { 80 // pthread_create() failed 81 mThandle = NULL; 82 } 83 84 if (mThandle) { 85 // set thread name 86 char lname[16]; 87 int len = sizeof(lname) - 1; 88 memcpy(lname, threadName, len); 89 lname[len] = 0; 90 // set the thread name here 91 pthread_setname_np(mThandle, lname); 92 93 // detach, if not joinable 94 if (!joinable) { 95 pthread_detach(mThandle); 96 } 97 } else { 98 // must set these values upon failure 99 mRunnable = NULL; 100 mJoinable = false; 101 mRefCount = 1; 102 } 103 } 104 105 inline 106 LocThreadDelegate::~LocThreadDelegate() { 107 // at this point nothing should need done any more 108 } 109 110 // factory method so that we could return NULL upon failure 111 LocThreadDelegate* LocThreadDelegate::create(LocThread::tCreate creator, 112 const char* threadName, LocRunnable* runnable, bool joinable) { 113 LocThreadDelegate* thread = NULL; 114 if (runnable) { 115 thread = new LocThreadDelegate(creator, threadName, runnable, joinable); 116 if (thread && !thread->isRunning()) { 117 thread->destroy(); 118 thread = NULL; 119 } 120 } 121 122 return thread; 123 } 124 125 // The order is importang 126 // NULLing mRunnalbe stops the while loop in threadMain() 127 // join() if mJoinble must come before destroy() call, as 128 // the obj must remain alive at this time so that mThandle 129 // remains valud. 130 void LocThreadDelegate::stop() { 131 // mRunnable and mJoinable are reset on different triggers. 132 // mRunnable may get nulled on the spawned thread's way out; 133 // or here. 134 // mJouinable (if ever been true) gets falsed when client 135 // thread triggers stop, with either a stop() 136 // call or the client releases thread obj handle. 137 if (mRunnable) { 138 mRunnable = NULL; 139 } 140 if (mJoinable) { 141 mJoinable = false; 142 pthread_join(mThandle, NULL); 143 } 144 // call destroy() to possibly delete the obj 145 destroy(); 146 } 147 148 // method for clients to call to release the obj 149 // when it is a detached thread, the client thread 150 // and the spawned thread can both try to destroy() 151 // asynchronously. And we delete this obj when 152 // mRefCount becomes 0. 153 void LocThreadDelegate::destroy() { 154 // else case shouldn't happen, unless there is a 155 // leaking obj. But only our code here has such 156 // obj, so if we test our code well, else case 157 // will never happen 158 if (mRefCount > 0) { 159 // we need a flag on the stack 160 bool callDelete = false; 161 162 // critical section between threads 163 pthread_mutex_lock(&mMutex); 164 // last destroy() call 165 callDelete = (1 == mRefCount--); 166 pthread_mutex_unlock(&mMutex); 167 168 // upon last destroy() call we delete this obj 169 if (callDelete) { 170 delete this; 171 } 172 } 173 } 174 175 void* LocThreadDelegate::threadMain(void* arg) { 176 LocThreadDelegate* locThread = (LocThreadDelegate*)(arg); 177 178 if (locThread) { 179 LocRunnable* runnable = locThread->mRunnable; 180 181 if (runnable) { 182 if (locThread->isRunning()) { 183 runnable->prerun(); 184 } 185 186 while (locThread->isRunning() && runnable->run()); 187 188 if (locThread->isRunning()) { 189 runnable->postrun(); 190 } 191 192 // at this time, locThread->mRunnable may or may not be NULL 193 // NULL it just to be safe and clean, as we want the field 194 // in the released memory slot to be NULL. 195 locThread->mRunnable = NULL; 196 delete runnable; 197 } 198 locThread->destroy(); 199 } 200 201 return NULL; 202 } 203 204 LocThread::~LocThread() { 205 if (mThread) { 206 mThread->bye(); 207 mThread = NULL; 208 } 209 } 210 211 bool LocThread::start(tCreate creator, const char* threadName, LocRunnable* runnable, bool joinable) { 212 bool success = false; 213 if (!mThread) { 214 mThread = LocThreadDelegate::create(creator, threadName, runnable, joinable); 215 // true only if thread is created successfully 216 success = (NULL != mThread); 217 } 218 return success; 219 } 220 221 void LocThread::stop() { 222 if (mThread) { 223 mThread->stop(); 224 mThread = NULL; 225 } 226 } 227 228 #ifdef __LOC_DEBUG__ 229 230 #include <stdio.h> 231 #include <stdlib.h> 232 #include <unistd.h> 233 234 class LocRunnableTest1 : public LocRunnable { 235 int mID; 236 public: 237 LocRunnableTest1(int id) : LocRunnable(), mID(id) {} 238 virtual bool run() { 239 printf("LocRunnableTest1: %d\n", mID++); 240 sleep(1); 241 return true; 242 } 243 }; 244 245 // on linux command line: 246 // 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 247 // test detached thread: valgrind ./a.out 0 248 // test joinable thread: valgrind ./a.out 1 249 int main(int argc, char** argv) { 250 LocRunnableTest1 test(10); 251 252 LocThread thread; 253 thread.start("LocThreadTest", test, atoi(argv[1])); 254 255 sleep(10); 256 257 thread.stop(); 258 259 sleep(5); 260 261 return 0; 262 } 263 264 #endif 265