Nektar++
ThreadSpecificPool.hpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: ThreadSpecificPool.hpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description:
32 //
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 #ifndef NEKTAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
37 #define NEKTAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
38 
39 #include <boost/pool/pool.hpp>
40 #include <memory>
41 #include <map>
44 
45 #ifdef NEKTAR_USE_THREAD_SAFETY
46 #include <boost/thread/mutex.hpp>
47 #endif
48 
49 #include <cstring>
50 
51 namespace Nektar
52 {
53  namespace detail
54  {
55  /// \internal
56  /// \brief A memory pool which exists on a thread by thread basis.
57  /// \param ByteSize The number of bytes in each chunk allocated by the pool.
58  ///
59  /// Provides a simple, thread specific memory pool that is based on byte size.
60  /// The pool allocates and deallocates raw memory - the user is responsible for
61  /// calling appropriate constructors/destructors when allocating objects.
62  ///
63  /// Example:
64  ///
65  /// \code
66  /// ThreadSpecificPool<sizeof(TestClass)> pool;
67  /// void* memory = pool.allocate();
68  ///
69  /// // Construct the object in the memory returned by the pool.
70  /// TestClass* t = new (memory) TestClass;
71  ///
72  /// // Do stuff with t.
73  ///
74  /// // Destruct t and return it.
75  /// t->~TestClass();
76  /// pool.deallocate(t);
77  /// \endcode
79  {
80  public:
81  ThreadSpecificPool(size_t ByteSize) :
82  m_pool(),
83  m_blockSize(ByteSize)
84  {
85  // We can't do the new in the constructor list because the
86  // thread specific pointer doesn't have a supporting
87  // constructor.
88  m_pool = new boost::pool<>(m_blockSize);
89  }
90 
92  {
93  // The documentation isn't particularly clear if delete needs to be called manually
94  // or if the thread specific pointer will call delete for me. Looking through the
95  // boost code doesn't make it any clearer.
96  }
97 
98  /// \brief Allocate a block of memory of size ByteSize.
99  /// \throw std::bad_alloc if memory is exhausted.
100  void* Allocate()
101  {
102 #ifdef NEKTAR_USE_THREAD_SAFETY
103  boost::mutex::scoped_lock l(m_mutex);
104 #endif
105  void* result = m_pool->malloc();
106 
107 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
108  memset(result, 0, m_blockSize);
109 #endif //defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
110 
111  return result;
112  }
113 
114  /// \brief Deallocate memory claimed by an earlier call to allocate.
115  ///
116  /// \attention It is an error to deallocate memory not allocated
117  /// from this pool. Doing this will result in undefined behavior.
118  void Deallocate(const void* p)
119  {
120 #ifdef NEKTAR_USE_THREAD_SAFETY
121  boost::mutex::scoped_lock l(m_mutex);
122 #endif
123 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
124  // The idea here is to fill the returned memory with some known
125  // pattern, then detect that pattern on the allocate. If the
126  // pattern is no longer there then some memory corruption has
127  // occurred. However, I'm not sure how to distinguish between first
128  // time allocations and repeat allocations.
129 
130  //memset(p, '+', m_pool->get_requested_size());
131 #endif //defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
132 
133  m_pool->free(const_cast<void*>(p));
134  }
135 
136 
137  private:
138  //boost::thread_specific_ptr<boost::pool<> > m_pool;
139  boost::pool<>* m_pool;
140  size_t m_blockSize;
141 #ifdef NEKTAR_USE_THREAD_SAFETY
142  boost::mutex m_mutex;
143 #endif
144  };
145  }
146 
147  class MemPool
148  {
149  public:
150  typedef std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool> > PoolMapType;
151 
152  public:
154  m_fourBytePool(4),
155  m_pools(),
156  m_upperBound(1024)
157  {
158  // The m_pools data member stores a collection of thread specific pools of varying size. All memory requests
159  // up to and including the largest pool size will be allocated from a pool (note that this means you may receive
160  // more memory than you asked for). For example, if there is a pool for 8 bytes and the next largest pool is 32
161  // bytes, then a request for 10 bytes will return a 32 byte chunk of memory from the 32 byte pool.
162 
163  typedef PoolMapType::value_type PairType;
164  m_pools.insert(PairType(8, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(8))));
165  m_pools.insert(PairType(16, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(16))));
166  m_pools.insert(PairType(32, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(32))));
167  m_pools.insert(PairType(64, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(64))));
168  m_pools.insert(PairType(128, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(128))));
169  m_pools.insert(PairType(256, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(256))));
170  m_pools.insert(PairType(512, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(512))));
171  m_pools.insert(PairType(1024, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(1024))));
172  }
173 
175  {
176  }
177 
178  /// \brief Allocate a block of memory of size ByteSize.
179  /// \throw std::bad_alloc if memory is exhausted.
180  /// \param bytes The number of bytes to allocate.
181  ///
182  /// If the bytes parameter specifies a size that is handled by memory pools then the memory
183  /// is allocated from the pool. Otherwise the memory is allocated with a call to new.
184  ///
185  /// Important: All memory allocated from this method must be returned to the pool
186  /// via the Deallocate method. Deleting pointers allocated from the memory pool with the
187  /// delete operator will result in undefined behavior.
188  void* Allocate(size_t bytes)
189  {
190  if( bytes <= 4 )
191  {
192  return m_fourBytePool.Allocate();
193  }
194  else if( bytes > m_upperBound )
195  {
196  return ::operator new(bytes);
197  }
198  else
199  {
200  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
201  ASSERTL1(iter != m_pools.end(), "The memory manager is mishandling a memory request for " +
202  std::to_string(bytes) + " bytes of memory.");
203 
204  return (*iter).second->Allocate();
205  }
206  }
207 
208  /// \brief Deallocate memory claimed by an earlier call to allocate.
209  ///
210  /// \attention It is an error to deallocate memory not allocated
211  /// from this pool. Doing this will result in undefined behavior.
212  void Deallocate(void* p, size_t bytes)
213  {
214  if( bytes <= 4 )
215  {
216  m_fourBytePool.Deallocate(p);
217  }
218  else if( bytes > m_upperBound )
219  {
220  ::operator delete(p);
221  }
222  else
223  {
224  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
225  ASSERTL1(iter != m_pools.end(), "The memory manager is mishandling a memory request for " +
226  std::to_string(bytes) + " bytes of memory.");
227 
228  (*iter).second->Deallocate(p);
229  }
230  }
231 
232  private:
234  std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool> > m_pools;
235  size_t m_upperBound;
236  };
237 
239 }
240 
241 
242 
243 #endif //NEKATAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
244 
detail::ThreadSpecificPool m_fourBytePool
void Deallocate(void *p, size_t bytes)
Deallocate memory claimed by an earlier call to allocate.
std::map< size_t, std::shared_ptr< detail::ThreadSpecificPool > > PoolMapType
void * Allocate(size_t bytes)
Allocate a block of memory of size ByteSize.
#define LIB_UTILITIES_EXPORT
MemPool & GetMemoryPool()
std::map< size_t, std::shared_ptr< detail::ThreadSpecificPool > > m_pools
void Deallocate(const void *p)
Deallocate memory claimed by an earlier call to allocate.
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
Definition: ErrorUtil.hpp:250
void * Allocate()
Allocate a block of memory of size ByteSize.