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