|
||||
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 */
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.2.1 LXR engine. The LXR team |