sys_spu...

This commit is contained in:
Nekotekina 2017-02-05 02:26:57 +03:00
parent 68f0393cf3
commit ec943b38a2
4 changed files with 261 additions and 362 deletions

View file

@ -52,7 +52,7 @@ void spu_int_ctrl_t::set(u64 ints)
// notify if at least 1 bit was set
if (ints && ~stat.fetch_or(ints) & ints && tag)
{
LV2_LOCK;
reader_lock rlock(id_manager::g_mutex);
if (tag)
{
@ -246,14 +246,16 @@ SPUThread::SPUThread(const std::string& name)
, m_name(name)
, index(0)
, offset(0)
, group(nullptr)
{
}
SPUThread::SPUThread(const std::string& name, u32 index)
SPUThread::SPUThread(const std::string& name, u32 index, lv2_spu_group* group)
: cpu_thread(idm::last_id())
, m_name(name)
, index(index)
, offset(verify("SPU LS" HERE, vm::alloc(0x40000, vm::main)))
, group(group)
{
}
@ -287,8 +289,6 @@ void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args)
const u32 index = (eal - SYS_SPU_THREAD_BASE_LOW) / SYS_SPU_THREAD_OFFSET; // thread number in group
const u32 offset = (eal - SYS_SPU_THREAD_BASE_LOW) % SYS_SPU_THREAD_OFFSET; // LS offset or MMIO register
const auto group = tg.lock();
if (group && index < group->num && group->threads[index])
{
auto& spu = static_cast<SPUThread&>(*group->threads[index]);
@ -630,8 +630,6 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
return true;
}
CHECK_EMU_STATUS;
if (test(state & cpu_flag::stop))
{
return false;
@ -703,8 +701,6 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
// simple waiting loop otherwise
while (!get_events(true) && !test(state & cpu_flag::stop))
{
CHECK_EMU_STATUS;
thread_ctrl::wait();
}
}
@ -747,8 +743,6 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{
while (!ch_out_intr_mbox.try_push(value))
{
CHECK_EMU_STATUS;
if (test(state & cpu_flag::stop))
{
return false;
@ -760,18 +754,17 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT);
return true;
}
else
const u32 code = value >> 24;
{
const u8 code = value >> 24;
if (code < 64)
{
/* ===== sys_spu_thread_send_event (used by spu_printf) ===== */
LV2_LOCK;
u32 spup = code & 63;
u32 data;
const u8 spup = code & 63;
if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(data))
{
fmt::throw_exception("sys_spu_thread_send_event(value=0x%x, spup=%d): Out_MBox is empty" HERE, value, spup);
}
@ -781,13 +774,9 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
fmt::throw_exception("sys_spu_thread_send_event(value=0x%x, spup=%d): In_MBox is not empty (count=%d)" HERE, value, spup, count);
}
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
LOG_TRACE(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data);
const auto queue = this->spup[spup].lock();
const auto queue = (semaphore_lock{group->mutex}, this->spup[spup].lock());
if (!queue)
{
@ -806,22 +795,17 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{
/* ===== sys_spu_thread_throw_event ===== */
LV2_LOCK;
u32 spup = code & 63;
u32 data;
const u8 spup = code & 63;
if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(data))
{
fmt::throw_exception("sys_spu_thread_throw_event(value=0x%x, spup=%d): Out_MBox is empty" HERE, value, spup);
}
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
LOG_TRACE(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data);
const auto queue = this->spup[spup].lock();
const auto queue = (semaphore_lock{group->mutex}, this->spup[spup].lock());
if (!queue)
{
@ -841,11 +825,10 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{
/* ===== sys_event_flag_set_bit ===== */
LV2_LOCK;
u32 flag = value & 0xffffff;
u32 data;
const u32 flag = value & 0xffffff;
if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(data))
{
fmt::throw_exception("sys_event_flag_set_bit(value=0x%x (flag=%d)): Out_MBox is empty" HERE, value, flag);
}
@ -855,10 +838,6 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
fmt::throw_exception("sys_event_flag_set_bit(value=0x%x (flag=%d)): In_MBox is not empty (%d)" HERE, value, flag, count);
}
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
if (flag > 63)
{
fmt::throw_exception("sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d)): Invalid flag" HERE, data, value, flag);
@ -873,19 +852,14 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{
/* ===== sys_event_flag_set_bit_impatient ===== */
LV2_LOCK;
u32 flag = value & 0xffffff;
u32 data;
const u32 flag = value & 0xffffff;
if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(data))
{
fmt::throw_exception("sys_event_flag_set_bit_impatient(value=0x%x (flag=%d)): Out_MBox is empty" HERE, value, flag);
}
const u32 data = ch_out_mbox.get_value();
ch_out_mbox.set_value(data, 0);
if (flag > 63)
{
fmt::throw_exception("sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d)): Invalid flag" HERE, data, value, flag);
@ -915,8 +889,6 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{
while (!ch_out_mbox.try_push(value))
{
CHECK_EMU_STATUS;
if (test(state & cpu_flag::stop))
{
return false;
@ -1120,9 +1092,9 @@ bool SPUThread::stop_and_signal(u32 code)
{
/* ===== sys_spu_thread_receive_event ===== */
LV2_LOCK;
u32 spuq;
if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(spuq))
{
fmt::throw_exception("sys_spu_thread_receive_event(): Out_MBox is empty" HERE);
}
@ -1133,19 +1105,8 @@ bool SPUThread::stop_and_signal(u32 code)
return ch_in_mbox.set_values(1, CELL_EBUSY), true;
}
const u32 spuq = ch_out_mbox.get_value();
ch_out_mbox.set_value(spuq, 0);
LOG_TRACE(SPU, "sys_spu_thread_receive_event(spuq=0x%x)", spuq);
const auto group = tg.lock();
if (!group)
{
fmt::throw_exception("Invalid SPU Thread Group" HERE);
}
if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate
{
return ch_in_mbox.set_values(1, CELL_EINVAL), true;
@ -1153,113 +1114,121 @@ bool SPUThread::stop_and_signal(u32 code)
std::shared_ptr<lv2_event_queue> queue;
for (auto& v : this->spuq)
while (true)
{
if (spuq == v.first)
{
queue = v.second.lock();
queue.reset();
if (queue)
// Check group status, wait if necessary
while (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_SUSPENDED)
{
if (test(state & cpu_flag::stop))
{
break;
return false;
}
thread_ctrl::wait();
}
reader_lock rlock(id_manager::g_mutex);
semaphore_lock lock(group->mutex);
if (group->run_state >= SPU_THREAD_GROUP_STATUS_WAITING && group->run_state <= SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED)
{
// Try again
continue;
}
for (auto& v : this->spuq)
{
if (spuq == v.first)
{
queue = v.second.lock();
if (queue)
{
break;
}
}
}
}
if (!queue)
{
return ch_in_mbox.set_values(1, CELL_EINVAL), true; // TODO: check error value
}
// check thread group status
while (group->state >= SPU_THREAD_GROUP_STATUS_WAITING && group->state <= SPU_THREAD_GROUP_STATUS_SUSPENDED)
{
CHECK_EMU_STATUS;
if (test(state & cpu_flag::stop))
if (!queue)
{
return false;
return ch_in_mbox.set_values(1, CELL_EINVAL), true; // TODO: check error value
}
group->cv.wait(lv2_lock, 1000);
}
// change group status
if (group->state == SPU_THREAD_GROUP_STATUS_RUNNING)
{
group->state = SPU_THREAD_GROUP_STATUS_WAITING;
for (auto& thread : group->threads)
{
if (thread)
{
thread->state += cpu_flag::suspend;
}
}
}
else
{
fmt::throw_exception("Unexpected SPU Thread Group state (%d)" HERE, (u32)group->state);
}
{
semaphore_lock lock(queue->mutex);
semaphore_lock qlock(queue->mutex);
if (queue->events.empty())
{
queue->sq.emplace_back(this);
group->run_state = SPU_THREAD_GROUP_STATUS_WAITING;
for (auto& thread : group->threads)
{
if (thread)
{
thread->state += cpu_flag::suspend;
}
}
// Wait
break;
}
else
{
// Return the event immediately
const auto event = queue->events.front();
const auto data1 = static_cast<u32>(std::get<1>(event));
const auto data2 = static_cast<u32>(std::get<2>(event));
const auto data3 = static_cast<u32>(std::get<3>(event));
ch_in_mbox.set_values(4, CELL_OK, data1, data2, data3);
queue->events.pop_front();
state += cpu_flag::signal;
return true;
}
}
// wait on the event queue
while (!state.test_and_reset(cpu_flag::signal))
while (true)
{
CHECK_EMU_STATUS;
if (test(state & cpu_flag::stop))
{
return false;
}
LV2_UNLOCK, thread_ctrl::wait();
if (!state.test_and_reset(cpu_flag::signal))
{
thread_ctrl::wait();
}
else
{
break;
}
}
semaphore_lock lock(group->mutex);
// restore thread group status
if (group->state == SPU_THREAD_GROUP_STATUS_WAITING)
if (group->run_state == SPU_THREAD_GROUP_STATUS_WAITING)
{
group->state = SPU_THREAD_GROUP_STATUS_RUNNING;
group->run_state = SPU_THREAD_GROUP_STATUS_RUNNING;
}
else if (group->state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED)
else if (group->run_state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED)
{
group->state = SPU_THREAD_GROUP_STATUS_SUSPENDED;
}
else
{
fmt::throw_exception("Unexpected SPU Thread Group state (%d)" HERE, (u32)group->state);
group->run_state = SPU_THREAD_GROUP_STATUS_SUSPENDED;
}
for (auto& thread : group->threads)
{
if (thread && thread.get() != this)
if (thread)
{
thread->state -= cpu_flag::suspend;
thread->notify();
if (thread.get() != this)
{
thread->notify();
}
}
}
state -= cpu_flag::suspend;
group->cv.notify_all();
return true;
}
@ -1267,25 +1236,16 @@ bool SPUThread::stop_and_signal(u32 code)
{
/* ===== sys_spu_thread_group_exit ===== */
LV2_LOCK;
u32 value;
if (!ch_out_mbox.get_count())
if (!ch_out_mbox.try_pop(value))
{
fmt::throw_exception("sys_spu_thread_group_exit(): Out_MBox is empty" HERE);
}
const u32 value = ch_out_mbox.get_value();
ch_out_mbox.set_value(value, 0);
LOG_TRACE(SPU, "sys_spu_thread_group_exit(status=0x%x)", value);
const auto group = tg.lock();
if (!group)
{
fmt::throw_exception("Invalid SPU Thread Group" HERE);
}
semaphore_lock lock(group->mutex);
for (auto& thread : group->threads)
{
@ -1296,7 +1256,7 @@ bool SPUThread::stop_and_signal(u32 code)
}
}
group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED;
group->run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED;
group->exit_status = value;
group->join_state |= SPU_TGJSF_GROUP_EXIT;
group->cv.notify_one();
@ -1309,8 +1269,6 @@ bool SPUThread::stop_and_signal(u32 code)
{
/* ===== sys_spu_thread_exit ===== */
LV2_LOCK;
if (!ch_out_mbox.get_count())
{
fmt::throw_exception("sys_spu_thread_exit(): Out_MBox is empty" HERE);
@ -1318,12 +1276,7 @@ bool SPUThread::stop_and_signal(u32 code)
LOG_TRACE(SPU, "sys_spu_thread_exit(status=0x%x)", ch_out_mbox.get_value());
const auto group = tg.lock();
if (!group)
{
fmt::throw_exception("Invalid SPU Thread Group" HERE);
}
semaphore_lock lock(group->mutex);
status |= SPU_STATUS_STOPPED_BY_STOP;
group->cv.notify_one();