Skip to content

Commit

Permalink
Refactoring and some emulation fixes
Browse files Browse the repository at this point in the history
* Use atomic waitables instead instead of global thread wait as often as possible.
* Add ::is_stopped() and and ::is_paued() which can be used in atomic loops and with atomic wait. (constexpr cpu flags test functions)
* Fix notification bug of sys_spu_thread_group_exit/terminate. (old bug, enhanced by RPCS3#9117)
* Function time statistics at Emu.Stop() restored. (instead of current "X syscall failed with 0x00000000 : 0")
* Remove cpu_flag::dbg_global_stop, use cpu_flag::exit instead. (essentially a duplicated flag)
  • Loading branch information
elad335 committed Dec 16, 2020
1 parent e82bef4 commit 7c33eb6
Show file tree
Hide file tree
Showing 41 changed files with 391 additions and 214 deletions.
6 changes: 3 additions & 3 deletions Utilities/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1505,14 +1505,14 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
else
{
// Wait until the thread is recovered
while (!cpu->state.test_and_reset(cpu_flag::signal))
while (auto state = cpu->state.fetch_sub(cpu_flag::signal))
{
if (cpu->is_stopped())
if (is_stopped(state) || state & cpu_flag::signal)
{
break;
}

thread_ctrl::wait();
cpu->state.wait(state);
}
}

Expand Down
36 changes: 35 additions & 1 deletion Utilities/Thread.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma once
#pragma once

#include "util/types.hpp"
#include "util/atomic.hpp"
Expand Down Expand Up @@ -234,6 +234,40 @@ class thread_ctrl final
_wait_for(-1, true);
}

// Wait for both thread sync var and provided atomic var
template <typename T, atomic_wait::op op = atomic_wait::op::eq>
static inline void wait_on(atomic_t<T>& wait, std::type_identity_t<T> old, atomic_wait_timeout nsec = atomic_wait_timeout::inf)
{
auto _this = g_tls_this_thread;

if (_this->m_sync.bit_test_reset(2))
{
return;
}

atomic_wait::list<2> list{};
list.set<0, op>(wait, old);
if (_this) list.set<1>(_this->m_sync, 0, 4 + 1);
list.wait(nsec);
}

// Wait for both thread sync var and provided atomic "structure"
template <typename T, atomic_wait::op op = atomic_wait::op::eq>
static inline void wait_on(T& wait, atomic_wait_timeout nsec = atomic_wait_timeout::inf)
{
auto _this = g_tls_this_thread;

if (_this->m_sync.bit_test_reset(2))
{
return;
}

atomic_wait::list<2> list{};
list.set<0, op>(wait);
if (_this) list.set<1>(_this->m_sync, 0, 4 + 1);
list.wait(nsec);
}

// Exit.
[[noreturn]] static void emergency_exit(std::string_view reason);

Expand Down
6 changes: 0 additions & 6 deletions Utilities/lockless.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,6 @@ class lf_queue final
if (!m_data)
{
m_data = _this->pop_all();

if (!m_data)
{
_this->wait();
m_data = _this->pop_all();
}
}

return *this;
Expand Down
34 changes: 25 additions & 9 deletions rpcs3/Emu/CPU/CPUThread.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "CPUThread.h"

#include "Emu/System.h"
Expand Down Expand Up @@ -46,7 +46,6 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
case cpu_flag::signal: return "sig";
case cpu_flag::memory: return "mem";
case cpu_flag::dbg_global_pause: return "G-PAUSE";
case cpu_flag::dbg_global_stop: return "G-EXIT";
case cpu_flag::dbg_pause: return "PAUSE";
case cpu_flag::dbg_step: return "STEP";
case cpu_flag::__bitset_enum_max: break;
Expand Down Expand Up @@ -182,7 +181,7 @@ struct cpu_prof
if (threads.empty())
{
// Wait for messages if no work (don't waste CPU)
atomic_wait::list(registered).wait();
thread_ctrl::wait_on(registered);
continue;
}

Expand Down Expand Up @@ -538,10 +537,17 @@ void cpu_thread::operator()()
cleanup.name = thread_ctrl::get_name();

// Check thread status
while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop)) && thread_ctrl::state() != thread_state::aborting)
while (thread_ctrl::state() != thread_state::aborting)
{
// Check stop status
if (!(state & cpu_flag::stop))
const auto state0 = +state;

if (is_stopped(state0 - cpu_flag::stop))
{
break;
}

if (!(state0 & cpu_flag::stop))
{
cpu_task();

Expand All @@ -553,7 +559,7 @@ void cpu_thread::operator()()
continue;
}

thread_ctrl::wait();
state.wait(state0);

if (state & cpu_flag::ret && state.test_and_reset(cpu_flag::ret))
{
Expand Down Expand Up @@ -586,6 +592,7 @@ bool cpu_thread::check_state() noexcept
while (true)
{
// Process all flags in a single atomic op
bs_t<cpu_flag> state1;
const auto state0 = state.fetch_op([&](bs_t<cpu_flag>& flags)
{
bool store = false;
Expand Down Expand Up @@ -639,7 +646,7 @@ bool cpu_thread::check_state() noexcept
}

// Atomically clean wait flag and escape
if (!(flags & (cpu_flag::exit + cpu_flag::dbg_global_stop + cpu_flag::ret + cpu_flag::stop)))
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
{
// Check pause flags which hold thread inside check_state (ignore suspend on cpu_flag::temp)
if (flags & (cpu_flag::pause + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::memory + (cpu_can_stop ? cpu_flag::suspend : cpu_flag::pause)))
Expand All @@ -651,6 +658,7 @@ bool cpu_thread::check_state() noexcept
}

escape = false;
state1 = flags;
return store;
}

Expand Down Expand Up @@ -682,6 +690,7 @@ bool cpu_thread::check_state() noexcept
}

escape = true;
state1 = flags;
return store;
}).first;

Expand All @@ -693,6 +702,11 @@ bool cpu_thread::check_state() noexcept
cpu_counter::add(this);
}

if (retval)
{
cpu_on_stop();
}

ensure(cpu_can_stop || !retval);
return retval;
}
Expand All @@ -718,7 +732,7 @@ bool cpu_thread::check_state() noexcept
g_fxo->get<gdb_server>()->pause_from(this);
}

thread_ctrl::wait();
state.wait(state1);
}
else
{
Expand Down Expand Up @@ -777,6 +791,9 @@ void cpu_thread::notify()

void cpu_thread::abort()
{
state += cpu_flag::exit;
state.notify_one(cpu_flag::exit);

// Downcast to correct type
if (id_type() == 1)
{
Expand Down Expand Up @@ -1032,7 +1049,6 @@ void cpu_thread::stop_all() noexcept
{
auto on_stop = [](u32, cpu_thread& cpu)
{
cpu.state += cpu_flag::dbg_global_stop;
cpu.abort();
};

Expand Down
35 changes: 29 additions & 6 deletions rpcs3/Emu/CPU/CPUThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,24 @@ enum class cpu_flag : u32
memory, // Thread must unlock memory mutex

dbg_global_pause, // Emulation paused
dbg_global_stop, // Emulation stopped
dbg_pause, // Thread paused
dbg_step, // Thread forced to pause after one step (one instruction, etc)

__bitset_enum_max
};

// Test stopped state
constexpr bool is_stopped(bs_t<cpu_flag> state)
{
return !!(state & (cpu_flag::stop + cpu_flag::exit));
}

// Test paused state
constexpr bool is_paused(bs_t<cpu_flag> state)
{
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
}

class cpu_thread
{
public:
Expand Down Expand Up @@ -61,16 +72,25 @@ class cpu_thread
return false;
}

// Test stopped state
// Wrappers
static constexpr bool is_stopped(bs_t<cpu_flag> s)
{
return ::is_stopped(s);
}

static constexpr bool is_paused(bs_t<cpu_flag> s)
{
return ::is_paused(s);
}

bool is_stopped() const
{
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::dbg_global_stop));
return ::is_stopped(state);
}

// Test paused state
bool is_paused() const
{
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
return ::is_paused(state);
}

bool has_pause_flag() const
Expand Down Expand Up @@ -122,6 +142,9 @@ class cpu_thread
// Callback for cpu_flag::ret
virtual void cpu_return() {}

// Callback for function abortion stats on Emu.Stop()
virtual void cpu_on_stop() {}

// For internal use
struct suspend_work
{
Expand Down Expand Up @@ -211,7 +234,7 @@ class cpu_thread
}
}

// Stop all threads with cpu_flag::dbg_global_stop
// Stop all threads with cpu_flag::exit
static void stop_all() noexcept;

// Send signal to the profiler(s) to flush results
Expand Down
15 changes: 10 additions & 5 deletions rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ struct msg_dlg_thread_info

if (new_value == 0)
{
wait_until.wait(0);
thread_ctrl::wait_on(wait_until, 0);
continue;
}

Expand Down Expand Up @@ -217,14 +217,19 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
lv2_obj::awake(&ppu);
});

while (!ppu.state.test_and_reset(cpu_flag::signal))
while (auto state = ppu.state.fetch_sub(cpu_flag::signal))
{
if (ppu.is_stopped())
if (is_stopped(state))
{
return 0;
return {};
}

thread_ctrl::wait();
if (state & cpu_flag::signal)
{
break;
}

ppu.state.wait(state);
}

if (is_blocking)
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/Modules/cellSaveData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
// Reschedule after a blocking dialog returns
if (ppu.check_state())
{
return 0;
return {};
}

if (res != CELL_OK)
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/Modules/cellSysutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu)

if (ppu.is_stopped())
{
return 0;
return {};
}
}

Expand Down
16 changes: 14 additions & 2 deletions rpcs3/Emu/Cell/Modules/cellVdec.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_sync.h"
Expand Down Expand Up @@ -205,8 +205,20 @@ struct vdec_context final
ppu_tid.release(ppu.id);

// pcmd can be nullptr
for (auto* pcmd : in_cmd)
for (auto it = in_cmd.begin();; [&]
{
if (++it != in_cmd.end())
{
return;
}

thread_ctrl::wait_on(in_cmd);
it = in_cmd.begin(); // Pop new command list
}())
{
// pcmd can be nullptr
auto* pcmd = *it;

if (thread_ctrl::state() == thread_state::aborting)
{
break;
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,

if (ppu.is_stopped())
{
return 0;
return {};
}
}
}
Expand Down
1 change: 1 addition & 0 deletions rpcs3/Emu/Cell/PPUFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ using ppu_function_t = bool(*)(ppu_thread&);
ppu.current_function = #func;\
std::memcpy(ppu.syscall_args, ppu.gpr + 3, sizeof(ppu.syscall_args)); \
ppu_func_detail::do_call(ppu, func);\
static_cast<void>(ppu.test_stopped());\
ppu.current_function = old_f;\
ppu.cia += 4;\
__VA_ARGS__;\
Expand Down

0 comments on commit 7c33eb6

Please sign in to comment.