SPU Channels improved

This commit is contained in:
Nekotekina 2015-07-17 19:27:12 +03:00
parent 9913c9059c
commit 43d3ccce95
8 changed files with 293 additions and 157 deletions

View file

@ -68,7 +68,18 @@ bool RawSPUThread::ReadReg(const u32 addr, u32& value)
case SPU_Out_MBox_offs: case SPU_Out_MBox_offs:
{ {
value = ch_out_mbox.pop_uncond(); bool notify;
std::tie(value, notify) = ch_out_mbox.pop();
if (notify)
{
// notify if necessary
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
return true; return true;
} }
@ -169,8 +180,14 @@ bool RawSPUThread::WriteReg(const u32 addr, const u32 value)
case SPU_In_MBox_offs: case SPU_In_MBox_offs:
{ {
ch_in_mbox.push_uncond(value); if (ch_in_mbox.push(value))
{
// notify if necessary
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one(); cv.notify_one();
}
return true; return true;
} }
@ -207,13 +224,13 @@ bool RawSPUThread::WriteReg(const u32 addr, const u32 value)
case SPU_RdSigNotify1_offs: case SPU_RdSigNotify1_offs:
{ {
write_snr(0, value); push_snr(0, value);
return true; return true;
} }
case SPU_RdSigNotify2_offs: case SPU_RdSigNotify2_offs:
{ {
write_snr(1, value); push_snr(1, value);
return true; return true;
} }
} }

View file

@ -326,7 +326,7 @@ void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args)
} }
else if ((cmd & MFC_PUT_CMD) && args.size == 4 && (offset == SYS_SPU_THREAD_SNR1 || offset == SYS_SPU_THREAD_SNR2)) else if ((cmd & MFC_PUT_CMD) && args.size == 4 && (offset == SYS_SPU_THREAD_SNR1 || offset == SYS_SPU_THREAD_SNR2))
{ {
spu.write_snr(SYS_SPU_THREAD_SNR2 == offset, read32(args.lsa)); spu.push_snr(SYS_SPU_THREAD_SNR2 == offset, read32(args.lsa));
return; return;
} }
else else
@ -399,7 +399,7 @@ void SPUThread::do_dma_list_cmd(u32 cmd, spu_mfc_arg_t args)
if (rec->sb & 0x8000) if (rec->sb & 0x8000)
{ {
ch_stall_stat.push_bit_or(1 << args.tag); ch_stall_stat.set_value((1 << args.tag) | ch_stall_stat.get_value());
spu_mfc_arg_t stalled; spu_mfc_arg_t stalled;
stalled.ea = (args.ea & ~0xffffffff) | (list_addr + (i + 1) * 8); stalled.ea = (args.ea & ~0xffffffff) | (list_addr + (i + 1) * 8);
@ -466,7 +466,7 @@ void SPUThread::process_mfc_cmd(u32 cmd)
last_raddr = raddr; last_raddr = raddr;
return ch_atomic_stat.push_uncond(MFC_GETLLAR_SUCCESS); return ch_atomic_stat.set_value(MFC_GETLLAR_SUCCESS);
} }
case MFC_PUTLLC_CMD: // store conditionally case MFC_PUTLLC_CMD: // store conditionally
@ -485,7 +485,7 @@ void SPUThread::process_mfc_cmd(u32 cmd)
last_raddr = 0; last_raddr = 0;
return ch_atomic_stat.push_uncond(MFC_PUTLLC_SUCCESS); return ch_atomic_stat.set_value(MFC_PUTLLC_SUCCESS);
} }
else else
{ {
@ -496,7 +496,7 @@ void SPUThread::process_mfc_cmd(u32 cmd)
last_raddr = 0; last_raddr = 0;
} }
return ch_atomic_stat.push_uncond(MFC_PUTLLC_FAILURE); return ch_atomic_stat.set_value(MFC_PUTLLC_FAILURE);
} }
} }
@ -522,7 +522,7 @@ void SPUThread::process_mfc_cmd(u32 cmd)
if (cmd == MFC_PUTLLUC_CMD) if (cmd == MFC_PUTLLUC_CMD)
{ {
ch_atomic_stat.push_uncond(MFC_PUTLLUC_SUCCESS); ch_atomic_stat.set_value(MFC_PUTLLUC_SUCCESS);
} }
return; return;
@ -643,10 +643,18 @@ u32 SPUThread::get_ch_value(u32 ch)
{ {
std::unique_lock<std::mutex> lock(mutex, std::defer_lock); std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
u32 result; while (true)
while (!channel.try_pop(result))
{ {
bool result;
u32 value;
std::tie(result, value) = channel.try_pop();
if (result)
{
return value;
}
CHECK_EMU_STATUS; CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{}; if (IsStopped()) throw CPUThreadStop{};
@ -657,10 +665,8 @@ u32 SPUThread::get_ch_value(u32 ch)
continue; continue;
} }
cv.wait_for(lock, std::chrono::milliseconds(1)); cv.wait(lock);
} }
return result;
}; };
switch (ch) switch (ch)
@ -672,10 +678,24 @@ u32 SPUThread::get_ch_value(u32 ch)
{ {
std::unique_lock<std::mutex> lock(mutex, std::defer_lock); std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
u32 result, count; while (true)
while (!ch_in_mbox.try_pop(result, count))
{ {
bool result;
u32 value;
u32 count;
std::tie(result, value, count) = ch_in_mbox.try_pop();
if (result)
{
if (count + 1 == 4 /* SPU_IN_MBOX_THRESHOLD */) // TODO: check this
{
int_ctrl[2].set(SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT);
}
return value;
}
CHECK_EMU_STATUS; CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{}; if (IsStopped()) throw CPUThreadStop{};
@ -686,15 +706,8 @@ u32 SPUThread::get_ch_value(u32 ch)
continue; continue;
} }
cv.wait_for(lock, std::chrono::milliseconds(1)); cv.wait(lock);
} }
if (count + 1 == 4 /* SPU_IN_MBOX_THRESHOLD */) // TODO: check this
{
int_ctrl[2].set(SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT);
}
return result;
} }
case MFC_RdTagStat: case MFC_RdTagStat:
@ -813,7 +826,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
continue; continue;
} }
cv.wait_for(lock, std::chrono::milliseconds(1)); cv.wait(lock);
} }
int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT); int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT);
@ -826,57 +839,68 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
{ {
/* ===== sys_spu_thread_send_event (used by spu_printf) ===== */ /* ===== sys_spu_thread_send_event (used by spu_printf) ===== */
u8 spup = code & 63; LV2_LOCK;
u32 data; const u8 spup = code & 63;
if (!ch_out_mbox.try_pop(data))
if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("sys_spu_thread_send_event(value=0x%x, spup=%d): Out_MBox is empty", value, spup); throw EXCEPTION("sys_spu_thread_send_event(value=0x%x, spup=%d): Out_MBox is empty", value, spup);
} }
if (u32 count = ch_in_mbox.get_count())
{
throw EXCEPTION("sys_spu_thread_send_event(value=0x%x, spup=%d): In_MBox is not empty (count=%d)", value, spup, count);
}
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
{ {
LOG_NOTICE(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data); LOG_NOTICE(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data);
} }
LV2_LOCK;
const auto queue = this->spup[spup].lock(); const auto queue = this->spup[spup].lock();
if (!queue) if (!queue)
{ {
LOG_WARNING(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (value & 0x00ffffff), data); LOG_WARNING(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (value & 0x00ffffff), data);
return ch_in_mbox.push_uncond(CELL_ENOTCONN); // TODO: check error passing return ch_in_mbox.set_values(1, CELL_ENOTCONN); // TODO: check error passing
} }
if (queue->events.size() >= queue->size) if (queue->events.size() >= queue->size)
{ {
return ch_in_mbox.push_uncond(CELL_EBUSY); return ch_in_mbox.set_values(1, CELL_EBUSY);
} }
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data);
return ch_in_mbox.push_uncond(CELL_OK); return ch_in_mbox.set_values(1, CELL_OK);
} }
else if (code < 128) else if (code < 128)
{ {
/* ===== sys_spu_thread_throw_event ===== */ /* ===== sys_spu_thread_throw_event ===== */
LV2_LOCK;
const u8 spup = code & 63; const u8 spup = code & 63;
u32 data; if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(data))
{ {
throw EXCEPTION("sys_spu_thread_throw_event(value=0x%x, spup=%d): Out_MBox is empty", value, spup); throw EXCEPTION("sys_spu_thread_throw_event(value=0x%x, spup=%d): Out_MBox is empty", value, spup);
} }
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
{ {
LOG_WARNING(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data); LOG_WARNING(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data);
} }
LV2_LOCK;
const auto queue = this->spup[spup].lock(); const auto queue = this->spup[spup].lock();
if (!queue) if (!queue)
@ -898,17 +922,28 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
else if (code == 128) else if (code == 128)
{ {
/* ===== sys_event_flag_set_bit ===== */ /* ===== sys_event_flag_set_bit ===== */
u32 flag = value & 0xffffff;
u32 data; LV2_LOCK;
if (!ch_out_mbox.try_pop(data))
const u32 flag = value & 0xffffff;
if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("sys_event_flag_set_bit(value=0x%x (flag=%d)): Out_MBox is empty", value, flag); throw EXCEPTION("sys_event_flag_set_bit(value=0x%x (flag=%d)): Out_MBox is empty", value, flag);
} }
if (u32 count = ch_in_mbox.get_count())
{
throw EXCEPTION("sys_event_flag_set_bit(value=0x%x (flag=%d)): In_MBox is not empty (%d)", value, flag, count);
}
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
if (flag > 63) if (flag > 63)
{ {
throw EXCEPTION("sys_event_flag_set_bit(id=%d, value=0x%x): flag > 63", data, value, flag); throw EXCEPTION("sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d)): Invalid flag", data, value, flag);
} }
if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
@ -916,13 +951,11 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
LOG_WARNING(SPU, "sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d))", data, value, flag); LOG_WARNING(SPU, "sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d))", data, value, flag);
} }
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(data); const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(data);
if (!ef) if (!ef)
{ {
return ch_in_mbox.push_uncond(CELL_ESRCH); return ch_in_mbox.set_values(1, CELL_ESRCH);
} }
while (ef->cancelled) while (ef->cancelled)
@ -937,22 +970,28 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
ef->cv.notify_all(); ef->cv.notify_all();
} }
return ch_in_mbox.push_uncond(CELL_OK); return ch_in_mbox.set_values(1, CELL_OK);
} }
else if (code == 192) else if (code == 192)
{ {
/* ===== sys_event_flag_set_bit_impatient ===== */ /* ===== sys_event_flag_set_bit_impatient ===== */
u32 flag = value & 0xffffff;
u32 data; LV2_LOCK;
if (!ch_out_mbox.try_pop(data))
const u32 flag = value & 0xffffff;
if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("sys_event_flag_set_bit_impatient(value=0x%x (flag=%d)): Out_MBox is empty", value, flag); throw EXCEPTION("sys_event_flag_set_bit_impatient(value=0x%x (flag=%d)): Out_MBox is empty", value, flag);
} }
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
if (flag > 63) if (flag > 63)
{ {
throw EXCEPTION("sys_event_flag_set_bit_impatient(id=%d, value=0x%x): flag > 63", data, value, flag); throw EXCEPTION("sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d)): Invalid flag", data, value, flag);
} }
if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
@ -960,8 +999,6 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
LOG_WARNING(SPU, "sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d))", data, value, flag); LOG_WARNING(SPU, "sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d))", data, value, flag);
} }
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(data); const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(data);
if (!ef) if (!ef)
@ -1013,7 +1050,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
continue; continue;
} }
cv.wait_for(lock, std::chrono::milliseconds(1)); cv.wait(lock);
} }
return; return;
@ -1027,7 +1064,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
case MFC_WrTagUpdate: case MFC_WrTagUpdate:
{ {
ch_tag_stat.push_uncond(ch_tag_mask); // hack ch_tag_stat.set_value(ch_tag_mask); // hack
return; return;
} }
@ -1210,25 +1247,28 @@ void SPUThread::stop_and_signal(u32 code)
{ {
/* ===== sys_spu_thread_receive_event ===== */ /* ===== sys_spu_thread_receive_event ===== */
u32 spuq = 0; LV2_LOCK;
if (!ch_out_mbox.try_pop(spuq))
if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("sys_spu_thread_receive_event(): cannot read Out_MBox"); throw EXCEPTION("sys_spu_thread_receive_event(): Out_MBox is empty");
} }
if (ch_in_mbox.get_count()) if (u32 count = ch_in_mbox.get_count())
{ {
LOG_ERROR(SPU, "sys_spu_thread_receive_event(spuq=0x%x): In_MBox is not empty", spuq); LOG_ERROR(SPU, "sys_spu_thread_receive_event(): In_MBox is not empty (%d)", count);
return ch_in_mbox.push_uncond(CELL_EBUSY); return ch_in_mbox.set_values(1, CELL_EBUSY);
} }
const u32 spuq = ch_out_mbox.get_value();
ch_out_mbox.set_value(spuq, 0);
if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
{ {
LOG_NOTICE(SPU, "sys_spu_thread_receive_event(spuq=0x%x)", spuq); LOG_NOTICE(SPU, "sys_spu_thread_receive_event(spuq=0x%x)", spuq);
} }
LV2_LOCK;
const auto group = tg.lock(); const auto group = tg.lock();
if (!group) if (!group)
@ -1238,7 +1278,7 @@ void SPUThread::stop_and_signal(u32 code)
if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate
{ {
return ch_in_mbox.push_uncond(CELL_EINVAL); return ch_in_mbox.set_values(1, CELL_EINVAL);
} }
std::shared_ptr<lv2_event_queue_t> queue; std::shared_ptr<lv2_event_queue_t> queue;
@ -1258,7 +1298,7 @@ void SPUThread::stop_and_signal(u32 code)
if (!queue) if (!queue)
{ {
return ch_in_mbox.push_uncond(CELL_EINVAL); // TODO: check error value return ch_in_mbox.set_values(1, CELL_EINVAL); // TODO: check error value
} }
// check thread group status // check thread group status
@ -1301,15 +1341,12 @@ void SPUThread::stop_and_signal(u32 code)
if (queue->cancelled) if (queue->cancelled)
{ {
ch_in_mbox.push_uncond(CELL_ECANCELED); ch_in_mbox.set_values(1, CELL_ECANCELED);
} }
else else
{ {
auto& event = queue->events.front(); auto& event = queue->events.front();
ch_in_mbox.push_uncond(CELL_OK); ch_in_mbox.set_values(4, CELL_OK, static_cast<u32>(event.data1), static_cast<u32>(event.data2), static_cast<u32>(event.data3));
ch_in_mbox.push_uncond((u32)event.data1);
ch_in_mbox.push_uncond((u32)event.data2);
ch_in_mbox.push_uncond((u32)event.data3);
queue->events.pop_front(); queue->events.pop_front();
queue->waiters--; queue->waiters--;
@ -1348,19 +1385,22 @@ void SPUThread::stop_and_signal(u32 code)
{ {
/* ===== sys_spu_thread_group_exit ===== */ /* ===== sys_spu_thread_group_exit ===== */
u32 value; LV2_LOCK;
if (!ch_out_mbox.try_pop(value))
if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("sys_spu_thread_group_exit(): cannot read Out_MBox"); throw EXCEPTION("sys_spu_thread_group_exit(): Out_MBox is empty");
} }
const u32 value = ch_out_mbox.get_value();
ch_out_mbox.set_value(value, 0);
if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
{ {
LOG_NOTICE(SPU, "sys_spu_thread_group_exit(status=0x%x)", value); LOG_NOTICE(SPU, "sys_spu_thread_group_exit(status=0x%x)", value);
} }
LV2_LOCK;
const auto group = tg.lock(); const auto group = tg.lock();
if (!group) if (!group)
@ -1388,6 +1428,8 @@ void SPUThread::stop_and_signal(u32 code)
{ {
/* ===== sys_spu_thread_exit ===== */ /* ===== sys_spu_thread_exit ===== */
LV2_LOCK;
if (!ch_out_mbox.get_count()) if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("sys_spu_thread_exit(): Out_MBox is empty"); throw EXCEPTION("sys_spu_thread_exit(): Out_MBox is empty");
@ -1398,8 +1440,6 @@ void SPUThread::stop_and_signal(u32 code)
LOG_NOTICE(SPU, "sys_spu_thread_exit(status=0x%x)", ch_out_mbox.get_value()); LOG_NOTICE(SPU, "sys_spu_thread_exit(status=0x%x)", ch_out_mbox.get_value());
} }
LV2_LOCK;
const auto group = tg.lock(); const auto group = tg.lock();
if (!group) if (!group)
@ -1416,11 +1456,11 @@ void SPUThread::stop_and_signal(u32 code)
if (!ch_out_mbox.get_count()) if (!ch_out_mbox.get_count())
{ {
throw EXCEPTION("Unknown STOP code: 0x%x", code); throw EXCEPTION("Unknown STOP code: 0x%x (Out_MBox is empty)", code);
} }
else else
{ {
throw EXCEPTION("Unknown STOP code: 0x%x; Out_MBox=0x%x", code, ch_out_mbox.get_value()); throw EXCEPTION("Unknown STOP code: 0x%x (Out_MBox=0x%x)", code, ch_out_mbox.get_value());
} }
} }

View file

@ -139,49 +139,48 @@ union spu_channel_t
{ {
struct sync_var_t struct sync_var_t
{ {
u32 count; struct
{
u32 waiting : 1; // waiting flag (0..1)
u32 count : 1; // channel count (0..1)
};
u32 value; u32 value;
}; };
atomic_t<sync_var_t> sync_var; atomic_t<sync_var_t> sync_var;
public: public:
// returns true on success
bool try_push(u32 value) bool try_push(u32 value)
{ {
return sync_var.atomic_op([=](sync_var_t& data) -> bool return sync_var.atomic_op([=](sync_var_t& data) -> bool
{ {
if (data.count == 0) if (data.count == 0)
{ {
data.waiting = 0;
data.count = 1; data.count = 1;
data.value = value; data.value = value;
return true; return true;
} }
data.waiting = 1;
return false; return false;
}); });
} }
void push_bit_or(u32 value) // push performing bitwise OR with previous value, returns true if needs signaling
bool push_or(u32 value)
{ {
sync_var._or({ 1, value }); return sync_var.atomic_op([=](sync_var_t& data) -> bool
} {
data.count = 1;
data.value |= value;
void push_uncond(u32 value) if (data.waiting)
{ {
sync_var.exchange({ 1, value }); data.waiting = 0;
}
bool try_pop(u32& out_value)
{
return sync_var.atomic_op([&](sync_var_t& data) -> bool
{
if (data.count != 0)
{
out_value = data.value;
data.count = 0;
data.value = 0;
return true; return true;
} }
@ -190,20 +189,58 @@ public:
}); });
} }
u32 pop_uncond() // push unconditionally (overwriting previous value), returns true if needs signaling
bool push(u32 value)
{ {
return sync_var.atomic_op([](sync_var_t& data) -> u32 return sync_var.atomic_op([=](sync_var_t& data) -> bool
{ {
data.count = 0; data.count = 1;
data.value = value;
if (data.waiting)
{
data.waiting = 0;
return true;
}
return false;
});
}
// returns true on success and u32 value
std::tuple<bool, u32> try_pop()
{
return sync_var.atomic_op([](sync_var_t& data)
{
const auto result = std::make_tuple(data.count != 0, u32{ data.value });
data.waiting = data.count == 0;
data.count = 0;
data.value = 0;
return result;
});
}
// pop unconditionally (loading last value), returns u32 value and bool value (true if needs signaling)
std::tuple<u32, bool> pop()
{
return sync_var.atomic_op([](sync_var_t& data)
{
const auto result = std::make_tuple(u32{ data.value }, data.waiting != 0);
data.waiting = 0;
data.count = 0;
// value is not cleared and may be read again // value is not cleared and may be read again
return data.value;
return result;
}); });
} }
void set_value(u32 value, u32 count = 1) void set_value(u32 value, u32 count = 1)
{ {
sync_var.store({ count, value }); sync_var.store({ { 0, count }, value });
} }
u32 get_value() volatile u32 get_value() volatile
@ -221,7 +258,12 @@ struct spu_channel_4_t
{ {
struct sync_var_t struct sync_var_t
{ {
u32 count; struct
{
u32 waiting : 1;
u32 count : 3;
};
u32 value0; u32 value0;
u32 value1; u32 value1;
u32 value2; u32 value2;
@ -237,11 +279,12 @@ public:
value3 = {}; value3 = {};
} }
void push_uncond(u32 value) // push unconditionally (overwriting latest value), returns true if needs signaling
bool push(u32 value)
{ {
value3.exchange(value); value3.exchange(value);
sync_var.atomic_op([value](sync_var_t& data) return sync_var.atomic_op([=](sync_var_t& data) -> bool
{ {
switch (data.count++) switch (data.count++)
{ {
@ -250,36 +293,57 @@ public:
case 2: data.value2 = value; break; case 2: data.value2 = value; break;
default: data.count = 4; default: data.count = 4;
} }
if (data.waiting)
{
data.waiting = 0;
return true;
}
return false;
}); });
} }
// out_count: count after removing first element // returns true on success and two u32 values: data and count after removing the first element
bool try_pop(u32& out_value, u32& out_count) std::tuple<bool, u32, u32> try_pop()
{ {
bool out_result; return sync_var.atomic_op([this](sync_var_t& data)
{
const auto result = std::make_tuple(data.count != 0, u32{ data.value0 }, u32{ data.count - 1u });
const u32 last_value = value3.load_sync(); if (data.count != 0)
sync_var.atomic_op([&out_result, &out_value, &out_count, last_value](sync_var_t& data)
{ {
if ((out_result = (data.count != 0))) data.waiting = 0;
{ data.count--;
out_value = data.value0;
out_count = --data.count;
data.value0 = data.value1; data.value0 = data.value1;
data.value1 = data.value2; data.value1 = data.value2;
data.value2 = last_value; data.value2 = value3.load_sync();
}
else
{
data.waiting = 1;
} }
});
return out_result; return result;
});
} }
u32 get_count() volatile u32 get_count() volatile
{ {
return sync_var.data.count; return sync_var.data.count;
} }
void set_values(u32 count, u32 value0, u32 value1 = 0, u32 value2 = 0, u32 value3 = 0)
{
sync_var.data.waiting = 0;
sync_var.data.count = count;
sync_var.data.value0 = value0;
sync_var.data.value1 = value1;
sync_var.data.value2 = value2;
this->value3.store(value3);
}
}; };
struct spu_int_ctrl_t struct spu_int_ctrl_t
@ -526,30 +590,37 @@ public:
const u32 index; // SPU index const u32 index; // SPU index
const u32 offset; // SPU LS offset const u32 offset; // SPU LS offset
void write_snr(bool number, u32 value) void push_snr(u32 number, u32 value)
{ {
if (!number) if (number == 0)
{ {
if (snr_config & 1) if (snr_config & 1)
{ {
ch_snr1.push_bit_or(value); if (!ch_snr1.push_or(value)) return;
} }
else else
{ {
ch_snr1.push_uncond(value); if (!ch_snr1.push(value)) return;
} }
} }
else else if (number == 1)
{ {
if (snr_config & 2) if (snr_config & 2)
{ {
ch_snr2.push_bit_or(value); if (!ch_snr2.push_or(value)) return;
} }
else else
{ {
ch_snr2.push_uncond(value); if (!ch_snr2.push(value)) return;
} }
} }
else
{
throw EXCEPTION("Unexpected");
}
// notify if required
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one(); cv.notify_one();
} }

View file

@ -10,7 +10,7 @@ sleep_queue_entry_t::sleep_queue_entry_t(CPUThread& cpu, sleep_queue_t& queue)
: m_queue(queue) : m_queue(queue)
, m_thread(cpu) , m_thread(cpu)
{ {
m_queue.emplace_back(std::move(cpu.shared_from_this())); m_queue.emplace_back(cpu.shared_from_this());
m_thread.Sleep(); m_thread.Sleep();
} }

View file

@ -167,22 +167,11 @@ s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout)
return CELL_EPERM; return CELL_EPERM;
} }
// unlock mutex // save the recursive value
cond->mutex->owner.reset();
// save recursive value
const u32 recursive_value = cond->mutex->recursive_count.exchange(0); const u32 recursive_value = cond->mutex->recursive_count.exchange(0);
if (cond->mutex->sq.size()) // unlock the mutex
{ cond->mutex->unlock(lv2_lock);
// pick another owner; protocol is ignored in current implementation
cond->mutex->owner = cond->mutex->sq.front();
if (!cond->mutex->owner->Signal())
{
throw EXCEPTION("Mutex owner not signaled");
}
}
{ {
// add waiter; protocol is ignored in current implementation // add waiter; protocol is ignored in current implementation

View file

@ -6,14 +6,11 @@ class PPUThread;
struct lv2_int_tag_t struct lv2_int_tag_t
{ {
//const u32 class_id; // 0 or 2 for RawSPU
const u32 id; const u32 id;
//const std::weak_ptr<class RawSPUThread> thread; // RawSPU thread
std::shared_ptr<struct lv2_int_serv_t> handler; std::shared_ptr<struct lv2_int_serv_t> handler;
lv2_int_tag_t(/*u32 class_id, const std::shared_ptr<RawSPUThread> thread*/); lv2_int_tag_t();
}; };
REG_ID_TYPE(lv2_int_tag_t, 0x0A); // SYS_INTR_TAG_OBJECT REG_ID_TYPE(lv2_int_tag_t, 0x0A); // SYS_INTR_TAG_OBJECT

View file

@ -24,7 +24,7 @@ void lv2_mutex_t::unlock(lv2_lock_t& lv2_lock)
if (!owner->Signal()) if (!owner->Signal())
{ {
throw EXCEPTION("Mutex owner not signaled"); throw EXCEPTION("Mutex owner already signaled");
} }
} }
} }

View file

@ -209,7 +209,14 @@ s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr<u32> status)
// TODO: check CELL_ESTAT condition // TODO: check CELL_ESTAT condition
*status = thread->ch_out_mbox.pop_uncond(); bool notify;
std::tie(*status, notify) = thread->ch_out_mbox.pop();
if (notify)
{
throw EXCEPTION("Unexpected");
}
return CELL_OK; return CELL_OK;
} }
@ -694,8 +701,13 @@ s32 sys_spu_thread_write_spu_mb(u32 id, u32 value)
return CELL_ESTAT; return CELL_ESTAT;
} }
thread->ch_in_mbox.push_uncond(value); if (thread->ch_in_mbox.push(value))
{
// notify if necessary
std::lock_guard<std::mutex> lock(thread->mutex);
thread->cv.notify_one(); thread->cv.notify_one();
}
return CELL_OK; return CELL_OK;
} }
@ -771,7 +783,7 @@ s32 sys_spu_thread_write_snr(u32 id, u32 number, u32 value)
// return CELL_ESTAT; // return CELL_ESTAT;
//} //}
thread->write_snr(number, value); thread->push_snr(number, value);
return CELL_OK; return CELL_OK;
} }
@ -1306,7 +1318,17 @@ s32 sys_raw_spu_read_puint_mb(u32 id, vm::ptr<u32> value)
return CELL_ESRCH; return CELL_ESRCH;
} }
*value = thread->ch_out_intr_mbox.pop_uncond(); bool notify;
std::tie(*value, notify) = thread->ch_out_intr_mbox.pop();
if (notify)
{
// notify if necessary
std::lock_guard<std::mutex> lock(thread->mutex);
thread->cv.notify_one();
}
return CELL_OK; return CELL_OK;
} }