| // | |
| // 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: | |
| // Task running thread for ANGLE, similar to a TaskRunner in Chromium. | |
| // Might be implemented differently depending on platform. | |
| // | |
| #include "libANGLE/WorkerThread.h" | |
| #include "libANGLE/trace.h" | |
| #if (ANGLE_DELEGATE_WORKERS == ANGLE_ENABLED) || (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| # include <condition_variable> | |
| # include <future> | |
| # include <mutex> | |
| # include <queue> | |
| # include <thread> | |
| #endif // (ANGLE_DELEGATE_WORKERS == ANGLE_ENABLED) || (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| namespace angle | |
| { | |
| WaitableEvent::WaitableEvent() = default; | |
| WaitableEvent::~WaitableEvent() = default; | |
| void WaitableEventDone::wait() {} | |
| bool WaitableEventDone::isReady() | |
| { | |
| return true; | |
| } | |
| WorkerThreadPool::WorkerThreadPool() = default; | |
| WorkerThreadPool::~WorkerThreadPool() = default; | |
| class SingleThreadedWaitableEvent final : public WaitableEvent | |
| { | |
| public: | |
| SingleThreadedWaitableEvent() = default; | |
| ~SingleThreadedWaitableEvent() override = default; | |
| void wait() override; | |
| bool isReady() override; | |
| }; | |
| void SingleThreadedWaitableEvent::wait() {} | |
| bool SingleThreadedWaitableEvent::isReady() | |
| { | |
| return true; | |
| } | |
| class SingleThreadedWorkerPool final : public WorkerThreadPool | |
| { | |
| public: | |
| std::shared_ptr<WaitableEvent> postWorkerTask(std::shared_ptr<Closure> task) override; | |
| void setMaxThreads(size_t maxThreads) override; | |
| bool isAsync() override; | |
| }; | |
| // SingleThreadedWorkerPool implementation. | |
| std::shared_ptr<WaitableEvent> SingleThreadedWorkerPool::postWorkerTask( | |
| std::shared_ptr<Closure> task) | |
| { | |
| (*task)(); | |
| return std::make_shared<SingleThreadedWaitableEvent>(); | |
| } | |
| void SingleThreadedWorkerPool::setMaxThreads(size_t maxThreads) {} | |
| bool SingleThreadedWorkerPool::isAsync() | |
| { | |
| return false; | |
| } | |
| #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| class AsyncWaitableEvent final : public WaitableEvent | |
| { | |
| public: | |
| AsyncWaitableEvent() : mIsPending(true) {} | |
| ~AsyncWaitableEvent() override = default; | |
| void wait() override; | |
| bool isReady() override; | |
| private: | |
| friend class AsyncWorkerPool; | |
| void setFuture(std::future<void> &&future); | |
| // To block wait() when the task is still in queue to be run. | |
| // Also to protect the concurrent accesses from both main thread and | |
| // background threads to the member fields. | |
| std::mutex mMutex; | |
| bool mIsPending; | |
| std::condition_variable mCondition; | |
| std::future<void> mFuture; | |
| }; | |
| void AsyncWaitableEvent::setFuture(std::future<void> &&future) | |
| { | |
| mFuture = std::move(future); | |
| } | |
| void AsyncWaitableEvent::wait() | |
| { | |
| ANGLE_TRACE_EVENT0("gpu.angle", "AsyncWaitableEvent::wait"); | |
| { | |
| std::unique_lock<std::mutex> lock(mMutex); | |
| mCondition.wait(lock, [this] { return !mIsPending; }); | |
| } | |
| ASSERT(mFuture.valid()); | |
| mFuture.wait(); | |
| } | |
| bool AsyncWaitableEvent::isReady() | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| if (mIsPending) | |
| { | |
| return false; | |
| } | |
| ASSERT(mFuture.valid()); | |
| return mFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready; | |
| } | |
| class AsyncWorkerPool final : public WorkerThreadPool | |
| { | |
| public: | |
| AsyncWorkerPool(size_t maxThreads) : mMaxThreads(maxThreads), mRunningThreads(0) {} | |
| ~AsyncWorkerPool() override = default; | |
| std::shared_ptr<WaitableEvent> postWorkerTask(std::shared_ptr<Closure> task) override; | |
| void setMaxThreads(size_t maxThreads) override; | |
| bool isAsync() override; | |
| private: | |
| void checkToRunPendingTasks(); | |
| // To protect the concurrent accesses from both main thread and background | |
| // threads to the member fields. | |
| std::mutex mMutex; | |
| size_t mMaxThreads; | |
| size_t mRunningThreads; | |
| std::queue<std::pair<std::shared_ptr<AsyncWaitableEvent>, std::shared_ptr<Closure>>> mTaskQueue; | |
| }; | |
| // AsyncWorkerPool implementation. | |
| std::shared_ptr<WaitableEvent> AsyncWorkerPool::postWorkerTask(std::shared_ptr<Closure> task) | |
| { | |
| ASSERT(mMaxThreads > 0); | |
| auto waitable = std::make_shared<AsyncWaitableEvent>(); | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| mTaskQueue.push(std::make_pair(waitable, task)); | |
| } | |
| checkToRunPendingTasks(); | |
| return std::move(waitable); | |
| } | |
| void AsyncWorkerPool::setMaxThreads(size_t maxThreads) | |
| { | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| mMaxThreads = (maxThreads == 0xFFFFFFFF ? std::thread::hardware_concurrency() : maxThreads); | |
| } | |
| checkToRunPendingTasks(); | |
| } | |
| bool AsyncWorkerPool::isAsync() | |
| { | |
| return true; | |
| } | |
| void AsyncWorkerPool::checkToRunPendingTasks() | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| while (mRunningThreads < mMaxThreads && !mTaskQueue.empty()) | |
| { | |
| auto task = mTaskQueue.front(); | |
| mTaskQueue.pop(); | |
| auto waitable = task.first; | |
| auto closure = task.second; | |
| auto future = std::async(std::launch::async, [closure, this] { | |
| { | |
| ANGLE_TRACE_EVENT0("gpu.angle", "AsyncWorkerPool::RunTask"); | |
| (*closure)(); | |
| } | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| ASSERT(mRunningThreads != 0); | |
| --mRunningThreads; | |
| } | |
| checkToRunPendingTasks(); | |
| }); | |
| ++mRunningThreads; | |
| { | |
| std::lock_guard<std::mutex> waitableLock(waitable->mMutex); | |
| waitable->mIsPending = false; | |
| waitable->setFuture(std::move(future)); | |
| } | |
| waitable->mCondition.notify_all(); | |
| } | |
| } | |
| #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| #if (ANGLE_DELEGATE_WORKERS == ANGLE_ENABLED) | |
| class DelegateWaitableEvent final : public WaitableEvent | |
| { | |
| public: | |
| DelegateWaitableEvent() = default; | |
| ~DelegateWaitableEvent() override = default; | |
| void wait() override; | |
| bool isReady() override; | |
| void markAsReady(); | |
| private: | |
| // To protect the concurrent accesses from both main thread and background | |
| // threads to the member fields. | |
| std::mutex mMutex; | |
| bool mIsReady = false; | |
| std::condition_variable mCondition; | |
| }; | |
| void DelegateWaitableEvent::markAsReady() | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| mIsReady = true; | |
| mCondition.notify_all(); | |
| } | |
| void DelegateWaitableEvent::wait() | |
| { | |
| std::unique_lock<std::mutex> lock(mMutex); | |
| mCondition.wait(lock, [this] { return mIsReady; }); | |
| } | |
| bool DelegateWaitableEvent::isReady() | |
| { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| return mIsReady; | |
| } | |
| class DelegateWorkerPool final : public WorkerThreadPool | |
| { | |
| public: | |
| DelegateWorkerPool() = default; | |
| ~DelegateWorkerPool() override = default; | |
| std::shared_ptr<WaitableEvent> postWorkerTask(std::shared_ptr<Closure> task) override; | |
| void setMaxThreads(size_t maxThreads) override; | |
| bool isAsync() override; | |
| }; | |
| // A function wrapper to execute the closure and to notify the waitable | |
| // event after the execution. | |
| class DelegateWorkerTask | |
| { | |
| public: | |
| DelegateWorkerTask(std::shared_ptr<Closure> task, | |
| std::shared_ptr<DelegateWaitableEvent> waitable) | |
| : mTask(task), mWaitable(waitable) | |
| {} | |
| DelegateWorkerTask() = delete; | |
| DelegateWorkerTask(DelegateWorkerTask &) = delete; | |
| static void RunTask(void *userData) | |
| { | |
| DelegateWorkerTask *workerTask = static_cast<DelegateWorkerTask *>(userData); | |
| (*workerTask->mTask)(); | |
| workerTask->mWaitable->markAsReady(); | |
| // Delete the task after its execution. | |
| delete workerTask; | |
| } | |
| private: | |
| ~DelegateWorkerTask() = default; | |
| std::shared_ptr<Closure> mTask; | |
| std::shared_ptr<DelegateWaitableEvent> mWaitable; | |
| }; | |
| std::shared_ptr<WaitableEvent> DelegateWorkerPool::postWorkerTask(std::shared_ptr<Closure> task) | |
| { | |
| auto waitable = std::make_shared<DelegateWaitableEvent>(); | |
| // The task will be deleted by DelegateWorkerTask::RunTask(...) after its execution. | |
| DelegateWorkerTask *workerTask = new DelegateWorkerTask(task, waitable); | |
| auto *platform = ANGLEPlatformCurrent(); | |
| platform->postWorkerTask(platform, DelegateWorkerTask::RunTask, workerTask); | |
| return std::move(waitable); | |
| } | |
| void DelegateWorkerPool::setMaxThreads(size_t maxThreads) {} | |
| bool DelegateWorkerPool::isAsync() | |
| { | |
| return true; | |
| } | |
| #endif | |
| // static | |
| std::shared_ptr<WorkerThreadPool> WorkerThreadPool::Create(bool multithreaded) | |
| { | |
| std::shared_ptr<WorkerThreadPool> pool(nullptr); | |
| #if (ANGLE_DELEGATE_WORKERS == ANGLE_ENABLED) | |
| const bool hasPostWorkerTaskImpl = ANGLEPlatformCurrent()->postWorkerTask; | |
| if (hasPostWorkerTaskImpl && multithreaded) | |
| { | |
| pool = std::shared_ptr<WorkerThreadPool>(new DelegateWorkerPool()); | |
| } | |
| #endif | |
| #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) | |
| if (!pool && multithreaded) | |
| { | |
| pool = std::shared_ptr<WorkerThreadPool>( | |
| new AsyncWorkerPool(std::thread::hardware_concurrency())); | |
| } | |
| #endif | |
| if (!pool) | |
| { | |
| return std::shared_ptr<WorkerThreadPool>(new SingleThreadedWorkerPool()); | |
| } | |
| return pool; | |
| } | |
| // static | |
| std::shared_ptr<WaitableEvent> WorkerThreadPool::PostWorkerTask( | |
| std::shared_ptr<WorkerThreadPool> pool, | |
| std::shared_ptr<Closure> task) | |
| { | |
| std::shared_ptr<WaitableEvent> event = pool->postWorkerTask(task); | |
| if (event.get()) | |
| { | |
| event->setWorkerThreadPool(pool); | |
| } | |
| return event; | |
| } | |
| } // namespace angle |