Browse Source
Merge pull request #6563 from ReinUsesLisp/thread-worker
Merge pull request #6563 from ReinUsesLisp/thread-worker
common: Add stateful thread worker and unique function utilitiesnce_cpp
committed by
GitHub
6 changed files with 266 additions and 67 deletions
-
2src/common/CMakeLists.txt
-
58src/common/thread_worker.cpp
-
102src/common/thread_worker.h
-
62src/common/unique_function.h
-
1src/tests/CMakeLists.txt
-
108src/tests/common/unique_function.cpp
@ -1,58 +0,0 @@ |
|||||
// Copyright 2020 yuzu emulator team
|
|
||||
// Licensed under GPLv2 or any later version
|
|
||||
// Refer to the license.txt file included.
|
|
||||
|
|
||||
#include "common/thread.h"
|
|
||||
#include "common/thread_worker.h"
|
|
||||
|
|
||||
namespace Common { |
|
||||
|
|
||||
ThreadWorker::ThreadWorker(std::size_t num_workers, const std::string& name) { |
|
||||
for (std::size_t i = 0; i < num_workers; ++i) |
|
||||
threads.emplace_back([this, thread_name{std::string{name}}] { |
|
||||
Common::SetCurrentThreadName(thread_name.c_str()); |
|
||||
|
|
||||
// Wait for first request
|
|
||||
{ |
|
||||
std::unique_lock lock{queue_mutex}; |
|
||||
condition.wait(lock, [this] { return stop || !requests.empty(); }); |
|
||||
} |
|
||||
|
|
||||
while (true) { |
|
||||
std::function<void()> task; |
|
||||
|
|
||||
{ |
|
||||
std::unique_lock lock{queue_mutex}; |
|
||||
condition.wait(lock, [this] { return stop || !requests.empty(); }); |
|
||||
if (stop || requests.empty()) { |
|
||||
return; |
|
||||
} |
|
||||
task = std::move(requests.front()); |
|
||||
requests.pop(); |
|
||||
} |
|
||||
|
|
||||
task(); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
ThreadWorker::~ThreadWorker() { |
|
||||
{ |
|
||||
std::unique_lock lock{queue_mutex}; |
|
||||
stop = true; |
|
||||
} |
|
||||
condition.notify_all(); |
|
||||
for (std::thread& thread : threads) { |
|
||||
thread.join(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void ThreadWorker::QueueWork(std::function<void()>&& work) { |
|
||||
{ |
|
||||
std::unique_lock lock{queue_mutex}; |
|
||||
requests.emplace(work); |
|
||||
} |
|
||||
condition.notify_one(); |
|
||||
} |
|
||||
|
|
||||
} // namespace Common
|
|
||||
@ -0,0 +1,62 @@ |
|||||
|
// Copyright 2021 yuzu emulator team |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <utility> |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
/// General purpose function wrapper similar to std::function. |
||||
|
/// Unlike std::function, the captured values don't have to be copyable. |
||||
|
/// This class can be moved but not copied. |
||||
|
template <typename ResultType, typename... Args> |
||||
|
class UniqueFunction { |
||||
|
class CallableBase { |
||||
|
public: |
||||
|
virtual ~CallableBase() = default; |
||||
|
virtual ResultType operator()(Args&&...) = 0; |
||||
|
}; |
||||
|
|
||||
|
template <typename Functor> |
||||
|
class Callable final : public CallableBase { |
||||
|
public: |
||||
|
Callable(Functor&& functor_) : functor{std::move(functor_)} {} |
||||
|
~Callable() override = default; |
||||
|
|
||||
|
ResultType operator()(Args&&... args) override { |
||||
|
return functor(std::forward<Args>(args)...); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
Functor functor; |
||||
|
}; |
||||
|
|
||||
|
public: |
||||
|
UniqueFunction() = default; |
||||
|
|
||||
|
template <typename Functor> |
||||
|
UniqueFunction(Functor&& functor) |
||||
|
: callable{std::make_unique<Callable<Functor>>(std::move(functor))} {} |
||||
|
|
||||
|
UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default; |
||||
|
UniqueFunction(UniqueFunction&& rhs) noexcept = default; |
||||
|
|
||||
|
UniqueFunction& operator=(const UniqueFunction&) = delete; |
||||
|
UniqueFunction(const UniqueFunction&) = delete; |
||||
|
|
||||
|
ResultType operator()(Args&&... args) const { |
||||
|
return (*callable)(std::forward<Args>(args)...); |
||||
|
} |
||||
|
|
||||
|
explicit operator bool() const noexcept { |
||||
|
return static_cast<bool>(callable); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::unique_ptr<CallableBase> callable; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Common |
||||
@ -0,0 +1,108 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <string>
|
||||
|
|
||||
|
#include <catch2/catch.hpp>
|
||||
|
|
||||
|
#include "common/unique_function.h"
|
||||
|
|
||||
|
namespace { |
||||
|
struct Noisy { |
||||
|
Noisy() : state{"Default constructed"} {} |
||||
|
Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} { |
||||
|
rhs.state = "Moved away"; |
||||
|
} |
||||
|
Noisy& operator=(Noisy&& rhs) noexcept { |
||||
|
state = "Move assigned"; |
||||
|
rhs.state = "Moved away"; |
||||
|
} |
||||
|
Noisy(const Noisy&) : state{"Copied constructed"} {} |
||||
|
Noisy& operator=(const Noisy&) { |
||||
|
state = "Copied assigned"; |
||||
|
} |
||||
|
|
||||
|
std::string state; |
||||
|
}; |
||||
|
} // Anonymous namespace
|
||||
|
|
||||
|
TEST_CASE("UniqueFunction", "[common]") { |
||||
|
SECTION("Capture reference") { |
||||
|
int value = 0; |
||||
|
Common::UniqueFunction<void> func = [&value] { value = 5; }; |
||||
|
func(); |
||||
|
REQUIRE(value == 5); |
||||
|
} |
||||
|
SECTION("Capture pointer") { |
||||
|
int value = 0; |
||||
|
int* pointer = &value; |
||||
|
Common::UniqueFunction<void> func = [pointer] { *pointer = 5; }; |
||||
|
func(); |
||||
|
REQUIRE(value == 5); |
||||
|
} |
||||
|
SECTION("Move object") { |
||||
|
Noisy noisy; |
||||
|
REQUIRE(noisy.state == "Default constructed"); |
||||
|
|
||||
|
Common::UniqueFunction<void> func = [noisy = std::move(noisy)] { |
||||
|
REQUIRE(noisy.state == "Move constructed"); |
||||
|
}; |
||||
|
REQUIRE(noisy.state == "Moved away"); |
||||
|
func(); |
||||
|
} |
||||
|
SECTION("Move construct function") { |
||||
|
int value = 0; |
||||
|
Common::UniqueFunction<void> func = [&value] { value = 5; }; |
||||
|
Common::UniqueFunction<void> new_func = std::move(func); |
||||
|
new_func(); |
||||
|
REQUIRE(value == 5); |
||||
|
} |
||||
|
SECTION("Move assign function") { |
||||
|
int value = 0; |
||||
|
Common::UniqueFunction<void> func = [&value] { value = 5; }; |
||||
|
Common::UniqueFunction<void> new_func; |
||||
|
new_func = std::move(func); |
||||
|
new_func(); |
||||
|
REQUIRE(value == 5); |
||||
|
} |
||||
|
SECTION("Default construct then assign function") { |
||||
|
int value = 0; |
||||
|
Common::UniqueFunction<void> func; |
||||
|
func = [&value] { value = 5; }; |
||||
|
func(); |
||||
|
REQUIRE(value == 5); |
||||
|
} |
||||
|
SECTION("Pass arguments") { |
||||
|
int result = 0; |
||||
|
Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; }; |
||||
|
func(5, 4); |
||||
|
REQUIRE(result == 9); |
||||
|
} |
||||
|
SECTION("Pass arguments and return value") { |
||||
|
Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; }; |
||||
|
REQUIRE(func(5, 4) == 9); |
||||
|
} |
||||
|
SECTION("Destructor") { |
||||
|
int num_destroyed = 0; |
||||
|
struct Foo { |
||||
|
Foo(int* num_) : num{num_} {} |
||||
|
Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {} |
||||
|
Foo(const Foo&) = delete; |
||||
|
|
||||
|
~Foo() { |
||||
|
if (num) { |
||||
|
++*num; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int* num = nullptr; |
||||
|
}; |
||||
|
Foo object{&num_destroyed}; |
||||
|
{ |
||||
|
Common::UniqueFunction<void> func = [object = std::move(object)] {}; |
||||
|
REQUIRE(num_destroyed == 0); |
||||
|
} |
||||
|
REQUIRE(num_destroyed == 1); |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue