diff options
| author | 2020-03-08 12:51:24 -0400 | |
|---|---|---|
| committer | 2020-06-27 11:35:40 -0400 | |
| commit | 6515c6e8c699584528486341579cf3a8dde3eea4 (patch) | |
| tree | 1663248cbd02cc0db108ed18feef5a994e44d63d /src/core/hle/kernel | |
| parent | Scheduler: Correct yields. (diff) | |
| download | yuzu-6515c6e8c699584528486341579cf3a8dde3eea4.tar.gz yuzu-6515c6e8c699584528486341579cf3a8dde3eea4.tar.xz yuzu-6515c6e8c699584528486341579cf3a8dde3eea4.zip | |
Kernel: Fixes, corrections and asserts to scheduler and different svcs.
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 27 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.h | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 27 | ||||
| -rw-r--r-- | src/core/hle/kernel/synchronization.cpp | 10 | ||||
| -rw-r--r-- | src/core/hle/kernel/time_manager.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/time_manager.h | 1 |
8 files changed, 38 insertions, 38 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 07acabc1d..e8f22b598 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -34,19 +34,9 @@ void AddressArbiter::WakeThreads(const std::vector<std::shared_ptr<Thread>>& wai | |||
| 34 | 34 | ||
| 35 | // Signal the waiting threads. | 35 | // Signal the waiting threads. |
| 36 | for (std::size_t i = 0; i < last; i++) { | 36 | for (std::size_t i = 0; i < last; i++) { |
| 37 | if (waiting_threads[i]->GetStatus() != ThreadStatus::WaitArb) { | ||
| 38 | last++; | ||
| 39 | last = std::min(waiting_threads.size(), last); | ||
| 40 | continue; | ||
| 41 | } | ||
| 42 | |||
| 43 | time_manager.CancelTimeEvent(waiting_threads[i].get()); | ||
| 44 | |||
| 45 | ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); | ||
| 46 | waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | 37 | waiting_threads[i]->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 47 | RemoveThread(waiting_threads[i]); | 38 | RemoveThread(waiting_threads[i]); |
| 48 | waiting_threads[i]->WaitForArbitration(false); | 39 | waiting_threads[i]->WaitForArbitration(false); |
| 49 | waiting_threads[i]->SetArbiterWaitAddress(0); | ||
| 50 | waiting_threads[i]->ResumeFromWait(); | 40 | waiting_threads[i]->ResumeFromWait(); |
| 51 | } | 41 | } |
| 52 | } | 42 | } |
| @@ -172,20 +162,25 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 | |||
| 172 | { | 162 | { |
| 173 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); | 163 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
| 174 | 164 | ||
| 165 | if (current_thread->IsPendingTermination()) { | ||
| 166 | lock.CancelSleep(); | ||
| 167 | return ERR_THREAD_TERMINATING; | ||
| 168 | } | ||
| 169 | |||
| 175 | // Ensure that we can read the address. | 170 | // Ensure that we can read the address. |
| 176 | if (!memory.IsValidVirtualAddress(address)) { | 171 | if (!memory.IsValidVirtualAddress(address)) { |
| 177 | lock.CancelSleep(); | 172 | lock.CancelSleep(); |
| 178 | return ERR_INVALID_ADDRESS_STATE; | 173 | return ERR_INVALID_ADDRESS_STATE; |
| 179 | } | 174 | } |
| 180 | 175 | ||
| 181 | /// TODO(Blinkhawk): Check termination pending. | ||
| 182 | |||
| 183 | s32 current_value = static_cast<s32>(memory.Read32(address)); | 176 | s32 current_value = static_cast<s32>(memory.Read32(address)); |
| 184 | if (current_value >= value) { | 177 | if (current_value >= value) { |
| 185 | lock.CancelSleep(); | 178 | lock.CancelSleep(); |
| 186 | return ERR_INVALID_STATE; | 179 | return ERR_INVALID_STATE; |
| 187 | } | 180 | } |
| 188 | 181 | ||
| 182 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 183 | |||
| 189 | s32 decrement_value; | 184 | s32 decrement_value; |
| 190 | 185 | ||
| 191 | const std::size_t current_core = system.CurrentCoreIndex(); | 186 | const std::size_t current_core = system.CurrentCoreIndex(); |
| @@ -207,7 +202,6 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 | |||
| 207 | return RESULT_TIMEOUT; | 202 | return RESULT_TIMEOUT; |
| 208 | } | 203 | } |
| 209 | 204 | ||
| 210 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | ||
| 211 | current_thread->SetArbiterWaitAddress(address); | 205 | current_thread->SetArbiterWaitAddress(address); |
| 212 | InsertThread(SharedFrom(current_thread)); | 206 | InsertThread(SharedFrom(current_thread)); |
| 213 | current_thread->SetStatus(ThreadStatus::WaitArb); | 207 | current_thread->SetStatus(ThreadStatus::WaitArb); |
| @@ -239,14 +233,17 @@ ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 t | |||
| 239 | { | 233 | { |
| 240 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); | 234 | SchedulerLockAndSleep lock(kernel, event_handle, current_thread, timeout); |
| 241 | 235 | ||
| 236 | if (current_thread->IsPendingTermination()) { | ||
| 237 | lock.CancelSleep(); | ||
| 238 | return ERR_THREAD_TERMINATING; | ||
| 239 | } | ||
| 240 | |||
| 242 | // Ensure that we can read the address. | 241 | // Ensure that we can read the address. |
| 243 | if (!memory.IsValidVirtualAddress(address)) { | 242 | if (!memory.IsValidVirtualAddress(address)) { |
| 244 | lock.CancelSleep(); | 243 | lock.CancelSleep(); |
| 245 | return ERR_INVALID_ADDRESS_STATE; | 244 | return ERR_INVALID_ADDRESS_STATE; |
| 246 | } | 245 | } |
| 247 | 246 | ||
| 248 | /// TODO(Blinkhawk): Check termination pending. | ||
| 249 | |||
| 250 | s32 current_value = static_cast<s32>(memory.Read32(address)); | 247 | s32 current_value = static_cast<s32>(memory.Read32(address)); |
| 251 | if (current_value != value) { | 248 | if (current_value != value) { |
| 252 | lock.CancelSleep(); | 249 | lock.CancelSleep(); |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ba051a7d8..721ab1e70 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -49,6 +49,7 @@ namespace Kernel { | |||
| 49 | * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time | 49 | * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time |
| 50 | */ | 50 | */ |
| 51 | static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | 51 | static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_late) { |
| 52 | UNREACHABLE(); | ||
| 52 | const auto proper_handle = static_cast<Handle>(thread_handle); | 53 | const auto proper_handle = static_cast<Handle>(thread_handle); |
| 53 | const auto& system = Core::System::GetInstance(); | 54 | const auto& system = Core::System::GetInstance(); |
| 54 | 55 | ||
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index ab17204bb..5322f0aae 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -133,6 +133,7 @@ u32 GlobalScheduler::SelectThreads() { | |||
| 133 | u32 cores_needing_context_switch{}; | 133 | u32 cores_needing_context_switch{}; |
| 134 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 134 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 135 | Scheduler& sched = kernel.Scheduler(core); | 135 | Scheduler& sched = kernel.Scheduler(core); |
| 136 | ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); | ||
| 136 | if (update_thread(top_threads[core], sched)) { | 137 | if (update_thread(top_threads[core], sched)) { |
| 137 | cores_needing_context_switch |= (1ul << core); | 138 | cores_needing_context_switch |= (1ul << core); |
| 138 | } | 139 | } |
| @@ -244,7 +245,7 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread | |||
| 244 | winner = yielding_thread; | 245 | winner = yielding_thread; |
| 245 | } | 246 | } |
| 246 | } else { | 247 | } else { |
| 247 | winner = scheduled_queue[i].front(); | 248 | winner = scheduled_queue[core_id].front(); |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | if (kernel.GetCurrentHostThreadID() != core_id) { | 251 | if (kernel.GetCurrentHostThreadID() != core_id) { |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 728cca802..5e062bf59 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 17 | 17 | ||
| 18 | namespace Common { | 18 | namespace Common { |
| 19 | class Fiber; | 19 | class Fiber; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | namespace Core { | 22 | namespace Core { |
| @@ -133,7 +133,8 @@ private: | |||
| 133 | /// and reschedules current core if needed. | 133 | /// and reschedules current core if needed. |
| 134 | void Unlock(); | 134 | void Unlock(); |
| 135 | 135 | ||
| 136 | void EnableInterruptAndSchedule(u32 cores_pending_reschedule, Core::EmuThreadHandle global_thread); | 136 | void EnableInterruptAndSchedule(u32 cores_pending_reschedule, |
| 137 | Core::EmuThreadHandle global_thread); | ||
| 137 | 138 | ||
| 138 | /** | 139 | /** |
| 139 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be | 140 | * Add a thread to the suggested queue of a cpu core. Suggested threads may be |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 371beed0d..aad2ac549 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1562,6 +1562,11 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add | |||
| 1562 | 1562 | ||
| 1563 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); | 1563 | current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); |
| 1564 | 1564 | ||
| 1565 | if (thread->IsPendingTermination()) { | ||
| 1566 | lock.CancelSleep(); | ||
| 1567 | return ERR_THREAD_TERMINATING; | ||
| 1568 | } | ||
| 1569 | |||
| 1565 | const auto release_result = current_process->GetMutex().Release(mutex_addr); | 1570 | const auto release_result = current_process->GetMutex().Release(mutex_addr); |
| 1566 | if (release_result.IsError()) { | 1571 | if (release_result.IsError()) { |
| 1567 | lock.CancelSleep(); | 1572 | lock.CancelSleep(); |
| @@ -1588,6 +1593,11 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add | |||
| 1588 | { | 1593 | { |
| 1589 | SchedulerLock lock(kernel); | 1594 | SchedulerLock lock(kernel); |
| 1590 | 1595 | ||
| 1596 | auto* owner = current_thread->GetLockOwner(); | ||
| 1597 | if (owner != nullptr) { | ||
| 1598 | owner->RemoveMutexWaiter(SharedFrom(current_thread)); | ||
| 1599 | } | ||
| 1600 | |||
| 1591 | current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); | 1601 | current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); |
| 1592 | } | 1602 | } |
| 1593 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | 1603 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| @@ -1618,19 +1628,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1618 | for (std::size_t index = 0; index < last; ++index) { | 1628 | for (std::size_t index = 0; index < last; ++index) { |
| 1619 | auto& thread = waiting_threads[index]; | 1629 | auto& thread = waiting_threads[index]; |
| 1620 | 1630 | ||
| 1621 | if (thread->GetStatus() != ThreadStatus::WaitCondVar) { | ||
| 1622 | last++; | ||
| 1623 | last = std::min(waiting_threads.size(), last); | ||
| 1624 | continue; | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | time_manager.CancelTimeEvent(thread.get()); | ||
| 1628 | |||
| 1629 | ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); | 1631 | ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); |
| 1630 | 1632 | ||
| 1631 | // liberate Cond Var Thread. | 1633 | // liberate Cond Var Thread. |
| 1632 | current_process->RemoveConditionVariableThread(thread); | 1634 | current_process->RemoveConditionVariableThread(thread); |
| 1633 | thread->SetCondVarWaitAddress(0); | ||
| 1634 | 1635 | ||
| 1635 | const std::size_t current_core = system.CurrentCoreIndex(); | 1636 | const std::size_t current_core = system.CurrentCoreIndex(); |
| 1636 | auto& monitor = system.Monitor(); | 1637 | auto& monitor = system.Monitor(); |
| @@ -1655,9 +1656,6 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1655 | monitor.ClearExclusive(); | 1656 | monitor.ClearExclusive(); |
| 1656 | if (mutex_val == 0) { | 1657 | if (mutex_val == 0) { |
| 1657 | // We were able to acquire the mutex, resume this thread. | 1658 | // We were able to acquire the mutex, resume this thread. |
| 1658 | ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); | ||
| 1659 | thread->ResumeFromWait(); | ||
| 1660 | |||
| 1661 | auto* const lock_owner = thread->GetLockOwner(); | 1659 | auto* const lock_owner = thread->GetLockOwner(); |
| 1662 | if (lock_owner != nullptr) { | 1660 | if (lock_owner != nullptr) { |
| 1663 | lock_owner->RemoveMutexWaiter(thread); | 1661 | lock_owner->RemoveMutexWaiter(thread); |
| @@ -1665,13 +1663,16 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_ | |||
| 1665 | 1663 | ||
| 1666 | thread->SetLockOwner(nullptr); | 1664 | thread->SetLockOwner(nullptr); |
| 1667 | thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); | 1665 | thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); |
| 1666 | thread->ResumeFromWait(); | ||
| 1668 | } else { | 1667 | } else { |
| 1669 | // The mutex is already owned by some other thread, make this thread wait on it. | 1668 | // The mutex is already owned by some other thread, make this thread wait on it. |
| 1670 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 1669 | const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 1671 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 1670 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 1672 | auto owner = handle_table.Get<Thread>(owner_handle); | 1671 | auto owner = handle_table.Get<Thread>(owner_handle); |
| 1673 | ASSERT(owner); | 1672 | ASSERT(owner); |
| 1674 | thread->SetStatus(ThreadStatus::WaitMutex); | 1673 | if (thread->GetStatus() == ThreadStatus::WaitCondVar) { |
| 1674 | thread->SetStatus(ThreadStatus::WaitMutex); | ||
| 1675 | } | ||
| 1675 | 1676 | ||
| 1676 | owner->AddMutexWaiter(thread); | 1677 | owner->AddMutexWaiter(thread); |
| 1677 | } | 1678 | } |
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp index 4323fc120..275bf11cc 100644 --- a/src/core/hle/kernel/synchronization.cpp +++ b/src/core/hle/kernel/synchronization.cpp | |||
| @@ -23,9 +23,10 @@ void Synchronization::SignalObject(SynchronizationObject& obj) const { | |||
| 23 | if (obj.IsSignaled()) { | 23 | if (obj.IsSignaled()) { |
| 24 | for (auto thread : obj.GetWaitingThreads()) { | 24 | for (auto thread : obj.GetWaitingThreads()) { |
| 25 | if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { | 25 | if (thread->GetSchedulingStatus() == ThreadSchedStatus::Paused) { |
| 26 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 27 | ASSERT(thread->IsWaitingSync()); | ||
| 26 | thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); | 28 | thread->SetSynchronizationResults(&obj, RESULT_SUCCESS); |
| 27 | thread->ResumeFromWait(); | 29 | thread->ResumeFromWait(); |
| 28 | time_manager.CancelTimeEvent(thread.get()); | ||
| 29 | } | 30 | } |
| 30 | } | 31 | } |
| 31 | obj.ClearWaitingThreads(); | 32 | obj.ClearWaitingThreads(); |
| @@ -91,10 +92,11 @@ std::pair<ResultCode, Handle> Synchronization::WaitFor( | |||
| 91 | ResultCode signaling_result = thread->GetSignalingResult(); | 92 | ResultCode signaling_result = thread->GetSignalingResult(); |
| 92 | SynchronizationObject* signaling_object = thread->GetSignalingObject(); | 93 | SynchronizationObject* signaling_object = thread->GetSignalingObject(); |
| 93 | thread->SetSynchronizationObjects(nullptr); | 94 | thread->SetSynchronizationObjects(nullptr); |
| 95 | auto shared_thread = SharedFrom(thread); | ||
| 94 | for (auto& obj : sync_objects) { | 96 | for (auto& obj : sync_objects) { |
| 95 | obj->RemoveWaitingThread(SharedFrom(thread)); | 97 | obj->RemoveWaitingThread(shared_thread); |
| 96 | } | 98 | } |
| 97 | if (signaling_result == RESULT_SUCCESS) { | 99 | if (signaling_object != nullptr) { |
| 98 | const auto itr = std::find_if( | 100 | const auto itr = std::find_if( |
| 99 | sync_objects.begin(), sync_objects.end(), | 101 | sync_objects.begin(), sync_objects.end(), |
| 100 | [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { | 102 | [signaling_object](const std::shared_ptr<SynchronizationObject>& object) { |
| @@ -103,7 +105,7 @@ std::pair<ResultCode, Handle> Synchronization::WaitFor( | |||
| 103 | ASSERT(itr != sync_objects.end()); | 105 | ASSERT(itr != sync_objects.end()); |
| 104 | signaling_object->Acquire(thread); | 106 | signaling_object->Acquire(thread); |
| 105 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | 107 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); |
| 106 | return {RESULT_SUCCESS, index}; | 108 | return {signaling_result, index}; |
| 107 | } | 109 | } |
| 108 | return {signaling_result, -1}; | 110 | return {signaling_result, -1}; |
| 109 | } | 111 | } |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index dab5fc4c6..cc228f5f7 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -22,7 +22,6 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { | |||
| 22 | if (cancelled_events[proper_handle]) { | 22 | if (cancelled_events[proper_handle]) { |
| 23 | return; | 23 | return; |
| 24 | } | 24 | } |
| 25 | event_fired[proper_handle] = true; | ||
| 26 | std::shared_ptr<Thread> thread = | 25 | std::shared_ptr<Thread> thread = |
| 27 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | 26 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 28 | thread->OnWakeUp(); | 27 | thread->OnWakeUp(); |
| @@ -39,7 +38,6 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 | |||
| 39 | event_handle = InvalidHandle; | 38 | event_handle = InvalidHandle; |
| 40 | } | 39 | } |
| 41 | cancelled_events[event_handle] = false; | 40 | cancelled_events[event_handle] = false; |
| 42 | event_fired[event_handle] = false; | ||
| 43 | } | 41 | } |
| 44 | 42 | ||
| 45 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | 43 | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { |
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index 3080ac838..307a18765 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h | |||
| @@ -42,7 +42,6 @@ private: | |||
| 42 | Core::System& system; | 42 | Core::System& system; |
| 43 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; | 43 | std::shared_ptr<Core::Timing::EventType> time_manager_event_type; |
| 44 | std::unordered_map<Handle, bool> cancelled_events; | 44 | std::unordered_map<Handle, bool> cancelled_events; |
| 45 | std::unordered_map<Handle, bool> event_fired; | ||
| 46 | }; | 45 | }; |
| 47 | 46 | ||
| 48 | } // namespace Kernel | 47 | } // namespace Kernel |