diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 596f41aeec..c829002568 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -867,22 +867,8 @@ bool SPUThread::set_ch_value(u32 ch, u32 value) LOG_TRACE(SPU, "sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d))", data, value, flag); - const auto eflag = idm::get(data); - - if (!eflag) - { - return ch_in_mbox.set_values(1, CELL_ESRCH), true; - } - - const u64 bitptn = 1ull << flag; - - if (~eflag->pattern.fetch_or(bitptn) & bitptn) - { - // notify if the bit was set - eflag->notify_all(lv2_lock); - } - - return ch_in_mbox.set_values(1, CELL_OK), true; + // Use the syscall to set flag + return ch_in_mbox.set_values(1, sys_event_flag_set(data, 1ull << flag)), true; } else if (code == 192) { @@ -908,21 +894,8 @@ bool SPUThread::set_ch_value(u32 ch, u32 value) LOG_TRACE(SPU, "sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d))", data, value, flag); - const auto eflag = idm::get(data); - - if (!eflag) - { - return true; - } - - const u64 bitptn = 1ull << flag; - - if (~eflag->pattern.fetch_or(bitptn) & bitptn) - { - // notify if the bit was set - eflag->notify_all(lv2_lock); - } - + // Use the syscall to set flag + sys_event_flag_set(data, 1ull << flag); return true; } else diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp index 28d5b75494..c7ba542c6d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp @@ -15,35 +15,7 @@ logs::channel sys_event_flag("sys_event_flag", logs::level::notice); extern u64 get_system_time(); -void lv2_event_flag::notify_all(lv2_lock_t) -{ - auto pred = [this](cpu_thread* thread) -> bool - { - auto& ppu = static_cast(*thread); - - // load pattern and mode from registers - const u64 bitptn = ppu.gpr[4]; - const u32 mode = static_cast(ppu.gpr[5]); - - // check specific pattern - if (check_pattern(bitptn, mode)) - { - // save pattern - ppu.gpr[4] = clear_pattern(bitptn, mode); - - thread->set_signal(); - - return true; - } - - return false; - }; - - // check all waiters; protocol is ignored in current implementation - sq.erase(std::remove_if(sq.begin(), sq.end(), pred), sq.end()); -} - -s32 sys_event_flag_create(vm::ptr id, vm::ptr attr, u64 init) +error_code sys_event_flag_create(vm::ptr id, vm::ptr attr, u64 init) { sys_event_flag.warning("sys_event_flag_create(id=*0x%x, attr=*0x%x, init=0x%llx)", id, attr, init); @@ -74,48 +46,55 @@ s32 sys_event_flag_create(vm::ptr id, vm::ptr a return CELL_EINVAL; } - *id = idm::make(init, protocol, type, attr->name_u64); + if (const u32 _id = idm::make(protocol, type, attr->name_u64, init)) + { + *id = _id; + return CELL_OK; + } - return CELL_OK; + return CELL_EAGAIN; } -s32 sys_event_flag_destroy(u32 id) +error_code sys_event_flag_destroy(u32 id) { sys_event_flag.warning("sys_event_flag_destroy(id=0x%x)", id); - LV2_LOCK; + const auto flag = idm::withdraw(id, [&](lv2_event_flag& flag) -> CellError + { + if (flag.waiters) + { + return CELL_EBUSY; + } - const auto eflag = idm::get(id); + return {}; + }); - if (!eflag) + if (!flag) { return CELL_ESRCH; } - if (!eflag->sq.empty()) + if (flag.ret) { - return CELL_EBUSY; + return flag.ret; } - idm::remove(id); - return CELL_OK; } -s32 sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr result, u64 timeout) +error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr result, u64 timeout) { sys_event_flag.trace("sys_event_flag_wait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x, timeout=0x%llx)", id, bitptn, mode, result, timeout); const u64 start_time = get_system_time(); - // If this syscall is called through the SC instruction, these registers must already contain corresponding values. - // But let's fixup them (in the case of explicit function call or something) because these values are used externally. + // Fix function arguments for external access ppu.gpr[4] = bitptn; ppu.gpr[5] = mode; + ppu.gpr[6] = 0; - LV2_LOCK; - - if (result) *result = 0; // This is very annoying. + // Always set result + if (result) *result = ppu.gpr[6]; if (!lv2_event_flag::check_mode(mode)) { @@ -123,75 +102,89 @@ s32 sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr(id); + const auto flag = idm::get(id, [&](lv2_event_flag& flag) -> CellError + { + if (flag.pattern.atomic_op(lv2_event_flag::check_pattern, bitptn, mode, &ppu.gpr[6])) + { + // TODO: is it possible to return EPERM in this case? + return {}; + } - if (!eflag) + semaphore_lock lock(flag.mutex); + + if (flag.pattern.atomic_op(lv2_event_flag::check_pattern, bitptn, mode, &ppu.gpr[6])) + { + return {}; + } + + if (flag.type == SYS_SYNC_WAITER_SINGLE && flag.sq.size()) + { + return CELL_EPERM; + } + + flag.waiters++; + flag.sq.emplace_back(&ppu); + return CELL_EBUSY; + }); + + if (!flag) { return CELL_ESRCH; } - if (eflag->type == SYS_SYNC_WAITER_SINGLE && eflag->sq.size() > 0) + if (flag.ret) { - return CELL_EPERM; + if (flag.ret != CELL_EBUSY) + { + return flag.ret; + } } - - if (eflag->check_pattern(bitptn, mode)) + else { - const u64 pattern = eflag->clear_pattern(bitptn, mode); - - if (result) *result = pattern; - + if (result) *result = ppu.gpr[6]; return CELL_OK; } - // add waiter; protocol is ignored in current implementation - sleep_entry waiter(eflag->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) { - if (result) *result = eflag->pattern; + semaphore_lock lock(flag->mutex); - return CELL_ETIMEDOUT; + if (!flag->unqueue(flag->sq, &ppu)) + { + timeout = 0; + continue; + } + + flag->waiters--; + if (result) *result = flag->pattern; + return not_an_error(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(); } } - // load pattern saved upon signaling - if (result) - { - *result = ppu.gpr[4]; - } - - // check cause - if (ppu.gpr[5] == 0) - { - return CELL_ECANCELED; - } - - return CELL_OK; + if (result) *result = ppu.gpr[6]; + return not_an_error(ppu.gpr[5] ? CELL_OK : CELL_ECANCELED); } -s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr result) +error_code sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr result) { sys_event_flag.trace("sys_event_flag_trywait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x)", id, bitptn, mode, result); - LV2_LOCK; - - if (result) *result = 0; // This is very annoying. + if (result) *result = 0; if (!lv2_event_flag::check_mode(mode)) { @@ -199,129 +192,164 @@ s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr result) return CELL_EINVAL; } - const auto eflag = idm::get(id); + u64 pattern; - if (!eflag) + const auto flag = idm::check(id, [&](lv2_event_flag& flag) + { + return flag.pattern.atomic_op(lv2_event_flag::check_pattern, bitptn, mode, &pattern); + }); + + if (!flag) { return CELL_ESRCH; } - if (eflag->check_pattern(bitptn, mode)) + if (!flag.ret) { - const u64 pattern = eflag->clear_pattern(bitptn, mode); + return not_an_error(CELL_EBUSY); + } - if (result) *result = pattern; + if (result) *result = pattern; + return CELL_OK; +} +error_code sys_event_flag_set(u32 id, u64 bitptn) +{ + // Warning: may be called from SPU thread. + + sys_event_flag.trace("sys_event_flag_set(id=0x%x, bitptn=0x%llx)", id, bitptn); + + const auto flag = idm::get(id); + + if (!flag) + { + return CELL_ESRCH; + } + + if ((flag->pattern & bitptn) == bitptn) + { return CELL_OK; } - return CELL_EBUSY; -} + semaphore_lock lock(flag->mutex); -s32 sys_event_flag_set(u32 id, u64 bitptn) -{ - sys_event_flag.trace("sys_event_flag_set(id=0x%x, bitptn=0x%llx)", id, bitptn); - - LV2_LOCK; - - const auto eflag = idm::get(id); - - if (!eflag) + // Sort sleep queue in required order + if (flag->protocol != SYS_SYNC_FIFO) { - return CELL_ESRCH; + std::stable_sort(flag->sq.begin(), flag->sq.end(), [](cpu_thread* a, cpu_thread* b) + { + return static_cast(a)->prio < static_cast(b)->prio; + }); } - if (bitptn && ~eflag->pattern.fetch_or(bitptn) & bitptn) + // Process all waiters in single atomic op + flag->pattern.atomic_op([&](u64& value) { - eflag->notify_all(lv2_lock); - } + value |= bitptn; + + for (auto cpu : flag->sq) + { + auto& ppu = static_cast(*cpu); + + const u64 pattern = ppu.gpr[4]; + const u64 mode = ppu.gpr[5]; + ppu.gpr[3] = lv2_event_flag::check_pattern(value, pattern, mode, &ppu.gpr[6]); + } + }); + + // Remove waiters + const auto tail = std::remove_if(flag->sq.begin(), flag->sq.end(), [&](cpu_thread* cpu) + { + auto& ppu = static_cast(*cpu); + + if (ppu.gpr[3]) + { + flag->waiters--; + ppu.set_signal(); + return true; + } + + return false; + }); + + flag->sq.erase(tail, flag->sq.end()); return CELL_OK; } -s32 sys_event_flag_clear(u32 id, u64 bitptn) +error_code sys_event_flag_clear(u32 id, u64 bitptn) { sys_event_flag.trace("sys_event_flag_clear(id=0x%x, bitptn=0x%llx)", id, bitptn); - LV2_LOCK; + const auto flag = idm::check(id, [&](lv2_event_flag& flag) + { + flag.pattern &= bitptn; + }); - const auto eflag = idm::get(id); - - if (!eflag) + if (!flag) { return CELL_ESRCH; } - eflag->pattern &= bitptn; - return CELL_OK; } -s32 sys_event_flag_cancel(u32 id, vm::ptr num) +error_code sys_event_flag_cancel(u32 id, vm::ptr num) { sys_event_flag.trace("sys_event_flag_cancel(id=0x%x, num=*0x%x)", id, num); - LV2_LOCK; + if (num) *num = 0; - if (num) - { - *num = 0; - } + const auto flag = idm::get(id); - const auto eflag = idm::get(id); - - if (!eflag) + if (!flag) { return CELL_ESRCH; } - if (num) - { - *num = static_cast(eflag->sq.size()); - } + semaphore_lock lock(flag->mutex); - const u64 pattern = eflag->pattern; + // Get current pattern + const u64 pattern = flag->pattern; - // signal all threads to return CELL_ECANCELED - for (auto& thread : eflag->sq) + // Set count + *num = ::size32(flag->sq); + + // Signal all threads to return CELL_ECANCELED + while (auto thread = flag->schedule(flag->sq, flag->protocol)) { auto& ppu = static_cast(*thread); - // save existing pattern ppu.gpr[4] = pattern; - - // clear "mode" as a sign of cancellation ppu.gpr[5] = 0; thread->set_signal(); + flag->waiters--; } - eflag->sq.clear(); - return CELL_OK; } -s32 sys_event_flag_get(u32 id, vm::ptr flags) +error_code sys_event_flag_get(u32 id, vm::ptr flags) { sys_event_flag.trace("sys_event_flag_get(id=0x%x, flags=*0x%x)", id, flags); - LV2_LOCK; - if (!flags) { return CELL_EFAULT; } - const auto eflag = idm::get(id); - - if (!eflag) + const auto flag = idm::check(id, [](lv2_event_flag& flag) { - *flags = 0; // This is very annoying. + return +flag.pattern; + }); + if (!flag) + { + *flags = 0; return CELL_ESRCH; } - *flags = eflag->pattern; - + *flags = flag.ret; return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.h b/rpcs3/Emu/Cell/lv2/sys_event_flag.h index a8bdcaa4a6..5b822ee2a8 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.h +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.h @@ -40,9 +40,10 @@ struct lv2_event_flag final : lv2_obj const s32 type; const u64 name; + semaphore<> mutex; + atomic_t waiters{0}; atomic_t pattern; - - sleep_queue sq; + std::deque sq; lv2_event_flag(u32 protocol, s32 type, u64 name, u64 pattern) : protocol(protocol) @@ -55,7 +56,8 @@ struct lv2_event_flag final : lv2_obj { } - static inline bool check_mode(u32 mode) + // Check mode arg + static bool check_mode(u32 mode) { switch (mode & 0xf) { @@ -75,54 +77,46 @@ struct lv2_event_flag final : lv2_obj return true; } - bool check_pattern(u64 bitptn, u32 mode) + // Check and clear pattern (must be atomic op) + static bool check_pattern(u64& pattern, u64 bitptn, u64 mode, u64* result) { - if ((mode & 0xf) == SYS_EVENT_FLAG_WAIT_AND) + // Write pattern + if (result) { - return (pattern & bitptn) == bitptn; + *result = pattern; } - else if ((mode & 0xf) == SYS_EVENT_FLAG_WAIT_OR) - { - return (pattern & bitptn) != 0; - } - else - { - fmt::throw_exception("Unknown mode (0x%x)" HERE, mode); - } - } - u64 clear_pattern(u64 bitptn, u32 mode) - { + // Check pattern + if ((mode & 0xf) == SYS_EVENT_FLAG_WAIT_AND && (pattern & bitptn) != bitptn || + (mode & 0xf) == SYS_EVENT_FLAG_WAIT_OR && (pattern & bitptn) == 0) + { + return false; + } + + // Clear pattern if necessary if ((mode & ~0xf) == SYS_EVENT_FLAG_WAIT_CLEAR) { - return pattern.fetch_and(~bitptn); + pattern &= ~bitptn; } else if ((mode & ~0xf) == SYS_EVENT_FLAG_WAIT_CLEAR_ALL) { - return pattern.exchange(0); + pattern = 0; } - else if ((mode & ~0xf) == 0) - { - return pattern; - } - else - { - fmt::throw_exception("Unknown mode (0x%x)" HERE, mode); - } - } - void notify_all(lv2_lock_t); + return true; + } }; // Aux class ppu_thread; -// SysCalls -s32 sys_event_flag_create(vm::ps3::ptr id, vm::ps3::ptr attr, u64 init); -s32 sys_event_flag_destroy(u32 id); -s32 sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ps3::ptr result, u64 timeout); -s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ps3::ptr result); -s32 sys_event_flag_set(u32 id, u64 bitptn); -s32 sys_event_flag_clear(u32 id, u64 bitptn); -s32 sys_event_flag_cancel(u32 id, vm::ps3::ptr num); -s32 sys_event_flag_get(u32 id, vm::ps3::ptr flags); +// Syscalls + +error_code sys_event_flag_create(vm::ps3::ptr id, vm::ps3::ptr attr, u64 init); +error_code sys_event_flag_destroy(u32 id); +error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ps3::ptr result, u64 timeout); +error_code sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ps3::ptr result); +error_code sys_event_flag_set(u32 id, u64 bitptn); +error_code sys_event_flag_clear(u32 id, u64 bitptn); +error_code sys_event_flag_cancel(u32 id, vm::ps3::ptr num); +error_code sys_event_flag_get(u32 id, vm::ps3::ptr flags); diff --git a/rpcs3/Gui/KernelExplorer.cpp b/rpcs3/Gui/KernelExplorer.cpp index f054f8f021..d1ef4affc7 100644 --- a/rpcs3/Gui/KernelExplorer.cpp +++ b/rpcs3/Gui/KernelExplorer.cpp @@ -229,7 +229,8 @@ void KernelExplorer::Update() case SYS_EVENT_FLAG_OBJECT: { auto& ef = static_cast(obj); - m_tree->AppendItem(node, fmt::format("Event Flag: ID = 0x%08x \"%s\", Type = 0x%x, Pattern = 0x%llx", id, +name64(ef.name), ef.type, ef.pattern.load())); + m_tree->AppendItem(node, fmt::format("Event Flag: ID = 0x%08x \"%s\", Type = 0x%x, Pattern = 0x%llx, Wq = %zu", id, +name64(ef.name), + ef.type, ef.pattern.load(), +ef.waiters)); break; } default: