Please review my code. I am trying to implement a thread-safe LRU cache using std::list
and std::unordered_map
. This is a header-only template class which you can use to keep an object cache.
The constructor takes a size as an input parameter and creates a cache of a specified size. Items will be removed based on the least recently used from the cache if the cache goes above the size.
#ifndef LRUCache_h__
#define LRUCache_h__
#pragma once
#include <vector>
#include <list>
#include <unordered_map>
#include <shared_mutex>
#include <iostream>
namespace objcache {
template <class T>
class LRUCache
{
struct _node {
int _key;
T _value;
};
// use map of key to iterator of LRU list
using ListIterator = typename std::list<_node>::const_iterator;
using CacheMap = std::unordered_map<int, ListIterator>;
size_t m_cacheSize;
std::list<_node> m_lruList;
CacheMap m_lruMap;
std::mutex m_lock; // reader writer lock, multiple getters are allowed
public:
/***
* Method: objcache::LRUCache::LRUCache
* Access: public
* Description: This creates a cache of given size.
*/
LRUCache(size_t size) :
m_cacheSize{ size },
m_lruList{},
m_lruMap{},
m_lock{}
{
//m_lruList.resize(m_cacheSize, 0);
m_lruMap.reserve(m_cacheSize);
}
/***
* Method: objcache::LRUCache::set
* Access: public
* Description : This API updates or sets the value val to key.
*/
void Set(int key, const T &val) noexcept
{
std::lock_guard<std::mutex> guard(m_lock);
if (auto mapIter = m_lruMap.find(key); mapIter != m_lruMap.end()) {
//key already exists, just update the queue
m_lruList.splice(m_lruList.begin(), m_lruList, mapIter->second);
}
else {
// new item in the cache.
m_lruList.push_front({ key, val });
m_lruMap.insert(std::make_pair(key, m_lruList.begin()));
}
//remove least recently used element if cache is full.
if (m_lruList.size() > m_cacheSize) {
m_lruMap.erase(m_lruList.back()._key);
m_lruList.pop_back();
}
}
/***
* Method: objcache::LRUCache::get
* Access: public
* Description: This API gets the value of key if it exists in cache,
* otherwise, just returns 0
*/
T Get(int key) noexcept
{
std::lock_guard<std::mutex> gaurd(m_lock);
T val = 0;
if (auto key_iter = m_lruMap.find(key); key_iter != m_lruMap.end()) {
m_lruList.splice(m_lruList.begin(), m_lruList, key_iter->second);
val = (key_iter->second)->_value;
}
return val;
}
/***
* Method: objcache::LRUCache::printCache
* Access: public
* Description : Prints the keys of the cache one-by-one
*/
void PrintCache() noexcept
{
std::lock_guard<std::mutex> guard(m_lock);
for (const auto &node : m_lruList)
std::cout << node._key << std::endl;
}
};
} // namespace objcache
#endif // LRUCache_h__