summaryrefslogtreecommitdiff
path: root/utils/LocThread.cpp
blob: 568a6bb34d52d8732d701ed79185055e30978b62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <LocThread.h>
#include <string.h>
#include <pthread.h>
#include <loc_pla.h>

class LocThreadDelegate {
    LocRunnable* mRunnable;
    bool mJoinable;
    pthread_t mThandle;
    pthread_mutex_t mMutex;
    int mRefCount;
    ~LocThreadDelegate();
    LocThreadDelegate(LocThread::tCreate creator, const char* threadName,
                      LocRunnable* runnable, bool joinable);
    void destroy();
public:
    static LocThreadDelegate* create(LocThread::tCreate creator,
            const char* threadName, LocRunnable* runnable, bool joinable);
    void stop();
    // bye() is for the parent thread to go away. if joinable,
    // parent must stop the spawned thread, join, and then
    // destroy(); if detached, the parent can go straight
    // ahead to destroy()
    inline void bye() { mJoinable ? stop() : destroy(); }
    inline bool isRunning() { return (NULL != mRunnable); }
    static void* threadMain(void* arg);
};

// it is important to note that internal members must be
// initialized to values as if pthread_create succeeds.
// This is to avoid the race condition between the threads,
// once the thread is created, some of these values will
// be check in the spawned thread, and must set correctly
// then and there.
// However, upon pthread_create failure, the data members
// must be set to  indicate failure, e.g. mRunnable, and
// threashold approprietly for destroy(), e.g. mRefCount.
LocThreadDelegate::LocThreadDelegate(LocThread::tCreate creator,
        const char* threadName, LocRunnable* runnable, bool joinable) :
    mRunnable(runnable), mJoinable(joinable), mThandle((pthread_t)NULL),
    mMutex(PTHREAD_MUTEX_INITIALIZER), mRefCount(2) {

    // set up thread name, if nothing is passed in
    if (!threadName) {
        threadName = "LocThread";
    }

    // create the thread here, then if successful
    // and a name is given, we set the thread name
    if (creator) {
        mThandle = creator(threadName, threadMain, this);
    } else if (pthread_create(&mThandle, NULL, threadMain, this)) {
        // pthread_create() failed
        mThandle = (pthread_t)NULL;
    }

    if (mThandle) {
        // set thread name
        char lname[16];
        int len = (sizeof(lname) > (strlen(threadName) + 1)) ?
          (strlen(threadName)):(sizeof(lname) - 1);
        memcpy(lname, threadName, len);
        lname[len] = 0;
        // set the thread name here
        pthread_setname_np(mThandle, lname);

        // detach, if not joinable
        if (!joinable) {
            pthread_detach(mThandle);
        }
    } else {
        // must set these values upon failure
        mRunnable = NULL;
        mJoinable = false;
        mRefCount = 1;
    }
}

inline
LocThreadDelegate::~LocThreadDelegate() {
    // at this point nothing should need done any more
}

// factory method so that we could return NULL upon failure
LocThreadDelegate* LocThreadDelegate::create(LocThread::tCreate creator,
        const char* threadName, LocRunnable* runnable, bool joinable) {
    LocThreadDelegate* thread = NULL;
    if (runnable) {
        thread = new LocThreadDelegate(creator, threadName, runnable, joinable);
        if (thread && !thread->isRunning()) {
            thread->destroy();
            thread = NULL;
        }
    }

    return thread;
}

// The order is importang
// NULLing mRunnalbe stops the while loop in threadMain()
// join() if mJoinble must come before destroy() call, as
// the obj must remain alive at this time so that mThandle
// remains valud.
void LocThreadDelegate::stop() {
    // mRunnable and mJoinable are reset on different triggers.
    // mRunnable may get nulled on the spawned thread's way out;
    //           or here.
    // mJouinable (if ever been true) gets falsed when client
    //            thread triggers stop, with either a stop()
    //            call or the client releases thread obj handle.
    if (mRunnable) {
        mRunnable = NULL;
    }
    if (mJoinable) {
        mJoinable = false;
        pthread_join(mThandle, NULL);
    }
    // call destroy() to possibly delete the obj
    destroy();
}

// method for clients to call to release the obj
// when it is a detached thread, the client thread
// and the spawned thread can both try to destroy()
// asynchronously. And we delete this obj when
// mRefCount becomes 0.
void LocThreadDelegate::destroy() {
    // else case shouldn't happen, unless there is a
    // leaking obj. But only our code here has such
    // obj, so if we test our code well, else case
    // will never happen
    if (mRefCount > 0) {
        // we need a flag on the stack
        bool callDelete = false;

        // critical section between threads
        pthread_mutex_lock(&mMutex);
        // last destroy() call
        callDelete = (1 == mRefCount--);
        pthread_mutex_unlock(&mMutex);

        // upon last destroy() call we delete this obj
        if (callDelete) {
            delete this;
        }
    }
}

void* LocThreadDelegate::threadMain(void* arg) {
    LocThreadDelegate* locThread = (LocThreadDelegate*)(arg);

    if (locThread) {
        LocRunnable* runnable = locThread->mRunnable;

        if (runnable) {
            if (locThread->isRunning()) {
                runnable->prerun();
            }

            while (locThread->isRunning() && runnable->run());

            if (locThread->isRunning()) {
                runnable->postrun();
            }

            // at this time, locThread->mRunnable may or may not be NULL
            // NULL it just to be safe and clean, as we want the field
            // in the released memory slot to be NULL.
            locThread->mRunnable = NULL;
            delete runnable;
        }
        locThread->destroy();
    }

    return NULL;
}

LocThread::~LocThread() {
    if (mThread) {
        mThread->bye();
        mThread = NULL;
    }
}

bool LocThread::start(tCreate creator, const char* threadName, LocRunnable* runnable, bool joinable) {
    bool success = false;
    if (!mThread) {
        mThread = LocThreadDelegate::create(creator, threadName, runnable, joinable);
        // true only if thread is created successfully
        success = (NULL != mThread);
    }
    return success;
}

void LocThread::stop() {
    if (mThread) {
        mThread->stop();
        mThread = NULL;
    }
}

#ifdef __LOC_DEBUG__

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

class LocRunnableTest1 : public LocRunnable {
    int mID;
public:
    LocRunnableTest1(int id) : LocRunnable(), mID(id) {}
    virtual bool run() {
        printf("LocRunnableTest1: %d\n", mID++);
        sleep(1);
        return true;
    }
};

// on linux command line:
// 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
// test detached thread: valgrind ./a.out 0
// test joinable thread: valgrind ./a.out 1
int main(int argc, char** argv) {
    LocRunnableTest1 test(10);

    LocThread thread;
    thread.start("LocThreadTest", test, atoi(argv[1]));

    sleep(10);

    thread.stop();

    sleep(5);

    return 0;
}

#endif