From 05181f6b88fd8ee671113bcafb81c65027f911a4 Mon Sep 17 00:00:00 2001 From: Scott Mansell Date: Mon, 13 Feb 2023 18:48:43 +1300 Subject: [PATCH] HookableEvent: Switch to construct on first use A registration might happen during static initialization, which opens us up to issues with ordering of static initialization. --- Source/Core/Common/HookableEvent.h | 43 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/Source/Core/Common/HookableEvent.h b/Source/Core/Common/HookableEvent.h index 8c87303725c7..82a9e57d55c3 100644 --- a/Source/Core/Common/HookableEvent.h +++ b/Source/Core/Common/HookableEvent.h @@ -67,36 +67,49 @@ class HookableEvent std::string m_name; }; + struct Storage + { + std::mutex m_mutex; + std::vector m_listeners; + }; + + // We use the "Construct On First Use" idiom to avoid the static initialization order fiasco. + // https://isocpp.org/wiki/faq/ctors#static-init-order + static Storage& GetStorage() + { + static Storage storage; + return storage; + } + + static void Remove(HookImpl* handle) + { + auto& storage = GetStorage(); + std::lock_guard lock(storage.m_mutex); + + std::erase(storage.m_listeners, handle); + } + public: // Returns a handle that will unregister the listener when destroyed. static EventHook Register(CallbackType callback, std::string name) { - std::lock_guard lock(m_mutex); + auto& storage = GetStorage(); + std::lock_guard lock(storage.m_mutex); DEBUG_LOG_FMT(COMMON, "Registering {} handler at {} event hook", name, EventName.value); auto handle = std::make_unique(callback, std::move(name)); - m_listeners.push_back(handle.get()); + storage.m_listeners.push_back(handle.get()); return handle; } static void Trigger(const CallbackArgs&... args) { - std::lock_guard lock(m_mutex); + auto& storage = GetStorage(); + std::lock_guard lock(storage.m_mutex); - for (const auto& handle : m_listeners) + for (const auto& handle : storage.m_listeners) handle->m_fn(args...); } - -private: - static void Remove(HookImpl* handle) - { - std::lock_guard lock(m_mutex); - - std::erase(m_listeners, handle); - } - - inline static std::vector m_listeners = {}; - inline static std::mutex m_mutex; }; } // namespace Common