mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-28 21:38:01 +03:00
atomic.cpp/threads: remove old wait callback
Add new wait callback which simply collects statistics. Shift workarounds towards actual problem detection.
This commit is contained in:
parent
3ac819ee70
commit
badb3dc2dd
7 changed files with 124 additions and 118 deletions
|
@ -86,6 +86,8 @@ LOG_CHANNEL(vm_log, "VM");
|
|||
thread_local u64 g_tls_fault_all = 0;
|
||||
thread_local u64 g_tls_fault_rsx = 0;
|
||||
thread_local u64 g_tls_fault_spu = 0;
|
||||
thread_local u64 g_tls_wait_time = 0;
|
||||
thread_local u64 g_tls_wait_fail = 0;
|
||||
extern thread_local std::string(*g_tls_log_prefix)();
|
||||
|
||||
template <>
|
||||
|
@ -1888,21 +1890,32 @@ void thread_base::start(native_entry entry, void(*trampoline)())
|
|||
#endif
|
||||
}
|
||||
|
||||
void thread_base::initialize(void (*error_cb)(), bool(*wait_cb)(const void*))
|
||||
void thread_base::initialize(void (*error_cb)())
|
||||
{
|
||||
// Initialize TLS variables
|
||||
thread_ctrl::g_tls_this_thread = this;
|
||||
|
||||
thread_ctrl::g_tls_error_callback = error_cb;
|
||||
|
||||
// Initialize atomic wait callback
|
||||
atomic_wait_engine::set_wait_callback(wait_cb);
|
||||
|
||||
g_tls_log_prefix = []
|
||||
{
|
||||
return thread_ctrl::get_name_cached();
|
||||
};
|
||||
|
||||
atomic_wait_engine::set_wait_callback([](const void*, u64 attempts, u64 stamp0) -> bool
|
||||
{
|
||||
if (attempts == umax)
|
||||
{
|
||||
g_tls_wait_time += __rdtsc() - stamp0;
|
||||
}
|
||||
else if (attempts > 1)
|
||||
{
|
||||
g_tls_wait_fail += attempts - 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
set_name(thread_ctrl::get_name_cached());
|
||||
}
|
||||
|
||||
|
@ -1949,23 +1962,6 @@ void thread_base::set_name(std::string name)
|
|||
#endif
|
||||
}
|
||||
|
||||
void thread_base::notify_abort() noexcept
|
||||
{
|
||||
u64 tid = m_thread.load();
|
||||
#ifdef _WIN32
|
||||
tid = GetThreadId(reinterpret_cast<HANDLE>(tid));
|
||||
#endif
|
||||
|
||||
while (auto ptr = m_state_notifier.load())
|
||||
{
|
||||
// Since this function is not perfectly implemented, run it in a loop
|
||||
if (atomic_wait_engine::raw_notify(ptr, tid))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64 thread_base::finalize(thread_state result_state) noexcept
|
||||
{
|
||||
// Report pending errors
|
||||
|
@ -2004,22 +2000,30 @@ u64 thread_base::finalize(thread_state result_state) noexcept
|
|||
return thread_ctrl::get_name_cached();
|
||||
};
|
||||
|
||||
sig_log.notice("Thread time: %fs (%fGc); Faults: %u [rsx:%u, spu:%u]; [soft:%u hard:%u]; Switches:[vol:%u unvol:%u]",
|
||||
sig_log.notice("Thread time: %fs (%fGc); Faults: %u [rsx:%u, spu:%u]; [soft:%u hard:%u]; Switches:[vol:%u unvol:%u]; Wait:[%.3fs, spur:%u]",
|
||||
time / 1000000000.,
|
||||
cycles / 1000000000.,
|
||||
g_tls_fault_all,
|
||||
g_tls_fault_rsx,
|
||||
g_tls_fault_spu,
|
||||
fsoft, fhard, ctxvol, ctxinv);
|
||||
fsoft, fhard, ctxvol, ctxinv,
|
||||
g_tls_wait_time / (utils::get_tsc_freq() / 1.),
|
||||
g_tls_wait_fail);
|
||||
|
||||
atomic_wait_engine::set_wait_callback(nullptr);
|
||||
|
||||
const u64 _self = m_thread;
|
||||
m_thread.release(0);
|
||||
|
||||
// Return true if need to delete thread object
|
||||
const bool ok = m_state.exchange(result_state) <= thread_state::aborting;
|
||||
// Return true if need to delete thread object (no)
|
||||
const bool ok = 0 == (3 & ~m_sync.fetch_op([&](u64& v)
|
||||
{
|
||||
v &= -4;
|
||||
v |= static_cast<u32>(result_state);
|
||||
}));
|
||||
|
||||
// Signal waiting threads
|
||||
m_state.notify_all();
|
||||
m_sync.notify_all(2);
|
||||
|
||||
// No detached thread supported atm
|
||||
return _self;
|
||||
|
@ -2154,12 +2158,13 @@ void thread_ctrl::_wait_for(u64 usec, bool alert /* true */)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (_this->m_signal && _this->m_signal.exchange(0))
|
||||
if (_this->m_sync.btr(2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_this->m_signal.wait(0, atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff});
|
||||
// Wait for signal and thread state abort
|
||||
_this->m_sync.wait(0, 4 + 1, atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff});
|
||||
}
|
||||
|
||||
std::string thread_ctrl::get_name_cached()
|
||||
|
@ -2200,23 +2205,52 @@ thread_base::~thread_base()
|
|||
|
||||
bool thread_base::join() const
|
||||
{
|
||||
for (auto state = m_state.load(); state != thread_state::finished && state != thread_state::errored;)
|
||||
// Hacked for too sleepy threads (1ms) TODO: make sure it's unneeded and remove
|
||||
const auto timeout = Emu.IsStopped() ? atomic_wait_timeout{1'000'000} : atomic_wait_timeout::inf;
|
||||
|
||||
bool warn = false;
|
||||
auto stamp0 = __rdtsc();
|
||||
|
||||
for (u64 i = 0; (m_sync & 3) <= 1; i++)
|
||||
{
|
||||
m_state.wait(state);
|
||||
state = m_state;
|
||||
m_sync.wait(0, 2, timeout);
|
||||
|
||||
if (m_sync & 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 20 && Emu.IsStopped())
|
||||
{
|
||||
stamp0 = __rdtsc();
|
||||
atomic_wait_engine::raw_notify(0, get_native_id());
|
||||
stamp0 = __rdtsc() - stamp0;
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
|
||||
return m_state.load() == thread_state::finished;
|
||||
if (warn)
|
||||
{
|
||||
sig_log.error("Thread [%s] is too sleepy. Took %.3fµs to wake it up!", *m_tname.load(), stamp0 / (utils::get_tsc_freq() / 1000000.));
|
||||
}
|
||||
|
||||
return (m_sync & 3) == 3;
|
||||
}
|
||||
|
||||
void thread_base::notify()
|
||||
{
|
||||
// Increment with saturation
|
||||
if (m_signal.try_inc())
|
||||
{
|
||||
// Considered impossible to have a situation when not notified
|
||||
m_signal.notify_all();
|
||||
}
|
||||
// Set notification
|
||||
m_sync |= 4;
|
||||
m_sync.notify_one(4);
|
||||
}
|
||||
|
||||
u64 thread_base::get_native_id() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return GetThreadId(reinterpret_cast<HANDLE>(m_thread.load()));
|
||||
#else
|
||||
return m_thread.load();
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 thread_base::get_cycles()
|
||||
|
@ -2242,7 +2276,7 @@ u64 thread_base::get_cycles()
|
|||
{
|
||||
cycles = static_cast<u64>(thread_time.tv_sec) * 1'000'000'000 + thread_time.tv_nsec;
|
||||
#endif
|
||||
if (const u64 old_cycles = m_cycles.exchange(cycles))
|
||||
if (const u64 old_cycles = m_sync.fetch_op([&](u64& v){ v &= 7; v |= (cycles << 3); }) >> 3)
|
||||
{
|
||||
return cycles - old_cycles;
|
||||
}
|
||||
|
@ -2252,7 +2286,7 @@ u64 thread_base::get_cycles()
|
|||
}
|
||||
else
|
||||
{
|
||||
return m_cycles;
|
||||
return m_sync >> 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue