Atmosphere/stratosphere/pm/source/impl/pm_process_tracker.cpp

194 lines
8.1 KiB
C++

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<ProcessInfo *>(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<svc::ProcessState>(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();
}
}