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()
bye()52 inline void bye() { mJoinable ? stop() : destroy(); }
isRunning()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.
LocThreadDelegate(LocThread::tCreate creator,const char * threadName,LocRunnable * runnable,bool joinable)66 LocThreadDelegate::LocThreadDelegate(LocThread::tCreate creator,
67 const char* threadName, LocRunnable* runnable, bool joinable) :
68 mRunnable(runnable), mJoinable(joinable), mThandle((pthread_t)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 = (pthread_t)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
~LocThreadDelegate()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
create(LocThread::tCreate creator,const char * threadName,LocRunnable * runnable,bool joinable)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.
stop()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.
destroy()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
threadMain(void * arg)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
~LocThread()206 LocThread::~LocThread() {
207 if (mThread) {
208 mThread->bye();
209 mThread = NULL;
210 }
211 }
212
start(tCreate creator,const char * threadName,LocRunnable * runnable,bool joinable)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
stop()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:
LocRunnableTest1(int id)239 LocRunnableTest1(int id) : LocRunnable(), mID(id) {}
run()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
main(int argc,char ** argv)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