Home | History | Annotate | Download | only in utils
      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()
     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.
     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
    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
    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.
    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.
    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 
    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 
    206 LocThread::~LocThread() {
    207     if (mThread) {
    208         mThread->bye();
    209         mThread = NULL;
    210     }
    211 }
    212 
    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 
    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:
    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
    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