Skip to content
Permalink
Browse files
Merge pull request #9311 from JosJuice/config-get-fast-2
Add caching to Config::Info
  • Loading branch information
leoetlino committed Dec 13, 2020
2 parents d312495 + d8744e6 commit 3634508
Show file tree
Hide file tree
Showing 17 changed files with 187 additions and 82 deletions.
@@ -86,7 +86,7 @@ template <typename T>
static void Set(jint layer, const Config::Location& location, T value)
{
GetLayer(layer, location)->Set(location, value);
Config::InvokeConfigChangedCallbacks();
Config::OnConfigChanged();
}

#ifdef __cplusplus
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.

#include <algorithm>
#include <atomic>
#include <list>
#include <map>
#include <mutex>
@@ -17,6 +18,7 @@ using Layers = std::map<LayerType, std::shared_ptr<Layer>>;
static Layers s_layers;
static std::list<ConfigChangedCallback> s_callbacks;
static u32 s_callback_guards = 0;
static std::atomic<u64> s_config_version = 0;

static std::shared_mutex s_layers_rw_lock;

@@ -31,7 +33,7 @@ static void AddLayerInternal(std::shared_ptr<Layer> layer)
const Config::LayerType layer_type = layer->GetLayer();
s_layers.insert_or_assign(layer_type, std::move(layer));
}
InvokeConfigChangedCallbacks();
OnConfigChanged();
}

void AddLayer(std::unique_ptr<ConfigLayerLoader> loader)
@@ -59,23 +61,30 @@ void RemoveLayer(LayerType layer)

s_layers.erase(layer);
}
InvokeConfigChangedCallbacks();
OnConfigChanged();
}

void AddConfigChangedCallback(ConfigChangedCallback func)
{
s_callbacks.emplace_back(std::move(func));
}

void InvokeConfigChangedCallbacks()
void OnConfigChanged()
{
if (s_callback_guards)
return;

s_config_version.fetch_add(1, std::memory_order_relaxed);

for (const auto& callback : s_callbacks)
callback();
}

u64 GetConfigVersion()
{
return s_config_version.load(std::memory_order_relaxed);
}

// Explicit load and save of layers
void Load()
{
@@ -85,7 +94,7 @@ void Load()
for (auto& layer : s_layers)
layer.second->Load();
}
InvokeConfigChangedCallbacks();
OnConfigChanged();
}

void Save()
@@ -96,7 +105,7 @@ void Save()
for (auto& layer : s_layers)
layer.second->Save();
}
InvokeConfigChangedCallbacks();
OnConfigChanged();
}

void Init()
@@ -207,7 +216,7 @@ ConfigChangeCallbackGuard::~ConfigChangeCallbackGuard()
if (--s_callback_guards)
return;

InvokeConfigChangedCallbacks();
OnConfigChanged();
}

} // namespace Config
@@ -24,7 +24,10 @@ std::shared_ptr<Layer> GetLayer(LayerType layer);
void RemoveLayer(LayerType layer);

void AddConfigChangedCallback(ConfigChangedCallback func);
void InvokeConfigChangedCallbacks();
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();
@@ -52,11 +55,28 @@ T Get(LayerType layer, const Info<T>& info)
template <typename T>
T Get(const Info<T>& info)
{
const std::optional<std::string> str = GetAsString(info.location);
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.default_value;
return info.GetDefaultValue();

return detail::TryParse<T>(*str).value_or(info.default_value);
return detail::TryParse<T>(*str).value_or(info.GetDefaultValue());
}

template <typename T>
@@ -68,14 +88,14 @@ T GetBase(const Info<T>& info)
template <typename T>
LayerType GetActiveLayerForConfig(const Info<T>& info)
{
return GetActiveLayerForConfig(info.location);
return GetActiveLayerForConfig(info.GetLocation());
}

template <typename T>
void Set(LayerType layer, const Info<T>& info, const std::common_type_t<T>& value)
{
GetLayer(layer)->Set(info, value);
InvokeConfigChangedCallbacks();
OnConfigChanged();
}

template <typename T>
@@ -99,7 +119,7 @@ void SetBaseOrCurrent(const Info<T>& info, const std::common_type_t<T>& value)
Set<T>(LayerType::CurrentRun, info, value);
}

// Used to defer InvokeConfigChangedCallbacks until after the completion of many config changes.
// Used to defer OnConfigChanged until after the completion of many config changes.
class ConfigChangeCallbackGuard
{
public:
@@ -4,9 +4,13 @@

#pragma once

#include <mutex>
#include <shared_mutex>
#include <string>
#include <type_traits>
#include <utility>

#include "Common/CommonTypes.h"
#include "Common/Config/Enums.h"

namespace Config
@@ -30,24 +34,92 @@ struct Location
};

template <typename T>
struct Info
struct CachedValue
{
Info(const Location& location_, const T& default_value_)
: location{location_}, default_value{default_value_}
T value;
u64 config_version;
};

template <typename T>
class Info
{
public:
constexpr Info(const Location& location, const T& default_value)
: m_location{location}, m_default_value{default_value}, m_cached_value{default_value, 0}
{
}

Info(const Info<T>& other) { *this = other; }

// Not thread-safe
Info(Info<T>&& other) { *this = std::move(other); }

// Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>>
// so that enum settings can still easily work with code that doesn't care about the enum values.
template <typename Enum,
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
Info(const Info<Enum>& other)
: location{other.location}, default_value{static_cast<detail::UnderlyingType<Enum>>(
other.default_value)}
{
*this = other;
}

Info<T>& operator=(const Info<T>& other)
{
m_location = other.GetLocation();
m_default_value = other.GetDefaultValue();
m_cached_value = other.GetCachedValue();
return *this;
}

// Not thread-safe
Info<T>& operator=(Info<T>&& other)
{
m_location = std::move(other.m_location);
m_default_value = std::move(other.m_default_value);
m_cached_value = std::move(other.m_cached_value);
return *this;
}

// Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>>
// so that enum settings can still easily work with code that doesn't care about the enum values.
template <typename Enum,
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
Info<T>& operator=(const Info<Enum>& other)
{
m_location = other.GetLocation();
m_default_value = static_cast<T>(other.GetDefaultValue());
m_cached_value = other.template GetCachedValueCasted<T>();
return *this;
}

constexpr const Location& GetLocation() const { return m_location; }
constexpr const T& GetDefaultValue() const { return m_default_value; }

CachedValue<T> GetCachedValue() const
{
std::shared_lock lock(m_cached_value_mutex);
return m_cached_value;
}

Location location;
T default_value;
template <typename U>
CachedValue<U> GetCachedValueCasted() const
{
std::shared_lock lock(m_cached_value_mutex);
return CachedValue<U>{static_cast<U>(m_cached_value.value), m_cached_value.config_version};
}

void SetCachedValue(const CachedValue<T>& cached_value) const
{
std::unique_lock lock(m_cached_value_mutex);
if (m_cached_value.config_version < cached_value.config_version)
m_cached_value = cached_value;
}

private:
Location m_location;
T m_default_value;

mutable CachedValue<T> m_cached_value;
mutable std::shared_mutex m_cached_value_mutex;
};
} // namespace Config
@@ -45,7 +45,7 @@ inline std::optional<std::string> TryParse(const std::string& str_value)
} // namespace detail

template <typename T>
struct Info;
class Info;

class Layer;
using LayerMap = std::map<Location, std::optional<std::string>>;
@@ -105,7 +105,7 @@ class Layer
template <typename T>
T Get(const Info<T>& config_info) const
{
return Get<T>(config_info.location).value_or(config_info.default_value);
return Get<T>(config_info.GetLocation()).value_or(config_info.GetDefaultValue());
}

template <typename T>
@@ -120,7 +120,7 @@ class Layer
template <typename T>
void Set(const Info<T>& config_info, const std::common_type_t<T>& value)
{
Set(config_info.location, value);
Set(config_info.GetLocation(), value);
}

template <typename T>
@@ -472,11 +472,11 @@ static void RestoreSYSCONF()
for (const auto& setting : Config::SYSCONF_SETTINGS)
{
std::visit(
[&](auto& info) {
[&](auto* info) {
// If this setting was overridden, then we copy the base layer value back to the SYSCONF.
// Otherwise we leave the new value in the SYSCONF.
if (Config::GetActiveLayerForConfig(info) == Config::LayerType::Base)
Config::SetBase(info, temp_layer.Get(info));
if (Config::GetActiveLayerForConfig(*info) == Config::LayerType::Base)
Config::SetBase(*info, temp_layer.Get(*info));
},
setting.config_info);
}
@@ -24,15 +24,15 @@ const Info<u32> SYSCONF_SPEAKER_VOLUME{{System::SYSCONF, "BT", "SPKV"}, 0x58};
const Info<bool> SYSCONF_WIIMOTE_MOTOR{{System::SYSCONF, "BT", "MOT"}, true};

const std::array<SYSCONFSetting, 11> SYSCONF_SETTINGS{
{{SYSCONF_SCREENSAVER, SysConf::Entry::Type::Byte},
{SYSCONF_LANGUAGE, SysConf::Entry::Type::Byte},
{SYSCONF_COUNTRY, SysConf::Entry::Type::BigArray},
{SYSCONF_WIDESCREEN, SysConf::Entry::Type::Byte},
{SYSCONF_PROGRESSIVE_SCAN, SysConf::Entry::Type::Byte},
{SYSCONF_PAL60, SysConf::Entry::Type::Byte},
{SYSCONF_SOUND_MODE, SysConf::Entry::Type::Byte},
{SYSCONF_SENSOR_BAR_POSITION, SysConf::Entry::Type::Byte},
{SYSCONF_SENSOR_BAR_SENSITIVITY, SysConf::Entry::Type::Long},
{SYSCONF_SPEAKER_VOLUME, SysConf::Entry::Type::Byte},
{SYSCONF_WIIMOTE_MOTOR, SysConf::Entry::Type::Byte}}};
{{&SYSCONF_SCREENSAVER, SysConf::Entry::Type::Byte},
{&SYSCONF_LANGUAGE, SysConf::Entry::Type::Byte},
{&SYSCONF_COUNTRY, SysConf::Entry::Type::BigArray},
{&SYSCONF_WIDESCREEN, SysConf::Entry::Type::Byte},
{&SYSCONF_PROGRESSIVE_SCAN, SysConf::Entry::Type::Byte},
{&SYSCONF_PAL60, SysConf::Entry::Type::Byte},
{&SYSCONF_SOUND_MODE, SysConf::Entry::Type::Byte},
{&SYSCONF_SENSOR_BAR_POSITION, SysConf::Entry::Type::Byte},
{&SYSCONF_SENSOR_BAR_SENSITIVITY, SysConf::Entry::Type::Long},
{&SYSCONF_SPEAKER_VOLUME, SysConf::Entry::Type::Byte},
{&SYSCONF_WIIMOTE_MOTOR, SysConf::Entry::Type::Byte}}};
} // namespace Config
@@ -33,7 +33,7 @@ extern const Info<bool> SYSCONF_WIIMOTE_MOTOR;

struct SYSCONFSetting
{
std::variant<Info<u32>, Info<bool>> config_info;
std::variant<const Info<u32>*, const Info<bool>*> config_info;
SysConf::Entry::Type type;
};

0 comments on commit 3634508

Please sign in to comment.