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
|
SocketContext.h
|
||||||
SpanUtils.h
|
SpanUtils.h
|
||||||
SPSCQueue.h
|
SPSCQueue.h
|
||||||
|
StdJThread.h
|
||||||
StringLiteral.h
|
StringLiteral.h
|
||||||
StringUtil.cpp
|
StringUtil.cpp
|
||||||
StringUtil.h
|
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, int>::value);
|
||||||
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed
|
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed
|
||||||
static_assert(!IsNOf<int, 2, int, char*>::value);
|
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
|
} // namespace Common
|
||||||
|
|
|
@ -158,6 +158,7 @@
|
||||||
<ClInclude Include="Common\SocketContext.h" />
|
<ClInclude Include="Common\SocketContext.h" />
|
||||||
<ClInclude Include="Common\SpanUtils.h" />
|
<ClInclude Include="Common\SpanUtils.h" />
|
||||||
<ClInclude Include="Common\SPSCQueue.h" />
|
<ClInclude Include="Common\SPSCQueue.h" />
|
||||||
|
<ClInclude Include="Common\StdJThread.h" />
|
||||||
<ClInclude Include="Common\StringLiteral.h" />
|
<ClInclude Include="Common\StringLiteral.h" />
|
||||||
<ClInclude Include="Common\StringUtil.h" />
|
<ClInclude Include="Common\StringUtil.h" />
|
||||||
<ClInclude Include="Common\Swap.h" />
|
<ClInclude Include="Common\Swap.h" />
|
||||||
|
|
Loading…
Add table
Reference in a new issue