Postpone thread launching on g_fxo->init

This commit is contained in:
Eladash 2023-12-14 22:20:21 +02:00 committed by Elad Ashkenazi
parent a4bcba8971
commit 0c410f8a14
11 changed files with 112 additions and 44 deletions

View File

@ -2040,6 +2040,12 @@ DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined
void thread_base::start()
{
m_sync.atomic_op([&](u32& v)
{
v &= ~static_cast<u32>(thread_state::mask);
v |= static_cast<u32>(thread_state::created);
});
#ifdef _WIN32
m_thread = ::_beginthreadex(nullptr, 0, entry_point, this, CREATE_SUSPENDED, nullptr);
ensure(m_thread);

View File

@ -5,6 +5,7 @@
#include "util/shared_ptr.hpp"
#include <string>
#include <execution>
#include "mutex.h"
#include "lockless.h"
@ -83,9 +84,9 @@ struct result_storage<Ctx, Args...>
};
template <typename T>
concept NamedThreadName = requires (const T& t)
concept NamedThreadName = requires (const T&)
{
std::string(t.thread_name);
std::string(T::thread_name);
};
// Base class for task queue (linked list)
@ -446,6 +447,11 @@ public:
}
};
namespace stx
{
struct launch_retainer;
}
// Derived from the callable object Context, possibly a lambda
template <class Context>
class named_thread final : public Context, result_storage<Context>, thread_base
@ -512,17 +518,27 @@ class named_thread final : public Context, result_storage<Context>, thread_base
public:
// Forwarding constructor with default name (also potentially the default constructor)
template <typename... Args> requires (std::is_constructible_v<Context, Args&&...>) && (NamedThreadName<Context>)
named_thread(Args&&... args)
template <typename... Args> requires (std::is_constructible_v<Context, Args&&...>) && (!(std::is_same_v<std::remove_cvref_t<Args>, stx::launch_retainer> || ...)) && (NamedThreadName<Context>)
named_thread(Args&&... args) noexcept
: Context(std::forward<Args>(args)...)
, thread(trampoline, std::string(Context::thread_name))
{
thread::start();
}
// Forwarding constructor with default name, does not automatically run the thread
template <typename... Args> requires (std::is_constructible_v<Context, Args&&...>) && (NamedThreadName<Context>)
named_thread(const stx::launch_retainer&, Args&&... args) noexcept
: Context(std::forward<Args>(args)...)
, thread(trampoline, std::string(Context::thread_name))
{
// Create a stand-by thread context
m_sync |= static_cast<u32>(thread_state::finished);
}
// Normal forwarding constructor
template <typename... Args> requires (std::is_constructible_v<Context, Args&&...>)
named_thread(std::string name, Args&&... args)
template <typename... Args> requires (std::is_constructible_v<Context, Args&&...>) && (!NamedThreadName<Context>)
named_thread(std::string name, Args&&... args) noexcept
: Context(std::forward<Args>(args)...)
, thread(trampoline, std::move(name))
{
@ -530,7 +546,7 @@ public:
}
// Lambda constructor, also the implicit deduction guide candidate
named_thread(std::string_view name, Context&& f)
named_thread(std::string_view name, Context&& f) noexcept requires (!NamedThreadName<Context>)
: Context(std::forward<Context>(f))
, thread(trampoline, std::string(name))
{
@ -644,12 +660,20 @@ public:
return static_cast<thread_state>(thread::m_sync.load() & 3);
}
// Try to abort by assigning thread_state::aborting/finished
// Join thread by thread_state::finished
named_thread& operator=(thread_state s)
{
if (s == thread_state::created)
{
// Run thread
ensure(operator thread_state() == thread_state::finished);
thread::start();
return *this;
}
bool notify_sync = false;
// Try to abort by assigning thread_state::aborting/finished
// Join thread by thread_state::finished
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u32& v) { return !(v & 3) && (v |= 1); }).second)
{
notify_sync = true;

View File

@ -179,11 +179,6 @@ private:
std::unordered_map<s32, rtt_info> rtts; // (sock_id, rtt)
};
void initialize_tcp_timeout_monitor()
{
g_fxo->need<named_thread<tcp_timeout_monitor>>();
}
u16 u2s_tcp_checksum(const le_t<u16>* buffer, usz size)
{
u32 cksum = 0;

View File

@ -50,7 +50,6 @@ enum p2ps_tcp_flags : u8
CWR = (1 << 7),
};
void initialize_tcp_timeout_monitor();
u16 u2s_tcp_checksum(const le_t<u16>* buffer, usz size);
std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u8* data, const u32 datasize);

View File

@ -77,16 +77,14 @@ std::vector<signaling_message> get_sign_msgs()
return msgs;
}
void need_network()
namespace np
{
g_fxo->need<network_context>();
initialize_tcp_timeout_monitor();
void init_np_handler_dependencies();
}
network_thread::network_thread()
{
// Ensures IDM for lv2_socket is always valid when the thread is running
g_fxo->init<id_manager::id_map<lv2_socket>>();
np::init_np_handler_dependencies();
}
void network_thread::bind_sce_np_port()

View File

@ -339,6 +339,28 @@ namespace np
return;
}
extern void init_np_handler_dependencies()
{
if (auto handler = g_fxo->try_get<named_thread<np_handler>>())
{
handler->init_np_handler_dependencies();
}
}
void np_handler::init_np_handler_dependencies()
{
if (is_psn_active && g_cfg.net.psn_status == np_psn_status::psn_rpcn && g_fxo->is_init<network_context>() && !m_inited_np_handler_dependencies)
{
m_inited_np_handler_dependencies = true;
auto& nc = g_fxo->get<network_context>();
nc.bind_sce_np_port();
std::lock_guard lock(mutex_rpcn);
rpcn = rpcn::rpcn_client::get_instance();
}
}
np_handler::np_handler()
{
g_fxo->need<named_thread<signaling_handler>>();
@ -388,16 +410,6 @@ namespace np
if (g_cfg.net.upnp_enabled)
upnp.upnp_enable();
}
if (is_psn_active && g_cfg.net.psn_status == np_psn_status::psn_rpcn)
{
g_fxo->need<network_context>();
auto& nc = g_fxo->get<network_context>();
nc.bind_sce_np_port();
std::lock_guard lock(mutex_rpcn);
rpcn = rpcn::rpcn_client::get_instance();
}
}
np_handler::np_handler(utils::serial& ar)

View File

@ -76,6 +76,7 @@ namespace np
np_handler(utils::serial& ar);
void save(utils::serial& ar);
void init_np_handler_dependencies();
const std::array<u8, 6>& get_ether_addr() const;
const std::string& get_hostname() const;
u32 get_local_ip_addr() const;
@ -305,6 +306,8 @@ namespace np
shared_mutex mutex_queue_basic_events;
std::queue<basic_event> queue_basic_events;
bool m_inited_np_handler_dependencies = false;
private:
bool is_connected = false;
bool is_psn_active = false;

View File

@ -17,7 +17,6 @@
LOG_CHANNEL(sign_log, "Signaling");
void need_network();
template <>
void fmt_class_string<SignalingCommand>::format(std::string& out, u64 arg)
@ -41,7 +40,6 @@ void fmt_class_string<SignalingCommand>::format(std::string& out, u64 arg)
signaling_handler::signaling_handler()
{
need_network();
}
/////////////////////////////

View File

@ -2875,9 +2875,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
for (const auto& [type, data] : *g_fxo)
{
if (type.stop)
if (type.thread_op)
{
type.stop(data, thread_state::aborting);
type.thread_op(data, thread_state::aborting);
}
}
@ -2928,9 +2928,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
// Join threads
for (const auto& [type, data] : *g_fxo)
{
if (type.stop)
if (type.thread_op)
{
type.stop(data, thread_state::finished);
type.thread_op(data, thread_state::finished);
}
}

View File

@ -15,6 +15,8 @@ extern thread_local std::string_view g_tls_serialize_name;
namespace stx
{
struct launch_retainer{};
// Simplified typemap with exactly one object of each used type, non-moveable. Initialized on init(). Destroyed on clear().
template <typename Tag /*Tag should be unique*/, u32 Size = 0, u32 Align = (Size ? 64 : __STDCPP_DEFAULT_NEW_ALIGNMENT__)>
class alignas(Align) manual_typemap
@ -62,7 +64,7 @@ namespace stx
struct typeinfo
{
bool(*create)(uchar* ptr, manual_typemap&, utils::serial*, std::string_view) noexcept = nullptr;
void(*stop)(void* ptr, thread_state) noexcept = nullptr;
void(*thread_op)(void* ptr, thread_state) noexcept = nullptr;
void(*save)(void* ptr, utils::serial&) noexcept = nullptr;
void(*destroy)(void* ptr) noexcept = nullptr;
std::string_view name;
@ -72,13 +74,19 @@ namespace stx
{
if (ar)
{
if constexpr (std::is_constructible_v<T, manual_typemap&, exact_t<utils::serial&>>)
if constexpr (std::is_constructible_v<T, exact_t<manual_typemap&>, exact_t<utils::serial&>>)
{
g_tls_serialize_name = name;
new (ptr) T(_this, exact_t<utils::serial&>(*ar));
return true;
}
if constexpr (std::is_constructible_v<T, exact_t<const launch_retainer&>, exact_t<utils::serial&>>)
{
new (ptr) T(exact_t<const launch_retainer&>(launch_retainer{}), exact_t<utils::serial&>(*ar));
return true;
}
if constexpr (std::is_constructible_v<T, exact_t<utils::serial&>>)
{
g_tls_serialize_name = name;
@ -88,12 +96,18 @@ namespace stx
}
// Allow passing reference to "this"
if constexpr (std::is_constructible_v<T, manual_typemap&>)
if constexpr (std::is_constructible_v<T, exact_t<manual_typemap&>>)
{
new (ptr) T(_this);
return true;
}
if constexpr (std::is_constructible_v<T, exact_t<const launch_retainer&>>)
{
new (ptr) T(exact_t<const launch_retainer&>(launch_retainer{}));
return true;
}
// Call default constructor only if available
if constexpr (std::is_default_constructible_v<T>)
{
@ -111,7 +125,7 @@ namespace stx
}
template <typename T>
static void call_stop(void* ptr, thread_state state) noexcept
static void call_thread_op(void* ptr, thread_state state) noexcept
{
// Abort and/or join (expected thread_state::aborting or thread_state::finished)
*std::launder(static_cast<T*>(ptr)) = state;
@ -134,7 +148,7 @@ namespace stx
if constexpr (std::is_assignable_v<T&, thread_state>)
{
r.stop = &call_stop<T>;
r.thread_op = &call_thread_op<T>;
}
if constexpr (!!(requires (T& a) { a.save(std::declval<stx::exact_t<utils::serial&>>()); }))
@ -233,6 +247,8 @@ namespace stx
return a.first < b.first;
});
const auto info_before = m_info;
for (pos = 0; pos < stx::typelist<typeinfo>().count(); pos++)
{
const auto& type = *order[pos].second;
@ -260,6 +276,15 @@ namespace stx
}
}
// Launch threads
for (auto it = m_info; it != info_before; it--)
{
if (auto op = (*std::prev(it))->thread_op)
{
op(*std::prev(m_order, m_info - it + 1), thread_state{});
}
}
g_tls_serialize_name = {};
}

View File

@ -1174,13 +1174,21 @@ namespace stx
template <typename T>
struct exact_t
{
static_assert(std::is_reference_v<T> || std::is_convertible_v<T, const T&>);
T obj;
exact_t(T&& _obj) : obj(std::forward<T>(_obj)) {}
explicit exact_t(T&& _obj) : obj(std::forward<T>(_obj)) {}
exact_t& operator=(const exact_t&) = delete;
// TODO: More conversions
template <typename U> requires (std::is_same_v<U&, T>)
operator U&() const { return obj; };
operator U&() const noexcept { return obj; };
template <typename U> requires (std::is_same_v<const U&, T>)
operator const U&() const noexcept { return obj; };
template <typename U> requires (std::is_same_v<U, T> && std::is_copy_constructible_v<T>)
operator U() const noexcept { return obj; };
};
}