This commit is contained in:
digant 2024-12-24 21:26:35 +01:00
parent f33b4a2932
commit 650947aada
18 changed files with 228 additions and 90 deletions

View file

@ -566,9 +566,41 @@ void fmt_class_string<std::source_location>::format(std::string& out, u64 arg)
fmt::append(out, "\n(in file %s", loc.file_name());
}
if (auto func = loc.function_name(); func && func[0])
if (std::string_view full_func{loc.function_name() ? loc.function_name() : ""}; !full_func.empty())
{
fmt::append(out, ", in function %s)", func);
// Remove useless disambiguators
std::string func = fmt::replace_all(std::string(full_func), {
{"struct ", ""},
{"class ", ""},
{"enum ", ""},
{"typename ", ""},
#ifdef _MSC_VER
{"__cdecl ", ""},
#endif
{"unsigned long long", "ullong"},
//{"unsigned long", "ulong"}, // ullong
{"unsigned int", "uint"},
{"unsigned short", "ushort"},
{"unsigned char", "uchar"}});
// Remove function argument signature for long names
for (usz index = func.find_first_of('('); index != umax && func.size() >= 100u; index = func.find_first_of('(', index))
{
// Operator() function
if (func.compare(0, 3, "()("sv) == 0 || func.compare(0, 3, "() "sv))
{
if (usz not_space = func.find_first_not_of(' ', index + 2); not_space != umax && func[not_space] == '(')
{
index += 2;
continue;
}
}
func = func.substr(0, index);
break;
}
fmt::append(out, ", in function '%s')", func);
}
else
{

View file

@ -2555,13 +2555,13 @@ std::string thread_ctrl::get_name_cached()
return *name_cache;
}
thread_base::thread_base(native_entry entry, std::string name)
thread_base::thread_base(native_entry entry, std::string name) noexcept
: entry_point(entry)
, m_tname(make_single_value(std::move(name)))
{
}
thread_base::~thread_base()
thread_base::~thread_base() noexcept
{
// Cleanup abandoned tasks: initialize default results and signal
this->exec();
@ -2602,7 +2602,7 @@ bool thread_base::join(bool dtor) const
if (i >= 16 && !(i & (i - 1)) && timeout != atomic_wait_timeout::inf)
{
sig_log.error(u8"Thread [%s] is too sleepy. Waiting for it %.3fus already!", *m_tname.load(), (utils::get_tsc() - stamp0) / (utils::get_tsc_freq() / 1000000.));
sig_log.error("Thread [%s] is too sleepy. Waiting for it %.3fus already!", *m_tname.load(), (utils::get_tsc() - stamp0) / (utils::get_tsc_freq() / 1000000.));
}
}

View file

@ -172,9 +172,9 @@ private:
friend class named_thread;
protected:
thread_base(native_entry, std::string name);
thread_base(native_entry, std::string name) noexcept;
~thread_base();
~thread_base() noexcept;
public:
// Get CPU cycles since last time this function was called. First call returns 0.
@ -351,7 +351,7 @@ public:
// Sets the native thread priority and returns it to zero at destructor
struct scoped_priority
{
explicit scoped_priority(int prio)
explicit scoped_priority(int prio) noexcept
{
set_native_priority(prio);
}
@ -360,7 +360,7 @@ public:
scoped_priority& operator=(const scoped_priority&) = delete;
~scoped_priority()
~scoped_priority() noexcept
{
set_native_priority(0);
}
@ -388,7 +388,7 @@ class thread_future_t : public thread_future, result_storage<Ctx, std::condition
using future = thread_future_t;
public:
thread_future_t(Ctx&& func, Args&&... args)
thread_future_t(Ctx&& func, Args&&... args) noexcept
: m_args(std::forward<Args>(args)...)
, m_func(std::forward<Ctx>(func))
{
@ -417,7 +417,7 @@ public:
};
}
~thread_future_t()
~thread_future_t() noexcept
{
if constexpr (!future::empty && !Discard)
{
@ -570,7 +570,7 @@ public:
named_thread& operator=(const named_thread&) = delete;
// Wait for the completion and access result (if not void)
[[nodiscard]] decltype(auto) operator()()
[[nodiscard]] decltype(auto) operator()() noexcept
{
thread::join();
@ -581,7 +581,7 @@ public:
}
// Wait for the completion and access result (if not void)
[[nodiscard]] decltype(auto) operator()() const
[[nodiscard]] decltype(auto) operator()() const noexcept
{
thread::join();
@ -593,7 +593,7 @@ public:
// Send command to the thread to invoke directly (references should be passed via std::ref())
template <bool Discard = true, typename Arg, typename... Args>
auto operator()(Arg&& arg, Args&&... args)
auto operator()(Arg&& arg, Args&&... args) noexcept
{
// Overloaded operator() of the Context.
constexpr bool v1 = std::is_invocable_v<Context, Arg&&, Args&&...>;
@ -667,12 +667,12 @@ public:
}
// Access thread state
operator thread_state() const
operator thread_state() const noexcept
{
return static_cast<thread_state>(thread::m_sync.load() & 3);
}
named_thread& operator=(thread_state s)
named_thread& operator=(thread_state s) noexcept
{
if (s == thread_state::created)
{
@ -693,7 +693,7 @@ public:
if constexpr (std::is_assignable_v<Context&, thread_state>)
{
static_cast<Context&>(*this) = s;
static_cast<Context&>(*this) = thread_state::aborting;
}
if (notify_sync)
@ -706,13 +706,18 @@ public:
{
// This participates in emulation stopping, use destruction-alike semantics
thread::join(true);
if constexpr (std::is_assignable_v<Context&, thread_state>)
{
static_cast<Context&>(*this) = thread_state::finished;
}
}
return *this;
}
// Context type doesn't need virtual destructor
~named_thread()
~named_thread() noexcept
{
// Assign aborting state forcefully and join thread
operator=(thread_state::finished);

View file

@ -109,10 +109,29 @@ void lv2_config::add_service_event(shared_ptr<lv2_config_service_event> event)
void lv2_config::remove_service_event(u32 id)
{
shared_ptr<lv2_config_service_event> ptr;
std::lock_guard lock(m_mutex);
events.erase(id);
if (auto it = events.find(id); it != events.end())
{
ptr = std::move(it->second);
events.erase(it);
}
}
lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) noexcept
{
if (s == thread_state::finished)
{
if (auto global = g_fxo->try_get<lv2_config>())
{
global->remove_service_event(id);
}
}
return *this;
}
// LV2 Config Service Listener
bool lv2_config_service_listener::check_service(const lv2_config_service& service) const

View file

@ -390,13 +390,9 @@ public:
}
// Destructor
~lv2_config_service_event() noexcept
{
if (auto global = g_fxo->try_get<lv2_config>())
{
global->remove_service_event(id);
}
}
lv2_config_service_event& operator=(thread_state s) noexcept;
~lv2_config_service_event() noexcept = default;
// Notify queue that this event exists
bool notify() const;

View file

@ -175,3 +175,14 @@ void lv2_socket::queue_wake(ppu_thread* ppu)
break;
}
}
lv2_socket& lv2_socket::operator=(thread_state s) noexcept
{
if (s == thread_state::finished)
{
close();
}
return *this;
}

View file

@ -27,6 +27,8 @@ using socket_type = uptr;
using socket_type = int;
#endif
enum class thread_state : u32;
class lv2_socket
{
public:
@ -62,7 +64,8 @@ public:
lv2_socket(utils::serial&, lv2_socket_type type);
static std::function<void(void*)> load(utils::serial& ar);
void save(utils::serial&, bool save_only_this_class = false);
virtual ~lv2_socket() = default;
~lv2_socket() noexcept = default;
lv2_socket& operator=(thread_state s) noexcept;
std::unique_lock<shared_mutex> lock();

View file

@ -57,19 +57,6 @@ void lv2_socket_native::save(utils::serial& ar)
ar(is_socket_connected());
}
lv2_socket_native::~lv2_socket_native()
{
std::lock_guard lock(mutex);
if (socket)
{
#ifdef _WIN32
::closesocket(socket);
#else
::close(socket);
#endif
}
}
s32 lv2_socket_native::create_socket()
{
ensure(family == SYS_NET_AF_INET);
@ -1114,10 +1101,12 @@ void lv2_socket_native::close()
socket = {};
}
auto& dnshook = g_fxo->get<np::dnshook>();
dnshook.remove_dns_spy(lv2_id);
if (auto dnshook = g_fxo->try_get<np::dnshook>())
{
dnshook->remove_dns_spy(lv2_id);
}
if (bound_port)
if (bound_port && g_fxo->is_init<named_thread<np::np_handler>>())
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
nph.upnp_remove_port_mapping(bound_port, type == SYS_NET_SOCK_STREAM ? "TCP" : "UDP");

View file

@ -35,7 +35,6 @@ public:
lv2_socket_native(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol);
lv2_socket_native(utils::serial& ar, lv2_socket_type type);
void save(utils::serial& ar);
~lv2_socket_native();
s32 create_socket();
std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr> accept(bool is_lock = true) override;

View file

@ -330,8 +330,9 @@ void lv2_socket_p2p::close()
return;
}
auto& nc = g_fxo->get<p2p_context>();
if (g_fxo->is_init<p2p_context>())
{
auto& nc = g_fxo->get<p2p_context>();
std::lock_guard lock(nc.list_p2p_ports_mutex);
if (!nc.list_p2p_ports.contains(port))

View file

@ -944,8 +944,9 @@ void lv2_socket_p2ps::close()
return;
}
auto& nc = g_fxo->get<p2p_context>();
if (g_fxo->is_init<p2p_context>())
{
auto& nc = g_fxo->get<p2p_context>();
std::lock_guard lock(nc.list_p2p_ports_mutex);
auto& p2p_port = ::at32(nc.list_p2p_ports, port);
{
@ -973,8 +974,10 @@ void lv2_socket_p2ps::close()
}
}
auto& tcpm = g_fxo->get<named_thread<tcp_timeout_monitor>>();
tcpm.clear_all_messages(lv2_id);
if (const auto tcpm = g_fxo->try_get<named_thread<tcp_timeout_monitor>>())
{
tcpm->clear_all_messages(lv2_id);
}
}
s32 lv2_socket_p2ps::shutdown([[maybe_unused]] s32 how)

View file

@ -434,9 +434,12 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector<std::string>& argv, std::vector<
using namespace id_manager;
shared_ptr<utils::serial> idm_capture = make_shared<utils::serial>();
if (!is_real_reboot)
{
reader_lock rlock{g_mutex};
reader_lock rlock{id_manager::g_mutex};
g_fxo->get<id_map<lv2_memory_container>>().save(*idm_capture);
stx::serial_breathe_and_tag(*idm_capture, "id_map<lv2_memory_container>", false);
}
idm_capture->set_reading_state();

View file

@ -587,7 +587,7 @@ bool gdb_thread::cmd_thread_info(gdb_cmd&)
bool gdb_thread::cmd_current_thread(gdb_cmd&)
{
return send_cmd_ack(selected_thread && selected_thread->state.none_of(cpu_flag::exit) ? "" : ("QC" + u64_to_padded_hex(selected_thread->id)));
return send_cmd_ack(selected_thread && selected_thread->state.none_of(cpu_flag::exit) ? ("QC" + u64_to_padded_hex(selected_thread->id)) : "");
}
bool gdb_thread::cmd_read_register(gdb_cmd& cmd)
@ -733,7 +733,7 @@ bool gdb_thread::cmd_read_all_registers(gdb_cmd&)
return send_cmd_ack(result);
}
GDB.warning("Unimplemented thread type %d.", selected_thread ->id_type());
GDB.warning("Unimplemented thread type %d.", selected_thread->id_type());
return send_cmd_ack("");
}

View file

@ -801,6 +801,15 @@ public:
}
}
if constexpr (std::is_assignable_v<Get&, thread_state>)
{
if (ptr)
{
constexpr thread_state finished{3};
*static_cast<Get*>(ptr.get()) = finished;
}
}
return true;
}
@ -824,6 +833,15 @@ public:
}
}
if constexpr (std::is_assignable_v<Get&, thread_state>)
{
if (ptr)
{
constexpr thread_state finished{3};
*static_cast<Get*>(ptr.get()) = finished;
}
}
return true;
}

View file

@ -2718,8 +2718,15 @@ bool Emulator::Pause(bool freeze_emulation, bool show_resume_message)
cpu.state += cpu_flag::dbg_global_pause;
};
idm::select<named_thread<ppu_thread>>(on_select);
idm::select<named_thread<spu_thread>>(on_select);
if (g_fxo->is_init<id_manager::id_map<named_thread<ppu_thread>>>())
{
idm::select<named_thread<ppu_thread>>(on_select);
}
if (g_fxo->is_init<id_manager::id_map<named_thread<spu_thread>>>())
{
idm::select<named_thread<spu_thread>>(on_select);
}
if (auto rsx = g_fxo->try_get<rsx::thread>())
{
@ -2760,6 +2767,7 @@ bool Emulator::Pause(bool freeze_emulation, bool show_resume_message)
std::unique_ptr<named_thread<decltype(refresh_l)>> m_thread;
};
g_fxo->need<thread_t>();
g_fxo->get<thread_t>().m_thread.reset();
g_fxo->get<thread_t>().m_thread = std::make_unique<named_thread<decltype(refresh_l)>>("Pause Message Thread"sv, std::move(refresh_l));
});

View file

@ -372,6 +372,7 @@ namespace stx
{
// Reset, probably a new utils::serial object
s_tls_call_count = 1;
s_tls_object_name = "none"sv;
}
s_tls_current_pos = ar.pos;

View file

@ -1049,8 +1049,15 @@ void debugger_frame::UpdateUnitList()
if (emu_state != system_state::stopped)
{
idm::select<named_thread<ppu_thread>>(on_select, idm::unlocked);
idm::select<named_thread<spu_thread>>(on_select, idm::unlocked);
if (g_fxo->is_init<id_manager::id_map<named_thread<ppu_thread>>>())
{
idm::select<named_thread<ppu_thread>>(on_select, idm::unlocked);
}
if (g_fxo->is_init<id_manager::id_map<named_thread<spu_thread>>>())
{
idm::select<named_thread<spu_thread>>(on_select, idm::unlocked);
}
if (const auto render = g_fxo->try_get<rsx::thread>(); render && render->ctrl)
{

View file

@ -154,6 +154,7 @@ namespace stx
if (m_ptr) [[likely]]
{
const auto o = d();
ensure(o->refs == 1);
o->destroy.load()(o);
m_ptr = nullptr;
}
@ -437,11 +438,15 @@ namespace stx
// Set to null
void reset() noexcept
{
const auto o = d();
if (m_ptr && !--o->refs) [[unlikely]]
if (m_ptr) [[unlikely]]
{
o->destroy(o);
const auto o = d();
if (!--o->refs)
{
o->destroy(o);
}
m_ptr = nullptr;
}
}
@ -571,7 +576,7 @@ namespace stx
{
mutable atomic_t<uptr> m_val{0};
static shared_counter* d(uptr val)
static shared_counter* d(uptr val) noexcept
{
return std::launder(reinterpret_cast<shared_counter*>((val >> c_ref_size) - sizeof(shared_counter)));
}
@ -581,9 +586,32 @@ namespace stx
return d(m_val);
}
static uptr to_val(const volatile std::remove_extent_t<T>* ptr) noexcept
{
return (reinterpret_cast<uptr>(ptr) << c_ref_size);
}
static std::remove_extent_t<T>* ptr_to(uptr val) noexcept
{
return reinterpret_cast<std::remove_extent_t<T>*>(val >> c_ref_size);
}
template <typename U>
friend class atomic_ptr;
// Helper struct to check if a type is an instance of a template
template <typename T1, template <typename> class Template>
struct is_instance_of : std::false_type {};
template <typename T1, template <typename> class Template>
struct is_instance_of<Template<T1>, Template> : std::true_type {};
template <typename T1>
static constexpr bool is_stx_pointer = false
|| is_instance_of<std::remove_cv_t<T1>, shared_ptr>::value
|| is_instance_of<std::remove_cv_t<T1>, single_ptr>::value
|| is_instance_of<std::remove_cv_t<T1>, atomic_ptr>::value;
public:
using element_type = std::remove_extent_t<T>;
@ -592,11 +620,14 @@ namespace stx
constexpr atomic_ptr() noexcept = default;
// Optimized value construct
template <typename... Args> requires (!(sizeof...(Args) == 1 && (std::is_same_v<std::remove_cvref_t<Args>, shared_type> || ...)) && std::is_constructible_v<T, Args...>)
template <typename... Args> requires (true
&& sizeof...(Args) != 0
&& !(sizeof...(Args) == 1 && (is_stx_pointer<Args> || ...))
&& std::is_constructible_v<element_type, Args&&...>)
explicit atomic_ptr(Args&&... args) noexcept
{
shared_type r = make_single<T>(std::forward<Args>(args)...);
m_val = reinterpret_cast<uptr>(std::exchange(r.m_ptr, nullptr)) << c_ref_size;
m_val.raw() = to_val(std::exchange(r.m_ptr, nullptr));
d()->refs.raw() += c_ref_mask;
}
@ -604,32 +635,38 @@ namespace stx
atomic_ptr(const shared_ptr<U>& r) noexcept
{
// Obtain a ref + as many refs as an atomic_ptr can additionally reference
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
if (m_val)
d()->refs += c_ref_mask + 1;
if (uptr rval = to_val(r.m_ptr))
{
m_val.raw() = rval;
d(rval)->refs += c_ref_mask + 1;
}
}
template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr(shared_ptr<U>&& r) noexcept
{
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
r.m_ptr = nullptr;
if (uptr rval = to_val(r.m_ptr))
{
m_val.raw() = rval;
d(rval)->refs += c_ref_mask;
}
if (m_val)
d()->refs += c_ref_mask;
r.m_ptr = nullptr;
}
template <typename U> requires same_ptr_implicit_v<T, U>
atomic_ptr(single_ptr<U>&& r) noexcept
{
m_val = reinterpret_cast<uptr>(r.m_ptr) << c_ref_size;
r.m_ptr = nullptr;
if (uptr rval = to_val(r.m_ptr))
{
m_val.raw() = rval;
d(rval)->refs += c_ref_mask;
}
if (m_val)
d()->refs += c_ref_mask;
r.m_ptr = nullptr;
}
~atomic_ptr()
~atomic_ptr() noexcept
{
const uptr v = m_val.raw();
@ -645,13 +682,13 @@ namespace stx
}
// Optimized value assignment
atomic_ptr& operator=(std::remove_cv_t<T> value) noexcept
atomic_ptr& operator=(std::remove_cv_t<T> value) noexcept requires (!is_stx_pointer<T>)
{
shared_type r = make_single<T>(std::move(value));
r.d()->refs.raw() += c_ref_mask;
atomic_ptr old;
old.m_val.raw() = m_val.exchange(reinterpret_cast<uptr>(std::exchange(r.m_ptr, nullptr)) << c_ref_size);
old.m_val.raw() = m_val.exchange(to_val(std::exchange(r.m_ptr, nullptr)));
return *this;
}
@ -704,7 +741,7 @@ namespace stx
}
// Set referenced pointer
r.m_ptr = std::launder(reinterpret_cast<element_type*>(prev >> c_ref_size));
r.m_ptr = std::launder(ptr_to(prev));
r.d()->refs++;
// Dereference if still the same pointer
@ -749,7 +786,7 @@ namespace stx
// Set fake unreferenced pointer
if (did_ref)
{
r.m_ptr = std::launder(reinterpret_cast<element_type*>(prev >> c_ref_size));
r.m_ptr = std::launder(ptr_to(prev));
}
// Result temp storage
@ -805,14 +842,17 @@ namespace stx
// Create an object from variadic args
// If a type needs shared_type to be constructed, std::reference_wrapper can be used
template <typename... Args> requires (!(sizeof...(Args) == 1 && (std::is_same_v<std::remove_cvref_t<Args>, shared_type> || ...)) && std::is_constructible_v<T, Args...>)
template <typename... Args> requires (true
&& sizeof...(Args) != 0
&& !(sizeof...(Args) == 1 && (is_stx_pointer<Args> || ...))
&& std::is_constructible_v<element_type, Args&&...>)
void store(Args&&... args) noexcept
{
shared_type r = make_single<T>(std::forward<Args>(args)...);
r.d()->refs.raw() += c_ref_mask;
atomic_ptr old;
old.m_val.raw() = m_val.exchange(reinterpret_cast<uptr>(std::exchange(r.m_ptr, nullptr)) << c_ref_size);
old.m_val.raw() = m_val.exchange(to_val(std::exchange(r.m_ptr, nullptr)));
}
void store(shared_type value) noexcept
@ -824,20 +864,23 @@ namespace stx
}
atomic_ptr old;
old.m_val.raw() = m_val.exchange(reinterpret_cast<uptr>(std::exchange(value.m_ptr, nullptr)) << c_ref_size);
old.m_val.raw() = m_val.exchange(to_val(std::exchange(value.m_ptr, nullptr)));
}
template <typename... Args> requires (!(sizeof...(Args) == 1 && (std::is_same_v<std::remove_cvref_t<Args>, shared_type> || ...)) && std::is_constructible_v<T, Args...>)
template <typename... Args> requires (true
&& sizeof...(Args) != 0
&& !(sizeof...(Args) == 1 && (is_stx_pointer<Args> || ...))
&& std::is_constructible_v<element_type, Args&...>)
[[nodiscard]] shared_type exchange(Args&&... args) noexcept
{
shared_type r = make_single<T>(std::forward<Args>(args)...);
r.d()->refs.raw() += c_ref_mask;
atomic_ptr old;
old.m_val.raw() += m_val.exchange(reinterpret_cast<uptr>(r.m_ptr) << c_ref_size);
old.m_val.raw() = m_val.exchange(to_val(r.m_ptr));
old.m_val.raw() += 1;
r.m_ptr = std::launder(reinterpret_cast<element_type*>(old.m_val >> c_ref_size));
r.m_ptr = std::launder(ptr_to(old.m_val));
return r;
}
@ -850,10 +893,10 @@ namespace stx
}
atomic_ptr old;
old.m_val.raw() += m_val.exchange(reinterpret_cast<uptr>(value.m_ptr) << c_ref_size);
old.m_val.raw() = m_val.exchange(to_val(value.m_ptr));
old.m_val.raw() += 1;
value.m_ptr = std::launder(reinterpret_cast<element_type*>(old.m_val >> c_ref_size));
value.m_ptr = std::launder(ptr_to(old.m_val));
return value;
}
@ -898,10 +941,10 @@ namespace stx
}
atomic_ptr old_exch;
old_exch.m_val.raw() = reinterpret_cast<uptr>(std::exchange(exch.m_ptr, nullptr)) << c_ref_size;
old_exch.m_val.raw() = to_val(std::exchange(exch.m_ptr, nullptr));
// Set to reset old cmp_and_old value
old.m_val.raw() = (reinterpret_cast<uptr>(cmp_and_old.m_ptr) << c_ref_size) | c_ref_mask;
old.m_val.raw() = to_val(cmp_and_old.m_ptr) | c_ref_mask;
if (!_val)
{
@ -909,7 +952,7 @@ namespace stx
}
// Set referenced pointer
cmp_and_old.m_ptr = std::launder(reinterpret_cast<element_type*>(_val >> c_ref_size));
cmp_and_old.m_ptr = std::launder(ptr_to(_val));
cmp_and_old.d()->refs++;
// Dereference if still the same pointer
@ -977,7 +1020,7 @@ namespace stx
}
// Failure (return references)
old.m_val.raw() = reinterpret_cast<uptr>(std::exchange(exch.m_ptr, nullptr)) << c_ref_size;
old.m_val.raw() = to_val(std::exchange(exch.m_ptr, nullptr));
return false;
}