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 #include <platform_lib_macros.h> 33 34 class LocThreadDelegate { 35 LocRunnable* mRunnable; 36 bool mJoinable; 37 pthread_t mThandle; 38 pthread_mutex_t mMutex; 39 int mRefCount; 40 ~LocThreadDelegate(); 41 LocThreadDelegate(LocThread::tCreate creator, const char* threadName, 42 LocRunnable* runnable, bool joinable); 43 void destroy(); 44 public: 45 static LocThreadDelegate* create(LocThread::tCreate creator, 46 const char* threadName, LocRunnable* runnable, bool joinable); 47 void stop(); 48 // bye() is for the parent thread to go away. if joinable, 49 // parent must stop the spawned thread, join, and then 50 // destroy(); if detached, the parent can go straight 51 // ahead to destroy() 52 inline void bye() { mJoinable ? stop() : destroy(); } 53 inline bool isRunning() { return (NULL != mRunnable); } 54 static void* threadMain(void* arg); 55 }; 56 57 // it is important to note that internal members must be 58 // initialized to values as if pthread_create succeeds. 59 // This is to avoid the race condition between the threads, 60 // once the thread is created, some of these values will 61 // be check in the spawned thread, and must set correctly 62 // then and there. 63 // However, upon pthread_create failure, the data members 64 // must be set to indicate failure, e.g. mRunnable, and 65 // threashold approprietly for destroy(), e.g. mRefCount. 66 LocThreadDelegate::LocThreadDelegate(LocThread::tCreate creator, 67 const char* threadName, LocRunnable* runnable, bool joinable) : 68 mRunnable(runnable), mJoinable(joinable), mThandle(NULL), 69 mMutex(PTHREAD_MUTEX_INITIALIZER), mRefCount(2) { 70 71 // set up thread name, if nothing is passed in 72 if (!threadName) { 73 threadName = "LocThread"; 74 } 75 76 // create the thread here, then if successful 77 // and a name is given, we set the thread name 78 if (creator) { 79 mThandle = creator(threadName, threadMain, this); 80 } else if (pthread_create(&mThandle, NULL, threadMain, this)) { 81 // pthread_create() failed 82 mThandle = NULL; 83 } 84 85 if (mThandle) { 86 // set thread name 87 char lname[16]; 88 int len = (sizeof(lname)>sizeof(threadName)) ? 89 (sizeof(threadName) -1):(sizeof(lname) - 1); 90 memcpy(lname, threadName, len); 91 lname[len] = 0; 92 // set the thread name here 93 pthread_setname_np(mThandle, lname); 94 95 // detach, if not joinable 96 if (!joinable) { 97 pthread_detach(mThandle); 98 } 99 } else { 100 // must set these values upon failure 101 mRunnable = NULL; 102 mJoinable = false; 103 mRefCount = 1; 104 } 105 } 106 107 inline 108 LocThreadDelegate::~LocThreadDelegate() { 109 // at this point nothing should need done any more 110 } 111 112 // factory method so that we could return NULL upon failure 113 LocThreadDelegate* LocThreadDelegate::create(LocThread::tCreate creator, 114 const char* threadName, LocRunnable* runnable, bool joinable) { 115 LocThreadDelegate* thread = NULL; 116 if (runnable) { 117 thread = new LocThreadDelegate(creator, threadName, runnable, joinable); 118 if (thread && !thread->isRunning()) { 119 thread->destroy(); 120 thread = NULL; 121 } 122 } 123 124 return thread; 125 } 126 127 // The order is importang 128 // NULLing mRunnalbe stops the while loop in threadMain() 129 // join() if mJoinble must come before destroy() call, as 130 // the obj must remain alive at this time so that mThandle 131 // remains valud. 132 void LocThreadDelegate::stop() { 133 // mRunnable and mJoinable are reset on different triggers. 134 // mRunnable may get nulled on the spawned thread's way out; 135 // or here. 136 // mJouinable (if ever been true) gets falsed when client 137 // thread triggers stop, with either a stop() 138 // call or the client releases thread obj handle. 139 if (mRunnable) { 140 mRunnable = NULL; 141 } 142 if (mJoinable) { 143 mJoinable = false; 144 pthread_join(mThandle, NULL); 145 } 146 // call destroy() to possibly delete the obj 147 destroy(); 148 } 149 150 // method for clients to call to release the obj 151 // when it is a detached thread, the client thread 152 // and the spawned thread can both try to destroy() 153 // asynchronously. And we delete this obj when 154 // mRefCount becomes 0. 155 void LocThreadDelegate::destroy() { 156 // else case shouldn't happen, unless there is a 157 // leaking obj. But only our code here has such 158 // obj, so if we test our code well, else case 159 // will never happen 160 if (mRefCount > 0) { 161 // we need a flag on the stack 162 bool callDelete = false; 163 164 // critical section between threads 165 pthread_mutex_lock(&mMutex); 166 // last destroy() call 167 callDelete = (1 == mRefCount--); 168 pthread_mutex_unlock(&mMutex); 169 170 // upon last destroy() call we delete this obj 171 if (callDelete) { 172 delete this; 173 } 174 } 175 } 176 177 void* LocThreadDelegate::threadMain(void* arg) { 178 LocThreadDelegate* locThread = (LocThreadDelegate*)(arg); 179 180 if (locThread) { 181 LocRunnable* runnable = locThread->mRunnable; 182 183 if (runnable) { 184 if (locThread->isRunning()) { 185 runnable->prerun(); 186 } 187 188 while (locThread->isRunning() && runnable->run()); 189 190 if (locThread->isRunning()) { 191 runnable->postrun(); 192 } 193 194 // at this time, locThread->mRunnable may or may not be NULL 195 // NULL it just to be safe and clean, as we want the field 196 // in the released memory slot to be NULL. 197 locThread->mRunnable = NULL; 198 delete runnable; 199 } 200 locThread->destroy(); 201 } 202 203 return NULL; 204 } 205 206 LocThread::~LocThread() { 207 if (mThread) { 208 mThread->bye(); 209 mThread = NULL; 210 } 211 } 212 213 bool LocThread::start(tCreate creator, const char* threadName, LocRunnable* runnable, bool joinable) { 214 bool success = false; 215 if (!mThread) { 216 mThread = LocThreadDelegate::create(creator, threadName, runnable, joinable); 217 // true only if thread is created successfully 218 success = (NULL != mThread); 219 } 220 return success; 221 } 222 223 void LocThread::stop() { 224 if (mThread) { 225 mThread->stop(); 226 mThread = NULL; 227 } 228 } 229 230 #ifdef __LOC_DEBUG__ 231 232 #include <stdio.h> 233 #include <stdlib.h> 234 #include <unistd.h> 235 236 class LocRunnableTest1 : public LocRunnable { 237 int mID; 238 public: 239 LocRunnableTest1(int id) : LocRunnable(), mID(id) {} 240 virtual bool run() { 241 printf("LocRunnableTest1: %d\n", mID++); 242 sleep(1); 243 return true; 244 } 245 }; 246 247 // on linux command line: 248 // 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 249 // test detached thread: valgrind ./a.out 0 250 // test joinable thread: valgrind ./a.out 1 251 int main(int argc, char** argv) { 252 LocRunnableTest1 test(10); 253 254 LocThread thread; 255 thread.start("LocThreadTest", test, atoi(argv[1])); 256 257 sleep(10); 258 259 thread.stop(); 260 261 sleep(5); 262 263 return 0; 264 } 265 266 #endif 267