diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp index a306f73653..a236398598 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp @@ -14,6 +14,7 @@ #include "Utilities/lockless.h" #include +#include LOG_CHANNEL(cellSysutil); @@ -58,6 +59,8 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +atomic_t g_sysutil_callback_id_assigner = 0; + struct sysutil_cb_manager { struct alignas(8) registered_dispatcher @@ -65,6 +68,7 @@ struct sysutil_cb_manager u32 event_code = 0; u32 func_addr = 0; }; + std::array, 32> dispatchers{}; struct alignas(8) registered_cb @@ -77,7 +81,26 @@ struct sysutil_cb_manager atomic_t callbacks[4]{}; - lf_queue> registered; + struct dispatcher_cb + { + std::function func; + std::shared_ptr> call_active; + }; + + std::deque>>> registered_callbacks_abort_handles = []() + { + // Do resize for deque (cheap container which can store all non-movable value types) + std::deque>>> result; + + for (usz i = 0 ; i < g_sysutil_callback_id_assigner; i++) + { + result.emplace_back(); + } + + return result; + }(); + + lf_queue registered; atomic_t draw_cb_started{}; atomic_t read_counter{0}; @@ -98,11 +121,40 @@ struct sysutil_cb_manager } }; +void sysutil_register_cb_with_id_internal(std::function&& cb, usz call_id) +{ + auto& cbm = *ensure(g_fxo->try_get()); + + sysutil_cb_manager::dispatcher_cb info{std::move(cb)}; + + if (call_id != umax) + { + info.call_active = std::make_shared>(true); + ::at32(cbm.registered_callbacks_abort_handles, call_id).push(info.call_active); + } + + cbm.registered.push(std::move(info)); +} + +extern void sysutil_unregister_cb_with_id_internal(usz call_id) +{ + const auto cbm = g_fxo->try_get(); + + if (!cbm) + { + return; + } + + for (auto&& abort_handle : ::at32(cbm->registered_callbacks_abort_handles, call_id).pop_all()) + { + // Deactivate the existing event once + abort_handle->store(false); + } +} + extern void sysutil_register_cb(std::function&& cb) { - auto& cbm = g_fxo->get(); - - cbm.registered.push(std::move(cb)); + sysutil_register_cb_with_id_internal(std::move(cb), umax); } extern s32 sysutil_send_system_cmd(u64 status, u64 param) @@ -136,12 +188,12 @@ extern s32 sysutil_send_system_cmd(u64 status, u64 param) { if (cb.callback) { - cbm->registered.push([=](ppu_thread& ppu) -> s32 + cbm->registered.push(sysutil_cb_manager::dispatcher_cb{[=](ppu_thread& ppu) -> s32 { // TODO: check it and find the source of the return value (void isn't equal to CELL_OK) cb.callback(ppu, status, param, cb.user_data); return CELL_OK; - }); + }}); count++; } @@ -532,9 +584,14 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu) for (auto&& func : cbm.registered.pop_all()) { + if (func.call_active && !*func.call_active) + { + continue; + } + read = true; - if (s32 res = func(ppu)) + if (s32 res = func.func(ppu)) { // Currently impossible return not_an_error(res); diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.h b/rpcs3/Emu/Cell/Modules/cellSysutil.h index 557fef9202..6ab8fadb5d 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.h +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.h @@ -323,6 +323,26 @@ struct SysutilEventStatus atomic_t active = false; }; +extern atomic_t g_sysutil_callback_id_assigner; + +extern void sysutil_register_cb_with_id_internal(std::function&& cb, usz call_id); +extern void sysutil_unregister_cb_with_id_internal(usz call_id); + +template +const usz g_sysutil_dispatcher_id = g_sysutil_callback_id_assigner++; + +template +void sysutil_register_cb_with_id(std::function&& cb) +{ + sysutil_register_cb_with_id_internal(std::move(cb), g_sysutil_dispatcher_id); +} + +template +void sysutil_unregister_cb_with_id() +{ + sysutil_unregister_cb_with_id_internal(g_sysutil_dispatcher_id); +} + using SysutilMenuOpenStatus = SysutilEventStatus; extern void sysutil_register_cb(std::function&&); diff --git a/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp b/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp index de617447ee..4cb7df8c28 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp @@ -112,6 +112,9 @@ struct avc2_settings } }; +// Callback handle tag type +struct avc2_cb_handle_t{}; + error_code cellSysutilAvc2GetPlayerInfo(vm::cptr player_id, vm::ptr player_info) { cellSysutilAvc2.todo("cellSysutilAvc2GetPlayerInfo(player_id=*0x%x, player_info=*0x%x)", player_id, player_info); @@ -434,6 +437,7 @@ error_code cellSysutilAvc2Unload() return CELL_AVC2_ERROR_NOT_INITIALIZED; } + sysutil_unregister_cb_with_id(); settings.avc2_cb = vm::null; settings.avc2_cb_arg = vm::null; @@ -455,6 +459,7 @@ error_code cellSysutilAvc2UnloadAsync() }); } + sysutil_unregister_cb_with_id(); settings.avc2_cb = vm::null; settings.avc2_cb_arg = vm::null; @@ -572,7 +577,7 @@ error_code cellSysutilAvc2JoinChatRequest(vm::cptr room_id if (settings.avc2_cb) { - sysutil_register_cb([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32 + sysutil_register_cb_with_id([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32 { avc2_cb(cb_ppu, CELL_AVC2_EVENT_JOIN_SUCCEEDED, 0, avc2_cb_arg); return 0; @@ -1032,7 +1037,7 @@ error_code cellSysutilAvc2Unload2(u32 mediaType) } // TODO: return error if the video chat is still loaded (probably CELL_AVC2_ERROR_INVALID_STATUS) - + sysutil_unregister_cb_with_id(); settings.avc2_cb = vm::null; settings.avc2_cb_arg = vm::null; break; @@ -1073,6 +1078,7 @@ error_code cellSysutilAvc2UnloadAsync2(u32 mediaType) if (mediaType == CELL_SYSUTIL_AVC2_VOICE_CHAT) { + sysutil_unregister_cb_with_id(); settings.avc2_cb = vm::null; settings.avc2_cb_arg = vm::null; }