AtomicPtrCache

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
#ifndef DataFormats_Common_AtomicPtrCache_h
#define DataFormats_Common_AtomicPtrCache_h
// -*- C++ -*-
//
// Package:     DataFormats/Common
// Class  :     AtomicPtrCache
//
/**\class edm::AtomicPtrCache AtomicPtrCache.h "DataFormats/Common/interface/AtomicPtrCache.h"

 Description: A thread safe cache managed by a pointer

 Usage:
    Data products which need to cache results into non-trivial structures (e.g. an std::vector)
 can use this class to manage the cache in a thread-safe way.
 The thread-safety guarantee is only the standard C++, if calls are made to const functions simultaneously
 then everything is thread safe. Calling a non-const function while calling any other functions
 is not thread-safe.
 
 This class also hides the std::atomic from ROOT so this class can safely be used in a stored class.

 WARNING: member data which uses this class must be made transient in the classes_def.xml file!

*/
//
// Original Author:  Chris Jones
//         Created:  Thu, 07 Nov 2013 00:50:40 GMT
//

// system include files
#include <atomic>
#include <memory>
// user include files

// forward declarations

namespace edm {
  template <typename T>
  class AtomicPtrCache {
  public:
    AtomicPtrCache();

    ///Takes exclusive ownership of the value
    explicit AtomicPtrCache(T*);

    ///Uses T's copy constructor to make a copy
    AtomicPtrCache(const AtomicPtrCache<T>&);
    AtomicPtrCache& operator=(const AtomicPtrCache<T>&);

    ~AtomicPtrCache();

    // ---------- const member functions ---------------------
    T const* operator->() const { return load(); }
    T const& operator*() const { return *load(); }
    T const* load() const;

    bool isSet() const;
    ///returns true if actually was set.
    /// Will delete value held by iNewValue if not the first time set
    bool set(std::unique_ptr<T> iNewValue) const;
    // ---------- static member functions --------------------

    // ---------- member functions ---------------------------
    T* operator->() { return load(); }
    T& operator*() { return *load(); }

    T* load();

    ///unsets the value and deletes the memory
    void reset();

    T* release();

  private:
    // ---------- member data --------------------------------
    mutable std::atomic<T*> m_data;
  };
  template <typename T>
  inline AtomicPtrCache<T>::AtomicPtrCache() : m_data{nullptr} {}

  template <typename T>
  inline AtomicPtrCache<T>::AtomicPtrCache(T* iValue) : m_data{iValue} {}

  template <typename T>
  inline AtomicPtrCache<T>::AtomicPtrCache(const AtomicPtrCache<T>& iOther) : m_data{nullptr} {
    auto ptr = iOther.m_data.load(std::memory_order_acquire);
    if (ptr != nullptr) {
      m_data.store(new T{*ptr}, std::memory_order_release);
    }
  }
  template <typename T>
  inline AtomicPtrCache<T>& AtomicPtrCache<T>::operator=(const AtomicPtrCache<T>& iOther) {
    auto ptr = iOther.m_data.load(std::memory_order_acquire);
    if (ptr != nullptr) {
      auto ourPtr = m_data.load(std::memory_order_acquire);
      if (ourPtr != nullptr) {
        *ourPtr = *ptr;
      } else {
        m_data.store(new T{*ptr}, std::memory_order_release);
      }
    } else {
      delete m_data.exchange(nullptr, std::memory_order_acq_rel);
    }
    return *this;
  }

  template <typename T>
  inline AtomicPtrCache<T>::~AtomicPtrCache() {
    delete m_data.load(std::memory_order_acquire);
  }

  template <typename T>
  inline T* AtomicPtrCache<T>::load() {
    return m_data.load(std::memory_order_acquire);
  }

  template <typename T>
  inline T const* AtomicPtrCache<T>::load() const {
    return m_data.load(std::memory_order_acquire);
  }

  template <typename T>
  inline bool AtomicPtrCache<T>::isSet() const {
    return nullptr != m_data.load(std::memory_order_acquire);
  }

  template <typename T>
  inline bool AtomicPtrCache<T>::set(std::unique_ptr<T> iNewValue) const {
    bool retValue;
    T* expected = nullptr;
    if ((retValue = m_data.compare_exchange_strong(expected, iNewValue.get(), std::memory_order_acq_rel))) {
      iNewValue.release();
    }
    return retValue;
  }

  template <typename T>
  inline void AtomicPtrCache<T>::reset() {
    delete m_data.exchange(nullptr, std::memory_order_acq_rel);
  }

  template <typename T>
  inline T* AtomicPtrCache<T>::release() {
    T* tmp = m_data.exchange(nullptr);
    return tmp;
  }
}  // namespace edm
#endif