I'm trying to implement a cache which must be (obviously) optimized for getting and recycling items.
There is one challenge though: The cache may be invalidated from time to time.
From an architecture perspective, what I did so far is to implement the cache comprising a pool. The pool itself is actually a lock-free object pool, well tested and working.
One problem is to allow the pool itself to be swapped with an updated version. I think I've got the swapping working (reference: of Lock-free swap of two unique_ptr<T>).
But how is it possible to handle lock-free get and set using a pool that can be swapped during operation (getPreparedDefaultRule() and recyclePreparedDefaultRule() below respectively)?
I'm almost at the point to assume it is not possible. Though I'd be glad to be proven wrong.
template <typename Data, typename Delegate>
class DefaultRulePoolCache {
public:
DefaultRulePoolCache() : _atomicDefaultRulePool(nullptr), _defaultRulePool(nullptr) {
}
std::unique_ptr<detail::PreparedRule<Data, Delegate>> getPreparedDefaultRule() {
auto atomicPool = _atomicDefaultRulePool.load();
if (!atomicPool) {
return nullptr;
}
return atomicPool->getPreparedRule();
}
void recyclePreparedDefaultRule(std::unique_ptr<detail::PreparedRulePool<Data, Delegate>> preparedRule) {
auto atomicPool = _atomicDefaultRulePool.load();
if (!atomicPool) {
return;
}
hdmCheck(preparedRule) << "Cannot recycle null rule.";
// we return the prepared rule only to the pool in case it's the current version of the default rul
if (atomicPool->getTag() == preparedRule->getTag()) {
atomicPool->recyclePreparedRule(std::move(preparedRule));
}
}
void setDefaultRulePool(std::shared_ptr<detail::PreparedRulePool<Data, Delegate>> defaultRulePool) {
std::lock_guard<std::mutex> lock(_defaultRulePoolMutex);
atomicSwapPool(defaultRulePool);
}
void removeDefaultRulePool() {
std::lock_guard<std::mutex> lock(_defaultRulePoolMutex);
atomicSwapPool(nullptr);
}
private:
std::atomic<detail::PreparedRulePool<Data, Delegate>*> _atomicDefaultRulePool;
std::shared_ptr<detail::PreparedRulePool<data::Location, KVOLocationValuesDelegate>> _defaultRulePool;
std::mutex _defaultRulePoolMutex;
void atomicSwapPool(std::shared_ptr<detail::PreparedRulePool<Data, Delegate>> defaultRulePool) {
std::atomic_exchange(&_atomicDefaultRulePool, defaultRulePool.get());
// destroy the old pool
_defaultRulePool = nullptr;
// asign the new pool
_defaultRulePool = defaultRulePool;
}
};