title: Lock-free cache of swappable object pools
body: <p>I'm trying to implement a cache which must be (obviously) optimized for getting and recycling items.</p>
  <p>There is one challenge though: The cache may be invalidated from time to time.</p>
  <p>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.</p>
  <p>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 <a href="https://stackoverflow.com/questions/15460829/lock-free-swap-of-two-unique-ptrt/15464924#15464924">Lock-free swap of two unique_ptr&lt;T&gt;</a>).</p>
  <p>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)?</p>
  <p>I'm almost at the point to assume it is not possible. Though I'd be glad to be proven wrong.</p>
  <pre><code>template &lt;typename Data, typename Delegate&gt;
  class DefaultRulePoolCache {
  public:
      DefaultRulePoolCache() : _atomicDefaultRulePool(nullptr), _defaultRulePool(nullptr) {
          
      }
      
      std::unique_ptr&lt;detail::PreparedRule&lt;Data, Delegate&gt;&gt; getPreparedDefaultRule() {
          auto atomicPool = _atomicDefaultRulePool.load();
          if (!atomicPool) {
              return nullptr;
          }
          return atomicPool-&gt;getPreparedRule();
      }
      
      void recyclePreparedDefaultRule(std::unique_ptr&lt;detail::PreparedRulePool&lt;Data, Delegate&gt;&gt; preparedRule) {
          auto atomicPool = _atomicDefaultRulePool.load();
          if (!atomicPool) {
              return;
          }
          hdmCheck(preparedRule) &lt;&lt; &quot;Cannot recycle null rule.&quot;;
          // we return the prepared rule only to the pool in case it's the current version of the default rul
          if (atomicPool-&gt;getTag() == preparedRule-&gt;getTag()) {
              atomicPool-&gt;recyclePreparedRule(std::move(preparedRule));
          }
      }
      
      void setDefaultRulePool(std::shared_ptr&lt;detail::PreparedRulePool&lt;Data, Delegate&gt;&gt; defaultRulePool) {
          std::lock_guard&lt;std::mutex&gt; lock(_defaultRulePoolMutex);
          atomicSwapPool(defaultRulePool);
      }
      
      void removeDefaultRulePool() {
          std::lock_guard&lt;std::mutex&gt; lock(_defaultRulePoolMutex);
          atomicSwapPool(nullptr);
      }
  
  private:
      std::atomic&lt;detail::PreparedRulePool&lt;Data, Delegate&gt;*&gt; _atomicDefaultRulePool;
      std::shared_ptr&lt;detail::PreparedRulePool&lt;data::Location, KVOLocationValuesDelegate&gt;&gt; _defaultRulePool;    
      std::mutex _defaultRulePoolMutex;
  
      
      void atomicSwapPool(std::shared_ptr&lt;detail::PreparedRulePool&lt;Data, Delegate&gt;&gt; defaultRulePool) {
         std::atomic_exchange(&amp;_atomicDefaultRulePool, defaultRulePool.get());
          // destroy the old pool
          _defaultRulePool = nullptr;
          // asign the new pool
          _defaultRulePool = defaultRulePool;
      }
  };
  </code></pre>