// // Copyright 2016 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // WorkerThread: // Asychronous tasks/threads for ANGLE, similar to a TaskRunner in Chromium. // Can be implemented as different targets, depending on platform. // #ifndef LIBANGLE_WORKER_THREAD_H_ #define LIBANGLE_WORKER_THREAD_H_ #include #include #include "common/debug.h" #include "libANGLE/features.h" #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) #include #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) namespace angle { // Indicates whether a WaitableEvent should automatically reset the event state after a single // waiting thread has been released or remain signaled until reset() is manually invoked. enum class EventResetPolicy { Manual, Automatic }; // Specify the initial state on creation. enum class EventInitialState { NonSignaled, Signaled }; // A callback function with no return value and no arguments. class Closure { public: virtual ~Closure() = default; virtual void operator()() = 0; }; namespace priv { // An event that we can wait on, useful for joining worker threads. template class WaitableEventBase : angle::NonCopyable { public: WaitableEventBase(EventResetPolicy resetPolicy, EventInitialState initialState); WaitableEventBase(WaitableEventBase &&other); // Puts the event in the un-signaled state. void reset(); // Waits indefinitely for the event to be signaled. void wait(); // Puts the event in the signaled state, causing any thread blocked on Wait to be woken up. // The event state is reset to non-signaled after a waiting thread has been released. void signal(); protected: Impl ©Base(Impl &&other); template static size_t WaitManyBase(std::array *waitables); EventResetPolicy mResetPolicy; bool mSignaled; }; template WaitableEventBase::WaitableEventBase(EventResetPolicy resetPolicy, EventInitialState initialState) : mResetPolicy(resetPolicy), mSignaled(initialState == EventInitialState::Signaled) { } template WaitableEventBase::WaitableEventBase(WaitableEventBase &&other) : mResetPolicy(other.mResetPolicy), mSignaled(other.mSignaled) { } template void WaitableEventBase::reset() { static_cast(this)->resetImpl(); } template void WaitableEventBase::wait() { static_cast(this)->waitImpl(); } template void WaitableEventBase::signal() { static_cast(this)->signalImpl(); } template template // static size_t WaitableEventBase::WaitManyBase(std::array *waitables) { ASSERT(Count > 0); for (size_t index = 0; index < Count; ++index) { (*waitables)[index].wait(); } return 0; } template Impl &WaitableEventBase::copyBase(Impl &&other) { std::swap(mSignaled, other.mSignaled); std::swap(mResetPolicy, other.mResetPolicy); return *static_cast(this); } class SingleThreadedWaitableEvent : public WaitableEventBase { public: SingleThreadedWaitableEvent(); SingleThreadedWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState); ~SingleThreadedWaitableEvent(); SingleThreadedWaitableEvent(SingleThreadedWaitableEvent &&other); SingleThreadedWaitableEvent &operator=(SingleThreadedWaitableEvent &&other); void resetImpl(); void waitImpl(); void signalImpl(); // Wait, synchronously, on multiple events. // returns the index of a WaitableEvent which has been signaled. template static size_t WaitMany(std::array *waitables); }; template // static size_t SingleThreadedWaitableEvent::WaitMany( std::array *waitables) { return WaitableEventBase::WaitManyBase(waitables); } #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) class AsyncWaitableEvent : public WaitableEventBase { public: AsyncWaitableEvent(); AsyncWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState); ~AsyncWaitableEvent(); AsyncWaitableEvent(AsyncWaitableEvent &&other); AsyncWaitableEvent &operator=(AsyncWaitableEvent &&other); void resetImpl(); void waitImpl(); void signalImpl(); // Wait, synchronously, on multiple events. // returns the index of a WaitableEvent which has been signaled. template static size_t WaitMany(std::array *waitables); private: friend class AsyncWorkerPool; void setFuture(std::future &&future); std::future mFuture; }; template // static size_t AsyncWaitableEvent::WaitMany(std::array *waitables) { return WaitableEventBase::WaitManyBase(waitables); } #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) // The traits class allows the the thread pool to return the "Typed" waitable event from postTask. // Otherwise postTask would always think it returns the current active type, so the unit tests // could not run on multiple worker types in the same compilation. template struct WorkerThreadPoolTraits; class SingleThreadedWorkerPool; template <> struct WorkerThreadPoolTraits { using WaitableEventType = SingleThreadedWaitableEvent; }; #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) class AsyncWorkerPool; template <> struct WorkerThreadPoolTraits { using WaitableEventType = AsyncWaitableEvent; }; #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) // Request WorkerThreads from the WorkerThreadPool. Each pool can keep worker threads around so // we avoid the costly spin up and spin down time. template class WorkerThreadPoolBase : angle::NonCopyable { public: WorkerThreadPoolBase(size_t maxThreads); ~WorkerThreadPoolBase(); using WaitableEventType = typename WorkerThreadPoolTraits::WaitableEventType; // Returns an event to wait on for the task to finish. // If the pool fails to create the task, returns null. WaitableEventType postWorkerTask(Closure *task); }; template WorkerThreadPoolBase::WorkerThreadPoolBase(size_t maxThreads) { } template WorkerThreadPoolBase::~WorkerThreadPoolBase() { } template typename WorkerThreadPoolBase::WaitableEventType WorkerThreadPoolBase::postWorkerTask( Closure *task) { return static_cast(this)->postWorkerTaskImpl(task); } class SingleThreadedWorkerPool : public WorkerThreadPoolBase { public: SingleThreadedWorkerPool(size_t maxThreads); ~SingleThreadedWorkerPool(); SingleThreadedWaitableEvent postWorkerTaskImpl(Closure *task); }; #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) class AsyncWorkerPool : public WorkerThreadPoolBase { public: AsyncWorkerPool(size_t maxThreads); ~AsyncWorkerPool(); AsyncWaitableEvent postWorkerTaskImpl(Closure *task); }; #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) } // namespace priv #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) using WaitableEvent = priv::AsyncWaitableEvent; using WorkerThreadPool = priv::AsyncWorkerPool; #else using WaitableEvent = priv::SingleThreadedWaitableEvent; using WorkerThreadPool = priv::SingleThreadedWorkerPool; #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) } // namespace angle #endif // LIBANGLE_WORKER_THREAD_H_