/* * Copyright (c) 2018-2020 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 "htcs_manager.hpp" #include "htcs_manager_impl.hpp" namespace ams::htcs::impl { HtcsMonitor::HtcsMonitor(mem::StandardAllocator *allocator, htc::server::driver::IDriver *drv, htc::server::rpc::RpcClient *rc, HtcsService *srv) : m_allocator(allocator), m_driver(drv), m_rpc_client(rc), m_service(srv), m_monitor_thread_stack(m_allocator->Allocate(os::MemoryPageSize, os::ThreadStackAlignment)), m_mutex(), m_cancel_event(os::EventClearMode_ManualClear), m_service_availability_event(os::EventClearMode_ManualClear), m_cancelled(false), m_is_service_available(false) { /* ... */ } HtcsMonitor::~HtcsMonitor() { /* Free thread stack. */ m_allocator->Free(m_monitor_thread_stack); } void HtcsMonitor::Start() { /* Create the monitor thread. */ R_ABORT_UNLESS(os::CreateThread(std::addressof(m_monitor_thread), ThreadEntry, this, m_monitor_thread_stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtcsMonitor))); /* Set thread name. */ os::SetThreadNamePointer(std::addressof(m_monitor_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtcsMonitor)); /* Start the monitor thread. */ os::StartThread(std::addressof(m_monitor_thread)); } void HtcsMonitor::Cancel() { /* Cancel, and signal. */ m_cancelled = true; m_cancel_event.Signal(); } void HtcsMonitor::Wait() { /* Wait for the thread. */ os::WaitThread(std::addressof(m_monitor_thread)); os::DestroyThread(std::addressof(m_monitor_thread)); } void HtcsMonitor::ThreadBody() { /* Loop so long as we're not cancelled. */ while (!m_cancelled) { /* Open the rpc client. */ m_rpc_client->Open(); /* Ensure we close, if something goes wrong. */ auto client_guard = SCOPE_GUARD { m_rpc_client->Close(); }; /* Wait for the rpc server. */ if (m_rpc_client->WaitAny(htclow::ChannelState_Connectable, m_cancel_event.GetBase()) != 0) { break; } /* Start the rpc client. */ const Result start_result = m_rpc_client->Start(); if (R_FAILED(start_result)) { /* DEBUG */ R_ABORT_UNLESS(start_result); continue; } /* We're available! */ this->SetServiceAvailability(true); client_guard.Cancel(); /* We're available, so we want to cleanup when we're done. */ ON_SCOPE_EXIT { m_rpc_client->Close(); m_rpc_client->Cancel(); m_rpc_client->Wait(); this->SetServiceAvailability(false); }; /* Wait to become disconnected. */ if (m_rpc_client->WaitAny(htclow::ChannelState_Disconnected, m_cancel_event.GetBase()) != 0) { break; } } } }