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