mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-05 06:21:26 +12:00
LV2: Postpone thread notifications to afterward mutex ownership(s)
This commit is contained in:
parent
6007fd630f
commit
dc851a729e
11 changed files with 138 additions and 61 deletions
|
@ -1192,6 +1192,7 @@ DECLARE(lv2_obj::g_pending);
|
||||||
DECLARE(lv2_obj::g_waiting);
|
DECLARE(lv2_obj::g_waiting);
|
||||||
DECLARE(lv2_obj::g_to_sleep);
|
DECLARE(lv2_obj::g_to_sleep);
|
||||||
|
|
||||||
|
thread_local DECLARE(lv2_obj::g_to_notify){};
|
||||||
thread_local DECLARE(lv2_obj::g_to_awake);
|
thread_local DECLARE(lv2_obj::g_to_awake);
|
||||||
|
|
||||||
namespace cpu_counter
|
namespace cpu_counter
|
||||||
|
@ -1199,22 +1200,22 @@ namespace cpu_counter
|
||||||
void remove(cpu_thread*) noexcept;
|
void remove(cpu_thread*) noexcept;
|
||||||
}
|
}
|
||||||
|
|
||||||
void lv2_obj::sleep(cpu_thread& cpu, const u64 timeout)
|
void lv2_obj::sleep(cpu_thread& cpu, const u64 timeout, bool notify_later)
|
||||||
{
|
{
|
||||||
vm::temporary_unlock(cpu);
|
vm::temporary_unlock(cpu);
|
||||||
cpu_counter::remove(&cpu);
|
cpu_counter::remove(&cpu);
|
||||||
{
|
{
|
||||||
std::lock_guard lock{g_mutex};
|
std::lock_guard lock{g_mutex};
|
||||||
sleep_unlocked(cpu, timeout);
|
sleep_unlocked(cpu, timeout, notify_later);
|
||||||
}
|
}
|
||||||
g_to_awake.clear();
|
g_to_awake.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lv2_obj::awake(cpu_thread* const thread, s32 prio)
|
bool lv2_obj::awake(cpu_thread* const thread, bool notify_later, s32 prio)
|
||||||
{
|
{
|
||||||
vm::temporary_unlock();
|
vm::temporary_unlock();
|
||||||
std::lock_guard lock(g_mutex);
|
std::lock_guard lock(g_mutex);
|
||||||
return awake_unlocked(thread, prio);
|
return awake_unlocked(thread, notify_later, prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lv2_obj::yield(cpu_thread& thread)
|
bool lv2_obj::yield(cpu_thread& thread)
|
||||||
|
@ -1226,10 +1227,10 @@ bool lv2_obj::yield(cpu_thread& thread)
|
||||||
ppu->raddr = 0; // Clear reservation
|
ppu->raddr = 0; // Clear reservation
|
||||||
}
|
}
|
||||||
|
|
||||||
return awake(&thread, yield_cmd);
|
return awake(&thread, false, yield_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout)
|
void lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout, bool notify_later)
|
||||||
{
|
{
|
||||||
const u64 start_time = get_guest_system_time();
|
const u64 start_time = get_guest_system_time();
|
||||||
|
|
||||||
|
@ -1325,15 +1326,15 @@ void lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout)
|
||||||
if (!g_to_awake.empty())
|
if (!g_to_awake.empty())
|
||||||
{
|
{
|
||||||
// Schedule pending entries
|
// Schedule pending entries
|
||||||
awake_unlocked({});
|
awake_unlocked({}, notify_later);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
schedule_all();
|
schedule_all(notify_later);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
|
bool lv2_obj::awake_unlocked(cpu_thread* cpu, bool notify_later, s32 prio)
|
||||||
{
|
{
|
||||||
// Check thread type
|
// Check thread type
|
||||||
AUDIT(!cpu || cpu->id_type() == 1);
|
AUDIT(!cpu || cpu->id_type() == 1);
|
||||||
|
@ -1469,7 +1470,7 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_all();
|
schedule_all(notify_later);
|
||||||
return changed_queue;
|
return changed_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1481,10 +1482,12 @@ void lv2_obj::cleanup()
|
||||||
g_to_sleep.clear();
|
g_to_sleep.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void lv2_obj::schedule_all()
|
void lv2_obj::schedule_all(bool notify_later)
|
||||||
{
|
{
|
||||||
if (g_pending.empty() && g_to_sleep.empty())
|
if (g_pending.empty() && g_to_sleep.empty())
|
||||||
{
|
{
|
||||||
|
usz notify_later_idx = notify_later ? 0 : std::size(g_to_notify) - 1;
|
||||||
|
|
||||||
// Wake up threads
|
// Wake up threads
|
||||||
for (usz i = 0, x = std::min<usz>(g_cfg.core.ppu_threads, g_ppu.size()); i < x; i++)
|
for (usz i = 0, x = std::min<usz>(g_cfg.core.ppu_threads, g_ppu.size()); i < x; i++)
|
||||||
{
|
{
|
||||||
|
@ -1495,10 +1498,20 @@ void lv2_obj::schedule_all()
|
||||||
ppu_log.trace("schedule(): %s", target->id);
|
ppu_log.trace("schedule(): %s", target->id);
|
||||||
target->state ^= (cpu_flag::signal + cpu_flag::suspend);
|
target->state ^= (cpu_flag::signal + cpu_flag::suspend);
|
||||||
target->start_time = 0;
|
target->start_time = 0;
|
||||||
|
|
||||||
|
if (notify_later_idx >= std::size(g_to_notify) - 1)
|
||||||
|
{
|
||||||
target->state.notify_one(cpu_flag::signal + cpu_flag::suspend);
|
target->state.notify_one(cpu_flag::signal + cpu_flag::suspend);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_to_notify[notify_later_idx++] = target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_to_notify[notify_later_idx] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Check registered timeouts
|
// Check registered timeouts
|
||||||
while (!g_waiting.empty())
|
while (!g_waiting.empty())
|
||||||
|
|
|
@ -143,6 +143,8 @@ error_code sys_cond_signal(ppu_thread& ppu, u32 cond_id)
|
||||||
{
|
{
|
||||||
if (cond.waiters)
|
if (cond.waiters)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex->mutex);
|
std::lock_guard lock(cond.mutex->mutex);
|
||||||
|
|
||||||
if (const auto cpu = cond.schedule<ppu_thread>(cond.sq, cond.mutex->protocol))
|
if (const auto cpu = cond.schedule<ppu_thread>(cond.sq, cond.mutex->protocol))
|
||||||
|
@ -158,7 +160,7 @@ error_code sys_cond_signal(ppu_thread& ppu, u32 cond_id)
|
||||||
|
|
||||||
if (cond.mutex->try_own(*cpu, cpu->id))
|
if (cond.mutex->try_own(*cpu, cpu->id))
|
||||||
{
|
{
|
||||||
cond.awake(cpu);
|
cond.awake(cpu, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,6 +236,8 @@ error_code sys_cond_signal_to(ppu_thread& ppu, u32 cond_id, u32 thread_id)
|
||||||
|
|
||||||
if (cond.waiters)
|
if (cond.waiters)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex->mutex);
|
std::lock_guard lock(cond.mutex->mutex);
|
||||||
|
|
||||||
for (auto cpu : cond.sq)
|
for (auto cpu : cond.sq)
|
||||||
|
@ -252,7 +256,7 @@ error_code sys_cond_signal_to(ppu_thread& ppu, u32 cond_id, u32 thread_id)
|
||||||
|
|
||||||
if (cond.mutex->try_own(*cpu, cpu->id))
|
if (cond.mutex->try_own(*cpu, cpu->id))
|
||||||
{
|
{
|
||||||
cond.awake(cpu);
|
cond.awake(cpu, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -294,6 +298,8 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex->mutex);
|
std::lock_guard lock(cond.mutex->mutex);
|
||||||
|
|
||||||
const u64 syscall_state = sstate.try_read<u64>().second;
|
const u64 syscall_state = sstate.try_read<u64>().second;
|
||||||
|
@ -313,7 +319,7 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
|
|
||||||
if (ppu.loaded_from_savestate)
|
if (ppu.loaded_from_savestate)
|
||||||
{
|
{
|
||||||
cond.sleep(ppu, timeout);
|
cond.sleep(ppu, timeout, true);
|
||||||
return static_cast<u32>(syscall_state >> 32);
|
return static_cast<u32>(syscall_state >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +332,7 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep current thread and schedule mutex waiter
|
// Sleep current thread and schedule mutex waiter
|
||||||
cond.sleep(ppu, timeout);
|
cond.sleep(ppu, timeout, true);
|
||||||
|
|
||||||
// Save the recursive value
|
// Save the recursive value
|
||||||
return count;
|
return count;
|
||||||
|
|
|
@ -107,8 +107,10 @@ std::shared_ptr<lv2_event_queue> lv2_event_queue::find(u64 ipc_key)
|
||||||
|
|
||||||
extern void resume_spu_thread_group_from_waiting(spu_thread& spu);
|
extern void resume_spu_thread_group_from_waiting(spu_thread& spu);
|
||||||
|
|
||||||
CellError lv2_event_queue::send(lv2_event event)
|
CellError lv2_event_queue::send(lv2_event event, bool notify_later)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
if (!exists)
|
if (!exists)
|
||||||
|
@ -149,7 +151,7 @@ CellError lv2_event_queue::send(lv2_event event)
|
||||||
|
|
||||||
std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = event;
|
std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = event;
|
||||||
|
|
||||||
awake(&ppu);
|
awake(&ppu, notify_later);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -407,6 +409,8 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_e
|
||||||
return CELL_EINVAL;
|
return CELL_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(queue.mutex);
|
std::lock_guard lock(queue.mutex);
|
||||||
|
|
||||||
// "/dev_flash/vsh/module/msmw2.sprx" seems to rely on some cryptic shared memory behaviour that we don't emulate correctly
|
// "/dev_flash/vsh/module/msmw2.sprx" seems to rely on some cryptic shared memory behaviour that we don't emulate correctly
|
||||||
|
@ -420,7 +424,7 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_e
|
||||||
if (queue.events.empty())
|
if (queue.events.empty())
|
||||||
{
|
{
|
||||||
queue.sq.emplace_back(&ppu);
|
queue.sq.emplace_back(&ppu);
|
||||||
queue.sleep(ppu, timeout);
|
queue.sleep(ppu, timeout, true);
|
||||||
return CELL_EBUSY;
|
return CELL_EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -671,13 +675,13 @@ error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3)
|
||||||
|
|
||||||
sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", eport_id, data1, data2, data3);
|
sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", eport_id, data1, data2, data3);
|
||||||
|
|
||||||
const auto port = idm::get<lv2_obj, lv2_event_port>(eport_id, [&](lv2_event_port& port) -> CellError
|
const auto port = idm::check<lv2_obj, lv2_event_port>(eport_id, [&](lv2_event_port& port) -> CellError
|
||||||
{
|
{
|
||||||
if (lv2_obj::check(port.queue))
|
if (lv2_obj::check(port.queue))
|
||||||
{
|
{
|
||||||
const u64 source = port.name ? port.name : (s64{process_getpid()} << 32) | u64{eport_id};
|
const u64 source = port.name ? port.name : (s64{process_getpid()} << 32) | u64{eport_id};
|
||||||
|
|
||||||
return port.queue->send(source, data1, data2, data3);
|
return port.queue->send(source, data1, data2, data3, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CELL_ENOTCONN;
|
return CELL_ENOTCONN;
|
||||||
|
|
|
@ -99,11 +99,11 @@ struct lv2_event_queue final : public lv2_obj
|
||||||
static void save_ptr(utils::serial&, lv2_event_queue*);
|
static void save_ptr(utils::serial&, lv2_event_queue*);
|
||||||
static std::shared_ptr<lv2_event_queue> load_ptr(utils::serial& ar, std::shared_ptr<lv2_event_queue>& queue);
|
static std::shared_ptr<lv2_event_queue> load_ptr(utils::serial& ar, std::shared_ptr<lv2_event_queue>& queue);
|
||||||
|
|
||||||
CellError send(lv2_event);
|
CellError send(lv2_event event, bool notify_later = false);
|
||||||
|
|
||||||
CellError send(u64 source, u64 d1, u64 d2, u64 d3)
|
CellError send(u64 source, u64 d1, u64 d2, u64 d3, bool notify_later = false)
|
||||||
{
|
{
|
||||||
return send(std::make_tuple(source, d1, d2, d3));
|
return send(std::make_tuple(source, d1, d2, d3), notify_later);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get event queue by its global key
|
// Get event queue by its global key
|
||||||
|
|
|
@ -150,6 +150,8 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(flag.mutex);
|
std::lock_guard lock(flag.mutex);
|
||||||
|
|
||||||
if (flag.pattern.fetch_op([&](u64& pat)
|
if (flag.pattern.fetch_op([&](u64& pat)
|
||||||
|
@ -167,7 +169,7 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
|
||||||
|
|
||||||
flag.waiters++;
|
flag.waiters++;
|
||||||
flag.sq.emplace_back(&ppu);
|
flag.sq.emplace_back(&ppu);
|
||||||
flag.sleep(ppu, timeout);
|
flag.sleep(ppu, timeout, true);
|
||||||
return CELL_EBUSY;
|
return CELL_EBUSY;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,8 @@ error_code _sys_lwcond_signal(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u6
|
||||||
|
|
||||||
if (cond.waiters)
|
if (cond.waiters)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex);
|
std::lock_guard lock(cond.mutex);
|
||||||
|
|
||||||
if (cpu)
|
if (cpu)
|
||||||
|
@ -188,7 +190,7 @@ error_code _sys_lwcond_signal(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u6
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
cond.awake(result);
|
cond.awake(result, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -255,6 +257,8 @@ error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
|
|
||||||
if (cond.waiters)
|
if (cond.waiters)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex);
|
std::lock_guard lock(cond.mutex);
|
||||||
|
|
||||||
u32 result = 0;
|
u32 result = 0;
|
||||||
|
@ -294,7 +298,7 @@ error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
|
|
||||||
if (need_awake)
|
if (need_awake)
|
||||||
{
|
{
|
||||||
lv2_obj::awake_all();
|
lv2_obj::awake_all(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -341,6 +345,8 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
// Increment lwmutex's lwcond's waiters count
|
// Increment lwmutex's lwcond's waiters count
|
||||||
mutex->lwcond_waiters++;
|
mutex->lwcond_waiters++;
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex);
|
std::lock_guard lock(cond.mutex);
|
||||||
|
|
||||||
const bool mutex_sleep = sstate.try_read<bool>().second;
|
const bool mutex_sleep = sstate.try_read<bool>().second;
|
||||||
|
@ -381,7 +387,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep current thread and schedule lwmutex waiter
|
// Sleep current thread and schedule lwmutex waiter
|
||||||
cond.sleep(ppu, timeout);
|
cond.sleep(ppu, timeout, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!cond || !mutex)
|
if (!cond || !mutex)
|
||||||
|
|
|
@ -144,6 +144,8 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(mutex.mutex);
|
std::lock_guard lock(mutex.mutex);
|
||||||
|
|
||||||
auto [old, _] = mutex.signaled.fetch_op([](s32& value)
|
auto [old, _] = mutex.signaled.fetch_op([](s32& value)
|
||||||
|
@ -168,7 +170,7 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex.add_waiter(&ppu);
|
mutex.add_waiter(&ppu);
|
||||||
mutex.sleep(ppu, timeout);
|
mutex.sleep(ppu, timeout, true);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -275,6 +277,8 @@ error_code _sys_lwmutex_unlock(ppu_thread& ppu, u32 lwmutex_id)
|
||||||
|
|
||||||
const auto mutex = idm::check<lv2_obj, lv2_lwmutex>(lwmutex_id, [&](lv2_lwmutex& mutex)
|
const auto mutex = idm::check<lv2_obj, lv2_lwmutex>(lwmutex_id, [&](lv2_lwmutex& mutex)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(mutex.mutex);
|
std::lock_guard lock(mutex.mutex);
|
||||||
|
|
||||||
if (const auto cpu = mutex.schedule<ppu_thread>(mutex.sq, mutex.protocol))
|
if (const auto cpu = mutex.schedule<ppu_thread>(mutex.sq, mutex.protocol))
|
||||||
|
@ -285,7 +289,7 @@ error_code _sys_lwmutex_unlock(ppu_thread& ppu, u32 lwmutex_id)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex.awake(cpu);
|
mutex.awake(cpu, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +312,8 @@ error_code _sys_lwmutex_unlock2(ppu_thread& ppu, u32 lwmutex_id)
|
||||||
|
|
||||||
const auto mutex = idm::check<lv2_obj, lv2_lwmutex>(lwmutex_id, [&](lv2_lwmutex& mutex)
|
const auto mutex = idm::check<lv2_obj, lv2_lwmutex>(lwmutex_id, [&](lv2_lwmutex& mutex)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(mutex.mutex);
|
std::lock_guard lock(mutex.mutex);
|
||||||
|
|
||||||
if (const auto cpu = mutex.schedule<ppu_thread>(mutex.sq, mutex.protocol))
|
if (const auto cpu = mutex.schedule<ppu_thread>(mutex.sq, mutex.protocol))
|
||||||
|
@ -319,7 +325,7 @@ error_code _sys_lwmutex_unlock2(ppu_thread& ppu, u32 lwmutex_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
static_cast<ppu_thread*>(cpu)->gpr[3] = CELL_EBUSY;
|
static_cast<ppu_thread*>(cpu)->gpr[3] = CELL_EBUSY;
|
||||||
mutex.awake(cpu);
|
mutex.awake(cpu, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,8 @@ error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout)
|
||||||
|
|
||||||
if (result == CELL_EBUSY)
|
if (result == CELL_EBUSY)
|
||||||
{
|
{
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(mutex.mutex);
|
std::lock_guard lock(mutex.mutex);
|
||||||
|
|
||||||
if (mutex.try_own(ppu, ppu.id))
|
if (mutex.try_own(ppu, ppu.id))
|
||||||
|
@ -147,7 +149,7 @@ error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mutex.sleep(ppu, timeout);
|
mutex.sleep(ppu, timeout, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,21 +260,17 @@ error_code sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id)
|
||||||
|
|
||||||
sys_mutex.trace("sys_mutex_unlock(mutex_id=0x%x)", mutex_id);
|
sys_mutex.trace("sys_mutex_unlock(mutex_id=0x%x)", mutex_id);
|
||||||
|
|
||||||
const auto mutex = idm::check<lv2_obj, lv2_mutex>(mutex_id, [&](lv2_mutex& mutex)
|
const auto mutex = idm::check<lv2_obj, lv2_mutex>(mutex_id, [&](lv2_mutex& mutex) -> CellError
|
||||||
{
|
{
|
||||||
return mutex.try_unlock(ppu.id);
|
CellError result = mutex.try_unlock(ppu.id);
|
||||||
});
|
|
||||||
|
|
||||||
if (!mutex)
|
if (result == CELL_EBUSY)
|
||||||
{
|
{
|
||||||
return CELL_ESRCH;
|
lv2_obj::notify_all_t notify;
|
||||||
}
|
|
||||||
|
|
||||||
if (mutex.ret == CELL_EBUSY)
|
std::lock_guard lock(mutex.mutex);
|
||||||
{
|
|
||||||
std::lock_guard lock(mutex->mutex);
|
|
||||||
|
|
||||||
if (auto cpu = mutex->reown<ppu_thread>())
|
if (auto cpu = mutex.reown<ppu_thread>())
|
||||||
{
|
{
|
||||||
if (cpu->state & cpu_flag::again)
|
if (cpu->state & cpu_flag::again)
|
||||||
{
|
{
|
||||||
|
@ -280,10 +278,21 @@ error_code sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex->awake(cpu);
|
mutex.awake(cpu, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = {};
|
||||||
}
|
}
|
||||||
else if (mutex.ret)
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mutex)
|
||||||
|
{
|
||||||
|
return CELL_ESRCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mutex.ret)
|
||||||
{
|
{
|
||||||
return mutex.ret;
|
return mutex.ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,8 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(rwlock.mutex);
|
std::lock_guard lock(rwlock.mutex);
|
||||||
|
|
||||||
const s64 _old = rwlock.owner.fetch_op([&](s64& val)
|
const s64 _old = rwlock.owner.fetch_op([&](s64& val)
|
||||||
|
@ -129,7 +131,7 @@ error_code sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
|
||||||
if (_old > 0 || _old & 1)
|
if (_old > 0 || _old & 1)
|
||||||
{
|
{
|
||||||
rwlock.rq.emplace_back(&ppu);
|
rwlock.rq.emplace_back(&ppu);
|
||||||
rwlock.sleep(ppu, timeout);
|
rwlock.sleep(ppu, timeout, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +336,8 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(rwlock.mutex);
|
std::lock_guard lock(rwlock.mutex);
|
||||||
|
|
||||||
const s64 _old = rwlock.owner.fetch_op([&](s64& val)
|
const s64 _old = rwlock.owner.fetch_op([&](s64& val)
|
||||||
|
@ -351,7 +355,7 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
|
||||||
if (_old != 0)
|
if (_old != 0)
|
||||||
{
|
{
|
||||||
rwlock.wq.emplace_back(&ppu);
|
rwlock.wq.emplace_back(&ppu);
|
||||||
rwlock.sleep(ppu, timeout);
|
rwlock.sleep(ppu, timeout, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _old;
|
return _old;
|
||||||
|
|
|
@ -121,12 +121,14 @@ error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lv2_obj::notify_all_t notify;
|
||||||
|
|
||||||
std::lock_guard lock(sema.mutex);
|
std::lock_guard lock(sema.mutex);
|
||||||
|
|
||||||
if (sema.val-- <= 0)
|
if (sema.val-- <= 0)
|
||||||
{
|
{
|
||||||
sema.sq.emplace_back(&ppu);
|
sema.sq.emplace_back(&ppu);
|
||||||
sema.sleep(ppu, timeout);
|
sema.sleep(ppu, timeout, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,17 +165,17 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Remove the current thread from the scheduling queue, register timeout
|
// Remove the current thread from the scheduling queue, register timeout
|
||||||
static void sleep_unlocked(cpu_thread&, u64 timeout);
|
static void sleep_unlocked(cpu_thread&, u64 timeout, bool notify_later);
|
||||||
|
|
||||||
// Schedule the thread
|
// Schedule the thread
|
||||||
static bool awake_unlocked(cpu_thread*, s32 prio = enqueue_cmd);
|
static bool awake_unlocked(cpu_thread*, bool notify_later = false, s32 prio = enqueue_cmd);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr u64 max_timeout = u64{umax} / 1000;
|
static constexpr u64 max_timeout = u64{umax} / 1000;
|
||||||
|
|
||||||
static void sleep(cpu_thread& cpu, const u64 timeout = 0);
|
static void sleep(cpu_thread& cpu, const u64 timeout = 0, bool notify_later = false);
|
||||||
|
|
||||||
static bool awake(cpu_thread* const thread, s32 prio = enqueue_cmd);
|
static bool awake(cpu_thread* const thread, bool notify_later = false, s32 prio = enqueue_cmd);
|
||||||
|
|
||||||
// Returns true on successful context switch, false otherwise
|
// Returns true on successful context switch, false otherwise
|
||||||
static bool yield(cpu_thread& thread);
|
static bool yield(cpu_thread& thread);
|
||||||
|
@ -183,12 +183,12 @@ public:
|
||||||
static void set_priority(cpu_thread& thread, s32 prio)
|
static void set_priority(cpu_thread& thread, s32 prio)
|
||||||
{
|
{
|
||||||
ensure(prio + 512u < 3712);
|
ensure(prio + 512u < 3712);
|
||||||
awake(&thread, prio);
|
awake(&thread, false, prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void awake_all()
|
static inline void awake_all(bool notify_later = false)
|
||||||
{
|
{
|
||||||
awake({});
|
awake({}, notify_later);
|
||||||
g_to_awake.clear();
|
g_to_awake.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,6 +433,28 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void notify_all()
|
||||||
|
{
|
||||||
|
for (auto cpu : g_to_notify)
|
||||||
|
{
|
||||||
|
if (!cpu)
|
||||||
|
{
|
||||||
|
g_to_notify[0] = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu->state.notify_one(cpu_flag::suspend + cpu_flag::signal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct notify_all_t
|
||||||
|
{
|
||||||
|
~notify_all_t() noexcept
|
||||||
|
{
|
||||||
|
lv2_obj::notify_all();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Scheduler mutex
|
// Scheduler mutex
|
||||||
static shared_mutex g_mutex;
|
static shared_mutex g_mutex;
|
||||||
|
|
||||||
|
@ -452,5 +474,8 @@ private:
|
||||||
// Threads which must call lv2_obj::sleep before the scheduler starts
|
// Threads which must call lv2_obj::sleep before the scheduler starts
|
||||||
static std::deque<class cpu_thread*> g_to_sleep;
|
static std::deque<class cpu_thread*> g_to_sleep;
|
||||||
|
|
||||||
static void schedule_all();
|
// Pending list of threads to notify
|
||||||
|
static thread_local std::add_pointer_t<class cpu_thread> g_to_notify[4];
|
||||||
|
|
||||||
|
static void schedule_all(bool notify_later);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue