cellSysutil: Implement dispatcher callback unloading

This commit is contained in:
Elad Ashkenazi 2024-05-17 09:36:09 +03:00
parent fe9bb31b09
commit 6fbe4c3e2e
3 changed files with 92 additions and 9 deletions

View file

@ -14,6 +14,7 @@
#include "Utilities/lockless.h"
#include <span>
#include <deque>
LOG_CHANNEL(cellSysutil);
@ -58,6 +59,8 @@ void fmt_class_string<CellBgmplaybackError>::format(std::string& out, u64 arg)
});
}
atomic_t<usz> 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<atomic_t<registered_dispatcher>, 32> dispatchers{};
struct alignas(8) registered_cb
@ -77,7 +81,26 @@ struct sysutil_cb_manager
atomic_t<registered_cb> callbacks[4]{};
lf_queue<std::function<s32(ppu_thread&)>> registered;
struct dispatcher_cb
{
std::function<s32(ppu_thread&)> func;
std::shared_ptr<atomic_t<bool>> call_active;
};
std::deque<lf_queue<std::shared_ptr<atomic_t<bool>>>> registered_callbacks_abort_handles = []()
{
// Do resize for deque (cheap container which can store all non-movable value types)
std::deque<lf_queue<std::shared_ptr<atomic_t<bool>>>> result;
for (usz i = 0 ; i < g_sysutil_callback_id_assigner; i++)
{
result.emplace_back();
}
return result;
}();
lf_queue<dispatcher_cb> registered;
atomic_t<bool> draw_cb_started{};
atomic_t<u64> read_counter{0};
@ -98,11 +121,40 @@ struct sysutil_cb_manager
}
};
void sysutil_register_cb_with_id_internal(std::function<s32(ppu_thread&)>&& cb, usz call_id)
{
auto& cbm = *ensure(g_fxo->try_get<sysutil_cb_manager>());
sysutil_cb_manager::dispatcher_cb info{std::move(cb)};
if (call_id != umax)
{
info.call_active = std::make_shared<atomic_t<bool>>(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<sysutil_cb_manager>();
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<s32(ppu_thread&)>&& cb)
{
auto& cbm = g_fxo->get<sysutil_cb_manager>();
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);

View file

@ -323,6 +323,26 @@ struct SysutilEventStatus
atomic_t<bool> active = false;
};
extern atomic_t<usz> g_sysutil_callback_id_assigner;
extern void sysutil_register_cb_with_id_internal(std::function<s32(ppu_thread&)>&& cb, usz call_id);
extern void sysutil_unregister_cb_with_id_internal(usz call_id);
template <typename T>
const usz g_sysutil_dispatcher_id = g_sysutil_callback_id_assigner++;
template <typename T>
void sysutil_register_cb_with_id(std::function<s32(ppu_thread&)>&& cb)
{
sysutil_register_cb_with_id_internal(std::move(cb), g_sysutil_dispatcher_id<T>);
}
template <typename T>
void sysutil_unregister_cb_with_id()
{
sysutil_unregister_cb_with_id_internal(g_sysutil_dispatcher_id<T>);
}
using SysutilMenuOpenStatus = SysutilEventStatus<CELL_SYSUTIL_SYSTEM_MENU_OPEN>;
extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&&);

View file

@ -112,6 +112,9 @@ struct avc2_settings
}
};
// Callback handle tag type
struct avc2_cb_handle_t{};
error_code cellSysutilAvc2GetPlayerInfo(vm::cptr<SceNpMatching2RoomMemberId> player_id, vm::ptr<CellSysutilAvc2PlayerInfo> 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<avc2_cb_handle_t>();
settings.avc2_cb = vm::null;
settings.avc2_cb_arg = vm::null;
@ -455,6 +459,7 @@ error_code cellSysutilAvc2UnloadAsync()
});
}
sysutil_unregister_cb_with_id<avc2_cb_handle_t>();
settings.avc2_cb = vm::null;
settings.avc2_cb_arg = vm::null;
@ -572,7 +577,7 @@ error_code cellSysutilAvc2JoinChatRequest(vm::cptr<SceNpMatching2RoomId> 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_handle_t>([=, 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<avc2_cb_handle_t>();
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<avc2_cb_handle_t>();
settings.avc2_cb = vm::null;
settings.avc2_cb_arg = vm::null;
}