RCFProto
 All Classes Functions Typedefs
ObjectPool.hpp
1 
2 //******************************************************************************
3 // RCF - Remote Call Framework
4 //
5 // Copyright (c) 2005 - 2013, 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: 2.0
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 
24 #include <boost/bind.hpp>
25 #include <boost/shared_ptr.hpp>
26 
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  typename std::allocator<void>::const_pointer = 0)
78  {
79  BOOST_STATIC_ASSERT( sizeof(T) <= CbSize );
80  RCF_ASSERT_EQ(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 boost::shared_ptr<ReallocBuffer> ReallocBufferPtr;
110 
111  class RCF_EXPORT ObjectPool
112  {
113  public:
114 
115  ObjectPool();
116  ~ObjectPool();
117 
118  template<typename T>
119  void enableCaching(std::size_t maxCount, boost::function1<void, T *> clearFunc)
120  {
121  enableCaching( (T *) NULL, maxCount, clearFunc);
122  }
123 
124  template<typename T>
125  void disableCaching()
126  {
127  disableCaching( (T *) NULL);
128  }
129 
130  template<typename T>
131  void enableCaching(T *, std::size_t maxCount, boost::function1<void, T *> clearFunc)
132  {
133  RCF::WriteLock lock(mObjPoolMutex);
134  RCF::TypeInfo ti( typeid(T) );
135  mObjPool[ti].reset( new RCF::ObjectPool::ObjList() );
136  mObjPool[ti]->mMaxSize = maxCount;
137  mObjPool[ti]->mOps.reset( new RCF::ObjectPool::Ops<T>(clearFunc) );
138  }
139 
140  template<typename T>
141  void disableCaching(T *)
142  {
143  RCF::WriteLock lock(mObjPoolMutex);
144  RCF::TypeInfo ti( typeid(T) );
145  mObjPool[ti]->mMaxSize = 0;
146  mObjPool[ti]->clear();
147  }
148 
149  template<typename T>
150  bool isCachingEnabled(T *)
151  {
152  ReadLock lock(mObjPoolMutex);
153  if (!mObjPool.empty())
154  {
155  RCF::TypeInfo ti( typeid(T) );
156  ObjPool::iterator iter = mObjPool.find(ti);
157  if (iter != mObjPool.end())
158  {
159  if (iter->second->mMaxSize > 0)
160  {
161  return true;
162  }
163  }
164  }
165  return false;
166  }
167 
168  MemOstreamPtr getMemOstreamPtr();
169  ReallocBufferPtr getReallocBufferPtr();
170 
171  void enumerateWriteBuffers(std::vector<std::size_t> & bufferSizes);
172  void enumerateReadBuffers(std::vector<std::size_t> & bufferSizes);
173 
174  void setBufferCountLimit(std::size_t bufferCountLimit);
175  std::size_t getBufferCountLimit();
176 
177  void setBufferSizeLimit(std::size_t bufferSizeLimit);
178  std::size_t getBufferSizeLimit();
179 
180  template<typename T>
181  void getObj(boost::shared_ptr<T> & objPtr, bool alwaysCreate = true)
182  {
183  T * pt = NULL;
184  void * pv = NULL;
185  boost::shared_ptr<void> spv;
186  bool pfnDeleter = false;
187 
188 
189  {
190  ReadLock poolLock(mObjPoolMutex);
191 
192  if (mObjPool.empty())
193  {
194  if (alwaysCreate)
195  {
196  pt = new T;
197  }
198  else
199  {
200  return;
201  }
202  }
203  else
204  {
205  TypeInfo ti( typeid(T) );
206  ObjPool::iterator iter = mObjPool.find(ti);
207  if (iter == mObjPool.end())
208  {
209  if (alwaysCreate)
210  {
211  pt = new T;
212  }
213  else
214  {
215  return;
216  }
217  }
218  else
219  {
220  ObjList & objList = *(iter->second);
221  Lock listLock(objList.mMutex);
222  if (objList.mMaxSize == 0)
223  {
224  if (alwaysCreate)
225  {
226  pt = new T;
227  }
228  else
229  {
230  return;
231  }
232  }
233  else if (objList.mVec.empty())
234  {
235  pt = new T;
236  pfnDeleter = true;
237  }
238  else
239  {
240  pv = objList.mVec.back();
241  pt = static_cast<T *>(pv);
242  objList.mVec.pop_back();
243  pfnDeleter = true;
244  }
245  }
246  }
247  }
248 
249  RCF_ASSERT(pt);
250  if (pfnDeleter)
251  {
252  TypeInfo ti( typeid(T) );
253 
254 #if BOOST_VERSION < 103400
255 
256  // 1.33.1 shared_ptr, and earlier, does not have allocator support. Consequently we
257  // have a (small) allocation each time a buffer is requested, and a corresponding
258  // deallocation when the buffer is returned to the pool.
259 
260  objPtr = boost::shared_ptr<T>(
261  pt,
262  boost::bind(&ObjectPool::putObj, this, ti, _1));
263 
264 #else
265 
266  // 1.34.0 shared_ptr has allocator support. Consequently we have no allocations
267  // at all when a buffer is requested.
268 
269  objPtr = boost::shared_ptr<T>(
270  pt,
271  boost::bind(&ObjectPool::putObj, this, ti, _1),
272  CbAllocator<void>(*this) );
273 
274 #endif
275  }
276  else
277  {
278  objPtr = boost::shared_ptr<T>(pt);
279  }
280  }
281 
282  void putObj(const TypeInfo & ti, void * pv)
283  {
284  ReadLock readLock(mObjPoolMutex);
285  RCF_ASSERT(!mObjPool.empty());
286  ObjPool::iterator iter = mObjPool.find(ti);
287  RCF_ASSERT(iter != mObjPool.end());
288  ObjList & objList = *(iter->second);
289  Lock lock(objList.mMutex);
290  if (objList.mVec.size() >= objList.mMaxSize)
291  {
292  lock.unlock();
293  readLock.unlock();
294  objList.mOps->kill(pv);
295  }
296  else
297  {
298  objList.mOps->clear(pv);
299  objList.mVec.push_back(pv);
300  }
301  }
302 
303  class I_Ops
304  {
305  public:
306  virtual ~I_Ops() {}
307  virtual void kill(void * pv) = 0;
308  virtual void clear(void * pv) = 0;
309  };
310 
311  template<typename T>
312  class Ops : public I_Ops
313  {
314  public:
315  Ops(boost::function1<void, T *> clearFunc) :
316  mClearFunc(clearFunc)
317  {
318  }
319 
320  void kill(void * pv)
321  {
322  T * pt = static_cast<T *>(pv);
323  delete pt;
324  }
325 
326  void clear(void * pv)
327  {
328  if (mClearFunc)
329  {
330  T * pt = static_cast<T *>(pv);
331  mClearFunc(pt);
332  }
333  }
334 
335  boost::function1<void, T *> mClearFunc;
336  };
337 
338  class ObjList : boost::noncopyable
339  {
340  public:
341  ObjList() : mMaxSize(0)
342  {
343  }
344  Mutex mMutex;
345  std::size_t mMaxSize;
346  std::vector<void *> mVec;
347  boost::scoped_ptr<I_Ops> mOps;
348 
349  void clear()
350  {
351  for (std::size_t i=0; i<mVec.size(); ++i)
352  {
353  mOps->kill(mVec[i]);
354  }
355  mVec.clear();
356  }
357  };
358 
359  typedef boost::shared_ptr<ObjList> ObjListPtr;
360 
361  typedef std::map< TypeInfo, ObjListPtr > ObjPool;
362  ReadWriteMutex mObjPoolMutex;
363  ObjPool mObjPool;
364 
365  private:
366 
367  friend class CbAllocatorBase;
368 
369  void * getPcb();
370  void putPcb(void * pcb);
371 
372  void putMemOstream(MemOstream * pOs);
373  void putReallocBuffer(ReallocBuffer * pRb);
374 
375  std::size_t mBufferCountLimit;
376  std::size_t mBufferSizeLimit;
377 
378  Mutex mOsPoolMutex;
379  std::vector< MemOstream * > mOsPool;
380 
381  Mutex mRbPoolMutex;
382  std::vector< ReallocBuffer * > mRbPool;
383 
384  Mutex mCbPoolMutex;
385  std::vector< void * > mCbPool;
386 
387  template<typename T, typename Spt, typename PtrList, typename Pfn>
388  void getPtr(
389  T *,
390  Spt & spt,
391  PtrList & ptrList,
392  Mutex & ptrListMutex,
393  Pfn pfn)
394  {
395  T * pt = NULL;
396 
397  {
398  Lock lock(ptrListMutex);
399 
400  if (ptrList.empty())
401  {
402  pt = new T();
403  }
404  else
405  {
406  pt = ptrList.back();
407  ptrList.pop_back();
408  }
409  }
410 
411 #if BOOST_VERSION < 103400
412 
413  // 1.33.1 shared_ptr, and earlier, does not have allocator support. Consequently we
414  // have a (small) allocation each time a buffer is requested, and a corresponding
415  // deallocation when the buffer is returned to the pool.
416 
417  spt = boost::shared_ptr<T>(
418  pt,
419  boost::bind(pfn, this, _1));
420 
421 #else
422 
423  // 1.34.0 shared_ptr has allocator support. Consequently we have no allocations
424  // at all when a buffer is requested.
425 
426  spt = boost::shared_ptr<T>(
427  pt,
428  boost::bind(pfn, this, _1),
429  CbAllocator<void>(*this) );
430 
431 #endif
432 
433  }
434 
435  };
436 
437  RCF_EXPORT ObjectPool & getObjectPool();
438 
439 } // namespace RCF
440 
441 #endif // ! INCLUDE_RCF_OBJECTPOOL_HPP