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
33class 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();
43public:
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()
51    inline void bye() { mJoinable ? stop() : destroy(); }
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.
65LocThreadDelegate::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
105inline
106LocThreadDelegate::~LocThreadDelegate() {
107    // at this point nothing should need done any more
108}
109
110// factory method so that we could return NULL upon failure
111LocThreadDelegate* 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.
130void 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.
153void 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
175void* 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
204LocThread::~LocThread() {
205    if (mThread) {
206        mThread->bye();
207        mThread = NULL;
208    }
209}
210
211bool 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
221void 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
234class LocRunnableTest1 : public LocRunnable {
235    int mID;
236public:
237    LocRunnableTest1(int id) : LocRunnable(), mID(id) {}
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
249int 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