/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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, see . */ #include #include "pm_process_tracker.hpp" #include "pm_process_info.hpp" #include "pm_spec.hpp" namespace ams::pm::impl { namespace { /* Global process event. */ constinit os::SystemEventType g_process_event; } void ProcessTracker::Initialize(void *stack, size_t stack_size) { /* Initialize our events. */ os::InitializeEvent(std::addressof(m_request_event), false, os::EventClearMode_AutoClear); os::InitializeEvent(std::addressof(m_reply_event), false, os::EventClearMode_AutoClear); /* Our process count should initially be zero. */ m_process_count = 0; /* Create the process tracking thread. */ R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ProcessTracker::ThreadFunction, this, stack, stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack))); os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack)); } void ProcessTracker::StartThread() { /* Start thread. */ os::StartThread(std::addressof(m_thread)); } void ProcessTracker::ThreadBody() { /* This is the main loop of the process tracking thread. */ /* Setup multi wait/holders. */ os::MultiWaitType process_multi_wait; os::MultiWaitHolderType enqueue_event_holder; os::InitializeMultiWait(std::addressof(process_multi_wait)); os::InitializeMultiWaitHolder(std::addressof(enqueue_event_holder), std::addressof(m_request_event)); os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(enqueue_event_holder)); while (true) { auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait)); if (signaled_holder == std::addressof(enqueue_event_holder)) { /* TryWait will clear signaled, preventing duplicate notifications. */ if (os::TryWaitEvent(std::addressof(m_request_event))) { /* Link the process to our multi-wait. */ os::LinkMultiWaitHolder(std::addressof(process_multi_wait), m_queued_process_info->GetMultiWaitHolder()); m_queued_process_info = nullptr; /* Increment our process count. */ ++m_process_count; /* Reply. */ os::SignalEvent(std::addressof(m_reply_event)); } } else { /* Some process was signaled. */ this->OnProcessSignaled(reinterpret_cast(os::GetMultiWaitHolderUserData(signaled_holder))); } } } void ProcessTracker::QueueEntry(ProcessInfo *process_info) { /* Lock ourselves. */ std::scoped_lock lk(m_mutex); /* Request to enqueue the process info. */ m_queued_process_info = process_info; os::SignalEvent(std::addressof(m_request_event)); /* Wait for acknowledgement. */ os::WaitEvent(std::addressof(m_reply_event)); } void ProcessTracker::OnProcessSignaled(ProcessInfo *process_info) { /* Get the process list. */ auto list = GetProcessList(); /* Reset the process's signal. */ svc::ResetSignal(process_info->GetHandle()); /* Update the process's state. */ const svc::ProcessState old_state = process_info->GetState(); { s64 tmp = 0; R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState)); process_info->SetState(static_cast(tmp)); } const svc::ProcessState new_state = process_info->GetState(); /* If we're transitioning away from crashed, clear waiting attached. */ if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) { process_info->ClearExceptionWaitingAttach(); } switch (new_state) { case svc::ProcessState_Created: case svc::ProcessState_CreatedAttached: case svc::ProcessState_Terminating: break; case svc::ProcessState_Running: if (process_info->ShouldSignalOnDebugEvent()) { process_info->ClearSuspended(); process_info->SetSuspendedStateChanged(); os::SignalSystemEvent(std::addressof(g_process_event)); } else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) { process_info->SetStartedStateChanged(); process_info->ClearSignalOnStart(); os::SignalSystemEvent(std::addressof(g_process_event)); } process_info->ClearUnhandledException(); break; case svc::ProcessState_Crashed: if (!process_info->HasUnhandledException()) { process_info->SetExceptionOccurred(); os::SignalSystemEvent(std::addressof(g_process_event)); } process_info->SetExceptionWaitingAttach(); break; case svc::ProcessState_RunningAttached: if (process_info->ShouldSignalOnDebugEvent()) { process_info->ClearSuspended(); process_info->SetSuspendedStateChanged(); os::SignalSystemEvent(std::addressof(g_process_event)); } process_info->ClearUnhandledException(); break; case svc::ProcessState_Terminated: /* Unlink from multi wait. */ os::UnlinkMultiWaitHolder(process_info->GetMultiWaitHolder()); /* Free process resources. */ process_info->Cleanup(); if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) { os::SignalSystemEvent(std::addressof(g_process_event)); } else { /* Handle the case where we need to keep the process alive some time longer. */ if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) { /* Remove from the living list. */ list->Remove(process_info); /* Add the process to the list of dead processes. */ { GetExitList()->push_back(*process_info); } /* Signal. */ os::SignalSystemEvent(std::addressof(g_process_event)); } else { /* Actually delete process. */ CleanupProcessInfo(list, process_info); } } break; case svc::ProcessState_DebugBreak: if (process_info->ShouldSignalOnDebugEvent()) { process_info->SetSuspended(); process_info->SetSuspendedStateChanged(); os::SignalSystemEvent(std::addressof(g_process_event)); } break; } } void CreateProcessEvent() { /* Create process event. */ R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true)); } Result GetProcessEventHandle(os::NativeHandle *out) { *out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event)); R_SUCCEED(); } }