mirror of
https://github.com/Vita3K/Vita3K.git
synced 2026-07-11 01:34:23 +02:00
kernel: two-phase thread shutdown
Replace `exit_delete_all_threads_and_wait()` with two separate operations: - `process_exit()` blocks until all guest threads have exited (host-only) - `request_process_exit()` fires the registered callback and returns immediately, safe to call from guest threads. Threads are now SDL_DetachThread'd at creation. `thread_deleted_cond` on `KernelState` is used for join semantics in `process_exit()`. Remove `shutting_down` from sync primitives; `exit_delete()` wakes blocked threads via `update_status(run)`, which all `status_cond` predicates already check. `process_exit_callback` carries an `optional<AppLaunchRequest>` so `LoadExec` relaunches flow through the same path as normal exits. `AppLaunchRequest`/`AppLaunchReason` extracted to `emuenv/app_launch_request.h` so kernel can include it without a circular dependency. After this, further simplification to `ThreadStatus` will be done in https://github.com/Vita3K/Vita3K/pull/3922.
This commit is contained in:
@@ -443,7 +443,7 @@ void deinit(EmuEnvState &state) {
|
||||
state.net.abort_all();
|
||||
state.http.shutdown_connections();
|
||||
|
||||
state.kernel.exit_delete_all_threads_and_wait();
|
||||
state.kernel.process_exit();
|
||||
|
||||
state.motion.reset_runtime();
|
||||
|
||||
|
||||
@@ -104,13 +104,10 @@ void wait_vblank(DisplayState &display, KernelState &kernel, const ThreadStatePt
|
||||
}
|
||||
|
||||
wait_thread->status_cond.wait(thread_lock, [&]() {
|
||||
return wait_thread->status == ThreadStatus::run || kernel.shutting_down.load(std::memory_order_relaxed);
|
||||
return wait_thread->status == ThreadStatus::run;
|
||||
});
|
||||
}
|
||||
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed))
|
||||
return;
|
||||
|
||||
if (is_cb) {
|
||||
for (auto &[_, cb] : display.vblank_callbacks) {
|
||||
if (cb->get_owner_thread_id() == wait_thread->id) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2026 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class AppLaunchReason {
|
||||
User,
|
||||
LoadExec,
|
||||
ProcessExit,
|
||||
};
|
||||
|
||||
struct AppLaunchRequest {
|
||||
std::string app_path{};
|
||||
std::string self_path{};
|
||||
std::vector<std::string> argv{};
|
||||
AppLaunchReason reason = AppLaunchReason::User;
|
||||
};
|
||||
@@ -81,17 +81,7 @@ typedef int SceUID;
|
||||
|
||||
using NIDSet = std::set<uint32_t>;
|
||||
|
||||
enum class AppLaunchReason {
|
||||
User,
|
||||
LoadExec
|
||||
};
|
||||
|
||||
struct AppLaunchRequest {
|
||||
std::string app_path{};
|
||||
std::string self_path{};
|
||||
std::vector<std::string> argv{};
|
||||
AppLaunchReason reason = AppLaunchReason::User;
|
||||
};
|
||||
#include <emuenv/app_launch_request.h>
|
||||
|
||||
/**
|
||||
* @brief State of the emulated PlayStation Vita environment
|
||||
|
||||
@@ -811,6 +811,7 @@ bool MainWindow::handle_pending_app_launch_request() {
|
||||
return false;
|
||||
|
||||
on_game_closed();
|
||||
if (request->reason != AppLaunchReason::ProcessExit)
|
||||
boot_game(*request, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <dialog/state.h>
|
||||
#include <display/functions.h>
|
||||
#include <display/state.h>
|
||||
#include <emuenv/state.h>
|
||||
#include <io/functions.h>
|
||||
#include <io/vfs.h>
|
||||
#include <kernel/state.h>
|
||||
@@ -365,6 +366,9 @@ static ExitCode load_app_impl(SceUID &main_module_id, EmuEnvState &emuenv, const
|
||||
const auto call_import = [&emuenv](CPUState &cpu, uint32_t nid, SceUID thread_id) {
|
||||
::call_import(emuenv, cpu, nid, thread_id);
|
||||
};
|
||||
emuenv.kernel.process_exit_callback = [&emuenv](int res, std::optional<AppLaunchRequest> relaunch) {
|
||||
emuenv.post_app_launch_request(relaunch.value_or(AppLaunchRequest{ .reason = AppLaunchReason::ProcessExit }));
|
||||
};
|
||||
if (!emuenv.kernel.init(emuenv.mem, call_import, emuenv.cfg.current_config.cpu_opt)) {
|
||||
LOG_WARN("Failed to init kernel!");
|
||||
return KernelInitFailed;
|
||||
|
||||
@@ -24,7 +24,7 @@ add_library(
|
||||
${SOURCE_LIST}
|
||||
)
|
||||
|
||||
target_include_directories(kernel PUBLIC include)
|
||||
target_include_directories(kernel PUBLIC include ${CMAKE_CURRENT_SOURCE_DIR}/../emuenv/include)
|
||||
target_link_libraries(kernel PUBLIC rtc cpu mem util nids)
|
||||
target_link_libraries(kernel PRIVATE patch SDL3::SDL3 miniz vita-toolchain)
|
||||
if(TRACY_ENABLE_ON_CORE_COMPONENTS)
|
||||
|
||||
@@ -31,17 +31,19 @@
|
||||
#include <util/containers.h>
|
||||
#include <util/types.h>
|
||||
|
||||
#include <emuenv/app_launch_request.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
struct ThreadState;
|
||||
struct MemState;
|
||||
|
||||
struct SDL_Thread;
|
||||
|
||||
struct CodecEngineBlock;
|
||||
|
||||
struct KernelModule {
|
||||
@@ -55,8 +57,6 @@ typedef std::shared_ptr<ThreadState> ThreadStatePtr;
|
||||
typedef std::map<SceUID, CodecEngineBlock> CodecEngineBlocks;
|
||||
typedef std::map<SceUID, Ptr<Ptr<void>>> SlotToAddress;
|
||||
typedef std::map<SceUID, ThreadStatePtr> ThreadStatePtrs;
|
||||
typedef std::shared_ptr<SDL_Thread> ThreadPtr;
|
||||
typedef std::map<SceUID, ThreadPtr> ThreadPtrs;
|
||||
typedef std::map<SceUID, SceKernelModulePtr> SceKernelModuleInfoPtrs;
|
||||
typedef std::map<SceUID, CallbackPtr> CallbackPtrs;
|
||||
typedef unordered_map_fast<uint32_t, Address> ExportNids;
|
||||
@@ -157,13 +157,7 @@ struct KernelState {
|
||||
// kubridge exception handlers (DABT=0, PABT=1, UNDEF=2)
|
||||
static constexpr int EXCEPTION_HANDLER_MAX = 3;
|
||||
std::atomic<Address> exception_handlers[EXCEPTION_HANDLER_MAX]{};
|
||||
std::mutex thread_lifecycle_mutex;
|
||||
std::map<SceUID, SDL_Thread *> host_threads;
|
||||
std::mutex host_threads_mutex;
|
||||
|
||||
std::atomic<bool> shutting_down{ false };
|
||||
std::mutex shutdown_mutex;
|
||||
std::condition_variable shutdown_condvar; // for delay_thread
|
||||
std::condition_variable thread_deleted_cond;
|
||||
|
||||
SceUID get_next_uid() {
|
||||
return next_uid++;
|
||||
@@ -178,11 +172,17 @@ struct KernelState {
|
||||
ThreadStatePtr get_thread(SceUID thread_id);
|
||||
Ptr<Ptr<void>> get_thread_tls_addr(MemState &mem, SceUID thread_id, int key);
|
||||
|
||||
void exit_delete_all_threads_and_wait();
|
||||
bool is_threads_paused() { return !paused_threads_status.empty(); }
|
||||
void pause_threads();
|
||||
void resume_threads();
|
||||
|
||||
// Kill all guest threads and block until they have exited. Must only be called from a host thread.
|
||||
void process_exit();
|
||||
std::function<void(int, std::optional<AppLaunchRequest>)> process_exit_callback;
|
||||
// Request process exit. Safe to call from a guest thread. Returns immediately.
|
||||
// The registered process_exit_callback is invoked to notify the host layer.
|
||||
void request_process_exit(int res, std::optional<AppLaunchRequest> relaunch = std::nullopt);
|
||||
|
||||
void set_memory_watch(bool enabled);
|
||||
void invalidate_jit_cache(Address start, size_t length);
|
||||
SceKernelModuleInfo *find_module_by_addr(Address address);
|
||||
|
||||
@@ -52,8 +52,6 @@ struct ThreadSignal {
|
||||
void wait();
|
||||
bool send();
|
||||
|
||||
std::atomic<bool> *shutting_down = nullptr;
|
||||
|
||||
private:
|
||||
std::mutex mutex;
|
||||
std::condition_variable recv_cond;
|
||||
|
||||
@@ -74,9 +74,12 @@ static int SDLCALL thread_function(void *data) {
|
||||
thread->run_loop();
|
||||
const uint32_t r0 = read_reg(*thread->cpu, 0);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(params.kernel->mutex);
|
||||
params.kernel->threads.erase(thread->id);
|
||||
params.kernel->corenum_allocator.free_corenum(get_processor_id(*thread->cpu));
|
||||
params.kernel->thread_deleted_cond.notify_all();
|
||||
}
|
||||
|
||||
return r0;
|
||||
}
|
||||
@@ -144,25 +147,12 @@ ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name, Ptr<c
|
||||
}
|
||||
|
||||
ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name, Ptr<const void> entry_point, int init_priority, SceInt32 affinity_mask, int stack_size, const SceKernelThreadOptParam *option) {
|
||||
if (shutting_down.load(std::memory_order_relaxed)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadStatePtr thread = std::make_shared<ThreadState>(get_next_uid(), *this, mem);
|
||||
if (thread->init(name, entry_point, init_priority, affinity_mask, stack_size, option) < 0)
|
||||
return nullptr;
|
||||
|
||||
const auto release_corenum = [&]() {
|
||||
corenum_allocator.free_corenum(get_processor_id(*thread->cpu));
|
||||
};
|
||||
|
||||
const std::lock_guard<std::mutex> lifecycle_lock(thread_lifecycle_mutex);
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
if (shutting_down.load(std::memory_order_acquire)) {
|
||||
release_corenum();
|
||||
return nullptr;
|
||||
}
|
||||
threads.emplace(thread->id, thread);
|
||||
}
|
||||
|
||||
@@ -171,15 +161,10 @@ ThreadStatePtr KernelState::create_thread(MemState &mem, const char *name, Ptr<c
|
||||
params.thid = thread->id;
|
||||
|
||||
params.host_may_destroy_params = SDL_CreateSemaphore(0);
|
||||
SDL_Thread *sdl_thread = SDL_CreateThread(&thread_function, thread->name.c_str(), ¶ms);
|
||||
SDL_DetachThread(SDL_CreateThread(&thread_function, thread->name.c_str(), ¶ms));
|
||||
SDL_WaitSemaphore(params.host_may_destroy_params);
|
||||
SDL_DestroySemaphore(params.host_may_destroy_params);
|
||||
|
||||
{
|
||||
const std::lock_guard<std::mutex> ht_lock(host_threads_mutex);
|
||||
// TODO: lazy cleanup i forgor
|
||||
host_threads.emplace(thread->id, sdl_thread);
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
@@ -195,55 +180,22 @@ Ptr<Ptr<void>> KernelState::get_thread_tls_addr(MemState &mem, SceUID thread_id,
|
||||
return address;
|
||||
}
|
||||
|
||||
void KernelState::exit_delete_all_threads_and_wait() {
|
||||
{
|
||||
const std::lock_guard<std::mutex> lifecycle_lock(thread_lifecycle_mutex);
|
||||
shutting_down.store(true, std::memory_order_release);
|
||||
}
|
||||
void KernelState::request_process_exit(int res, std::optional<AppLaunchRequest> relaunch) {
|
||||
if (process_exit_callback)
|
||||
process_exit_callback(res, std::move(relaunch));
|
||||
}
|
||||
|
||||
void KernelState::process_exit() {
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto &[_, timer] : timers) {
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto &[_, timer] : timers)
|
||||
timer->condvar.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
shutdown_condvar.notify_all();
|
||||
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto &[_, thread] : threads)
|
||||
thread->exit_delete(false);
|
||||
}
|
||||
|
||||
std::map<SceUID, SDL_Thread *> threads_to_join;
|
||||
SDL_Thread *self_thread = nullptr;
|
||||
SceUID self_thread_id = SCE_KERNEL_ERROR_ILLEGAL_THREAD_ID;
|
||||
const SDL_ThreadID current_thread_id = SDL_GetCurrentThreadID();
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(host_threads_mutex);
|
||||
threads_to_join.swap(host_threads);
|
||||
}
|
||||
|
||||
for (auto it = threads_to_join.begin(); it != threads_to_join.end(); ++it) {
|
||||
if (SDL_GetThreadID(it->second) != current_thread_id)
|
||||
continue;
|
||||
|
||||
self_thread = it->second;
|
||||
self_thread_id = it->first;
|
||||
threads_to_join.erase(it);
|
||||
break;
|
||||
}
|
||||
|
||||
if (self_thread) {
|
||||
LOG_DEBUG("Detaching current host thread to avoid self-join (guest thread {}, host thread {})", self_thread_id, current_thread_id);
|
||||
SDL_DetachThread(self_thread);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Joining {} host threads", threads_to_join.size());
|
||||
for (auto &[id, sdl_thread] : threads_to_join) {
|
||||
SDL_WaitThread(sdl_thread, nullptr);
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
thread_deleted_cond.wait(lock, [this] { return threads.empty(); });
|
||||
}
|
||||
|
||||
void KernelState::pause_threads() {
|
||||
@@ -265,10 +217,7 @@ void KernelState::resume_threads() {
|
||||
}
|
||||
|
||||
void KernelState::deinit(MemState &mem) {
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(host_threads_mutex);
|
||||
assert(host_threads.empty());
|
||||
}
|
||||
process_exit();
|
||||
threads.clear();
|
||||
|
||||
simple_events.clear();
|
||||
@@ -323,8 +272,6 @@ void KernelState::deinit(MemState &mem) {
|
||||
|
||||
next_uid = 1;
|
||||
|
||||
shutting_down.store(false, std::memory_order_relaxed);
|
||||
|
||||
paused_threads_status.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -81,17 +81,6 @@ inline static int handle_timeout(KernelState &kernel, const ThreadStatePtr &thre
|
||||
std::unique_lock<std::mutex> &primitive_lock, WaitingThreadQueuePtr &queue,
|
||||
const ThreadDataQueueInterator<WaitingThreadData> &data_it, const char *export_name,
|
||||
SceUInt *const timeout) {
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
thread_lock.lock();
|
||||
if (thread->status == ThreadStatus::wait)
|
||||
thread->update_status(ThreadStatus::run);
|
||||
thread_lock.unlock();
|
||||
auto found = queue->find(thread);
|
||||
if (found != queue->end())
|
||||
queue->erase(found);
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
bool status = false;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
@@ -472,23 +461,21 @@ SceInt32 timer_waitorpoll(KernelState &kernel, const char *export_name, SceUID t
|
||||
const auto data_it = timer->waiting_threads->push(data);
|
||||
|
||||
bool got_event = false;
|
||||
while (!got_event && !kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
while (!got_event) {
|
||||
uint64_t wait_time = timer->next_event - current_time;
|
||||
// wait before we got an event and we are the first thread in the waiting list
|
||||
timer->condvar.wait_for(lock, std::chrono::microseconds(wait_time), [&] {
|
||||
return kernel.shutting_down.load(std::memory_order_relaxed)
|
||||
return thread->status == ThreadStatus::run
|
||||
|| (*timer->waiting_threads->begin()).thread->id == thread_id;
|
||||
});
|
||||
current_time = get_current_time();
|
||||
got_event = timer->event_set || current_time > timer->next_event;
|
||||
}
|
||||
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
if (thread->status == ThreadStatus::run) {
|
||||
timer->waiting_threads->erase(data_it);
|
||||
thread->update_status(ThreadStatus::run, ThreadStatus::wait);
|
||||
timer->condvar.notify_all();
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
current_time = get_current_time();
|
||||
got_event = timer->event_set || current_time > timer->next_event;
|
||||
}
|
||||
|
||||
timer->waiting_threads->pop();
|
||||
thread->update_status(ThreadStatus::run, ThreadStatus::wait);
|
||||
@@ -1766,12 +1753,8 @@ SceSize msgpipe_recv(KernelState &kernel, const char *export_name, SceUID thread
|
||||
// FIXME sleep on SimpleEvent
|
||||
msgpipe_lock.unlock(); // Unlock message pipe object, else we'll deadlock
|
||||
thread->status_cond.wait(thread_lock, [&] {
|
||||
return thread->status == ThreadStatus::run || kernel.shutting_down.load(std::memory_order_relaxed);
|
||||
return thread->status == ThreadStatus::run;
|
||||
});
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
thread->update_status(ThreadStatus::run);
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
if (msgpipe->beingDeleted) { // if beingDeleted then message pipe is locked, so we can't lock again
|
||||
std::atomic_fetch_add(&msgpipe->remainingThreads, static_cast<size_t>(-1));
|
||||
return SCE_KERNEL_ERROR_WAIT_DELETE;
|
||||
@@ -1784,12 +1767,8 @@ SceSize msgpipe_recv(KernelState &kernel, const char *export_name, SceUID thread
|
||||
} else { // There's a timeout - wait until we can fill buffer or timeout
|
||||
msgpipe_lock.unlock(); // Unlock message pipe object, else we'll deadlock
|
||||
auto status = thread->status_cond.wait_for(thread_lock, std::chrono::microseconds{ *pTimeout }, [&] {
|
||||
return thread->status == ThreadStatus::run || kernel.shutting_down.load(std::memory_order_relaxed);
|
||||
return thread->status == ThreadStatus::run;
|
||||
});
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
thread->update_status(ThreadStatus::run);
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
if (msgpipe->beingDeleted) {
|
||||
std::atomic_fetch_add(&msgpipe->remainingThreads, static_cast<size_t>(-1));
|
||||
return SCE_KERNEL_ERROR_WAIT_DELETE;
|
||||
@@ -1881,12 +1860,8 @@ SceSize msgpipe_send(KernelState &kernel, const char *export_name, SceUID thread
|
||||
// FIXME sleep on SimpleEvent
|
||||
msgpipe_lock.unlock(); // Unlock message pipe object, else we'll deadlock
|
||||
thread->status_cond.wait(thread_lock, [&] {
|
||||
return thread->status == ThreadStatus::run || kernel.shutting_down.load(std::memory_order_relaxed);
|
||||
return thread->status == ThreadStatus::run;
|
||||
});
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
thread->update_status(ThreadStatus::run);
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
if (msgpipe->beingDeleted) { // if beingDeleted then message pipe is locked, so we can't lock again
|
||||
std::atomic_fetch_add(&msgpipe->remainingThreads, static_cast<size_t>(-1));
|
||||
return SCE_KERNEL_ERROR_WAIT_DELETE;
|
||||
@@ -1900,12 +1875,8 @@ SceSize msgpipe_send(KernelState &kernel, const char *export_name, SceUID thread
|
||||
} else { // There's a timeout - wait until we can fill buffer or timeout
|
||||
msgpipe_lock.unlock(); // Unlock message pipe object, else we'll deadlock
|
||||
auto status = thread->status_cond.wait_for(thread_lock, std::chrono::microseconds{ *pTimeout }, [&] {
|
||||
return thread->status == ThreadStatus::run || kernel.shutting_down.load(std::memory_order_relaxed);
|
||||
return thread->status == ThreadStatus::run;
|
||||
});
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed)) {
|
||||
thread->update_status(ThreadStatus::run);
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
if (msgpipe->beingDeleted) {
|
||||
std::atomic_fetch_add(&msgpipe->remainingThreads, static_cast<size_t>(-1));
|
||||
return SCE_KERNEL_ERROR_WAIT_DELETE;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
void ThreadSignal::wait() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
recv_cond.wait(lock, [&]() { return signaled || (shutting_down && shutting_down->load(std::memory_order_relaxed)); });
|
||||
recv_cond.wait(lock, [&]() { return signaled; });
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
@@ -183,8 +183,7 @@ void ThreadState::exit_delete(bool exit) {
|
||||
stop(*cpu);
|
||||
}
|
||||
|
||||
// Wake if thread waiting on status_cond
|
||||
if (status == ThreadStatus::wait)
|
||||
// Thread may enter a sync primitive wait after this runs, so always notify
|
||||
update_status(ThreadStatus::run);
|
||||
|
||||
// Wake if thread waiting on sceKernelWaitSignal
|
||||
@@ -426,13 +425,10 @@ uint32_t ThreadState::run_guest_function(Address callback_address, SceSize args,
|
||||
{
|
||||
// wait for the function to return
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (status != ThreadStatus::dormant || to_do == ThreadToDo::run) {
|
||||
status_cond.wait(lock, [&]() {
|
||||
return kernel.shutting_down.load(std::memory_order_relaxed)
|
||||
|| (status == ThreadStatus::dormant && to_do != ThreadToDo::run);
|
||||
return status == ThreadStatus::dormant && to_do != ThreadToDo::run;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entry_point = old_entry_point;
|
||||
return returned_value;
|
||||
@@ -442,13 +438,16 @@ ThreadState::ThreadState(SceUID id, KernelState &kernel, MemState &mem)
|
||||
: id(id)
|
||||
, kernel(kernel)
|
||||
, mem(mem) {
|
||||
signal.shutting_down = &kernel.shutting_down;
|
||||
}
|
||||
|
||||
void ThreadState::update_status(ThreadStatus status, std::optional<ThreadStatus> expected) {
|
||||
if (expected)
|
||||
assert(expected.value() == this->status);
|
||||
|
||||
// Keep status as run after removal so handle_timeout's predicate is immediately satisfied
|
||||
if (status == ThreadStatus::wait && to_do == ThreadToDo::remove)
|
||||
return;
|
||||
|
||||
this->status = status;
|
||||
status_cond.notify_all();
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "SceAppMgr.h"
|
||||
|
||||
#include <app/functions.h>
|
||||
#include <io/state.h>
|
||||
#include <kernel/state.h>
|
||||
#include <packages/sfo.h>
|
||||
@@ -436,9 +435,7 @@ EXPORT(SceInt32, _sceAppMgrLoadExec, const char *appPath, Ptr<char> const argv[]
|
||||
return RET_ERROR(SCE_APPMGR_ERROR_TOO_LONG_ARGV);
|
||||
}
|
||||
|
||||
emuenv.kernel.exit_delete_all_threads_and_wait();
|
||||
|
||||
app::request_in_process_launch(emuenv, { .app_path = emuenv.io.app_path, .self_path = std::move(exec_path), .argv = std::move(exec_argv), .reason = AppLaunchReason::LoadExec });
|
||||
emuenv.kernel.request_process_exit(0, AppLaunchRequest{ .app_path = emuenv.io.app_path, .self_path = std::move(exec_path), .argv = std::move(exec_argv), .reason = AppLaunchReason::LoadExec });
|
||||
|
||||
return SCE_KERNEL_OK;
|
||||
}
|
||||
|
||||
@@ -849,10 +849,8 @@ static int wait_thread_end(KernelState &kernel, ThreadStatePtr &waiter, ThreadSt
|
||||
target->waiting_threads.push_back(waiter);
|
||||
}
|
||||
waiter->status_cond.wait(waiter_lock, [&]() {
|
||||
return waiter->status == ThreadStatus::run || kernel.shutting_down.load(std::memory_order_relaxed);
|
||||
return waiter->status == ThreadStatus::run;
|
||||
});
|
||||
if (kernel.shutting_down.load(std::memory_order_relaxed))
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1052,16 +1050,17 @@ EXPORT(int, sceKernelCreateThreadForUser, const char *name, SceKernelThreadEntry
|
||||
return thread->id;
|
||||
}
|
||||
|
||||
static int delay_thread(KernelState &kernel, SceUInt delay_us) {
|
||||
static int delay_thread(KernelState &kernel, SceUID thread_id, SceUInt delay_us) {
|
||||
if (delay_us == 0)
|
||||
return SCE_KERNEL_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
std::unique_lock<std::mutex> lock(kernel.shutdown_mutex);
|
||||
if (kernel.shutdown_condvar.wait_for(lock, std::chrono::microseconds(delay_us),
|
||||
[&] { return kernel.shutting_down.load(std::memory_order_relaxed); })) {
|
||||
return SCE_KERNEL_ERROR_WAIT_CANCEL;
|
||||
}
|
||||
|
||||
const ThreadStatePtr thread = kernel.get_thread(thread_id);
|
||||
std::unique_lock<std::mutex> lock(thread->mutex);
|
||||
thread->update_status(ThreadStatus::wait);
|
||||
thread->status_cond.wait_for(lock, std::chrono::microseconds(delay_us),
|
||||
[&] { return thread->status == ThreadStatus::run; });
|
||||
if (thread->status != ThreadStatus::run)
|
||||
thread->update_status(ThreadStatus::run);
|
||||
return SCE_KERNEL_OK;
|
||||
}
|
||||
|
||||
@@ -1072,21 +1071,21 @@ static int delay_thread_cb(EmuEnvState &emuenv, SceUID thread_id, SceUInt delay_
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
if (delay_us > elapsed.count()) // If we spent less time than requested processing callbacks, sleep the remaining time
|
||||
return delay_thread(emuenv.kernel, delay_us - elapsed.count());
|
||||
return delay_thread(emuenv.kernel, thread_id, delay_us - elapsed.count());
|
||||
else // Else return directly
|
||||
return SCE_KERNEL_OK;
|
||||
}
|
||||
|
||||
EXPORT(int, sceKernelDelayThread, SceUInt delay) {
|
||||
TRACY_FUNC(sceKernelDelayThread, delay);
|
||||
return delay_thread(emuenv.kernel, delay);
|
||||
return delay_thread(emuenv.kernel, thread_id, delay);
|
||||
}
|
||||
|
||||
EXPORT(int, sceKernelDelayThread200, SceUInt delay) {
|
||||
TRACY_FUNC(sceKernelDelayThread200, delay);
|
||||
if (delay < 201)
|
||||
delay = 201;
|
||||
return delay_thread(emuenv.kernel, delay);
|
||||
return delay_thread(emuenv.kernel, thread_id, delay);
|
||||
}
|
||||
|
||||
EXPORT(int, sceKernelDelayThreadCB, SceUInt delay) {
|
||||
|
||||
@@ -40,14 +40,12 @@ EXPORT(int, sceDbgAssertionHandler, const char *filename, int line, bool do_stop
|
||||
LOG_INFO("file {}, line {}, {}", filename, line, buffer.data());
|
||||
|
||||
if (do_stop)
|
||||
emuenv.kernel.exit_delete_all_threads_and_wait();
|
||||
emuenv.kernel.request_process_exit(0);
|
||||
|
||||
if (!result) {
|
||||
return SCE_KERNEL_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
assert(!do_stop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1290,9 +1290,7 @@ EXPORT(int, sceKernelDeleteLwMutex, Ptr<SceKernelLwMutexWork> workarea) {
|
||||
|
||||
EXPORT(int, sceKernelExitProcess, int res) {
|
||||
TRACY_FUNC(sceKernelExitProcess, res);
|
||||
// TODO Handle exit code?
|
||||
emuenv.kernel.exit_delete_all_threads_and_wait();
|
||||
|
||||
emuenv.kernel.request_process_exit(res);
|
||||
return SCE_KERNEL_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user