kern: KAutoObject destruction is now scheduled for next dpc-time

This commit is contained in:
Michael Scire 2021-04-07 13:38:51 -07:00 committed by SciresM
parent 15956fcf9a
commit 4407237f5b
7 changed files with 94 additions and 18 deletions

View file

@ -69,11 +69,12 @@ namespace ams::kern {
private: private:
MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); MESOSPHERE_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
private: private:
KAutoObject *m_next_closed_object;
std::atomic<u32> m_ref_count; std::atomic<u32> m_ref_count;
public: public:
static KAutoObject *Create(KAutoObject *ptr); static KAutoObject *Create(KAutoObject *ptr);
public: 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(); } virtual ~KAutoObject() { MESOSPHERE_ASSERT_THIS(); }
/* Destroy is responsible for destroying the auto object's resources when ref_count hits zero. */ /* 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(); MESOSPHERE_ASSERT_THIS();
/* Atomically increment the reference count, only if it's positive. */ /* Atomically increment the reference count, only if it's positive. */
@ -136,7 +137,7 @@ namespace ams::kern {
return true; return true;
} }
ALWAYS_INLINE void Close() { NOINLINE void Close() {
MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT_THIS();
/* Atomically decrement the reference count, not allowing it to become negative. */ /* 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); 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)); } 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) { 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; class KAutoObjectWithListContainer;
@ -198,7 +207,7 @@ namespace ams::kern {
} }
} }
~KScopedAutoObject() { ALWAYS_INLINE ~KScopedAutoObject() {
if (m_obj != nullptr) { if (m_obj != nullptr) {
m_obj->Close(); m_obj->Close();
} }

View file

@ -77,8 +77,9 @@ namespace ams::kern {
}; };
enum DpcFlag : u32 { enum DpcFlag : u32 {
DpcFlag_Terminating = (1 << 0), DpcFlag_Terminating = (1 << 0),
DpcFlag_Terminated = (1 << 1), DpcFlag_Terminated = (1 << 1),
DpcFlag_PerformDestruction = (1 << 2),
}; };
struct StackParameters { struct StackParameters {
@ -203,6 +204,7 @@ namespace ams::kern {
WaiterList m_pinned_waiter_list{}; WaiterList m_pinned_waiter_list{};
KThread *m_lock_owner{}; KThread *m_lock_owner{};
uintptr_t m_debug_params[3]{}; uintptr_t m_debug_params[3]{};
KAutoObject *m_closed_object{};
u32 m_address_key_value{}; u32 m_address_key_value{};
u32 m_suspend_request_flags{}; u32 m_suspend_request_flags{};
u32 m_suspend_allowed_flags{}; u32 m_suspend_allowed_flags{};
@ -324,15 +326,15 @@ namespace ams::kern {
} }
ALWAYS_INLINE void RegisterDpc(DpcFlag flag) { ALWAYS_INLINE void RegisterDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags |= flag; this->GetStackParameters().dpc_flags.fetch_or(flag);
} }
ALWAYS_INLINE void ClearDpc(DpcFlag flag) { ALWAYS_INLINE void ClearDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags &= ~flag; this->GetStackParameters().dpc_flags.fetch_and(~flag);;
} }
ALWAYS_INLINE u8 GetDpc() const { ALWAYS_INLINE u8 GetDpc() const {
return this->GetStackParameters().dpc_flags; return this->GetStackParameters().dpc_flags.load();
} }
ALWAYS_INLINE bool HasDpc() const { ALWAYS_INLINE bool HasDpc() const {
@ -491,6 +493,39 @@ namespace ams::kern {
void SetInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->interrupt_flag = 1; } void SetInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->interrupt_flag = 1; }
void ClearInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(m_tls_heap_address)->interrupt_flag = 0; } void ClearInterruptFlag() const { static_cast<ams::svc::ThreadLocalRegion *>(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 void SetDebugAttached() { m_debug_attached = true; }
constexpr bool IsAttachedToDebugger() const { return m_debug_attached; } constexpr bool IsAttachedToDebugger() const { return m_debug_attached; }
@ -603,4 +638,14 @@ namespace ams::kern {
return GetCurrentThread().GetCurrentCore(); 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);
}
} }

View file

@ -174,13 +174,20 @@ namespace ams::kern {
MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled()); MESOSPHERE_ASSERT(!KInterruptManager::AreInterruptsEnabled());
MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread()); MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread());
/* The only deferred procedure supported by Horizon is thread termination. */ /* Get reference to the current thread. */
/* Check if we need to terminate the current thread. */ KThread &cur_thread = GetCurrentThread();
KThread *cur_thread = GetCurrentThreadPointer();
if (cur_thread->IsTerminationRequested()) { /* Enable interrupts, temporarily. */
KScopedInterruptEnable ei; KScopedInterruptEnable ei;
cur_thread->Exit();
/* 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() { void KDpcManager::Sync() {

View file

@ -83,6 +83,9 @@ namespace ams::kern {
/* Do the task. */ /* Do the task. */
task->DoTask(); task->DoTask();
/* Destroy any objects we may need to close. */
m_thread->DestroyClosedObjects();
} }
} }

View file

@ -155,8 +155,9 @@ namespace ams::kern {
m_lock_owner = nullptr; m_lock_owner = nullptr;
m_num_core_migration_disables = 0; 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_num_kernel_waiters = 0;
m_closed_object = nullptr;
/* Set our current core id. */ /* Set our current core id. */
m_current_core_id = phys_core; m_current_core_id = phys_core;
@ -1157,6 +1158,9 @@ namespace ams::kern {
m_parent->DecrementRunningThreadCount(); m_parent->DecrementRunningThreadCount();
} }
/* Destroy any dependent objects. */
this->DestroyClosedObjects();
/* Perform termination. */ /* Perform termination. */
{ {
KScopedSchedulerLock sl; KScopedSchedulerLock sl;

View file

@ -68,6 +68,9 @@ namespace ams::kern {
/* Do the task. */ /* Do the task. */
task->DoWorkerTask(); task->DoWorkerTask();
/* Destroy any objects we may need to close. */
m_thread->DestroyClosedObjects();
} }
} }

View file

@ -74,12 +74,17 @@ namespace ams::kern::svc {
/* Wait for a message. */ /* Wait for a message. */
while (true) { while (true) {
/* Close any pending objects before we wait. */
GetCurrentThread().DestroyClosedObjects();
/* Wait for an object. */
s32 index; s32 index;
Result result = KSynchronizationObject::Wait(std::addressof(index), objs, num_objects, timeout); Result result = KSynchronizationObject::Wait(std::addressof(index), objs, num_objects, timeout);
if (svc::ResultTimedOut::Includes(result)) { if (svc::ResultTimedOut::Includes(result)) {
return result; return result;
} }
/* Receive the request. */
if (R_SUCCEEDED(result)) { if (R_SUCCEEDED(result)) {
KServerSession *session = objs[index]->DynamicCast<KServerSession *>(); KServerSession *session = objs[index]->DynamicCast<KServerSession *>();
if (session != nullptr) { if (session != nullptr) {