-
Notifications
You must be signed in to change notification settings - Fork 352
[lldb] LRUCache for Swift type system mangling/demangling #9191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: stable/20230725
Are you sure you want to change the base?
Changes from all commits
553cd0e
e7a1989
9754562
363361c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| //===-- LRUCache.h ----------------------------------------------*- C++ -*-===// | ||
| // | ||
| // This source file is part of the Swift.org open source project | ||
| // | ||
| // Copyright (c) 2024 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef liblldb_LRUCache_h_ | ||
| #define liblldb_LRUCache_h_ | ||
|
|
||
| #include "lldb/Utility/ConstString.h" | ||
| #include "llvm/ADT/DenseMap.h" | ||
| #include "llvm/ADT/STLFunctionalExtras.h" | ||
| #include <list> | ||
| #include <mutex> | ||
|
|
||
| namespace lldb_private { | ||
| namespace swift_demangle { | ||
|
|
||
| /// A thread-safe Least Recently Used (LRU) cache implementation. | ||
| /// | ||
| /// This class template implements an LRU cache with a fixed capacity, which | ||
| /// supports insertion, retrieval, and a factory-based retrieval mechanism. It | ||
| /// ensures thread safety through the use of a mutex lock. Keys are always | ||
| /// `ConstString` | ||
| template <typename Value> | ||
| class LRUCache { | ||
| public: | ||
| /// Constructs an instance with the specified capacity. | ||
| LRUCache(size_t capacity) : m_capacity(capacity) {} | ||
|
|
||
| /// Retrieves a value from the cache for the given key. | ||
| /// If the key is found in the cache, the corresponding value is returned and | ||
| /// the element is moved to the front of the LRU list, indicating it was | ||
| /// recently used. If the key is not found, std::nullopt is returned | ||
| std::optional<Value> Get(ConstString key) { | ||
| std::lock_guard lock{m_mutex}; | ||
| auto map_it = m_map.find(key); | ||
| if (map_it == m_map.end()) | ||
| return std::nullopt; | ||
| const auto &map_value = map_it->second; | ||
| MoveListElementToFront(map_value.list_it); | ||
| return map_value.value; | ||
| } | ||
|
|
||
| /// Inserts a key-value pair into the cache. | ||
| /// If the key already exists in the cache, its value is updated and the | ||
| /// element is moved to the front of the LRU list. If the key does not exist, | ||
| /// it is inserted into the cache. If the cache is at full capacity, the least | ||
| /// recently used element is evicted to make space for the new element. | ||
| void Put(ConstString key, const Value &value) { | ||
| if (m_capacity == 0) | ||
| return; | ||
| std::lock_guard lock{m_mutex}; | ||
| auto map_it = m_map.find(key); | ||
| if (map_it != m_map.end()) { | ||
| auto &map_value = map_it->second; | ||
| map_value.value = value; | ||
| MoveListElementToFront(map_value.list_it); | ||
| } else { | ||
| InsertElement(key, value); | ||
| } | ||
| } | ||
|
|
||
| /// Retrieves a value from the cache or creates it if not present. | ||
| /// If the key is found in the cache, the corresponding value is returned and | ||
| /// the element is moved to the front of the LRU list. If the key is not | ||
| /// found, the given factory function is called to create a new value, which | ||
| /// is then inserted into the cache before being returned. | ||
| Value GetOrCreate(ConstString key, llvm::function_ref<Value()> factory) { | ||
| if (m_capacity == 0) | ||
| return factory(); | ||
|
|
||
| { | ||
| std::lock_guard lock{m_mutex}; | ||
| if (auto map_it = m_map.find(key); map_it != m_map.end()) { | ||
| const auto &map_value = map_it->second; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just thought about this: it'd be nice if in debug mode we assert that the cached value and the one created from the factory are the same (sorry for giving you more work!)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. The
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| MoveListElementToFront(map_value.list_it); | ||
| return map_value.value; | ||
| } | ||
| } | ||
|
|
||
| // Call `factory` with `m_mutex` unlocked | ||
| const auto value = factory(); | ||
|
|
||
| { | ||
| std::lock_guard lock{m_mutex}; | ||
| InsertElement(key, value); | ||
| } | ||
|
|
||
| return value; | ||
| } | ||
|
|
||
| private: | ||
| using List = std::list<ConstString>; | ||
| struct MapValue { | ||
| List::iterator list_it; | ||
| Value value; | ||
| }; | ||
| using Map = llvm::DenseMap<ConstString, MapValue>; | ||
|
|
||
| void InsertElement(ConstString key, const Value &value) { | ||
| assert(m_capacity > 0); | ||
|
|
||
| if (m_list.size() >= m_capacity) { | ||
| // Evict the least recent element | ||
| auto key = m_list.back(); | ||
| m_list.pop_back(); | ||
| m_map.erase(key); | ||
| } | ||
|
|
||
| m_list.emplace_front(key); | ||
| std::pair<typename Map::iterator, bool> emplace_result = | ||
| m_map.try_emplace(key, MapValue{m_list.begin(), value}); | ||
| assert(emplace_result.second && "Element with this key already exists"); | ||
| } | ||
|
|
||
| void MoveListElementToFront(typename List::iterator it) { | ||
| m_list.splice(m_list.begin(), m_list, it); | ||
| } | ||
|
|
||
| size_t m_capacity; | ||
| List m_list; | ||
| Map m_map; | ||
| std::mutex m_mutex; | ||
| }; | ||
|
|
||
| } // namespace swift_demangle | ||
| } // namespace lldb_private | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| //===-- SwiftDemangle.cpp ---------------------------------------*- C++ -*-===// | ||
| // | ||
| // This source file is part of the Swift.org open source project | ||
| // | ||
| // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "SwiftDemangle.h" | ||
| #include "LRUCache.h" | ||
|
|
||
| namespace lldb_private { | ||
| namespace swift_demangle { | ||
|
|
||
| SharedDemangledNode GetCachedDemangledSymbol(ConstString name) { | ||
| static LRUCache<SharedDemangledNode> g_shared_cache{10}; | ||
| auto factory = [name] { | ||
| return SharedDemangledNode::FromMangledSymbol(name); | ||
| }; | ||
| #ifndef NDEBUG | ||
| auto validation_result = factory(); | ||
| #endif | ||
| auto result = g_shared_cache.GetOrCreate(name, factory); | ||
| #ifndef NDEBUG | ||
| assert(validation_result == result && | ||
| "Cached SharedDemangledNode isn't equal to a fresh one"); | ||
| #endif | ||
| return result; | ||
| } | ||
|
|
||
|
|
||
| } // namespace swift_demangle | ||
| } // namespace lldb_private |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add short doxygen comments for the class and the public methods?