Browse Source
[nce] Remove LRU-Cache to fix unhandled invalidated branches on Skyline mods on SSBU
[nce] Remove LRU-Cache to fix unhandled invalidated branches on Skyline mods on SSBU
Signed-off-by: lizzie <lizzie@eden-emu.dev>pull/3500/head
3 changed files with 2 additions and 232 deletions
@ -1,187 +0,0 @@ |
|||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later |
|
||||
|
|
||||
#pragma once |
|
||||
#include <list> |
|
||||
#include <optional> |
|
||||
#include <shared_mutex> |
|
||||
#include <ankerl/unordered_dense.h> |
|
||||
#include <utility> |
|
||||
|
|
||||
#include "common/logging/log.h" |
|
||||
|
|
||||
template <typename KeyType, typename ValueType> |
|
||||
class LRUCache { |
|
||||
public: |
|
||||
using key_type = KeyType; |
|
||||
using value_type = ValueType; |
|
||||
using size_type = std::size_t; |
|
||||
|
|
||||
struct Statistics { |
|
||||
size_type hits = 0; |
|
||||
size_type misses = 0; |
|
||||
void reset() noexcept { hits = misses = 0; } |
|
||||
}; |
|
||||
|
|
||||
explicit LRUCache(size_type capacity, bool enabled = true) |
|
||||
: enabled_{enabled}, capacity_{capacity} { |
|
||||
cache_map_.reserve(capacity_); |
|
||||
LOG_WARNING(Core, "LRU Cache initialised (state: {} | capacity: {})", enabled_ ? "enabled" : "disabled", capacity_); |
|
||||
} |
|
||||
|
|
||||
// Non-movable copy semantics |
|
||||
LRUCache(const LRUCache&) = delete; |
|
||||
LRUCache& operator=(const LRUCache&) = delete; |
|
||||
LRUCache(LRUCache&& other) noexcept { *this = std::move(other); } |
|
||||
LRUCache& operator=(LRUCache&& other) noexcept { |
|
||||
if (this == &other) return *this; |
|
||||
std::unique_lock this_lock(mutex_, std::defer_lock); |
|
||||
std::unique_lock other_lock(other.mutex_, std::defer_lock); |
|
||||
std::lock(this_lock, other_lock); |
|
||||
enabled_ = other.enabled_; |
|
||||
capacity_ = other.capacity_; |
|
||||
cache_list_ = std::move(other.cache_list_); |
|
||||
cache_map_ = std::move(other.cache_map_); |
|
||||
stats_ = other.stats_; |
|
||||
return *this; |
|
||||
} |
|
||||
~LRUCache() = default; |
|
||||
|
|
||||
[[nodiscard]] value_type* get(const key_type& key) { |
|
||||
if (!enabled_) [[unlikely]] return nullptr; |
|
||||
std::unique_lock lock(mutex_); |
|
||||
auto it = cache_map_.find(key); |
|
||||
if (it == cache_map_.end()) { |
|
||||
++stats_.misses; |
|
||||
return nullptr; |
|
||||
} |
|
||||
move_to_front(it); |
|
||||
++stats_.hits; |
|
||||
return &it->second.second; |
|
||||
} |
|
||||
|
|
||||
[[nodiscard]] value_type* peek(const key_type& key) const { |
|
||||
if (!enabled_) [[unlikely]] return nullptr; |
|
||||
std::shared_lock lock(mutex_); |
|
||||
auto it = cache_map_.find(key); |
|
||||
return it == cache_map_.end() ? nullptr : &it->second.second; |
|
||||
} |
|
||||
|
|
||||
template <typename V> |
|
||||
void put(const key_type& key, V&& value) { |
|
||||
if (!enabled_) [[unlikely]] return; |
|
||||
std::unique_lock lock(mutex_); |
|
||||
insert_or_update(key, std::forward<V>(value)); |
|
||||
} |
|
||||
|
|
||||
template <typename ValueFactory> |
|
||||
value_type& get_or_emplace(const key_type& key, ValueFactory&& factory) { |
|
||||
std::unique_lock lock(mutex_); |
|
||||
auto it = cache_map_.find(key); |
|
||||
if (it != cache_map_.end()) { |
|
||||
move_to_front(it); |
|
||||
return it->second.second; |
|
||||
} |
|
||||
value_type new_value = factory(); |
|
||||
insert_or_update(key, std::move(new_value)); |
|
||||
return cache_map_.find(key)->second.second; |
|
||||
} |
|
||||
|
|
||||
[[nodiscard]] bool contains(const key_type& key) const { |
|
||||
if (!enabled_) return false; |
|
||||
std::shared_lock lock(mutex_); |
|
||||
return cache_map_.find(key) != cache_map_.end(); |
|
||||
} |
|
||||
|
|
||||
bool erase(const key_type& key) { |
|
||||
if (!enabled_) return false; |
|
||||
std::unique_lock lock(mutex_); |
|
||||
auto it = cache_map_.find(key); |
|
||||
if (it == cache_map_.end()) return false; |
|
||||
cache_list_.erase(it->second.first); |
|
||||
cache_map_.erase(it); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
void clear() { |
|
||||
std::unique_lock lock(mutex_); |
|
||||
cache_list_.clear(); |
|
||||
cache_map_.clear(); |
|
||||
stats_.reset(); |
|
||||
} |
|
||||
|
|
||||
[[nodiscard]] size_type size() const { |
|
||||
if (!enabled_) return 0; |
|
||||
std::shared_lock lock(mutex_); |
|
||||
return cache_map_.size(); |
|
||||
} |
|
||||
|
|
||||
[[nodiscard]] size_type get_capacity() const { return capacity_; } |
|
||||
|
|
||||
void resize(size_type new_capacity) { |
|
||||
if (!enabled_) return; |
|
||||
std::unique_lock lock(mutex_); |
|
||||
capacity_ = new_capacity; |
|
||||
shrink_if_needed(); |
|
||||
cache_map_.reserve(capacity_); |
|
||||
} |
|
||||
|
|
||||
void setEnabled(bool state) { |
|
||||
std::unique_lock lock(mutex_); |
|
||||
enabled_ = state; |
|
||||
LOG_WARNING(Core, "LRU Cache state changed to: {}", state ? "enabled" : "disabled"); |
|
||||
if (!enabled_) clear(); |
|
||||
} |
|
||||
|
|
||||
[[nodiscard]] bool isEnabled() const { return enabled_; } |
|
||||
|
|
||||
[[nodiscard]] Statistics stats() const { |
|
||||
std::shared_lock lock(mutex_); |
|
||||
return stats_; |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
using list_type = std::list<key_type>; |
|
||||
using list_iterator = typename list_type::iterator; |
|
||||
using map_value_type = std::pair<list_iterator, value_type>; |
|
||||
using map_type = ankerl::unordered_dense::map<key_type, map_value_type>; |
|
||||
|
|
||||
template <typename V> |
|
||||
void insert_or_update(const key_type& key, V&& value) { |
|
||||
auto it = cache_map_.find(key); |
|
||||
if (it != cache_map_.end()) { |
|
||||
it->second.second = std::forward<V>(value); |
|
||||
move_to_front(it); |
|
||||
return; |
|
||||
} |
|
||||
// evict LRU if full |
|
||||
if (cache_map_.size() >= capacity_) { |
|
||||
const auto& lru_key = cache_list_.back(); |
|
||||
cache_map_.erase(lru_key); |
|
||||
cache_list_.pop_back(); |
|
||||
} |
|
||||
cache_list_.push_front(key); |
|
||||
cache_map_[key] = {cache_list_.begin(), std::forward<V>(value)}; |
|
||||
} |
|
||||
|
|
||||
void move_to_front(typename map_type::iterator it) { |
|
||||
cache_list_.splice(cache_list_.begin(), cache_list_, it->second.first); |
|
||||
it->second.first = cache_list_.begin(); |
|
||||
} |
|
||||
|
|
||||
void shrink_if_needed() { |
|
||||
while (cache_map_.size() > capacity_) { |
|
||||
const auto& lru_key = cache_list_.back(); |
|
||||
cache_map_.erase(lru_key); |
|
||||
cache_list_.pop_back(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private: |
|
||||
mutable std::shared_mutex mutex_; |
|
||||
bool enabled_{true}; |
|
||||
size_type capacity_; |
|
||||
list_type cache_list_; |
|
||||
map_type cache_map_; |
|
||||
mutable Statistics stats_; |
|
||||
}; |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue