diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 0e7b5f9436..6bac6722f1 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -142,16 +142,24 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
 }
 
 void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
-                                 std::uintptr_t user_data) {
-    std::scoped_lock scope{basic_lock};
-    const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
-        return e.type.lock().get() == event_type.get() && e.user_data == user_data;
-    });
+                                 std::uintptr_t user_data, bool wait) {
+    {
+        std::scoped_lock lk{basic_lock};
+        const auto itr =
+            std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
+                return e.type.lock().get() == event_type.get() && e.user_data == user_data;
+            });
 
-    // Removing random items breaks the invariant so we have to re-establish it.
-    if (itr != event_queue.end()) {
-        event_queue.erase(itr, event_queue.end());
-        std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+        // Removing random items breaks the invariant so we have to re-establish it.
+        if (itr != event_queue.end()) {
+            event_queue.erase(itr, event_queue.end());
+            std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+        }
+    }
+
+    // Force any in-progress events to finish
+    if (wait) {
+        std::scoped_lock lk{advance_lock};
     }
 }
 
@@ -190,20 +198,6 @@ u64 CoreTiming::GetClockTicks() const {
     return CpuCyclesToClockCycles(ticks);
 }
 
-void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
-    std::scoped_lock lock{basic_lock};
-
-    const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
-        return e.type.lock().get() == event_type.get();
-    });
-
-    // Removing random items breaks the invariant so we have to re-establish it.
-    if (itr != event_queue.end()) {
-        event_queue.erase(itr, event_queue.end());
-        std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
-    }
-}
-
 std::optional<s64> CoreTiming::Advance() {
     std::scoped_lock lock{advance_lock, basic_lock};
     global_timer = GetGlobalTimeNs().count();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index b5925193c7..da366637be 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -98,10 +98,13 @@ public:
                               const std::shared_ptr<EventType>& event_type,
                               std::uintptr_t user_data = 0, bool absolute_time = false);
 
-    void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
+    void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data,
+                         bool wait = true);
 
-    /// We only permit one event of each type in the queue at a time.
-    void RemoveEvent(const std::shared_ptr<EventType>& event_type);
+    void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
+                                    std::uintptr_t user_data) {
+        UnscheduleEvent(event_type, user_data, false);
+    }
 
     void AddTicks(u64 ticks_to_add);
 
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 6bba79ea06..4dcd53821e 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -18,7 +18,8 @@ void KHardwareTimer::Initialize() {
 }
 
 void KHardwareTimer::Finalize() {
-    this->DisableInterrupt();
+    m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+    m_wakeup_time = std::numeric_limits<s64>::max();
     m_event_type.reset();
 }
 
@@ -59,7 +60,8 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
 }
 
 void KHardwareTimer::DisableInterrupt() {
-    m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+    m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type,
+                                                              reinterpret_cast<uintptr_t>(this));
     m_wakeup_time = std::numeric_limits<s64>::max();
 }