Merge pull request #1926 from fisherjoey/fix/exit-teardown-order
fix(ajm, apr): keep AJM and APR state alive through static teardown
This commit is contained in:
commit
9ac075e8c0
|
|
@ -36,6 +36,13 @@ if(BUILD_TESTING)
|
|||
target_link_libraries(guest_ajm_tests PRIVATE libSceAjm.native libc)
|
||||
configure_windows_unwind(guest_ajm_tests)
|
||||
add_test(NAME guest_ajm COMMAND guest_ajm_tests)
|
||||
if(UNIX)
|
||||
add_test_executable(guest_ajm_teardown_tests ${LIBS_INCLUDE_DIR}/tests/GuestAjmTeardown.cpp)
|
||||
target_include_directories(guest_ajm_teardown_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_ajm_teardown_tests PRIVATE libc ${CMAKE_DL_LIBS})
|
||||
add_dependencies(guest_ajm_teardown_tests libSceAjm.native)
|
||||
add_test(NAME guest_ajm_teardown COMMAND guest_ajm_teardown_tests $<TARGET_FILE:libSceAjm.native>)
|
||||
endif()
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_ajm PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAjm.native>")
|
||||
|
|
|
|||
|
|
@ -144,13 +144,13 @@ struct Instance {
|
|||
}
|
||||
};
|
||||
|
||||
std::mutex g_lock;
|
||||
std::map<std::uint32_t, std::unique_ptr<Instance>> g_instances;
|
||||
std::mutex& g_lock = *new std::mutex();
|
||||
std::map<std::uint32_t, std::unique_ptr<Instance>>& g_instances = *new std::map<std::uint32_t, std::unique_ptr<Instance>>();
|
||||
std::atomic<std::uint32_t> g_nextContext{1};
|
||||
std::atomic<std::uint32_t> g_nextInstance{1};
|
||||
std::atomic<std::uint32_t> g_nextBatch{1};
|
||||
std::mutex g_batchLock;
|
||||
std::set<std::uint32_t> g_batches;
|
||||
std::mutex& g_batchLock = *new std::mutex();
|
||||
std::set<std::uint32_t>& g_batches = *new std::set<std::uint32_t>();
|
||||
|
||||
enum class JobKind : std::uint32_t {
|
||||
Initialize = 1,
|
||||
|
|
|
|||
|
|
@ -39,15 +39,15 @@ struct AprFile {
|
|||
std::uint64_t size;
|
||||
};
|
||||
|
||||
std::mutex g_filesLock;
|
||||
std::vector<AprFile> g_files;
|
||||
std::unordered_map<std::string, std::uint32_t> g_idsByPath;
|
||||
std::mutex& g_filesLock = *new std::mutex();
|
||||
std::vector<AprFile>& g_files = *new std::vector<AprFile>();
|
||||
std::unordered_map<std::string, std::uint32_t>& g_idsByPath = *new std::unordered_map<std::string, std::uint32_t>();
|
||||
|
||||
// File sizes by directory, listed on first use. Titles resolve thousands of package paths at startup,
|
||||
// and one size query per path took over 20 s; a listing costs one directory read. Names are compared
|
||||
// case-insensitively, like the host file system.
|
||||
std::mutex g_directoriesLock;
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::uint64_t>> g_directories;
|
||||
std::mutex& g_directoriesLock = *new std::mutex();
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::uint64_t>>& g_directories = *new std::unordered_map<std::string, std::unordered_map<std::string, std::uint64_t>>();
|
||||
|
||||
std::string _foldCase(std::string text) {
|
||||
for (auto& character : text) {
|
||||
|
|
@ -164,7 +164,7 @@ void _writeAddress(const Apr::WriteAddressCommand& command) {
|
|||
|
||||
std::array<std::atomic<std::uint32_t>, 256> g_counters{};
|
||||
|
||||
std::mutex g_counterWrites;
|
||||
std::mutex& g_counterWrites = *new std::mutex();
|
||||
|
||||
std::uint32_t _counter(std::uint32_t index) {
|
||||
return g_counters[index % g_counters.size()].load(std::memory_order_acquire);
|
||||
|
|
@ -263,7 +263,7 @@ struct AmmState {
|
|||
};
|
||||
|
||||
AmmState& _amm() {
|
||||
static AmmState state;
|
||||
static auto& state = *new AmmState();
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using Initialize = int (APS5_VABI*)(std::int64_t, std::uint32_t*);
|
||||
using InstanceCreate = int (APS5_VABI*)(std::uint32_t, std::uint32_t, std::uint64_t, std::uint32_t*);
|
||||
using InstanceDestroy = int (APS5_VABI*)(std::uint32_t, std::uint32_t);
|
||||
using BatchInitialize = int (APS5_VABI*)(void*, std::size_t, AjmBatchInfo*);
|
||||
using BatchJobClearContext = int (APS5_VABI*)(AjmBatchInfo*, std::uint32_t, void*);
|
||||
using BatchStart = int (APS5_VABI*)(std::uint32_t, const AjmBatchInfo*, int, AjmBatchError*, std::uint32_t*);
|
||||
using BatchWait = int (APS5_VABI*)(std::uint32_t, std::uint32_t, std::uint32_t, AjmBatchError*);
|
||||
|
||||
struct Functions {
|
||||
Initialize initialize;
|
||||
InstanceCreate instanceCreate;
|
||||
InstanceDestroy instanceDestroy;
|
||||
BatchInitialize batchInitialize;
|
||||
BatchJobClearContext batchJobClearContext;
|
||||
BatchStart batchStart;
|
||||
BatchWait batchWait;
|
||||
};
|
||||
|
||||
struct SidebandResult {
|
||||
std::int32_t result;
|
||||
std::int32_t internalResult;
|
||||
};
|
||||
|
||||
Functions ajm{};
|
||||
std::uint32_t context = 0;
|
||||
std::uint32_t instance = 0;
|
||||
|
||||
[[noreturn]] void Fail(const char* message) {
|
||||
std::fprintf(stderr, "%s\n", message);
|
||||
std::fflush(stderr);
|
||||
std::_Exit(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Resolve(void* library, const char* name) {
|
||||
auto* symbol = dlsym(library, name);
|
||||
if (symbol == nullptr) Fail(name);
|
||||
return reinterpret_cast<T>(symbol);
|
||||
}
|
||||
|
||||
void UseAfterStaticTeardown() {
|
||||
alignas(16) std::uint8_t buffer[0x100]{};
|
||||
AjmBatchInfo info{};
|
||||
SidebandResult sideband{-1, -1};
|
||||
if (ajm.batchInitialize(buffer, sizeof(buffer), &info) != 0 || ajm.batchJobClearContext(&info, instance, &sideband) != 0) Fail("a batch cannot be built after static teardown");
|
||||
std::uint32_t batch = 0;
|
||||
AjmBatchError error{};
|
||||
if (ajm.batchStart(context, &info, 0, &error, &batch) != 0) Fail("sceAjmBatchStart failed after static teardown");
|
||||
if (sideband.result != 0) Fail("an instance created before exit is unknown after static teardown");
|
||||
if (ajm.batchWait(context, batch, 0, &error) != 0) Fail("sceAjmBatchWait does not find a batch started after static teardown");
|
||||
if (ajm.instanceDestroy(context, instance) != 0) Fail("sceAjmInstanceDestroy does not find the instance after static teardown");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) Fail("usage: guest_ajm_teardown_tests <libSceAjm.native>");
|
||||
if (std::atexit(UseAfterStaticTeardown) != 0) Fail("atexit failed");
|
||||
auto* library = dlopen(argv[1], RTLD_NOW);
|
||||
if (library == nullptr) Fail(dlerror());
|
||||
ajm = {
|
||||
Resolve<Initialize>(library, "sceAjmInitialize"),
|
||||
Resolve<InstanceCreate>(library, "sceAjmInstanceCreate"),
|
||||
Resolve<InstanceDestroy>(library, "sceAjmInstanceDestroy"),
|
||||
Resolve<BatchInitialize>(library, "sceAjmBatchInitialize"),
|
||||
Resolve<BatchJobClearContext>(library, "sceAjmBatchJobClearContext"),
|
||||
Resolve<BatchStart>(library, "sceAjmBatchStart"),
|
||||
Resolve<BatchWait>(library, "sceAjmBatchWait"),
|
||||
};
|
||||
if (ajm.initialize(0, &context) != 0 || ajm.instanceCreate(context, 0, 0, &instance) != 0) Fail("AJM setup failed");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -398,3 +398,4 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceVideodec2CreateDecoder](../../core/libs/prx/libSceVideodec2/Export.cpp) (libSceVideodec2) decodes HEVC (codec type 974921) and VP9 (2382845) through FFmpeg, besides H.264. The tags come from SharpProspero's `Videodec2CodecType` and EVO-PLAYER-PS5's `sce_videodec2.h`, which records them from hardware runs. HEVC input is converted from length-prefixed NAL units to Annex B as for H.264, and VP9 frames are passed through. Output is the same 8-bit NV12 for every codec; a decoder for HEVC Main10 or VP9 Profile 2 is created, but a frame deeper than 8 bits throws, since the console's 10-bit output layout is unknown, and `QueryDecoderMemoryInfo` reports the 8-bit frame size for it. `sceVideodec2GetPictureInfo` and `sceVideodec2GetAvcPictureInfo` throw for HEVC and VP9 pictures, which use other layouts (`SceVideodec2HevcPictureInfo`, `SceVideodec2Vp9PictureInfo`), and `sceVideodec2GetHevcPictureInfo` and `sceVideodec2GetVp9PictureInfo` are not exported
|
||||
- [syslog](../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER`, and errno is preserved. `openlog`, `setlogmask` and `closelog` are not implemented, so the identity, options and mask they set are not applied
|
||||
- [sigaction](../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's `sigaction` (`EINVAL` for other signals, and for a handler on `SIGKILL` or `SIGSTOP`); `signal` records its handler with `SA_RESTART`, as FreeBSD's libc does. Only handlers without `SA_SIGINFO` on the signals `signal` already delivers (`SIGINT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM`) run, the same way `signal` handlers do; their mask and flags are not applied. `SA_SIGINFO` handlers and other signals (`SIGBUS`, `SIGHUP`, ...) are recorded but never called, since that needs FreeBSD's `siginfo_t` and `ucontext_t`; `raise` of an `SA_SIGINFO` handler on a signal `signal` delivers throws instead of terminating, and `raise` of the other signals still returns `EINVAL`
|
||||
- [AJM](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp), [APR](../../core/libs/prx/libkernel/Apr/src/Apr.cpp) and [NGS2](../../core/libs/prx/libSceNgs2.native/src/System.cpp) state is allocated with `new` and never freed on purpose: title threads still call into these libraries while `std::exit` runs static destructors, so ordinary statics would be destroyed under them. Keep them leaked; `guest_ajm_teardown` covers the AJM case on Unix.
|
||||
|
|
|
|||
Loading…
Reference in New Issue