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(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
~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