Atmosphere/libraries/libstratosphere/source/lm/srv/lm_flush_thread.cpp

182 lines
6.5 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 "lm_log_server_proxy.hpp"
#include "lm_sd_card_logger.hpp"
#include "lm_log_buffer.hpp"
#include "lm_event_log_transmitter.hpp"
namespace ams::lm::srv {
bool IsSleeping();
namespace {
alignas(os::ThreadStackAlignment) u8 g_flush_thread_stack[8_KB];
constinit u8 g_fs_heap[32_KB];
constinit lmem::HeapHandle g_fs_heap_handle;
constinit os::ThreadType g_flush_thread = {};
os::Event g_stop_event(os::EventClearMode_ManualClear);
os::Event g_sd_logging_event(os::EventClearMode_ManualClear);
os::Event g_host_connection_event(os::EventClearMode_ManualClear);
constinit std::unique_ptr<fs::IEventNotifier> g_sd_card_detection_event_notifier;
os::SystemEvent g_sd_card_detection_event;
void *AllocateForFs(size_t size) {
return lmem::AllocateFromExpHeap(g_fs_heap_handle, size);
}
void DeallocateForFs(void *ptr, size_t size) {
AMS_UNUSED(size);
return lmem::FreeToExpHeap(g_fs_heap_handle, ptr);
}
void HostConnectionObserver(bool is_connected) {
/* Update the host connection event. */
if (is_connected) {
g_host_connection_event.Signal();
} else {
g_host_connection_event.Clear();
/* Potentially cancel the log buffer push. */
if (!g_sd_logging_event.TryWait()) {
LogBuffer::GetDefaultInstance().CancelPush();
}
}
}
void SdLoggingObserver(bool is_available) {
/* Update the SD card logging event. */
if (is_available) {
g_sd_logging_event.Signal();
} else {
g_sd_logging_event.Clear();
/* Potentially cancel the log buffer push. */
if (!g_host_connection_event.TryWait()) {
LogBuffer::GetDefaultInstance().CancelPush();
}
}
}
bool WaitForFlush() {
while (true) {
/* Wait for something to be signaled. */
os::WaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase(), g_sd_card_detection_event.GetBase());
/* If we're stopping, no flush. */
if (g_stop_event.TryWait()) {
return false;
}
/* If host is connected/we're logging to sd, flush. */
if (g_host_connection_event.TryWait() || g_sd_logging_event.TryWait()) {
return true;
}
/* If the sd card is newly inserted, flush. */
if (g_sd_card_detection_event.TryWait()) {
g_sd_card_detection_event.Clear();
if (fs::IsSdCardInserted()) {
return true;
}
}
}
}
void FlushThreadFunction(void *) {
/* Initialize fs. */
fs::InitializeWithMultiSessionForSystem();
fs::SetEnabledAutoAbort(false);
/* Create fs heap. */
g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap, sizeof(g_fs_heap), lmem::CreateOption_None);
AMS_ABORT_UNLESS(g_fs_heap_handle != nullptr);
/* Set fs allocator functions. */
fs::SetAllocator(AllocateForFs, DeallocateForFs);
/* Create SD card detection event notifier. */
R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(g_sd_card_detection_event_notifier)));
R_ABORT_UNLESS(g_sd_card_detection_event_notifier->BindEvent(g_sd_card_detection_event.GetBase(), os::EventClearMode_ManualClear));
/* Set connection observers. */
SdCardLogger::GetInstance().SetLoggingObserver(SdLoggingObserver);
LogServerProxy::GetInstance().SetConnectionObserver(HostConnectionObserver);
/* Do flush loop. */
do {
if (LogBuffer::GetDefaultInstance().Flush()) {
EventLogTransmitter::GetDefaultInstance().PushLogPacketDropCountIfExists();
}
} while (WaitForFlush());
/* Clear connection observer. */
LogServerProxy::GetInstance().SetConnectionObserver(nullptr);
/* Finalize the SD card logger. */
SdCardLogger::GetInstance().Finalize();
SdCardLogger::GetInstance().SetLoggingObserver(nullptr);
/* Destroy the fs heap. */
lmem::DestroyExpHeap(g_fs_heap_handle);
}
}
bool IsFlushAvailable() {
/* If we're sleeping, we can't flush. */
if (IsSleeping()) {
return false;
}
/* Try to wait for an event. */
if (os::TryWaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase()) < 0) {
return false;
}
/* Return whether we're not stopping. */
return !os::TryWaitEvent(g_stop_event.GetBase());
}
void InitializeFlushThread() {
/* Create the flush thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_flush_thread), FlushThreadFunction, nullptr, g_flush_thread_stack, sizeof(g_flush_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, Flush)));
os::SetThreadNamePointer(std::addressof(g_flush_thread), AMS_GET_SYSTEM_THREAD_NAME(lm, Flush));
/* Clear the stop event. */
g_stop_event.Clear();
/* Start the flush thread. */
os::StartThread(std::addressof(g_flush_thread));
}
void FinalizeFlushThread() {
/* Signal the flush thread to stop. */
g_stop_event.Signal();
/* Wait for the flush thread to stop. */
os::WaitThread(std::addressof(g_flush_thread));
os::DestroyThread(std::addressof(g_flush_thread));
}
}