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