From 213527ca714b56a355a3810cc5ea0bb34302ce5b Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Tue, 31 Jan 2017 02:09:55 +0300 Subject: [PATCH] sys_semaphore... --- rpcs3/Emu/Cell/lv2/sys_semaphore.cpp | 213 +++++++++++++++++---------- rpcs3/Emu/Cell/lv2/sys_semaphore.h | 35 +++-- rpcs3/Emu/Cell/lv2/sys_sync.h | 50 +++++++ rpcs3/Gui/KernelExplorer.cpp | 2 +- 4 files changed, 210 insertions(+), 90 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp b/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp index b5405bd8e1..be9e9e6ae1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_semaphore.cpp @@ -13,7 +13,7 @@ logs::channel sys_semaphore("sys_semaphore", logs::level::notice); extern u64 get_system_time(); -s32 sys_semaphore_create(vm::ptr sem_id, vm::ptr attr, s32 initial_val, s32 max_val) +error_code sys_semaphore_create(vm::ptr sem_id, vm::ptr attr, s32 initial_val, s32 max_val) { sys_semaphore.warning("sys_semaphore_create(sem_id=*0x%x, attr=*0x%x, initial_val=%d, max_val=%d)", sem_id, attr, initial_val, max_val); @@ -42,169 +42,232 @@ s32 sys_semaphore_create(vm::ptr sem_id, vm::ptr return CELL_EINVAL; } - *sem_id = idm::make(protocol, max_val, attr->name_u64, initial_val); + if (const u32 id = idm::make(protocol, attr->name_u64, max_val, initial_val)) + { + *sem_id = id; + return CELL_OK; + } - return CELL_OK; + return CELL_EAGAIN; } -s32 sys_semaphore_destroy(u32 sem_id) +error_code sys_semaphore_destroy(u32 sem_id) { sys_semaphore.warning("sys_semaphore_destroy(sem_id=0x%x)", sem_id); - LV2_LOCK; + const auto sem = idm::withdraw(sem_id, [](lv2_sema& sema) -> CellError + { + if (sema.val < 0) + { + return CELL_EBUSY; + } - const auto sem = idm::get(sem_id); + return {}; + }); if (!sem) { return CELL_ESRCH; } - if (sem->sq.size()) + if (sem.value) { - return CELL_EBUSY; + return sem.value; } - idm::remove(sem_id); - return CELL_OK; } -s32 sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) +error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout) { sys_semaphore.trace("sys_semaphore_wait(sem_id=0x%x, timeout=0x%llx)", sem_id, timeout); const u64 start_time = get_system_time(); - LV2_LOCK; + const auto sem = idm::get(sem_id, [&](lv2_sema& sema) + { + const s32 val = sema.val; + + if (val > 0) + { + if (sema.val.compare_and_swap_test(val, val - 1)) + { + return true; + } + } - const auto sem = idm::get(sem_id); + semaphore_lock lock(sema.mutex); + + if (sema.val-- <= 0) + { + sema.sq.emplace_back(&ppu); + return false; + } + + return true; + }); if (!sem) { return CELL_ESRCH; } - if (sem->value > 0) + if (sem.value) { - sem->value--; - return CELL_OK; } - // add waiter; protocol is ignored in current implementation - sleep_entry waiter(sem->sq, ppu); + // SLEEP while (!ppu.state.test_and_reset(cpu_flag::signal)) { - CHECK_EMU_STATUS; - if (timeout) { const u64 passed = get_system_time() - start_time; if (passed >= timeout) { + semaphore_lock lock(sem->mutex); + + const s32 val = sem->val.fetch_op([](s32& val) + { + if (val < 0) + { + val++; + } + }); + + if (val >= 0) + { + timeout = 0; + continue; + } + + verify(HERE), sem->unqueue(sem->sq, &ppu); return CELL_ETIMEDOUT; } - LV2_UNLOCK, thread_ctrl::wait_for(timeout - passed); + thread_ctrl::wait_for(timeout - passed); } else { - LV2_UNLOCK, thread_ctrl::wait(); + thread_ctrl::wait(); } } return CELL_OK; } -s32 sys_semaphore_trywait(u32 sem_id) +error_code sys_semaphore_trywait(u32 sem_id) { sys_semaphore.trace("sys_semaphore_trywait(sem_id=0x%x)", sem_id); - LV2_LOCK; + const auto sem = idm::check(sem_id, [&](lv2_sema& sema) + { + const s32 val = sema.val; + + if (val > 0) + { + if (sema.val.compare_and_swap_test(val, val - 1)) + { + return true; + } + } - const auto sem = idm::get(sem_id); + return false; + }); if (!sem) { return CELL_ESRCH; } - if (sem->value <= 0 || sem->sq.size()) + if (!sem.value) { - return CELL_EBUSY; + return not_an_error(CELL_EBUSY); } - sem->value--; - return CELL_OK; } -s32 sys_semaphore_post(u32 sem_id, s32 count) +error_code sys_semaphore_post(u32 sem_id, s32 count) { sys_semaphore.trace("sys_semaphore_post(sem_id=0x%x, count=%d)", sem_id, count); - LV2_LOCK; - - const auto sem = idm::get(sem_id); - - if (!sem) - { - return CELL_ESRCH; - } - if (count < 0) { return CELL_EINVAL; } - // get comparable values considering waiting threads - const u64 new_value = sem->value + count; - const u64 max_value = sem->max + sem->sq.size(); - - if (new_value > max_value) + const auto sem = idm::get(sem_id, [&](lv2_sema& sema) { - return CELL_EBUSY; - } + const s32 val = sema.val; - // wakeup as much threads as possible - while (count && !sem->sq.empty()) - { - count--; + if (val >= 0 && count <= sema.max - val) + { + if (sema.val.compare_and_swap_test(val, val + count)) + { + return true; + } + } - const auto thread = sem->sq.front(); - thread->set_signal(); - - sem->sq.pop_front(); - } - - // add the rest to the value - sem->value += count; - - return CELL_OK; -} - -s32 sys_semaphore_get_value(u32 sem_id, vm::ptr count) -{ - sys_semaphore.trace("sys_semaphore_get_value(sem_id=0x%x, count=*0x%x)", sem_id, count); - - LV2_LOCK; - - if (!count) - { - return CELL_EFAULT; - } - - const auto sem = idm::get(sem_id); + return false; + }); if (!sem) { return CELL_ESRCH; } - *count = sem->value; + if (sem.value) + { + return CELL_OK; + } + else + { + semaphore_lock lock(sem->mutex); + + const s32 val = sem->val.fetch_op([=](s32& val) + { + if (val + s64{count} <= sem->max) + { + val += count; + } + }); + + if (val + s64{count} > sem->max) + { + return not_an_error(CELL_EBUSY); + } + + // Wake threads + for (s32 i = std::min(-std::min(val, 0), count); i > 0; i--) + { + const auto cpu = verify(HERE, sem->schedule(sem->sq, sem->protocol)); + + cpu->set_signal(); + } + } + + return CELL_OK; +} + +error_code sys_semaphore_get_value(u32 sem_id, vm::ptr count) +{ + sys_semaphore.trace("sys_semaphore_get_value(sem_id=0x%x, count=*0x%x)", sem_id, count); + + if (!count) + { + return CELL_EFAULT; + } + + if (!idm::check(sem_id, [=](lv2_sema& sema) + { + *count = std::max(0, sema.val); + })) + { + return CELL_ESRCH; + } return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_semaphore.h b/rpcs3/Emu/Cell/lv2/sys_semaphore.h index b16e4a2786..5159c0db04 100644 --- a/rpcs3/Emu/Cell/lv2/sys_semaphore.h +++ b/rpcs3/Emu/Cell/lv2/sys_semaphore.h @@ -22,18 +22,24 @@ struct lv2_sema final : lv2_obj static const u32 id_base = 0x96000000; const u32 protocol; - const s32 max; + const u32 shared; + const u64 key; const u64 name; + const s32 flags; + const s32 max; - atomic_t value; + semaphore<> mutex; + atomic_t val; + std::deque sq; - sleep_queue sq; - - lv2_sema(u32 protocol, s32 max, u64 name, s32 value) + lv2_sema(u32 protocol, u64 name, s32 max, s32 value) : protocol(protocol) - , max(max) + , shared(0) + , key(0) + , flags(0) , name(name) - , value(value) + , max(max) + , val(value) { } }; @@ -41,10 +47,11 @@ struct lv2_sema final : lv2_obj // Aux class ppu_thread; -// SysCalls -s32 sys_semaphore_create(vm::ps3::ptr sem_id, vm::ps3::ptr attr, s32 initial_val, s32 max_val); -s32 sys_semaphore_destroy(u32 sem_id); -s32 sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout); -s32 sys_semaphore_trywait(u32 sem_id); -s32 sys_semaphore_post(u32 sem_id, s32 count); -s32 sys_semaphore_get_value(u32 sem_id, vm::ps3::ptr count); +// Syscalls + +error_code sys_semaphore_create(vm::ps3::ptr sem_id, vm::ps3::ptr attr, s32 initial_val, s32 max_val); +error_code sys_semaphore_destroy(u32 sem_id); +error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout); +error_code sys_semaphore_trywait(u32 sem_id); +error_code sys_semaphore_post(u32 sem_id, s32 count); +error_code sys_semaphore_get_value(u32 sem_id, vm::ps3::ptr count); diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index d39ab4d253..8f4aefa432 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -52,6 +52,56 @@ struct lv2_obj static const u32 id_step = 0x100; static const u32 id_count = 8192; + + // Find and remove the object from the container (deque or vector) + template + static bool unqueue(std::deque& queue, const E& object) + { + for (auto found = queue.cbegin(), end = queue.cend(); found != end; found++) + { + if (*found == object) + { + queue.erase(found); + return true; + } + } + + return false; + } + + template + static T* schedule(std::deque& queue, u32 protocol) + { + if (queue.empty()) + { + return nullptr; + } + + if (protocol == SYS_SYNC_FIFO) + { + const auto res = queue.front(); + queue.pop_front(); + return res; + } + + u32 prio = -1; + auto it = queue.cbegin(); + + for (auto found = it, end = queue.cend(); found != end; found++) + { + const u32 _prio = static_cast(*found)->prio; + + if (_prio < prio) + { + it = found; + prio = _prio; + } + } + + const auto res = *it; + queue.erase(it); + return res; + } }; // Temporary implementation for LV2_UNLOCK (TODO: remove it) diff --git a/rpcs3/Gui/KernelExplorer.cpp b/rpcs3/Gui/KernelExplorer.cpp index 765bc688b1..ae7286f6ac 100644 --- a/rpcs3/Gui/KernelExplorer.cpp +++ b/rpcs3/Gui/KernelExplorer.cpp @@ -214,7 +214,7 @@ void KernelExplorer::Update() { auto& sema = static_cast(obj); m_tree->AppendItem(node, fmt::format("Semaphore: ID = 0x%08x \"%s\", Count = %d, Max Count = %d, Waiters = %#zu", id, +name64(sema.name), - sema.value.load(), sema.max, sema.sq.size())); + sema.val.load(), sema.max, sema.sq.size())); break; } case SYS_LWCOND_OBJECT: