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
34class 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();
44public:
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.
66LocThreadDelegate::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
107inline
108LocThreadDelegate::~LocThreadDelegate() {
109    // at this point nothing should need done any more
110}
111
112// factory method so that we could return NULL upon failure
113LocThreadDelegate* 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.
132void 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.
155void 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
177void* 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
206LocThread::~LocThread() {
207    if (mThread) {
208        mThread->bye();
209        mThread = NULL;
210    }
211}
212
213bool 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
223void 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
236class LocRunnableTest1 : public LocRunnable {
237    int mID;
238public:
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
251int 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