1/*
2 * Copyright (C) 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#ifndef AsyncTask_h
27#define AsyncTask_h
28
29#include "BAssert.h"
30#include "Inline.h"
31#include "Mutex.h"
32#include <atomic>
33#include <condition_variable>
34#include <pthread.h>
35#include <thread>
36
37namespace bmalloc {
38
39template<typename Object, typename Function>
40class AsyncTask {
41public:
42    AsyncTask(Object&, const Function&);
43
44    void run();
45    void join();
46
47private:
48    enum State { Exited, Sleeping, Running, Signaled };
49
50    static const constexpr std::chrono::seconds exitDelay = std::chrono::seconds(1);
51
52    void runSlowCase();
53
54    static void* pthreadEntryPoint(void*);
55    void entryPoint();
56
57    std::atomic<State> m_state;
58
59    Mutex m_conditionMutex;
60    std::condition_variable_any m_condition;
61    pthread_t m_thread;
62
63    Object& m_object;
64    Function m_function;
65};
66
67template<typename Object, typename Function> const std::chrono::seconds AsyncTask<Object, Function>::exitDelay;
68
69template<typename Object, typename Function>
70AsyncTask<Object, Function>::AsyncTask(Object& object, const Function& function)
71    : m_state(Exited)
72    , m_condition()
73    , m_thread()
74    , m_object(object)
75    , m_function(function)
76{
77}
78
79template<typename Object, typename Function>
80void AsyncTask<Object, Function>::join()
81{
82    if (m_state == Exited)
83        return;
84
85    { std::lock_guard<Mutex> lock(m_conditionMutex); }
86    m_condition.notify_one();
87
88    while (m_state != Exited)
89        std::this_thread::yield();
90}
91
92template<typename Object, typename Function>
93inline void AsyncTask<Object, Function>::run()
94{
95    if (m_state == Signaled)
96        return;
97    runSlowCase();
98}
99
100template<typename Object, typename Function>
101NO_INLINE void AsyncTask<Object, Function>::runSlowCase()
102{
103    State oldState = m_state.exchange(Signaled);
104    if (oldState == Signaled || oldState == Running)
105        return;
106
107    if (oldState == Sleeping) {
108        { std::lock_guard<Mutex> lock(m_conditionMutex); }
109        m_condition.notify_one();
110        return;
111    }
112
113    BASSERT(oldState == Exited);
114    pthread_create(&m_thread, nullptr, &pthreadEntryPoint, this);
115    pthread_detach(m_thread);
116}
117
118template<typename Object, typename Function>
119void* AsyncTask<Object, Function>::pthreadEntryPoint(void* asyncTask)
120{
121    static_cast<AsyncTask*>(asyncTask)->entryPoint();
122    return nullptr;
123}
124
125template<typename Object, typename Function>
126void AsyncTask<Object, Function>::entryPoint()
127{
128    while (1) {
129        State expectedState = Signaled;
130        if (m_state.compare_exchange_weak(expectedState, Running))
131            (m_object.*m_function)();
132
133        expectedState = Running;
134        if (m_state.compare_exchange_weak(expectedState, Sleeping)) {
135            std::unique_lock<Mutex> lock(m_conditionMutex);
136            m_condition.wait_for(lock, exitDelay, [=]() { return this->m_state != Sleeping; });
137        }
138
139        expectedState = Sleeping;
140        if (m_state.compare_exchange_weak(expectedState, Exited))
141            return;
142    }
143}
144
145} // namespace bmalloc
146
147#endif // AsyncTask_h
148