1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkTypes.h"
9
10#include "SkThreadUtils.h"
11#include "SkThreadUtils_pthread.h"
12
13#include <pthread.h>
14#include <signal.h>
15
16PThreadEvent::PThreadEvent() : fConditionFlag(false) {
17    pthread_cond_init(&fCondition, NULL);
18    pthread_mutex_init(&fConditionMutex, NULL);
19}
20PThreadEvent::~PThreadEvent() {
21    pthread_mutex_destroy(&fConditionMutex);
22    pthread_cond_destroy(&fCondition);
23}
24void PThreadEvent::trigger() {
25    pthread_mutex_lock(&fConditionMutex);
26    fConditionFlag = true;
27    pthread_cond_signal(&fCondition);
28    pthread_mutex_unlock(&fConditionMutex);
29}
30void PThreadEvent::wait() {
31    pthread_mutex_lock(&fConditionMutex);
32    while (!fConditionFlag) {
33        pthread_cond_wait(&fCondition, &fConditionMutex);
34    }
35    pthread_mutex_unlock(&fConditionMutex);
36}
37bool PThreadEvent::isTriggered() {
38    bool currentFlag;
39    pthread_mutex_lock(&fConditionMutex);
40    currentFlag = fConditionFlag;
41    pthread_mutex_unlock(&fConditionMutex);
42    return currentFlag;
43}
44
45SkThread_PThreadData::SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data)
46    : fPThread()
47    , fValidPThread(false)
48    , fParam(data)
49    , fEntryPoint(entryPoint)
50{
51    pthread_attr_init(&fAttr);
52    pthread_attr_setdetachstate(&fAttr, PTHREAD_CREATE_JOINABLE);
53}
54
55SkThread_PThreadData::~SkThread_PThreadData() {
56    pthread_attr_destroy(&fAttr);
57}
58
59static void* thread_start(void* arg) {
60    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(arg);
61    // Wait for start signal
62    pthreadData->fStarted.wait();
63
64    // Call entry point only if thread was not canceled before starting.
65    if (!pthreadData->fCanceled.isTriggered()) {
66        pthreadData->fEntryPoint(pthreadData->fParam);
67    }
68    return NULL;
69}
70
71SkThread::SkThread(entryPointProc entryPoint, void* data) {
72    SkThread_PThreadData* pthreadData = new SkThread_PThreadData(entryPoint, data);
73    fData = pthreadData;
74
75    int ret = pthread_create(&(pthreadData->fPThread),
76                             &(pthreadData->fAttr),
77                             thread_start,
78                             pthreadData);
79
80    pthreadData->fValidPThread = (0 == ret);
81}
82
83SkThread::~SkThread() {
84    if (fData != NULL) {
85        SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
86        // If created thread but start was never called, kill the thread.
87        if (pthreadData->fValidPThread && !pthreadData->fStarted.isTriggered()) {
88            pthreadData->fCanceled.trigger();
89            if (this->start()) {
90                this->join();
91            }
92        }
93        delete pthreadData;
94    }
95}
96
97bool SkThread::start() {
98    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
99    if (!pthreadData->fValidPThread) {
100        return false;
101    }
102
103    if (pthreadData->fStarted.isTriggered()) {
104        return false;
105    }
106    pthreadData->fStarted.trigger();
107    return true;
108}
109
110void SkThread::join() {
111    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
112    if (!pthreadData->fValidPThread || !pthreadData->fStarted.isTriggered()) {
113        return;
114    }
115
116    pthread_join(pthreadData->fPThread, NULL);
117}
118