mirror of
https://github.com/Swordpdf/ProsperoEden.git
synced 2026-10-21 21:22:16 -05:00
106 lines
2.9 KiB
C++
106 lines
2.9 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include "stop_limit.h"
|
|
#include <condition_variable>
|
|
#include <fcntl.h>
|
|
#include <mutex>
|
|
#include <thread>
|
|
#include <unistd.h>
|
|
#include <utility>
|
|
|
|
#ifdef PS5_NATIVE
|
|
extern "C" int eden_restart_app(void);
|
|
extern "C" int sceKernelDebugOutText(int, const char*);
|
|
#endif
|
|
|
|
namespace Eden::StopLimit {
|
|
namespace {
|
|
struct State {
|
|
std::mutex mutex;
|
|
std::condition_variable wake;
|
|
bool watching = false;
|
|
bool armed = false;
|
|
std::chrono::steady_clock::time_point deadline;
|
|
std::chrono::milliseconds limit = kLimit;
|
|
void (*at_limit)() noexcept = nullptr;
|
|
std::string note;
|
|
};
|
|
// Never destroyed: the watcher is a detached thread and must not meet a destroyed mutex at exit.
|
|
State& state() {
|
|
static State* const instance = new State;
|
|
return *instance;
|
|
}
|
|
|
|
void Watch() {
|
|
State& s = state();
|
|
std::unique_lock lock(s.mutex);
|
|
for (;;) {
|
|
if (!s.armed) {
|
|
s.wake.wait(lock);
|
|
continue;
|
|
}
|
|
const auto deadline = s.deadline;
|
|
if (s.wake.wait_until(lock, deadline) != std::cv_status::timeout) continue;
|
|
// Timed out: still the same stop?
|
|
if (!s.armed || s.deadline != deadline) continue;
|
|
s.armed = false;
|
|
const auto action = s.at_limit ? s.at_limit : &RestartNow;
|
|
lock.unlock();
|
|
action(); // on the console this returns only when the system refuses the restart
|
|
lock.lock();
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
void Start(std::string note) noexcept {
|
|
State& s = state();
|
|
std::lock_guard lock(s.mutex);
|
|
s.note = std::move(note);
|
|
if (s.watching) return;
|
|
try {
|
|
std::thread(Watch).detach();
|
|
s.watching = true;
|
|
} catch (...) {
|
|
// No watcher: a stop then takes as long as it takes, as before.
|
|
}
|
|
}
|
|
|
|
void Begin() noexcept {
|
|
State& s = state();
|
|
std::lock_guard lock(s.mutex);
|
|
if (s.armed) return;
|
|
s.armed = true;
|
|
s.deadline = std::chrono::steady_clock::now() + s.limit;
|
|
s.wake.notify_all();
|
|
}
|
|
|
|
void End() noexcept {
|
|
State& s = state();
|
|
std::lock_guard lock(s.mutex);
|
|
s.armed = false;
|
|
s.wake.notify_all();
|
|
}
|
|
|
|
void RestartNow() noexcept {
|
|
#ifdef PS5_NATIVE
|
|
// No stdio here: a thread that is stuck in the stop may hold one of its locks.
|
|
sceKernelDebugOutText(0, "[ProsperoEden] shutdown: The game did not stop in time; starting ProsperoEden again\n");
|
|
const std::string& note = state().note; // set once, before any game
|
|
if (!note.empty()) {
|
|
const int fd = open(note.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (fd >= 0) {
|
|
(void)!write(fd, "1\n", 2);
|
|
close(fd);
|
|
}
|
|
}
|
|
eden_restart_app();
|
|
#endif
|
|
}
|
|
|
|
void Configure(void (*at_limit)() noexcept, std::chrono::milliseconds limit) noexcept {
|
|
State& s = state();
|
|
std::lock_guard lock(s.mutex);
|
|
s.at_limit = at_limit;
|
|
s.limit = limit;
|
|
}
|
|
} // namespace Eden::StopLimit
|