SPU: Use waitable atomics for SPU channels interface

This commit is contained in:
Eladash 2020-06-26 12:17:15 +03:00 committed by Ivan
parent 3157a10428
commit a029a94c73
6 changed files with 123 additions and 64 deletions

View File

@ -1892,6 +1892,7 @@ void thread_base::initialize(void (*error_cb)(), bool(*wait_cb)(const void*))
void thread_base::notify_abort() noexcept void thread_base::notify_abort() noexcept
{ {
m_signal.try_inc();
atomic_storage_futex::raw_notify(+m_state_notifier); atomic_storage_futex::raw_notify(+m_state_notifier);
} }

View File

@ -210,6 +210,12 @@ public:
static_cast<thread_base&>(thread).notify(); static_cast<thread_base&>(thread).notify();
} }
template <typename T>
static void raw_notify(named_thread<T>& thread)
{
static_cast<thread_base&>(thread).notify_abort();
}
// Read current state // Read current state
static inline thread_state state() static inline thread_state state()
{ {

View File

@ -22,7 +22,7 @@ inline void try_start(spu_thread& spu)
}).second) }).second)
{ {
spu.state -= cpu_flag::stop; spu.state -= cpu_flag::stop;
thread_ctrl::notify(static_cast<named_thread<spu_thread>&>(spu)); thread_ctrl::raw_notify(static_cast<named_thread<spu_thread>&>(spu));
} }
}; };
@ -123,7 +123,7 @@ bool spu_thread::read_reg(const u32 addr, u32& value)
case SPU_Out_MBox_offs: case SPU_Out_MBox_offs:
{ {
value = ch_out_mbox.pop(*this); value = ch_out_mbox.pop();
return true; return true;
} }

View File

@ -1247,11 +1247,11 @@ void spu_thread::push_snr(u32 number, u32 value)
// Check corresponding SNR register settings // Check corresponding SNR register settings
if ((snr_config >> number) & 1) if ((snr_config >> number) & 1)
{ {
channel->push_or(*this, value); channel->push_or(value);
} }
else else
{ {
channel->push(*this, value); channel->push(value);
} }
} }
@ -2448,19 +2448,8 @@ s64 spu_thread::get_ch_value(u32 ch)
busy_wait(); busy_wait();
} }
u32 out = 0; const s64 out = channel.pop_wait(*this);
static_cast<void>(test_stopped());
while (!channel.try_pop(out))
{
if (is_stopped())
{
return -1;
}
thread_ctrl::wait();
}
check_state();
return out; return out;
}; };
@ -2508,9 +2497,8 @@ s64 spu_thread::get_ch_value(u32 ch)
case MFC_RdTagStat: case MFC_RdTagStat:
{ {
if (ch_tag_stat.get_count()) if (u32 out; ch_tag_stat.try_read(out))
{ {
u32 out = ch_tag_stat.get_value();
ch_tag_stat.set_value(0, false); ch_tag_stat.set_value(0, false);
return out; return out;
} }
@ -2536,9 +2524,8 @@ s64 spu_thread::get_ch_value(u32 ch)
case MFC_RdAtomicStat: case MFC_RdAtomicStat:
{ {
if (ch_atomic_stat.get_count()) if (u32 out; ch_atomic_stat.try_read(out))
{ {
u32 out = ch_atomic_stat.get_value();
ch_atomic_stat.set_value(0, false); ch_atomic_stat.set_value(0, false);
return out; return out;
} }
@ -2549,9 +2536,8 @@ s64 spu_thread::get_ch_value(u32 ch)
case MFC_RdListStallStat: case MFC_RdListStallStat:
{ {
if (ch_stall_stat.get_count()) if (u32 out; ch_stall_stat.try_read(out))
{ {
u32 out = ch_stall_stat.get_value();
ch_stall_stat.set_value(0, false); ch_stall_stat.set_value(0, false);
return out; return out;
} }
@ -2658,16 +2644,14 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
{ {
if (get_type() >= spu_type::raw) if (get_type() >= spu_type::raw)
{ {
while (!ch_out_intr_mbox.try_push(value)) if (ch_out_intr_mbox.get_count())
{ {
state += cpu_flag::wait; state += cpu_flag::wait;
if (is_stopped())
{
return false;
} }
thread_ctrl::wait(); if (!ch_out_intr_mbox.push_wait(*this, value))
{
return false;
} }
int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT); int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT);
@ -2798,16 +2782,14 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
case SPU_WrOutMbox: case SPU_WrOutMbox:
{ {
while (!ch_out_mbox.try_push(value)) if (ch_out_mbox.get_count())
{ {
state += cpu_flag::wait; state += cpu_flag::wait;
if (is_stopped())
{
return false;
} }
thread_ctrl::wait(); if (!ch_out_mbox.push_wait(*this, value))
{
return false;
} }
check_state(); check_state();
@ -3196,7 +3178,7 @@ bool spu_thread::stop_and_signal(u32 code)
if (thread.get() != this) if (thread.get() != this)
{ {
thread_ctrl::notify(*thread); thread_ctrl::raw_notify(*thread);
} }
} }
} }
@ -3325,7 +3307,7 @@ bool spu_thread::stop_and_signal(u32 code)
if (thread && thread.get() != this) if (thread && thread.get() != this)
{ {
thread->state += cpu_flag::stop; thread->state += cpu_flag::stop;
thread_ctrl::notify(*thread); thread_ctrl::raw_notify(*thread);
} }
} }

View File

@ -175,23 +175,20 @@ public:
// Returns true on success // Returns true on success
bool try_push(u32 value) bool try_push(u32 value)
{ {
const u64 old = data.fetch_op([value](u64& data) return data.fetch_op([value](u64& data)
{ {
if (data & bit_count) [[unlikely]] if (!(data & bit_count)) [[likely]]
{
data |= bit_wait;
}
else
{ {
data = bit_count | value; data = bit_count | value;
return true;
} }
});
return !(old & bit_count); return false;
}).second;
} }
// Push performing bitwise OR with previous value, may require notification // Push performing bitwise OR with previous value, may require notification
void push_or(cpu_thread& spu, u32 value) void push_or(u32 value)
{ {
const u64 old = data.fetch_op([value](u64& data) const u64 old = data.fetch_op([value](u64& data)
{ {
@ -201,7 +198,7 @@ public:
if (old & bit_wait) if (old & bit_wait)
{ {
spu.notify(); data.notify_one();
} }
} }
@ -211,31 +208,28 @@ public:
} }
// Push unconditionally (overwriting previous value), may require notification // Push unconditionally (overwriting previous value), may require notification
void push(cpu_thread& spu, u32 value) void push(u32 value)
{ {
if (data.exchange(bit_count | value) & bit_wait) if (data.exchange(bit_count | value) & bit_wait)
{ {
spu.notify(); data.notify_one();
} }
} }
// Returns true on success // Returns true on success
bool try_pop(u32& out) bool try_pop(u32& out)
{ {
const u64 old = data.fetch_op([&](u64& data) return data.fetch_op([&](u64& data)
{ {
if (data & bit_count) [[likely]] if (data & bit_count) [[likely]]
{ {
out = static_cast<u32>(data); out = static_cast<u32>(data);
data = 0; data = 0;
return true;
} }
else
{
data |= bit_wait;
}
});
return (old & bit_count) != 0; return false;
}).second;
} }
// Reading without modification // Reading without modification
@ -253,19 +247,95 @@ public:
} }
// Pop unconditionally (loading last value), may require notification // Pop unconditionally (loading last value), may require notification
u32 pop(cpu_thread& spu) u32 pop()
{ {
// Value is not cleared and may be read again // Value is not cleared and may be read again
const u64 old = data.fetch_and(~(bit_count | bit_wait)); const u64 old = data.fetch_and(~(bit_count | bit_wait));
if (old & bit_wait) if (old & bit_wait)
{ {
spu.notify(); data.notify_one();
} }
return static_cast<u32>(old); return static_cast<u32>(old);
} }
// Waiting for channel pop state availability, actually popping if specified
s64 pop_wait(cpu_thread& spu, bool pop = true)
{
while (true)
{
const u64 old = data.fetch_op([&](u64& data)
{
if (data & bit_count) [[likely]]
{
if (pop)
{
data = 0;
return true;
}
return false;
}
data = bit_wait;
return true;
}).first;
if (old & bit_count)
{
return static_cast<u32>(old);
}
if (spu.is_stopped())
{
return -1;
}
data.wait(bit_wait);
}
}
// Waiting for channel push state availability, actually pushing if specified
bool push_wait(cpu_thread& spu, u32 value, bool push = true)
{
while (true)
{
u64 state;
data.fetch_op([&](u64& data)
{
if (data & bit_count) [[unlikely]]
{
data |= bit_wait;
}
else if (push)
{
data = bit_count | value;
}
else
{
state = data;
return false;
}
state = data;
return true;
});
if (!(state & bit_wait))
{
return true;
}
if (spu.is_stopped())
{
return false;
}
data.wait(state);
}
}
void set_value(u32 value, bool count = true) void set_value(u32 value, bool count = true)
{ {
data.release(u64{count} << off_count | value); data.release(u64{count} << off_count | value);

View File

@ -754,7 +754,7 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id)
if (thread && ran_threads--) if (thread && ran_threads--)
{ {
thread->state -= cpu_flag::stop; thread->state -= cpu_flag::stop;
thread_ctrl::notify(*thread); thread_ctrl::raw_notify(*thread);
} }
} }
@ -904,7 +904,7 @@ error_code sys_spu_thread_group_resume(ppu_thread& ppu, u32 id)
if (thread) if (thread)
{ {
thread->state -= cpu_flag::suspend; thread->state -= cpu_flag::suspend;
thread_ctrl::notify(*thread); thread_ctrl::raw_notify(*thread);
} }
} }
@ -1011,7 +1011,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value)
{ {
if (thread && group->running) if (thread && group->running)
{ {
thread_ctrl::notify(*thread); thread_ctrl::raw_notify(*thread);
} }
} }
@ -2203,7 +2203,7 @@ error_code raw_spu_read_puint_mb(u32 id, vm::ptr<u32> value)
return CELL_ESRCH; return CELL_ESRCH;
} }
*value = thread->ch_out_intr_mbox.pop(*thread); *value = thread->ch_out_intr_mbox.pop();
return CELL_OK; return CELL_OK;
} }