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