blob: 30c454dd267ce6fd0be483476cfc8885ed830d81 [file] [log] [blame]
//
// 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