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