mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-24 14:24:54 +00:00
Common: Add a std::jthread implementation.
This commit is contained in:
parent
879a8889aa
commit
12e29828f8
4 changed files with 177 additions and 0 deletions
|
@ -134,6 +134,7 @@ add_library(common
|
|||
SocketContext.h
|
||||
SpanUtils.h
|
||||
SPSCQueue.h
|
||||
StdJThread.h
|
||||
StringLiteral.h
|
||||
StringUtil.cpp
|
||||
StringUtil.h
|
||||
|
|
164
Source/Core/Common/StdJThread.h
Normal file
164
Source/Core/Common/StdJThread.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// This header provides a basic implementation of jthread in the StdCompat namespace
|
||||
// It pulls the stdlib provided std::jthread into the StdCompat namespace when available.
|
||||
|
||||
// TODO: Eliminate this when we can rely on std::jthread being in the stdlib.
|
||||
// Clang libc++ provides P0660R10 in version 18 (with -fexperimental-library) or version 20.
|
||||
// GCC libstdc++ provides it in version 10.
|
||||
|
||||
#include <thread>
|
||||
|
||||
#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L
|
||||
namespace StdCompat
|
||||
{
|
||||
using std::jthread;
|
||||
using std::stop_source;
|
||||
using std::stop_token;
|
||||
} // namespace StdCompat
|
||||
#else
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/TypeUtils.h"
|
||||
|
||||
namespace StdCompat
|
||||
{
|
||||
class stop_source;
|
||||
|
||||
class stop_token
|
||||
{
|
||||
friend stop_source;
|
||||
|
||||
public:
|
||||
stop_token() = default;
|
||||
|
||||
bool stop_requested() const
|
||||
{
|
||||
return stop_possible() && m_stop_state->stop_requested_flag.test();
|
||||
}
|
||||
|
||||
bool stop_possible() const { return m_stop_state != nullptr; }
|
||||
|
||||
void swap(stop_token& other) { m_stop_state.swap(other.m_stop_state); }
|
||||
|
||||
friend bool operator==(const stop_token&, const stop_token&) = default;
|
||||
|
||||
private:
|
||||
struct StopState
|
||||
{
|
||||
std::atomic_flag stop_requested_flag = false;
|
||||
};
|
||||
|
||||
explicit stop_token(std::shared_ptr<StopState> stop_state) : m_stop_state{std::move(stop_state)}
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<StopState> m_stop_state;
|
||||
};
|
||||
|
||||
struct nostopstate_t
|
||||
{
|
||||
explicit nostopstate_t() = default;
|
||||
};
|
||||
inline constexpr nostopstate_t nostopstate{};
|
||||
|
||||
class stop_source
|
||||
{
|
||||
public:
|
||||
stop_source() : m_stop_token{std::make_shared<stop_token::StopState>()} {}
|
||||
explicit stop_source(nostopstate_t) {}
|
||||
|
||||
stop_source(const stop_source&) = default;
|
||||
stop_source(stop_source&&) = default;
|
||||
|
||||
stop_source& operator=(const stop_source&) = default;
|
||||
stop_source& operator=(stop_source&&) = default;
|
||||
|
||||
~stop_source() = default;
|
||||
|
||||
bool request_stop()
|
||||
{
|
||||
return m_stop_token.stop_possible() &&
|
||||
!m_stop_token.m_stop_state->stop_requested_flag.test_and_set();
|
||||
}
|
||||
|
||||
void swap(stop_source& other) { m_stop_token.swap(other.m_stop_token); }
|
||||
|
||||
stop_token get_token() const { return m_stop_token; }
|
||||
|
||||
bool stop_requested() const { return m_stop_token.stop_requested(); }
|
||||
bool stop_possible() const { return m_stop_token.stop_possible(); }
|
||||
|
||||
friend bool operator==(const stop_source&, const stop_source&) = default;
|
||||
|
||||
private:
|
||||
stop_token m_stop_token;
|
||||
};
|
||||
|
||||
class jthread
|
||||
{
|
||||
public:
|
||||
using native_handle_type = std::thread::native_handle_type;
|
||||
|
||||
jthread() : m_stop_source(nostopstate) {}
|
||||
|
||||
template <typename F, typename... Args>
|
||||
explicit jthread(F&& f, Args&&... args)
|
||||
{
|
||||
if constexpr (std::is_invocable_v<std::decay_t<F>, stop_token, std::decay_t<Args>...>)
|
||||
{
|
||||
m_thread = std::thread{std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...};
|
||||
}
|
||||
else if constexpr (std::is_invocable_v<std::decay_t<F>, std::decay_t<Args>...>)
|
||||
{
|
||||
m_thread = std::thread{std::forward<F>(f), std::forward<Args>(args)...};
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(Common::DependentFalse<F>(),
|
||||
"thread function is not invocable with provided arguments.");
|
||||
}
|
||||
}
|
||||
|
||||
jthread(jthread&& other) = default;
|
||||
jthread& operator=(jthread&& other) = default;
|
||||
|
||||
jthread(const jthread&) = delete;
|
||||
jthread& operator=(const jthread&) = delete;
|
||||
|
||||
~jthread()
|
||||
{
|
||||
if (joinable())
|
||||
{
|
||||
request_stop();
|
||||
join();
|
||||
}
|
||||
}
|
||||
|
||||
bool joinable() const { return m_thread.joinable(); }
|
||||
auto get_id() const { return m_thread.get_id(); };
|
||||
auto native_handle() { return m_thread.native_handle(); }
|
||||
static auto hardware_concurrency() { return std::thread::hardware_concurrency(); }
|
||||
|
||||
void join() { m_thread.join(); }
|
||||
void detach() { m_thread.detach(); }
|
||||
void swap(jthread& other)
|
||||
{
|
||||
m_thread.swap(other.m_thread);
|
||||
m_stop_source.swap(other.m_stop_source);
|
||||
}
|
||||
|
||||
stop_source get_stop_source() { return m_stop_source; }
|
||||
stop_token get_stop_token() const { return m_stop_source.get_token(); }
|
||||
bool request_stop() { return m_stop_source.request_stop(); }
|
||||
|
||||
private:
|
||||
std::thread m_thread;
|
||||
stop_source m_stop_source;
|
||||
};
|
||||
} // namespace StdCompat
|
||||
#endif
|
|
@ -82,4 +82,15 @@ static_assert(!IsNOf<int, 1, int, int>::value);
|
|||
static_assert(IsNOf<int, 2, int, int>::value);
|
||||
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed
|
||||
static_assert(!IsNOf<int, 2, int, char*>::value);
|
||||
|
||||
template <typename... T>
|
||||
struct DependentFalse : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename... T>
|
||||
struct DependentTrue : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
<ClInclude Include="Common\SocketContext.h" />
|
||||
<ClInclude Include="Common\SpanUtils.h" />
|
||||
<ClInclude Include="Common\SPSCQueue.h" />
|
||||
<ClInclude Include="Common\StdJThread.h" />
|
||||
<ClInclude Include="Common\StringLiteral.h" />
|
||||
<ClInclude Include="Common\StringUtil.h" />
|
||||
<ClInclude Include="Common\Swap.h" />
|
||||
|
|
Loading…
Add table
Reference in a new issue