diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index f798ca806..ad08ee659 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp @@ -69,11 +69,12 @@ namespace ams::kern { private: MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); private: + KAutoObject *m_next_closed_object; std::atomic m_ref_count; public: static KAutoObject *Create(KAutoObject *ptr); public: - constexpr ALWAYS_INLINE explicit KAutoObject() : m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); } + constexpr ALWAYS_INLINE explicit KAutoObject() : m_next_closed_object(nullptr), m_ref_count(0) { MESOSPHERE_ASSERT_THIS(); } virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); } /* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */ @@ -120,7 +121,7 @@ namespace ams::kern { } } - ALWAYS_INLINE bool Open() { + NOINLINE bool Open() { MESOSPHERE_ASSERT_THIS(); /* Atomically increment the reference count, only if it's positive. */ @@ -136,7 +137,7 @@ namespace ams::kern { return true; } - ALWAYS_INLINE void Close() { + NOINLINE void Close() { MESOSPHERE_ASSERT_THIS(); /* Atomically decrement the reference count, not allowing it to become negative. */ @@ -145,11 +146,19 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(cur_ref_count > 0); } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, std::memory_order_relaxed)); - /* If ref count hits zero, destroy the object. */ + /* If ref count hits zero, schedule the object for destruction. */ if (cur_ref_count - 1 == 0) { - this->Destroy(); + this->ScheduleDestruction(); } } + private: + /* NOTE: This has to be defined *after* KThread is defined. */ + /* Nintendo seems to handle this by defining Open/Close() in a cpp, but we'd like them to remain in headers. */ + /* Implementation for this will be inside kern_k_thread.hpp, so it can be ALWAYS_INLINE. */ + void ScheduleDestruction(); + public: + /* Getter, for KThread. */ + ALWAYS_INLINE KAutoObject *GetNextClosedObject() { return m_next_closed_object; } }; class KAutoObjectWithListContainer; @@ -198,7 +207,7 @@ namespace ams::kern { } } - ~KScopedAutoObject() { + ALWAYS_INLINE ~KScopedAutoObject() { if (m_obj != nullptr) { m_obj->Close(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index ff8dfadea..b8478f5b5 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -77,8 +77,9 @@ namespace ams::kern { }; enum DpcFlag : u32 { - DpcFlag_Terminating = (1 << 0), - DpcFlag_Terminated = (1 << 1), + DpcFlag_Terminating = (1 << 0), + DpcFlag_Terminated = (1 << 1), + DpcFlag_PerformDestruction = (1 << 2), }; struct StackParameters { @@ -203,6 +204,7 @@ namespace ams::kern { WaiterList m_pinned_waiter_list{}; KThread *m_lock_owner{}; uintptr_t m_debug_params[3]{}; + KAutoObject *m_closed_object{}; u32 m_address_key_value{}; u32 m_suspend_request_flags{}; u32 m_suspend_allowed_flags{}; @@ -324,15 +326,15 @@ namespace ams::kern { } ALWAYS_INLINE void RegisterDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags |= flag; + this->GetStackParameters().dpc_flags.fetch_or(flag); } ALWAYS_INLINE void ClearDpc(DpcFlag flag) { - this->GetStackParameters().dpc_flags &= ~flag; + this->GetStackParameters().dpc_flags.fetch_and(~flag);; } ALWAYS_INLINE u8 GetDpc() const { - return this->GetStackParameters().dpc_flags; + return this->GetStackParameters().dpc_flags.load(); } ALWAYS_INLINE bool HasDpc() const { @@ -491,6 +493,39 @@ namespace ams::kern { void SetInterruptFlag() const { static_cast(m_tls_heap_address)->interrupt_flag = 1; } void ClearInterruptFlag() const { static_cast(m_tls_heap_address)->interrupt_flag = 0; } + ALWAYS_INLINE KAutoObject *GetClosedObject() { return m_closed_object; } + + ALWAYS_INLINE void SetClosedObject(KAutoObject *object) { + MESOSPHERE_ASSERT(object != nullptr); + + /* Set the object to destroy. */ + m_closed_object = object; + + /* Schedule destruction DPC. */ + if ((this->GetStackParameters().dpc_flags.load(std::memory_order_relaxed) & DpcFlag_PerformDestruction) == 0) { + this->RegisterDpc(DpcFlag_PerformDestruction); + } + } + + ALWAYS_INLINE void DestroyClosedObjects() { + /* Destroy all objects that have been closed. */ + if (KAutoObject *cur = m_closed_object; cur != nullptr) { + do { + /* Set our closed object as the next to close. */ + m_closed_object = cur->GetNextClosedObject(); + + /* Destroy the current object. */ + cur->Destroy(); + + /* Advance. */ + cur = m_closed_object; + } while (cur != nullptr); + + /* Clear the pending DPC. */ + this->ClearDpc(DpcFlag_PerformDestruction); + } + } + constexpr void SetDebugAttached() { m_debug_attached = true; } constexpr bool IsAttachedToDebugger() const { return m_debug_attached; } @@ -603,4 +638,14 @@ namespace ams::kern { return GetCurrentThread().GetCurrentCore(); } + ALWAYS_INLINE void KAutoObject::ScheduleDestruction() { + MESOSPHERE_ASSERT_THIS(); + + /* Set our object to destroy. */ + m_next_closed_object = GetCurrentThread().GetClosedObject(); + + /* Set ourselves as the thread's next object to destroy. */ + GetCurrentThread().SetClosedObject(this); + } + } diff --git a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp index 62ae50a6e..a55c30a2b 100644 --- a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp @@ -174,13 +174,20 @@ namespace ams::kern { MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled()); MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread()); - /* The only deferred procedure supported by Horizon is thread termination. */ - /* Check if we need to terminate the current thread. */ - KThread *cur_thread = GetCurrentThreadPointer(); - if (cur_thread->IsTerminationRequested()) { - KScopedInterruptEnable ei; - cur_thread->Exit(); + /* Get reference to the current thread. */ + KThread &cur_thread = GetCurrentThread(); + + /* Enable interrupts, temporarily. */ + KScopedInterruptEnable ei; + + /* If the thread is scheduled for termination, exit the thread. */ + if (cur_thread.IsTerminationRequested()) { + cur_thread.Exit(); + __builtin_unreachable(); } + + /* We may also need to destroy any closed objects. */ + cur_thread.DestroyClosedObjects(); } void KDpcManager::Sync() { diff --git a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp index 4c318ca88..1fe3a6492 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_task_manager.cpp @@ -83,6 +83,9 @@ namespace ams::kern { /* Do the task. */ task->DoTask(); + + /* Destroy any objects we may need to close. */ + m_thread->DestroyClosedObjects(); } } diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index 972e4220d..fbc565eaa 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -155,8 +155,9 @@ namespace ams::kern { m_lock_owner = nullptr; m_num_core_migration_disables = 0; - /* We have no waiters, but we do have an entrypoint. */ + /* We have no waiters, and no closed objects. */ m_num_kernel_waiters = 0; + m_closed_object = nullptr; /* Set our current core id. */ m_current_core_id = phys_core; @@ -1157,6 +1158,9 @@ namespace ams::kern { m_parent->DecrementRunningThreadCount(); } + /* Destroy any dependent objects. */ + this->DestroyClosedObjects(); + /* Perform termination. */ { KScopedSchedulerLock sl; diff --git a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp index f2ddc9953..f525acd28 100644 --- a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp @@ -68,6 +68,9 @@ namespace ams::kern { /* Do the task. */ task->DoWorkerTask(); + + /* Destroy any objects we may need to close. */ + m_thread->DestroyClosedObjects(); } } diff --git a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp index 7454237bc..673dfe702 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_ipc.cpp @@ -74,12 +74,17 @@ namespace ams::kern::svc { /* Wait for a message. */ while (true) { + /* Close any pending objects before we wait. */ + GetCurrentThread().DestroyClosedObjects(); + + /* Wait for an object. */ s32 index; Result result = KSynchronizationObject::Wait(std::addressof(index), objs, num_objects, timeout); if (svc::ResultTimedOut::Includes(result)) { return result; } + /* Receive the request. */ if (R_SUCCEEDED(result)) { KServerSession *session = objs[index]->DynamicCast(); if (session != nullptr) {