From cb4eecde529f54ec8c62f9b2137888716c09ba27 Mon Sep 17 00:00:00 2001 From: Silent Date: Tue, 30 Jul 2019 16:40:52 +0200 Subject: [PATCH] Fix race conditions in Config Layers API has been made stricter, layers are now managed with shared pointers, so using them temporarily increased their reference counters. Additionally, any s_layers map has been guarded by a read/write lock, as concurrent write/reads to it were possible. --- Source/Core/Common/Config/Config.cpp | 96 ++++++++++++++++++++-------- Source/Core/Common/Config/Config.h | 6 +- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/Source/Core/Common/Config/Config.cpp b/Source/Core/Common/Config/Config.cpp index 122d2d6ff1..ba7f5b9007 100644 --- a/Source/Core/Common/Config/Config.cpp +++ b/Source/Core/Common/Config/Config.cpp @@ -5,51 +5,78 @@ #include #include #include +#if __APPLE__ +#include +#else +#include +#endif #include "Common/Config/Config.h" namespace Config { +using Layers = std::map>; + static Layers s_layers; static std::list s_callbacks; static u32 s_callback_guards = 0; -Layers* GetLayers() -{ - return &s_layers; -} +// Mac supports shared_mutex since 10.12 and we're targeting 10.10, +// so only use unique locks there... +#if __APPLE__ +static std::mutex s_layers_rw_lock; -void AddLayer(std::unique_ptr layer) +using ReadLock = std::unique_lock; +using WriteLock = std::unique_lock; +#else +static std::shared_mutex s_layers_rw_lock; + +using ReadLock = std::shared_lock; +using WriteLock = std::unique_lock; +#endif + +void AddLayerInternal(std::shared_ptr layer) { - s_layers[layer->GetLayer()] = std::move(layer); + { + WriteLock lock(s_layers_rw_lock); + + const Config::LayerType layer_type = layer->GetLayer(); + s_layers.insert_or_assign(layer_type, std::move(layer)); + } InvokeConfigChangedCallbacks(); } void AddLayer(std::unique_ptr loader) { - AddLayer(std::make_unique(std::move(loader))); + AddLayerInternal(std::make_shared(std::move(loader))); } -Layer* GetLayer(LayerType layer) +std::shared_ptr GetLayer(LayerType layer) { - if (!LayerExists(layer)) - return nullptr; - return s_layers[layer].get(); + ReadLock lock(s_layers_rw_lock); + + std::shared_ptr result; + const auto it = s_layers.find(layer); + if (it != s_layers.end()) + { + result = it->second; + } + return result; } void RemoveLayer(LayerType layer) { - s_layers.erase(layer); + { + WriteLock lock(s_layers_rw_lock); + + s_layers.erase(layer); + } InvokeConfigChangedCallbacks(); } -bool LayerExists(LayerType layer) -{ - return s_layers.find(layer) != s_layers.end(); -} void AddConfigChangedCallback(ConfigChangedCallback func) { - s_callbacks.emplace_back(func); + s_callbacks.emplace_back(std::move(func)); } void InvokeConfigChangedCallbacks() @@ -64,15 +91,23 @@ void InvokeConfigChangedCallbacks() // Explicit load and save of layers void Load() { - for (auto& layer : s_layers) - layer.second->Load(); + { + ReadLock lock(s_layers_rw_lock); + + for (auto& layer : s_layers) + layer.second->Load(); + } InvokeConfigChangedCallbacks(); } void Save() { - for (auto& layer : s_layers) - layer.second->Save(); + { + ReadLock lock(s_layers_rw_lock); + + for (auto& layer : s_layers) + layer.second->Save(); + } InvokeConfigChangedCallbacks(); } @@ -84,13 +119,17 @@ void Init() void Shutdown() { + WriteLock lock(s_layers_rw_lock); + s_layers.clear(); s_callbacks.clear(); } void ClearCurrentRunLayer() { - s_layers[LayerType::CurrentRun] = std::make_unique(LayerType::CurrentRun); + WriteLock lock(s_layers_rw_lock); + + s_layers.insert_or_assign(LayerType::CurrentRun, std::make_shared(LayerType::CurrentRun)); } static const std::map system_to_name = { @@ -129,13 +168,16 @@ const std::string& GetLayerName(LayerType layer) LayerType GetActiveLayerForConfig(const ConfigLocation& config) { + ReadLock lock(s_layers_rw_lock); + for (auto layer : SEARCH_ORDER) { - if (!LayerExists(layer)) - continue; - - if (GetLayer(layer)->Exists(config)) - return layer; + const auto it = s_layers.find(layer); + if (it != s_layers.end()) + { + if (it->second->Exists(config)) + return layer; + } } // If config is not present in any layer, base layer is considered active. diff --git a/Source/Core/Common/Config/Config.h b/Source/Core/Common/Config/Config.h index 4120d029ba..2d69e0809e 100644 --- a/Source/Core/Common/Config/Config.h +++ b/Source/Core/Common/Config/Config.h @@ -16,16 +16,12 @@ namespace Config { -using Layers = std::map>; using ConfigChangedCallback = std::function; // Layer management -Layers* GetLayers(); -void AddLayer(std::unique_ptr layer); void AddLayer(std::unique_ptr loader); -Layer* GetLayer(LayerType layer); +std::shared_ptr GetLayer(LayerType layer); void RemoveLayer(LayerType layer); -bool LayerExists(LayerType layer); void AddConfigChangedCallback(ConfigChangedCallback func); void InvokeConfigChangedCallbacks();