nfx-lrucache 0.1.1
High-performance C++20 thread-safe LRU cache with sliding expiration
Loading...
Searching...
No Matches
LruCache.h
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 nfx
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
29
30#pragma once
31
32#include <chrono>
33#include <functional>
34#include <mutex>
35#include <optional>
36#include <unordered_map>
37
38namespace nfx::cache
39{
40 //=====================================================================
41 // LruCacheOptions struct
42 //=====================================================================
43
47 struct LruCacheOptions final
48 {
49 //----------------------------------------------
50 // Construction
51 //----------------------------------------------
52
60 std::size_t sizeLimit = 0,
61 std::chrono::milliseconds slidingExpiration = std::chrono::hours{ 1 },
62 std::chrono::milliseconds backgroundCleanupInterval = std::chrono::milliseconds{ 0 } );
63
64 //----------------------------------------------
65 // Accessors
66 //----------------------------------------------
67
73 [[nodiscard]] inline std::size_t sizeLimit() const;
74
80 [[nodiscard]] inline std::chrono::milliseconds slidingExpiration() const;
81
87 [[nodiscard]] inline std::chrono::milliseconds backgroundCleanupInterval() const;
88
89 private:
91 std::size_t m_sizeLimit{ 0 };
92
94 std::chrono::milliseconds m_slidingExpiration{ std::chrono::minutes{ 60 } };
95
96 /*
97 * Background cleanup design:
98 * - When enabled (interval > 0), cache tracks last cleanup time
99 * - During get/find operations, checks if cleanup interval has elapsed
100 * - If elapsed, performs incremental cleanup of expired entries
101 * - Amortizes cleanup cost across normal operations without requiring separate thread
102 * - Ideal for write-heavy scenarios with unique keys (logging, batch processing)
103 * - For very low-activity caches, still requires occasional manual cleanupExpired() calls
104 */
105 std::chrono::milliseconds m_backgroundCleanupInterval{ std::chrono::milliseconds{ 0 } };
106 };
107
108 //=====================================================================
109 // CacheEntry struct
110 //=====================================================================
111
113 struct CacheEntry final
114 {
116 std::chrono::steady_clock::time_point lastAccessed;
117
119 std::chrono::milliseconds slidingExpiration;
120
122 std::size_t size{ 1 };
123
125 CacheEntry* lruPrev{ nullptr };
126
128 CacheEntry* lruNext{ nullptr };
129
131 const void* keyPtr{ nullptr };
132
133 //----------------------------------------------
134 // Construction
135 //----------------------------------------------
136
141 inline CacheEntry( std::chrono::milliseconds expiration = std::chrono::hours( 1 ) );
142
143 //----------------------------------------------
144 // Expiration checking
145 //----------------------------------------------
146
152 [[nodiscard]] inline bool isExpired() const noexcept;
153
154 //----------------------------------------------
155 // Access management
156 //----------------------------------------------
157
162 void inline touch() noexcept;
163 };
164
165 //=====================================================================
166 // LruCache class
167 //=====================================================================
168
174 template <typename TKey, typename TValue>
175 class LruCache final
176 {
177 public:
178 //----------------------------------------------
179 // Type aliases
180 //----------------------------------------------
181
183 using FactoryFunction = std::function<TValue()>;
184
186 using ConfigFunction = std::function<void( CacheEntry& )>;
187
188 //----------------------------------------------
189 // Construction
190 //----------------------------------------------
191
196 inline explicit LruCache( const LruCacheOptions& options = {} );
197
198 //----------------------------------------------
199 // Copy and move operations
200 //----------------------------------------------
201
202 LruCache( const LruCache& ) = delete;
203 LruCache( LruCache&& ) = delete;
204
205 //----------------------------------------------
206 // Assignment operations
207 //----------------------------------------------
208
209 LruCache& operator=( const LruCache& ) = delete;
210 LruCache& operator=( LruCache&& ) = delete;
211
212 //----------------------------------------------
213 // Destruction
214 //----------------------------------------------
215
216 // Default destructor
217 ~LruCache() = default;
218
219 //----------------------------------------------
220 // Cache operations
221 //----------------------------------------------
222
230 inline TValue* get( const TKey& key, FactoryFunction factory, ConfigFunction configure = nullptr );
231
232 //----------------------------------------------
233 // Lookup operations
234 //----------------------------------------------
235
241 inline TValue* find( const TKey& key );
242
243 //----------------------------------------------
244 // Modification operations
245 //----------------------------------------------
246
252 inline bool remove( const TKey& key );
253
257 inline void clear();
258
263 inline std::size_t size() const;
264
265 //----------------------------------------------
266 // State inspection
267 //----------------------------------------------
268
273 inline bool isEmpty() const;
274
278 inline void cleanupExpired();
279
280 private:
281 //----------------------------------------------
282 // Background cleanup
283 //----------------------------------------------
284
290 static constexpr size_t MAX_CLEANUP_PER_CYCLE = 10;
291
296 inline void checkAndPerformBackgroundCleanup();
297
298 //----------------------------------------------
299 // Internal data structures
300 //----------------------------------------------
301
303 struct CachedItem
304 {
306 TValue value;
307
309 CacheEntry metadata;
310
312 CachedItem( TValue val, CacheEntry meta );
313 };
314
315 mutable std::mutex m_mutex;
316 std::unordered_map<TKey, CachedItem> m_cache;
317 LruCacheOptions m_options;
318
320 CacheEntry* m_lruHead;
321
323 CacheEntry* m_lruTail;
324
326 std::chrono::steady_clock::time_point m_lastCleanupTime;
327
328 //----------------------------------------------
329 // LRU list management
330 //----------------------------------------------
331
336 inline void addToLruHead( CacheEntry* entry ) noexcept;
337
342 inline void removeFromLru( CacheEntry* entry ) noexcept;
343
348 inline void moveToLruHead( CacheEntry* entry ) noexcept;
349
353 inline void evictLeastRecentlyUsed();
354 };
355} // namespace nfx::cache
356
357#include "nfx/detail/cache/LruCache.inl"
Configuration options for LruCache behavior.
Definition LruCache.h:48
std::size_t sizeLimit() const
Get the maximum number of cache entries allowed.
std::chrono::milliseconds slidingExpiration() const
Get the default sliding expiration time.
std::chrono::milliseconds backgroundCleanupInterval() const
Get the background cleanup interval.
LruCacheOptions(std::size_t sizeLimit=0, std::chrono::milliseconds slidingExpiration=std::chrono::hours{ 1 }, std::chrono::milliseconds backgroundCleanupInterval=std::chrono::milliseconds{ 0 })
Construct LruCacheOptions with specified parameters.
Cache entry metadata with intrusive LRU list support.
Definition LruCache.h:114
CacheEntry * lruPrev
Previous entry in the LRU doubly-linked list.
Definition LruCache.h:125
const void * keyPtr
Pointer to the key for this cache entry.
Definition LruCache.h:131
std::chrono::milliseconds slidingExpiration
Sliding expiration time for this specific entry.
Definition LruCache.h:119
void touch() noexcept
Update the last accessed timestamp to current time.
std::chrono::steady_clock::time_point lastAccessed
Timestamp of the last access to this cache entry.
Definition LruCache.h:116
CacheEntry(std::chrono::milliseconds expiration=std::chrono::hours(1))
Construct cache entry with specified expiration time.
std::size_t size
Size of this cache entry for memory accounting.
Definition LruCache.h:122
CacheEntry * lruNext
Next entry in the LRU doubly-linked list.
Definition LruCache.h:128
bool isExpired() const noexcept
Check if this cache entry has expired based on sliding expiration.
Thread-safe memory cache with size limits and expiration policies.
Definition LruCache.h:176
TValue * find(const TKey &key)
Find a cached value without creating it.
bool isEmpty() const
Check if cache is empty.
LruCache(const LruCacheOptions &options={})
Construct memory cache with specified options.
bool remove(const TKey &key)
Remove an entry from the cache.
std::function< void(CacheEntry &)> ConfigFunction
Function type for configuring cache entry metadata.
Definition LruCache.h:186
std::size_t size() const
Get current cache size.
void clear()
Clear all cache entries.
void cleanupExpired()
Manually trigger cleanup of expired entries.
std::function< TValue()> FactoryFunction
Function type for creating cache values when not found.
Definition LruCache.h:183
TValue * get(const TKey &key, FactoryFunction factory, ConfigFunction configure=nullptr)
Get a cache entry, creating it with factory function if not found.