| /* | 
 |  * Copyright 2012 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "SkTypes.h" | 
 |  | 
 | #include "SkThreadUtils.h" | 
 | #include "SkThreadUtils_pthread.h" | 
 |  | 
 | #include <pthread.h> | 
 | #include <signal.h> | 
 |  | 
 | PThreadEvent::PThreadEvent() : fConditionFlag(false) { | 
 |     pthread_cond_init(&fCondition, NULL); | 
 |     pthread_mutex_init(&fConditionMutex, NULL); | 
 | } | 
 | PThreadEvent::~PThreadEvent() { | 
 |     pthread_mutex_destroy(&fConditionMutex); | 
 |     pthread_cond_destroy(&fCondition); | 
 | } | 
 | void PThreadEvent::trigger() { | 
 |     pthread_mutex_lock(&fConditionMutex); | 
 |     fConditionFlag = true; | 
 |     pthread_cond_signal(&fCondition); | 
 |     pthread_mutex_unlock(&fConditionMutex); | 
 | } | 
 | void PThreadEvent::wait() { | 
 |     pthread_mutex_lock(&fConditionMutex); | 
 |     while (!fConditionFlag) { | 
 |         pthread_cond_wait(&fCondition, &fConditionMutex); | 
 |     } | 
 |     pthread_mutex_unlock(&fConditionMutex); | 
 | } | 
 | bool PThreadEvent::isTriggered() { | 
 |     bool currentFlag; | 
 |     pthread_mutex_lock(&fConditionMutex); | 
 |     currentFlag = fConditionFlag; | 
 |     pthread_mutex_unlock(&fConditionMutex); | 
 |     return currentFlag; | 
 | } | 
 |  | 
 | SkThread_PThreadData::SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data) | 
 |     : fPThread() | 
 |     , fValidPThread(false) | 
 |     , fParam(data) | 
 |     , fEntryPoint(entryPoint) | 
 | { | 
 |     pthread_attr_init(&fAttr); | 
 |     pthread_attr_setdetachstate(&fAttr, PTHREAD_CREATE_JOINABLE); | 
 | } | 
 |  | 
 | SkThread_PThreadData::~SkThread_PThreadData() { | 
 |     pthread_attr_destroy(&fAttr); | 
 | } | 
 |  | 
 | static void* thread_start(void* arg) { | 
 |     SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(arg); | 
 |     // Wait for start signal | 
 |     pthreadData->fStarted.wait(); | 
 |  | 
 |     // Call entry point only if thread was not canceled before starting. | 
 |     if (!pthreadData->fCanceled.isTriggered()) { | 
 |         pthreadData->fEntryPoint(pthreadData->fParam); | 
 |     } | 
 |     return NULL; | 
 | } | 
 |  | 
 | SkThread::SkThread(entryPointProc entryPoint, void* data) { | 
 |     SkThread_PThreadData* pthreadData = new SkThread_PThreadData(entryPoint, data); | 
 |     fData = pthreadData; | 
 |  | 
 |     int ret = pthread_create(&(pthreadData->fPThread), | 
 |                              &(pthreadData->fAttr), | 
 |                              thread_start, | 
 |                              pthreadData); | 
 |  | 
 |     pthreadData->fValidPThread = (0 == ret); | 
 | } | 
 |  | 
 | SkThread::~SkThread() { | 
 |     if (fData != NULL) { | 
 |         SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); | 
 |         // If created thread but start was never called, kill the thread. | 
 |         if (pthreadData->fValidPThread && !pthreadData->fStarted.isTriggered()) { | 
 |             pthreadData->fCanceled.trigger(); | 
 |             if (this->start()) { | 
 |                 this->join(); | 
 |             } | 
 |         } | 
 |         delete pthreadData; | 
 |     } | 
 | } | 
 |  | 
 | bool SkThread::start() { | 
 |     SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); | 
 |     if (!pthreadData->fValidPThread) { | 
 |         return false; | 
 |     } | 
 |  | 
 |     if (pthreadData->fStarted.isTriggered()) { | 
 |         return false; | 
 |     } | 
 |     pthreadData->fStarted.trigger(); | 
 |     return true; | 
 | } | 
 |  | 
 | void SkThread::join() { | 
 |     SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData); | 
 |     if (!pthreadData->fValidPThread || !pthreadData->fStarted.isTriggered()) { | 
 |         return; | 
 |     } | 
 |  | 
 |     pthread_join(pthreadData->fPThread, NULL); | 
 | } |