mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-26 15:55:31 +01:00
71ce8bb6f0
In theory, our config system supports calling Set from any thread. But because we have config callbacks that call RunAsCPUThread, it's a lot more restricted in practice. Calling Set from any thread other than the host thread or the CPU thread is formally thread unsafe, and calling Set on the host thread while the CPU thread is showing a panic alert causes a deadlock. This is especially a problem because 04072f0 made the "Ignore for this session" button in panic alerts call Set. Because so many of our config callbacks want their code to run on the CPU thread, I thought it would make sense to have a centralized way to move execution to the CPU thread for config callbacks. To solve the deadlock problem, this new way is non-blocking. This means that threads other than the CPU thread might continue executing before the CPU thread is informed of the new config, but I don't think there's any problem with that. Intends to fix https://bugs.dolphin-emu.org/issues/13108.
142 lines
3.5 KiB
C++
142 lines
3.5 KiB
C++
// Copyright 2016 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
#include "Common/Config/ConfigInfo.h"
|
|
#include "Common/Config/Enums.h"
|
|
#include "Common/Config/Layer.h"
|
|
|
|
namespace Config
|
|
{
|
|
using ConfigChangedCallback = std::function<void()>;
|
|
|
|
// Layer management
|
|
void AddLayer(std::unique_ptr<ConfigLayerLoader> loader);
|
|
std::shared_ptr<Layer> GetLayer(LayerType layer);
|
|
void RemoveLayer(LayerType layer);
|
|
|
|
// Returns an ID that can be passed to RemoveConfigChangedCallback().
|
|
// The callback may be called from any thread.
|
|
size_t AddConfigChangedCallback(ConfigChangedCallback func);
|
|
void RemoveConfigChangedCallback(size_t callback_id);
|
|
void OnConfigChanged();
|
|
|
|
// Returns the number of times the config has changed in the current execution of the program
|
|
u64 GetConfigVersion();
|
|
|
|
// Explicit load and save of layers
|
|
void Load();
|
|
void Save();
|
|
|
|
void Init();
|
|
void Shutdown();
|
|
void ClearCurrentRunLayer();
|
|
|
|
const std::string& GetSystemName(System system);
|
|
std::optional<System> GetSystemFromName(const std::string& system);
|
|
const std::string& GetLayerName(LayerType layer);
|
|
LayerType GetActiveLayerForConfig(const Location&);
|
|
|
|
std::optional<std::string> GetAsString(const Location&);
|
|
|
|
template <typename T>
|
|
T Get(LayerType layer, const Info<T>& info)
|
|
{
|
|
if (layer == LayerType::Meta)
|
|
return Get(info);
|
|
return GetLayer(layer)->Get(info);
|
|
}
|
|
|
|
template <typename T>
|
|
T Get(const Info<T>& info)
|
|
{
|
|
CachedValue<T> cached = info.GetCachedValue();
|
|
const u64 config_version = GetConfigVersion();
|
|
|
|
if (cached.config_version < config_version)
|
|
{
|
|
cached.value = GetUncached(info);
|
|
cached.config_version = config_version;
|
|
|
|
info.SetCachedValue(cached);
|
|
}
|
|
|
|
return cached.value;
|
|
}
|
|
|
|
template <typename T>
|
|
T GetUncached(const Info<T>& info)
|
|
{
|
|
const std::optional<std::string> str = GetAsString(info.GetLocation());
|
|
if (!str)
|
|
return info.GetDefaultValue();
|
|
|
|
return detail::TryParse<T>(*str).value_or(info.GetDefaultValue());
|
|
}
|
|
|
|
template <typename T>
|
|
T GetBase(const Info<T>& info)
|
|
{
|
|
return Get(LayerType::Base, info);
|
|
}
|
|
|
|
template <typename T>
|
|
LayerType GetActiveLayerForConfig(const Info<T>& info)
|
|
{
|
|
return GetActiveLayerForConfig(info.GetLocation());
|
|
}
|
|
|
|
template <typename T>
|
|
void Set(LayerType layer, const Info<T>& info, const std::common_type_t<T>& value)
|
|
{
|
|
if (GetLayer(layer)->Set(info, value))
|
|
OnConfigChanged();
|
|
}
|
|
|
|
template <typename T>
|
|
void SetBase(const Info<T>& info, const std::common_type_t<T>& value)
|
|
{
|
|
Set<T>(LayerType::Base, info, value);
|
|
}
|
|
|
|
template <typename T>
|
|
void SetCurrent(const Info<T>& info, const std::common_type_t<T>& value)
|
|
{
|
|
Set<T>(LayerType::CurrentRun, info, value);
|
|
}
|
|
|
|
template <typename T>
|
|
void SetBaseOrCurrent(const Info<T>& info, const std::common_type_t<T>& value)
|
|
{
|
|
if (GetActiveLayerForConfig(info) == LayerType::Base)
|
|
Set<T>(LayerType::Base, info, value);
|
|
else
|
|
Set<T>(LayerType::CurrentRun, info, value);
|
|
}
|
|
|
|
template <typename T>
|
|
void DeleteKey(LayerType layer, const Info<T>& info)
|
|
{
|
|
if (GetLayer(layer)->DeleteKey(info.GetLocation()))
|
|
OnConfigChanged();
|
|
}
|
|
|
|
// Used to defer OnConfigChanged until after the completion of many config changes.
|
|
class ConfigChangeCallbackGuard
|
|
{
|
|
public:
|
|
ConfigChangeCallbackGuard();
|
|
~ConfigChangeCallbackGuard();
|
|
|
|
ConfigChangeCallbackGuard(const ConfigChangeCallbackGuard&) = delete;
|
|
ConfigChangeCallbackGuard& operator=(const ConfigChangeCallbackGuard&) = delete;
|
|
};
|
|
} // namespace Config
|