Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-09-07 04:36:26

0001 #ifndef FWCore_Utilities_ReusableObjectHolder_h
0002 #define FWCore_Utilities_ReusableObjectHolder_h
0003 
0004 // -*- C++ -*-
0005 //
0006 // Package:     FWCore/Utilities
0007 // Class  :     ReusableObjectHolder
0008 //
0009 /**\class edm::ReusableObjectHolder ReusableObjectHolder "ReusableObjectHolder.h"
0010  
0011  Description: Thread safe way to do create and reuse a group of the same object type.
0012  
0013  Usage:
0014  This class can be used to safely reuse a series of objects created on demand. The reuse
0015  of the objects is safe even across different threads since one can safely call all member
0016  functions of this class on the same instance of this class from multiple threads.
0017 
0018  This class manages the cache of reusable objects and therefore an instance of this
0019  class must live as long as you want the cache to live.
0020 
0021  The primary way of using the class is to call makeOrGet:
0022  \code
0023  auto objectToUse = holder.makeOrGet([]() { return new MyObject(); });
0024  use(*objectToUse);
0025  \endcode
0026 
0027  If the returned object should be be automatically set or reset, call makeOrGetAndClear:
0028  \code
0029  auto objectToUse = holder.makeOrGetAndClear([]() { return new MyObject(10); },   // makes new objects
0030                                              [](MyObject* old) { old->reset(); }  // resets any object before returning it
0031  );
0032  \endcode
0033  which is equivalent to
0034  \code
0035  auto objectToUse = holder.makeOrGet([]() { return new MyObject(10); });
0036  objectToUse->reset();
0037  \endcode
0038  
0039  NOTE: If you hold onto the std::shared_ptr<> until another call to the ReusableObjectHolder,
0040  make sure to release the shared_ptr before the call. That way the object you were just
0041  using can go back into the cache and be reused for the call you are going to make.
0042  An example
0043  \code
0044   std::shared_ptr<MyObject> obj;
0045   while(someCondition()) {
0046     //release object so it can re-enter the cache
0047     obj.release();
0048     obj = holder.makeOrGet([]{ return new MyObject();} );
0049     obj->setValue(someNewValue());
0050     useTheObject(obj);
0051   }
0052  \endcode
0053  
0054  The above example is very contrived, since the better way to do the above is
0055  \code
0056  while(someCondition()) {
0057    auto obj = holder.makeOrGet([]{ return new MyObject();} );
0058    obj->setValue(someNewValue());
0059    useTheObject(obj);
0060    //obj goes out of scope and returns the object to the cache
0061  }
0062  \endcode
0063 
0064  When a custom deleter is used, the deleter type must be the same to
0065  all objects. The deleter is allowed to have state that depends on the
0066  object. The deleter object is passed along the std::unique_ptr, and
0067  is internally kept along the object. The deleter object must be copyable.
0068  */
0069 //
0070 // Original Author:  Chris Jones
0071 //         Created:  Fri, 31 July 2014 14:29:41 GMT
0072 //
0073 
0074 #include <atomic>
0075 #include <cassert>
0076 #include <memory>
0077 
0078 #include <oneapi/tbb/concurrent_queue.h>
0079 
0080 namespace edm {
0081   template <class T, class Deleter = std::default_delete<T>>
0082   class ReusableObjectHolder {
0083   public:
0084     using deleter_type = Deleter;
0085 
0086     ReusableObjectHolder() : m_outstandingObjects(0) {}
0087     ReusableObjectHolder(ReusableObjectHolder&& iOther)
0088         : m_availableQueue(std::move(iOther.m_availableQueue)), m_outstandingObjects(0) {
0089       assert(0 == iOther.m_outstandingObjects);
0090     }
0091     ~ReusableObjectHolder() noexcept {
0092       assert(0 == m_outstandingObjects);
0093       std::unique_ptr<T, Deleter> item;
0094       while (m_availableQueue.try_pop(item)) {
0095         item.reset();
0096       }
0097     }
0098 
0099     ///Adds the item to the cache.
0100     /// Use this function if you know ahead of time
0101     /// how many cached items you will need.
0102     void add(std::unique_ptr<T, Deleter> iItem) {
0103       if (nullptr != iItem) {
0104         m_availableQueue.push(std::move(iItem));
0105       }
0106     }
0107 
0108     ///Tries to get an already created object,
0109     /// if none are available, returns an empty shared_ptr.
0110     /// Use this function in conjunction with add()
0111     std::shared_ptr<T> tryToGet() {
0112       std::unique_ptr<T, Deleter> item;
0113       if (m_availableQueue.try_pop(item)) {
0114         return wrapCustomDeleter(std::move(item));
0115       } else {
0116         return std::shared_ptr<T>{};
0117       }
0118     }
0119 
0120     ///Takes an object from the queue if one is available, or creates one using iMakeFunc.
0121     template <typename FM>
0122     std::shared_ptr<T> makeOrGet(FM&& iMakeFunc) {
0123       std::unique_ptr<T, Deleter> item;
0124       if (m_availableQueue.try_pop(item)) {
0125         return wrapCustomDeleter(std::move(item));
0126       } else {
0127         return wrapCustomDeleter(makeUnique(iMakeFunc()));
0128       }
0129     }
0130 
0131     ///Takes an object from the queue if one is available, or creates one using iMakeFunc.
0132     ///Then, passes the object to iClearFunc, and returns it.
0133     template <typename FM, typename FC>
0134     std::shared_ptr<T> makeOrGetAndClear(FM&& iMakeFunc, FC&& iClearFunc) {
0135       std::shared_ptr<T> returnValue = makeOrGet(std::forward<FM>(iMakeFunc));
0136       iClearFunc(returnValue.get());
0137       return returnValue;
0138     }
0139 
0140   private:
0141     ///Wraps an object in a shared_ptr<T> with a custom deleter, that hands the wrapped object
0142     // back to the queue instead of deleting it
0143     std::shared_ptr<T> wrapCustomDeleter(std::unique_ptr<T, Deleter> item) {
0144       auto deleter = item.get_deleter();
0145       ++m_outstandingObjects;
0146       return std::shared_ptr<T>{
0147           item.release(), [this, deleter](T* iItem) { this->addBack(std::unique_ptr<T, Deleter>{iItem, deleter}); }};
0148     }
0149 
0150     std::unique_ptr<T> makeUnique(T* ptr) {
0151       static_assert(std::is_same_v<Deleter, std::default_delete<T>>,
0152                     "Generating functions returning raw pointers are supported only with std::default_delete<T>");
0153       return std::unique_ptr<T>{ptr};
0154     }
0155 
0156     std::unique_ptr<T, Deleter> makeUnique(std::unique_ptr<T, Deleter> ptr) { return ptr; }
0157 
0158     void addBack(std::unique_ptr<T, Deleter> iItem) {
0159       m_availableQueue.push(std::move(iItem));
0160       --m_outstandingObjects;
0161     }
0162 
0163     oneapi::tbb::concurrent_queue<std::unique_ptr<T, Deleter>> m_availableQueue;
0164     std::atomic<size_t> m_outstandingObjects;
0165   };
0166 
0167 }  // namespace edm
0168 
0169 #endif /* end of include guard: FWCore_Utilities_ReusableObjectHolder_h */