Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class VulkanDevice {
// Starts the journal replay if the device has not (a headless device starts it at its first
// draw or dispatch) and joins it (tests; the teardown joins it anyway).
void WaitForPipelineJournalReplay();
void SavePipelineCache();
// Compute pipelines this device built, by dispatches and by the journal replay (tests).
std::uint64_t ComputePipelinesCreated() const;
~VulkanDevice();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ Driver::Driver() {
InstallAsyncLog_nid_no_patch();
static const bool driverMemory = (MapDriverMemory(), true);
static_cast<void>(driverMemory);
LibcSetExitDrain_nid_no_patch([] {
const auto current = Driver::Get().device.Load();
if (current) current->SavePipelineCache();
});
static_cast<void>(GuestMemory::TrackerGeneration());
ShaderMemory::SetWaitedMsProvider(&Graphics::Recorder::ThreadWaitedMs);
try {
Expand Down
4 changes: 4 additions & 0 deletions core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2044,6 +2044,10 @@ void VulkanDevice::WaitForPipelineJournalReplay() {
if (state->journalReplay != nullptr) state->journalReplay->Wait();
}

void VulkanDevice::SavePipelineCache() {
if (state->pipelineCache) state->pipelineCache->SaveNow();
}

std::uint64_t VulkanDevice::ComputePipelinesCreated() const {
return state->computePipelinesCreated.load(std::memory_order_relaxed);
}
Expand Down
4 changes: 3 additions & 1 deletion core/libs/prx/libSceSystemService/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ if(BUILD_TESTING)
add_test(NAME guest_requested_exit COMMAND guest_system_exit_tests --requested-exit-driver)
add_test(NAME guest_quick_exit COMMAND guest_system_exit_tests --quick-exit)
add_test(NAME guest_immediate_exit COMMAND guest_system_exit_tests --immediate)
add_test(NAME guest_immediate_exit_drain COMMAND guest_system_exit_tests --exit-drain-driver)
set_tests_properties(guest_immediate_exit_drain PROPERTIES TIMEOUT 20)
add_test(NAME guest_return_from_main COMMAND guest_system_exit_tests --return-from-main)
add_test(NAME guest_system_relaunch COMMAND guest_system_exit_tests --relaunch-driver)
add_test(NAME guest_nested_exit COMMAND guest_system_exit_tests --nested-exit-driver)
Expand All @@ -62,7 +64,7 @@ if(BUILD_TESTING)
set_tests_properties(guest_system_load_exec_standard_error PROPERTIES TIMEOUT 20)
endif()
if(WIN32)
set_tests_properties(guest_system_exit guest_requested_exit guest_system_exit_parameters guest_immediate_exit guest_return_from_main guest_quick_exit guest_system_relaunch guest_nested_exit guest_exit_finalizers_before_shutdown PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(guest_system_exit guest_requested_exit guest_system_exit_parameters guest_immediate_exit guest_immediate_exit_drain guest_return_from_main guest_quick_exit guest_system_relaunch guest_nested_exit guest_exit_finalizers_before_shutdown PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_system_service_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemService.cpp)
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libc/include/Shutdown.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ extern "C" [[noreturn]] void LibcExitWithoutHandlers_nid_no_patch(int code);
extern "C" [[noreturn]] void LibcTerminate_nid_no_patch(int code);
extern "C" void LibcSetEndFlush_nid_no_patch(void (*flush)());
extern "C" void LibcRunEndFlush_nid_no_patch();
extern "C" void LibcSetExitDrain_nid_no_patch(void (*drain)());

#endif
28 changes: 28 additions & 0 deletions core/libs/prx/libc/src/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cerrno>
#include <condition_variable>
#include <exception>
#include <future>
#include <mutex>
#include <thread>
#include <vector>
Expand Down Expand Up @@ -100,6 +101,7 @@ extern "C" void LibcRunShutdown_nid_postfix() {
namespace {

std::atomic<void (*)()> endFlush{nullptr};
std::atomic<void (*)()> exitDrain{nullptr};
void (*previousAbortHandler)(int) = SIG_DFL;

void FlushOnAbort(int number) {
Expand Down Expand Up @@ -129,6 +131,12 @@ extern "C" void LibcRunEndFlush_nid_no_patch() {
if (const auto flush = endFlush.load(std::memory_order_acquire)) flush();
}

extern "C" void LibcSetExitDrain_nid_no_patch(void (*drain)()) {
if (drain == nullptr) throw std::runtime_error("libc: invalid exit drain registration");
void (*registered)() = nullptr;
if (!exitDrain.compare_exchange_strong(registered, drain) && registered != drain) throw std::runtime_error("libc: exit drain already registered");
}

namespace {

void StartExit() {
Expand All @@ -154,6 +162,26 @@ extern "C" {
}

[[noreturn]] void LibcTerminate_nid_no_patch(int code) {
if (const auto drain = exitDrain.exchange(nullptr)) {
try {
auto completion = std::make_shared<std::promise<void>>();
auto finished = completion->get_future();
std::thread([completion, drain] {
try {
drain();
completion->set_value();
} catch (...) {
completion->set_exception(std::current_exception());
}
}).detach();
if (finished.wait_for(std::chrono::seconds(8)) == std::future_status::ready) finished.get();
else std::fputs("[libc] host exit drain exceeded 8 seconds\n", stderr);
} catch (const std::exception& error) {
std::fprintf(stderr, "[libc] host exit drain failed: %s\n", error.what());
} catch (...) {
std::fputs("[libc] host exit drain failed\n", stderr);
}
}
LibcRunEndFlush_nid_no_patch();
#ifdef _WIN32
TerminateProcess(GetCurrentProcess(), static_cast<UINT>(code));
Expand Down
40 changes: 40 additions & 0 deletions core/libs/tests/GuestSystemExit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ void SecondaryShutdown() { AppendMarker("secondary shutdown"); }
void APS5_VABI RemainingHandler(void*) { AppendMarker("remaining handler"); }
void APS5_VABI ExitingHandler(void*) { exit_nid_postfix(7); }
void GuestExitShutdown() { AppendMarker("shutdown"); }
void DelayedExitDrain() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
AppendMarker("drain");
}
void ExitFlushAfterDrain() { AppendMarker("flush"); }
void ThrowingExitDrain() { throw std::runtime_error("exit drain test failure"); }
void BlockedExitDrain() {
for (;;) std::this_thread::sleep_for(std::chrono::seconds(1));
}
void APS5_VABI CheckShutdownStillRunning(void*) {
if (LibcShutdownToken_nid_postfix().stop_requested()) std::_Exit(8);
AppendMarker("guest finalizer");
Expand Down Expand Up @@ -183,9 +192,40 @@ int DriveRelaunch() {
std::puts("Guest relaunch completed");
return 0;
}

int DriveExitDrain() {
const auto self = OwnExecutable();
const auto marker = std::filesystem::temp_directory_path() / ("anyps5-exit-drain-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".txt");
SetVariable(RelaunchMarker, marker.string().c_str());
Require(RunChild(self, "--exit-drain") == 7);
std::ifstream file(marker);
const std::string text{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
file.close();
std::filesystem::remove(marker);
Require(text == "drain\nflush\n");
Require(RunChild(self, "--exit-drain-throws") == 7);
const auto started = std::chrono::steady_clock::now();
Require(RunChild(self, "--exit-drain-blocked") == 7);
const auto elapsed = std::chrono::steady_clock::now() - started;
Require(elapsed >= std::chrono::seconds(8) && elapsed < std::chrono::seconds(12));
std::puts("Host exit drain completed before immediate termination and stayed bounded");
return 0;
}
}
int main(int argc, char** argv) {
const int firstArgument = argc > 0 && std::strncmp(argv[0], "--", 2) == 0 ? 0 : 1;
if (argc > firstArgument && std::strcmp(argv[firstArgument], "--exit-drain-driver") == 0) return DriveExitDrain();
if (argc > firstArgument && std::strncmp(argv[firstArgument], "--exit-drain", 12) == 0) {
LibcRegisterShutdown_nid_postfix(UnexpectedCleanup);
Require(std::atexit(UnexpectedCleanup) == 0);
if (std::strcmp(argv[firstArgument], "--exit-drain-throws") == 0) LibcSetExitDrain_nid_no_patch(ThrowingExitDrain);
else if (std::strcmp(argv[firstArgument], "--exit-drain-blocked") == 0) LibcSetExitDrain_nid_no_patch(BlockedExitDrain);
else {
LibcSetExitDrain_nid_no_patch(DelayedExitDrain);
LibcSetEndFlush_nid_no_patch(ExitFlushAfterDrain);
}
_Exit_nid_postfix(7);
}
if (argc > firstArgument && std::strcmp(argv[firstArgument], "--requested-exit-driver") == 0) {
Require(RunChild(OwnExecutable(), "--requested-exit") == 0);
return 0;
Expand Down
Loading