#include "common/WorkerThread.h"
#include "common/angleutils.h"
#include "common/system_utils.h"
#if !defined(ANGLE_STD_ASYNC_WORKERS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
# define ANGLE_STD_ASYNC_WORKERS 1
#endif
#if ANGLE_DELEGATE_WORKERS || ANGLE_STD_ASYNC_WORKERS
# include <future>
# include <queue>
# include <thread>
#endif
namespace angle
{
WaitableEvent::WaitableEvent() = default;
WaitableEvent::~WaitableEvent() = default;
void WaitableEventDone::wait() {}
bool WaitableEventDone::isReady()
{
return true;
}
void AsyncWaitableEvent::markAsReady()
{
std::lock_guard<std::mutex> lock(mMutex);
mIsReady = true;
mCondition.notify_all();
}
void AsyncWaitableEvent::markAsAborted()
{
std::lock_guard<std::mutex> lock(mMutex);
mAborted = true;
mCondition.notify_all();
}
void AsyncWaitableEvent::wait()
{
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock, [this] { return mIsReady || mAborted; });
}
bool AsyncWaitableEvent::isReady()
{
std::lock_guard<std::mutex> lock(mMutex);
return mIsReady;
}
WorkerThreadPool::WorkerThreadPool() = default;
WorkerThreadPool::~WorkerThreadPool() = default;
class SingleThreadedWorkerPool final : public WorkerThreadPool
{
public:
std::shared_ptr<WaitableEvent> postWorkerTask(const std::shared_ptr<Closure> &task) override;
bool isAsync() override;
};
std::shared_ptr<WaitableEvent> SingleThreadedWorkerPool::postWorkerTask(
const std::shared_ptr<Closure> &task)
{
(*task)();
return std::make_shared<WaitableEventDone>();
}
bool SingleThreadedWorkerPool::isAsync()
{
return false;
}
#if ANGLE_STD_ASYNC_WORKERS
class AsyncWorkerPool final : public WorkerThreadPool
{
public:
AsyncWorkerPool(size_t numThreads);
~AsyncWorkerPool() override;
std::shared_ptr<WaitableEvent> postWorkerTask(const std::shared_ptr<Closure> &task) override;
bool isAsync() override;
private:
void createThreads();
using Task = std::pair<std::shared_ptr<AsyncWaitableEvent>, std::shared_ptr<Closure>>;
void threadLoop();
bool mTerminated = false;
std::mutex mMutex;
std::condition_variable mCondVar;
std::queue<Task> mTaskQueue;
std::deque<std::thread> mThreads;
size_t mDesiredThreadCount;
};
AsyncWorkerPool::AsyncWorkerPool(size_t numThreads) : mDesiredThreadCount(numThreads)
{
ASSERT(numThreads != 0);
}
AsyncWorkerPool::~AsyncWorkerPool()
{
{
std::unique_lock<std::mutex> lock(mMutex);
for (; !mTaskQueue.empty(); mTaskQueue.pop())
{
auto task = mTaskQueue.front();
task.first->markAsAborted();
}
mTerminated = true;
}
mCondVar.notify_all();
for (auto &thread : mThreads)
{
ASSERT(thread.get_id() != std::this_thread::get_id());
thread.join();
}
}
void AsyncWorkerPool::createThreads()
{
if (mDesiredThreadCount == mThreads.size())
{
return;
}
ASSERT(mThreads.empty());
for (size_t i = 0; i < mDesiredThreadCount; ++i)
{
mThreads.emplace_back(&AsyncWorkerPool::threadLoop, this);
}
}
std::shared_ptr<WaitableEvent> AsyncWorkerPool::postWorkerTask(const std::shared_ptr<Closure> &task)
{
auto waitable = std::make_shared<AsyncWaitableEvent>();
{
std::lock_guard<std::mutex> lock(mMutex);
ASSERT(!mTerminated);
createThreads();
mTaskQueue.push(std::make_pair(waitable, task));
}
mCondVar.notify_one();
return waitable;
}
void AsyncWorkerPool::threadLoop()
{
angle::SetCurrentThreadName("ANGLE-Worker");
while (true)
{
Task task;
{
std::unique_lock<std::mutex> lock(mMutex);
mCondVar.wait(lock, [this] { return !mTaskQueue.empty() || mTerminated; });
if (mTerminated)
{
ASSERT(mTaskQueue.empty());
return;
}
task = mTaskQueue.front();
mTaskQueue.pop();
}
auto &waitable = task.first;
auto &closure = task.second;
(*closure)();
task.second.reset();
waitable->markAsReady();
}
}
bool AsyncWorkerPool::isAsync()
{
return true;
}
#endif
#if ANGLE_DELEGATE_WORKERS
class DelegateWorkerPool final : public WorkerThreadPool
{
public:
DelegateWorkerPool(PlatformMethods *platform) : mPlatform(platform) {}
~DelegateWorkerPool() override = default;
std::shared_ptr<WaitableEvent> postWorkerTask(const std::shared_ptr<Closure> &task) override;
bool isAsync() override;
private:
PlatformMethods *mPlatform;
};
class DelegateWorkerTask
{
public:
DelegateWorkerTask(const std::shared_ptr<Closure> &task,
std::shared_ptr<AsyncWaitableEvent> 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 workerTask;
}
private:
~DelegateWorkerTask() = default;
std::shared_ptr<Closure> mTask;
std::shared_ptr<AsyncWaitableEvent> mWaitable;
};
ANGLE_NO_SANITIZE_CFI_ICALL
std::shared_ptr<WaitableEvent> DelegateWorkerPool::postWorkerTask(
const std::shared_ptr<Closure> &task)
{
if (mPlatform->postWorkerTask == nullptr)
{
(*task)();
return std::make_shared<WaitableEventDone>();
}
auto waitable = std::make_shared<AsyncWaitableEvent>();
DelegateWorkerTask *workerTask = new DelegateWorkerTask(task, waitable);
mPlatform->postWorkerTask(mPlatform, DelegateWorkerTask::RunTask, workerTask);
return waitable;
}
bool DelegateWorkerPool::isAsync()
{
return mPlatform->postWorkerTask != nullptr;
}
#endif
std::shared_ptr<WorkerThreadPool> WorkerThreadPool::Create(size_t numThreads,
PlatformMethods *platform)
{
const bool multithreaded = numThreads != 1;
std::shared_ptr<WorkerThreadPool> pool(nullptr);
#if ANGLE_DELEGATE_WORKERS
const bool hasPostWorkerTaskImpl = platform->postWorkerTask != nullptr;
if (hasPostWorkerTaskImpl && multithreaded)
{
pool = std::shared_ptr<WorkerThreadPool>(new DelegateWorkerPool(platform));
}
#endif
#if ANGLE_STD_ASYNC_WORKERS
if (!pool && multithreaded)
{
pool = std::shared_ptr<WorkerThreadPool>(new AsyncWorkerPool(
numThreads == 0 ? std::thread::hardware_concurrency() : numThreads));
}
#endif
if (!pool)
{
return std::shared_ptr<WorkerThreadPool>(new SingleThreadedWorkerPool());
}
return pool;
}
}