mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-04-24 14:24:54 +00:00
164 lines
4.1 KiB
C++
164 lines
4.1 KiB
C++
// 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
|