Remote Call Framework 3.4
ObjectPool.hpp
1 
2 //******************************************************************************
3 // RCF - Remote Call Framework
4 //
5 // Copyright (c) 2005 - 2023, Delta V Software. All rights reserved.
6 // https://www.deltavsoft.com
7 //
8 // RCF is distributed under dual licenses - closed source or GPL.
9 // Consult your particular license for conditions of use.
10 //
11 // If you have not purchased a commercial license, you are using RCF under GPL terms.
12 //
13 // Version: 3.4
14 // Contact: support <at> deltavsoft.com
15 //
16 //******************************************************************************
17 
18 #ifndef INCLUDE_RCF_OBJECTPOOL_HPP
19 #define INCLUDE_RCF_OBJECTPOOL_HPP
20 
21 #include <vector>
22 #include <functional>
23 #include <map>
24 #include <memory>
25 
26 #include <RCF/MemStream.hpp>
27 #include <RCF/Tools.hpp>
28 #include <RCF/ThreadLibrary.hpp>
29 
30 namespace RCF {
31 
32  static const std::size_t CbSize = 128;
33 
34  class ObjectPool;
35 
36  class RCF_EXPORT CbAllocatorBase
37  {
38  public:
39 
40  CbAllocatorBase(ObjectPool & objectPool);
41  CbAllocatorBase(const CbAllocatorBase & rhs);
42 
43  void * allocate();
44  void deallocate(void * pcb) ;
45 
46  private:
47  ObjectPool & mObjectPool;
48  };
49 
50  template<typename T>
51  class CbAllocator : public CbAllocatorBase
52  {
53  public:
54 
55  typedef T value_type;
56  typedef value_type* pointer;
57  typedef std::size_t size_type;
58  typedef std::ptrdiff_t difference_type;
59 
60  template<typename U>
61  struct rebind
62  {
63  typedef CbAllocator<U> other;
64  };
65 
66  CbAllocator(ObjectPool & objectPool) : CbAllocatorBase(objectPool)
67  {
68  }
69 
70  template<typename U>
71  CbAllocator( const CbAllocator<U> & rhs) : CbAllocatorBase(rhs)
72  {
73  }
74 
75  pointer allocate(
76  size_type cnt,
77  const void * = 0)
78  {
79  static_assert( sizeof(T) <= CbSize, "Invalid type T." );
80  RCF_ASSERT(cnt == 1);
81  RCF_UNUSED_VARIABLE(cnt);
82  return reinterpret_cast<pointer>(CbAllocatorBase::allocate());
83  }
84 
85  void deallocate(pointer p, size_type)
86  {
87  CbAllocatorBase::deallocate(p);
88  }
89  };
90 
91  class TypeInfo
92  {
93  public:
94  TypeInfo(const std::type_info & ti) : mpTypeInfo(&ti)
95  {
96 
97  }
98 
99  bool operator<(const TypeInfo & rhs) const
100  {
101  return (*mpTypeInfo).before(*rhs.mpTypeInfo) ? true : false;
102  }
103 
104  private:
105  const std::type_info * mpTypeInfo;
106  };
107 
108  class ReallocBuffer;
109  typedef std::shared_ptr<ReallocBuffer> ReallocBufferPtr;
110 
112  class RCF_EXPORT ObjectPool
113  {
114  public:
115 
116  ObjectPool();
117  ~ObjectPool();
118 
122  template<typename T>
123  void enableCaching(std::size_t maxCount, std::function<void(T *)> clearFunc)
124  {
125  enableCaching( (T *) NULL, maxCount, clearFunc);
126  }
127 
129  template<typename T>
131  {
132  disableCaching( (T *) NULL);
133  }
134 
135  template<typename T>
136  void enableCaching(T *, std::size_t maxCount, std::function<void(T *)> clearFunc)
137  {
138  RCF::WriteLock lock(mObjPoolMutex);
139  RCF::TypeInfo ti( typeid(T) );
140  mObjPool[ti].reset( new RCF::ObjectPool::ObjList() );
141  mObjPool[ti]->mMaxSize = maxCount;
142  mObjPool[ti]->mOps.reset( new RCF::ObjectPool::Ops<T>(clearFunc) );
143  }
144 
145  template<typename T>
146  void disableCaching(T *)
147  {
148  RCF::WriteLock lock(mObjPoolMutex);
149  RCF::TypeInfo ti( typeid(T) );
150  mObjPool[ti]->mMaxSize = 0;
151  mObjPool[ti]->clear();
152  }
153 
154  // Returns a value indicating of caching is enabled for type T.
155  template<typename T>
156  bool isCachingEnabled(T *)
157  {
158  ReadLock lock(mObjPoolMutex);
159  if (!mObjPool.empty())
160  {
161  RCF::TypeInfo ti( typeid(T) );
162  ObjPool::iterator iter = mObjPool.find(ti);
163  if (iter != mObjPool.end())
164  {
165  if (iter->second->mMaxSize > 0)
166  {
167  return true;
168  }
169  }
170  }
171  return false;
172  }
173 
174  MemOstreamPtr getMemOstreamPtr();
175  ReallocBufferPtr getReallocBufferPtr();
176 
177  void enumerateWriteBuffers(std::vector<std::size_t> & bufferSizes);
178  void enumerateReadBuffers(std::vector<std::size_t> & bufferSizes);
179 
180  void setBufferCountLimit(std::size_t bufferCountLimit);
181  std::size_t getBufferCountLimit();
182 
183  void setBufferSizeLimit(std::size_t bufferSizeLimit);
184  std::size_t getBufferSizeLimit();
185 
190  template<typename T>
191  void getObj(std::shared_ptr<T> & objPtr, bool alwaysCreate = true)
192  {
193  T * pt = NULL;
194  void * pv = NULL;
195  std::shared_ptr<void> spv;
196  bool pfnDeleter = false;
197 
198  {
199  ReadLock poolLock(mObjPoolMutex);
200 
201  if (mObjPool.empty())
202  {
203  if (alwaysCreate)
204  {
205  pt = new T;
206  }
207  else
208  {
209  return;
210  }
211  }
212  else
213  {
214  TypeInfo ti( typeid(T) );
215  ObjPool::iterator iter = mObjPool.find(ti);
216  if (iter == mObjPool.end())
217  {
218  if (alwaysCreate)
219  {
220  pt = new T;
221  }
222  else
223  {
224  return;
225  }
226  }
227  else
228  {
229  ObjList & objList = *(iter->second);
230  Lock listLock(objList.mMutex);
231  if (objList.mMaxSize == 0)
232  {
233  if (alwaysCreate)
234  {
235  pt = new T;
236  }
237  else
238  {
239  return;
240  }
241  }
242  else if (objList.mVec.empty())
243  {
244  pt = new T;
245  pfnDeleter = true;
246  }
247  else
248  {
249  pv = objList.mVec.back();
250  pt = static_cast<T *>(pv);
251  objList.mVec.pop_back();
252  pfnDeleter = true;
253  }
254  }
255  }
256  }
257 
258  RCF_ASSERT(pt);
259  if (pfnDeleter)
260  {
261  TypeInfo ti( typeid(T) );
262 
263  // Use shared_ptr custom allocator, to avoid memory allocations when a buffer is requested.
264 
265  objPtr = std::shared_ptr<T>(
266  pt,
267  std::bind(&ObjectPool::putObj, this, ti, std::placeholders::_1),
268  CbAllocator<void>(*this) );
269  }
270  else
271  {
272  objPtr = std::shared_ptr<T>(pt);
273  }
274  }
275 
276  void putObj(const TypeInfo & ti, void * pv)
277  {
278  ReadLock readLock(mObjPoolMutex);
279  RCF_ASSERT(!mObjPool.empty());
280  ObjPool::iterator iter = mObjPool.find(ti);
281  RCF_ASSERT(iter != mObjPool.end());
282  ObjList & objList = *(iter->second);
283  Lock lock(objList.mMutex);
284  if (objList.mVec.size() >= objList.mMaxSize)
285  {
286  lock.unlock();
287  readLock.unlock();
288  objList.mOps->kill(pv);
289  }
290  else
291  {
292  objList.mOps->clear(pv);
293  objList.mVec.push_back(pv);
294  }
295  }
296 
297  class I_Ops
298  {
299  public:
300  virtual ~I_Ops() {}
301  virtual void kill(void * pv) = 0;
302  virtual void clear(void * pv) = 0;
303  };
304 
305  template<typename T>
306  class Ops : public I_Ops
307  {
308  public:
309  Ops(std::function<void(T *)> clearFunc) :
310  mClearFunc(clearFunc)
311  {
312  }
313 
314  void kill(void * pv)
315  {
316  T * pt = static_cast<T *>(pv);
317  delete pt;
318  }
319 
320  void clear(void * pv)
321  {
322  if (mClearFunc)
323  {
324  T * pt = static_cast<T *>(pv);
325  mClearFunc(pt);
326  }
327  }
328 
329  std::function<void(T *)> mClearFunc;
330  };
331 
332  class ObjList : Noncopyable
333  {
334  public:
335  ObjList() : mMaxSize(0)
336  {
337  }
338  Mutex mMutex;
339  std::size_t mMaxSize;
340  std::vector<void *> mVec;
341  std::unique_ptr<I_Ops> mOps;
342 
343  void clear()
344  {
345  for (std::size_t i=0; i<mVec.size(); ++i)
346  {
347  mOps->kill(mVec[i]);
348  }
349  mVec.clear();
350  }
351  };
352 
353  typedef std::shared_ptr<ObjList> ObjListPtr;
354 
355  typedef std::map< TypeInfo, ObjListPtr > ObjPool;
356  ReadWriteMutex mObjPoolMutex;
357  ObjPool mObjPool;
358 
359  private:
360 
361  friend class CbAllocatorBase;
362 
363  void * getPcb();
364  void putPcb(void * pcb);
365 
366  void putMemOstream(MemOstream * pOs);
367  void putReallocBuffer(ReallocBuffer * pRb);
368 
369  std::size_t mBufferCountLimit;
370  std::size_t mBufferSizeLimit;
371 
372  Mutex mOsPoolMutex;
373  std::vector< MemOstream * > mOsPool;
374 
375  Mutex mRbPoolMutex;
376  std::vector< ReallocBuffer * > mRbPool;
377 
378  Mutex mCbPoolMutex;
379  std::vector< void * > mCbPool;
380 
381  template<typename T, typename Spt, typename PtrList, typename Pfn>
382  void getPtr(
383  T *,
384  Spt & spt,
385  PtrList & ptrList,
386  Mutex & ptrListMutex,
387  Pfn pfn)
388  {
389  T * pt = NULL;
390 
391  {
392  Lock lock(ptrListMutex);
393 
394  if (ptrList.empty())
395  {
396  pt = new T();
397  }
398  else
399  {
400  pt = ptrList.back();
401  ptrList.pop_back();
402  }
403  }
404 
405  // Use shared_ptr allocator to avoid all allocations when a buffer is requested.
406 
407  spt = std::shared_ptr<T>(
408  pt,
409  std::bind(pfn, this, std::placeholders::_1),
410  CbAllocator<void>(*this) );
411 
412  }
413 
414  };
415 
416  RCF_EXPORT ObjectPool & getObjectPool();
417 
418 } // namespace RCF
419 
420 #endif // ! INCLUDE_RCF_OBJECTPOOL_HPP
void enableCaching(std::size_t maxCount, std::function< void(T *)> clearFunc)
Definition: ObjectPool.hpp:123
Manages a cache of objects of various types.
Definition: ObjectPool.hpp:112
Definition: AmiIoHandler.hpp:23
void disableCaching()
Disables caching of objects of type T.
Definition: ObjectPool.hpp:130
void getObj(std::shared_ptr< T > &objPtr, bool alwaysCreate=true)
Definition: ObjectPool.hpp:191