Implement AsyncWaitableEvent with std::condition_variable

This patch implements the default implementation of WaitableEvent
(AsyncWaitableEvent) with std::condition_variable instead of
std::future as std::future will always block its destructor until
the async function returns, which makes us unable to clean up all
the execution environment of the async task inside the async
function.

This patch also implements WorkerThreadTaskManager to manage all
the async tasks (inherited from WorkerThreadTask) in the future,
for example all the Create*PipelineAsync() tasks.

This patch also updates the related dawn_unittest WorkerThreadTest.
Basic.

BUG=dawn:529
TEST=dawn_unittests

Change-Id: Ie789ba788789e91128ffc416e7e768923828a367
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51740
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
Jiawei Shao
2021-05-27 00:49:03 +00:00
committed by Dawn LUCI CQ
parent 462a3ba917
commit 5d39860fef
10 changed files with 294 additions and 200 deletions

View File

@@ -14,40 +14,84 @@
#include "dawn_platform/WorkerThread.h"
#include <future>
#include <condition_variable>
#include <functional>
#include <thread>
#include "common/Assert.h"
namespace {
class AsyncWaitableEvent final : public dawn_platform::WaitableEvent {
class AsyncWaitableEventImpl {
public:
explicit AsyncWaitableEvent(std::function<void()> func) {
mFuture = std::async(std::launch::async, func);
AsyncWaitableEventImpl() : mIsComplete(false) {
}
void Wait() override {
ASSERT(mFuture.valid());
mFuture.wait();
void Wait() {
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock, [this] { return mIsComplete; });
}
bool IsComplete() override {
ASSERT(mFuture.valid());
return mFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
bool IsComplete() {
std::lock_guard<std::mutex> lock(mMutex);
return mIsComplete;
}
void MarkAsComplete() {
{
std::lock_guard<std::mutex> lock(mMutex);
mIsComplete = true;
}
mCondition.notify_all();
}
private:
// It is safe not to call Wait() in the destructor of AsyncWaitableEvent because since
// C++14 the destructor of std::future will always be blocked until its state becomes
// std::future_status::ready when it was created by a call of std::async and it is the
// last reference to the shared state.
// See https://en.cppreference.com/w/cpp/thread/future/~future for more details.
std::future<void> mFuture;
std::mutex mMutex;
std::condition_variable mCondition;
bool mIsComplete;
};
class AsyncWaitableEvent final : public dawn_platform::WaitableEvent {
public:
explicit AsyncWaitableEvent()
: mWaitableEventImpl(std::make_shared<AsyncWaitableEventImpl>()) {
}
void Wait() override {
mWaitableEventImpl->Wait();
}
bool IsComplete() override {
return mWaitableEventImpl->IsComplete();
}
std::shared_ptr<AsyncWaitableEventImpl> GetWaitableEventImpl() const {
return mWaitableEventImpl;
}
private:
std::shared_ptr<AsyncWaitableEventImpl> mWaitableEventImpl;
};
} // anonymous namespace
std::unique_ptr<dawn_platform::WaitableEvent> AsyncWorkerThreadPool::PostWorkerTask(
dawn_platform::PostWorkerTaskCallback callback,
void* userdata) {
std::function<void()> doTask = [callback, userdata]() { callback(userdata); };
return std::make_unique<AsyncWaitableEvent>(doTask);
}
namespace dawn_platform {
std::unique_ptr<dawn_platform::WaitableEvent> AsyncWorkerThreadPool::PostWorkerTask(
dawn_platform::PostWorkerTaskCallback callback,
void* userdata) {
std::unique_ptr<AsyncWaitableEvent> waitableEvent = std::make_unique<AsyncWaitableEvent>();
std::function<void()> doTask =
[callback, userdata, waitableEventImpl = waitableEvent->GetWaitableEventImpl()]() {
callback(userdata);
waitableEventImpl->MarkAsComplete();
};
std::thread thread(doTask);
thread.detach();
return waitableEvent;
}
} // namespace dawn_platform

View File

@@ -18,11 +18,15 @@
#include "common/NonCopyable.h"
#include "dawn_platform/DawnPlatform.h"
class AsyncWorkerThreadPool : public dawn_platform::WorkerTaskPool, public NonCopyable {
public:
std::unique_ptr<dawn_platform::WaitableEvent> PostWorkerTask(
dawn_platform::PostWorkerTaskCallback callback,
void* userdata) override;
};
namespace dawn_platform {
class AsyncWorkerThreadPool : public dawn_platform::WorkerTaskPool, public NonCopyable {
public:
std::unique_ptr<dawn_platform::WaitableEvent> PostWorkerTask(
dawn_platform::PostWorkerTaskCallback callback,
void* userdata) override;
};
} // namespace dawn_platform
#endif