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