| // | |
| // 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 <array> | |
| #include <vector> | |
| #include "common/debug.h" | |
| #include "libANGLE/features.h" | |
| #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| #include <future> | |
| #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 <typename Impl> | |
| 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 <size_t Count> | |
| static size_t WaitManyBase(std::array<Impl, Count> *waitables); | |
| EventResetPolicy mResetPolicy; | |
| bool mSignaled; | |
| }; | |
| template <typename Impl> | |
| WaitableEventBase<Impl>::WaitableEventBase(EventResetPolicy resetPolicy, | |
| EventInitialState initialState) | |
| : mResetPolicy(resetPolicy), mSignaled(initialState == EventInitialState::Signaled) | |
| { | |
| } | |
| template <typename Impl> | |
| WaitableEventBase<Impl>::WaitableEventBase(WaitableEventBase &&other) | |
| : mResetPolicy(other.mResetPolicy), mSignaled(other.mSignaled) | |
| { | |
| } | |
| template <typename Impl> | |
| void WaitableEventBase<Impl>::reset() | |
| { | |
| static_cast<Impl *>(this)->resetImpl(); | |
| } | |
| template <typename Impl> | |
| void WaitableEventBase<Impl>::wait() | |
| { | |
| static_cast<Impl *>(this)->waitImpl(); | |
| } | |
| template <typename Impl> | |
| void WaitableEventBase<Impl>::signal() | |
| { | |
| static_cast<Impl *>(this)->signalImpl(); | |
| } | |
| template <typename Impl> | |
| template <size_t Count> | |
| // static | |
| size_t WaitableEventBase<Impl>::WaitManyBase(std::array<Impl, Count> *waitables) | |
| { | |
| ASSERT(Count > 0); | |
| for (size_t index = 0; index < Count; ++index) | |
| { | |
| (*waitables)[index].wait(); | |
| } | |
| return 0; | |
| } | |
| template <typename Impl> | |
| Impl &WaitableEventBase<Impl>::copyBase(Impl &&other) | |
| { | |
| std::swap(mSignaled, other.mSignaled); | |
| std::swap(mResetPolicy, other.mResetPolicy); | |
| return *static_cast<Impl *>(this); | |
| } | |
| class SingleThreadedWaitableEvent : public WaitableEventBase<SingleThreadedWaitableEvent> | |
| { | |
| 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 <size_t Count> | |
| static size_t WaitMany(std::array<SingleThreadedWaitableEvent, Count> *waitables); | |
| }; | |
| template <size_t Count> | |
| // static | |
| size_t SingleThreadedWaitableEvent::WaitMany( | |
| std::array<SingleThreadedWaitableEvent, Count> *waitables) | |
| { | |
| return WaitableEventBase<SingleThreadedWaitableEvent>::WaitManyBase(waitables); | |
| } | |
| #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| class AsyncWaitableEvent : public WaitableEventBase<AsyncWaitableEvent> | |
| { | |
| 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 <size_t Count> | |
| static size_t WaitMany(std::array<AsyncWaitableEvent, Count> *waitables); | |
| private: | |
| friend class AsyncWorkerPool; | |
| void setFuture(std::future<void> &&future); | |
| std::future<void> mFuture; | |
| }; | |
| template <size_t Count> | |
| // static | |
| size_t AsyncWaitableEvent::WaitMany(std::array<AsyncWaitableEvent, Count> *waitables) | |
| { | |
| return WaitableEventBase<AsyncWaitableEvent>::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 <typename Impl> | |
| struct WorkerThreadPoolTraits; | |
| class SingleThreadedWorkerPool; | |
| template <> | |
| struct WorkerThreadPoolTraits<SingleThreadedWorkerPool> | |
| { | |
| using WaitableEventType = SingleThreadedWaitableEvent; | |
| }; | |
| #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| class AsyncWorkerPool; | |
| template <> | |
| struct WorkerThreadPoolTraits<AsyncWorkerPool> | |
| { | |
| 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 <typename Impl> | |
| class WorkerThreadPoolBase : angle::NonCopyable | |
| { | |
| public: | |
| WorkerThreadPoolBase(size_t maxThreads); | |
| ~WorkerThreadPoolBase(); | |
| using WaitableEventType = typename WorkerThreadPoolTraits<Impl>::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 <typename Impl> | |
| WorkerThreadPoolBase<Impl>::WorkerThreadPoolBase(size_t maxThreads) | |
| { | |
| } | |
| template <typename Impl> | |
| WorkerThreadPoolBase<Impl>::~WorkerThreadPoolBase() | |
| { | |
| } | |
| template <typename Impl> | |
| typename WorkerThreadPoolBase<Impl>::WaitableEventType WorkerThreadPoolBase<Impl>::postWorkerTask( | |
| Closure *task) | |
| { | |
| return static_cast<Impl *>(this)->postWorkerTaskImpl(task); | |
| } | |
| class SingleThreadedWorkerPool : public WorkerThreadPoolBase<SingleThreadedWorkerPool> | |
| { | |
| public: | |
| SingleThreadedWorkerPool(size_t maxThreads); | |
| ~SingleThreadedWorkerPool(); | |
| SingleThreadedWaitableEvent postWorkerTaskImpl(Closure *task); | |
| }; | |
| #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| class AsyncWorkerPool : public WorkerThreadPoolBase<AsyncWorkerPool> | |
| { | |
| 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_ |