Atomic waiting refactoring (#9208)

* 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 #9117)
* Function time statistics at Emu.Stop() restored. (instead of current "X syscall failed with 0x00000000 : 0")
This commit is contained in:
Eladash 2021-02-13 16:50:07 +02:00 committed by GitHub
parent cf384795d2
commit f43260bd58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 375 additions and 234 deletions

View file

@ -185,7 +185,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, nullptr);
continue;
}
@ -557,7 +557,14 @@ void cpu_thread::operator()()
while (!(state & cpu_flag::exit) && 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();
@ -569,7 +576,7 @@ void cpu_thread::operator()()
continue;
}
thread_ctrl::wait();
thread_ctrl::wait_on(state, state0);
if (state & cpu_flag::ret && state.test_and_reset(cpu_flag::ret))
{
@ -593,9 +600,9 @@ cpu_thread::cpu_thread(u32 id)
g_threads_created++;
}
void cpu_thread::cpu_wait()
void cpu_thread::cpu_wait(bs_t<cpu_flag> old)
{
thread_ctrl::wait();
thread_ctrl::wait_on(state, old);
}
bool cpu_thread::check_state() noexcept
@ -607,6 +614,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;
@ -660,7 +668,7 @@ bool cpu_thread::check_state() noexcept
}
// Atomically clean wait flag and escape
if (!(flags & (cpu_flag::exit + 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)))
@ -672,6 +680,7 @@ bool cpu_thread::check_state() noexcept
}
escape = false;
state1 = flags;
return store;
}
@ -703,6 +712,7 @@ bool cpu_thread::check_state() noexcept
}
escape = true;
state1 = flags;
return store;
}).first;
@ -714,6 +724,11 @@ bool cpu_thread::check_state() noexcept
cpu_counter::add(this);
}
if (retval)
{
cpu_on_stop();
}
ensure(cpu_can_stop || !retval);
return retval;
}
@ -739,7 +754,7 @@ bool cpu_thread::check_state() noexcept
g_fxo->get<gdb_server>()->pause_from(this);
}
cpu_wait();
cpu_wait(state1);
}
else
{
@ -799,6 +814,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)
{
@ -1076,7 +1094,6 @@ void cpu_thread::stop_all() noexcept
{
auto on_stop = [](u32, cpu_thread& cpu)
{
cpu.state += cpu_flag::exit;
cpu.abort();
};