mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-04 20:18:32 +01:00
194 lines
8.1 KiB
C++
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();
|
|
}
|
|
|
|
}
|