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 
41 #include <memory>
42 #include <map>
45 
46 #ifdef NEKTAR_USE_THREAD_SAFETY
47 #include <boost/thread/mutex.hpp>
48 #endif
49 
50 #ifdef NEKTAR_USE_ALIGNED_MEM
51 #include <boost/align/aligned_alloc.hpp>
54 #endif
55 
56 #include <cstring>
57 
58 namespace Nektar
59 {
60  namespace detail
61  {
62  /// \internal
63  /// \brief A memory pool which exists on a thread by thread basis.
64  /// \param ByteSize The number of bytes in each chunk allocated by the pool.
65  ///
66  /// Provides a simple, thread specific memory pool that is based on byte size.
67  /// The pool allocates and deallocates raw memory - the user is responsible for
68  /// calling appropriate constructors/destructors when allocating objects.
69  ///
70  /// Example:
71  ///
72  /// \code
73  /// ThreadSpecificPool<sizeof(TestClass)> pool;
74  /// void* memory = pool.allocate();
75  ///
76  /// // Construct the object in the memory returned by the pool.
77  /// TestClass* t = new (memory) TestClass;
78  ///
79  /// // Do stuff with t.
80  ///
81  /// // Destruct t and return it.
82  /// t->~TestClass();
83  /// pool.deallocate(t);
84  /// \endcode
86  {
87  public:
88  ThreadSpecificPool(size_t ByteSize) :
89  m_pool(),
90  m_blockSize(ByteSize)
91  {
92  // We can't do the new in the constructor list because the
93  // thread specific pointer doesn't have a supporting
94  // constructor.
95  m_pool = new boost::pool<>(m_blockSize);
96  }
97 
99  {
100  // Need to call delete manually, otherwise memory is leaking.
101  delete m_pool;
102 
103  }
104 
105  /// \brief Allocate a block of memory of size ByteSize.
106  /// \throw std::bad_alloc if memory is exhausted.
107  void* Allocate()
108  {
109 #ifdef NEKTAR_USE_THREAD_SAFETY
110  boost::mutex::scoped_lock l(m_mutex);
111 #endif
112  void* result = m_pool->malloc();
113 
114 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
115  memset(result, 0, m_blockSize);
116 #endif //defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
117 
118  return result;
119  }
120 
121  /// \brief Deallocate memory claimed by an earlier call to allocate.
122  ///
123  /// \attention It is an error to deallocate memory not allocated
124  /// from this pool. Doing this will result in undefined behavior.
125  void Deallocate(const void* p)
126  {
127 #ifdef NEKTAR_USE_THREAD_SAFETY
128  boost::mutex::scoped_lock l(m_mutex);
129 #endif
130 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
131  // The idea here is to fill the returned memory with some known
132  // pattern, then detect that pattern on the allocate. If the
133  // pattern is no longer there then some memory corruption has
134  // occurred. However, I'm not sure how to distinguish between first
135  // time allocations and repeat allocations.
136 
137  //memset(p, '+', m_pool->get_requested_size());
138 #endif //defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
139 
140  m_pool->free(const_cast<void*>(p));
141  }
142 
143 
144  private:
145  //boost::thread_specific_ptr<boost::pool<> > m_pool;
146  boost::pool<>* m_pool;
147  size_t m_blockSize;
148 #ifdef NEKTAR_USE_THREAD_SAFETY
149  boost::mutex m_mutex;
150 #endif
151  };
152  }
153 
154  class MemPool
155  {
156  public:
157  typedef std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool> > PoolMapType;
158 
159  public:
161  m_fourBytePool(4),
162  m_pools(),
163  m_upperBound(1024)
164  {
165  // The m_pools data member stores a collection of thread specific pools of varying size. All memory requests
166  // up to and including the largest pool size will be allocated from a pool (note that this means you may receive
167  // more memory than you asked for). For example, if there is a pool for 8 bytes and the next largest pool is 32
168  // bytes, then a request for 10 bytes will return a 32 byte chunk of memory from the 32 byte pool.
169 
170  typedef PoolMapType::value_type PairType;
171  m_pools.insert(PairType(8, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(8))));
172  m_pools.insert(PairType(16, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(16))));
173  m_pools.insert(PairType(32, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(32))));
174  m_pools.insert(PairType(64, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(64))));
175  m_pools.insert(PairType(128, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(128))));
176  m_pools.insert(PairType(256, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(256))));
177  m_pools.insert(PairType(512, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(512))));
178  m_pools.insert(PairType(1024, std::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(1024))));
179  }
180 
182  {
183  }
184 
185  /// \brief Allocate a block of memory of size ByteSize.
186  /// \throw std::bad_alloc if memory is exhausted.
187  /// \param bytes The number of bytes to allocate.
188  ///
189  /// If the bytes parameter specifies a size that is handled by memory pools then the memory
190  /// is allocated from the pool. Otherwise the memory is allocated with a call to new.
191  ///
192  /// Important: All memory allocated from this method must be returned to the pool
193  /// via the Deallocate method. Deleting pointers allocated from the memory pool with the
194  /// delete operator will result in undefined behavior.
195  void* Allocate(size_t bytes)
196  {
197  if( bytes <= 4 )
198  {
199  return m_fourBytePool.Allocate();
200  }
201  else if( bytes > m_upperBound )
202  {
203 #ifdef NEKTAR_USE_ALIGNED_MEM
204  return boost::alignment::aligned_alloc(tinysimd::simd<NekDouble>::alignment, bytes);
205 #else
206  return ::operator new(bytes);
207 #endif
208  }
209  else
210  {
211  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
212  ASSERTL1(iter != m_pools.end(), "The memory manager is mishandling a memory request for " +
213  std::to_string(bytes) + " bytes of memory.");
214 
215  return (*iter).second->Allocate();
216  }
217  }
218 
219  /// \brief Deallocate memory claimed by an earlier call to allocate.
220  ///
221  /// \attention It is an error to deallocate memory not allocated
222  /// from this pool. Doing this will result in undefined behavior.
223  void Deallocate(void* p, size_t bytes)
224  {
225  if( bytes <= 4 )
226  {
228  }
229  else if( bytes > m_upperBound )
230  {
231 #ifdef NEKTAR_USE_ALIGNED_MEM
232  boost::alignment::aligned_free(p);
233 #else
234  ::operator delete(p);
235 #endif
236  }
237  else
238  {
239  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
240  ASSERTL1(iter != m_pools.end(), "The memory manager is mishandling a memory request for " +
241  std::to_string(bytes) + " bytes of memory.");
242 
243  (*iter).second->Deallocate(p);
244  }
245  }
246 
247  private:
249  std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool> > m_pools;
250  size_t m_upperBound;
251  };
252 
254 }
255 
256 
257 
258 #endif //NEKATAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
259 
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:250
#define LIB_UTILITIES_EXPORT
void Deallocate(void *p, size_t bytes)
Deallocate memory claimed by an earlier call to allocate.
detail::ThreadSpecificPool m_fourBytePool
std::map< size_t, std::shared_ptr< detail::ThreadSpecificPool > > m_pools
std::map< size_t, std::shared_ptr< detail::ThreadSpecificPool > > PoolMapType
void * Allocate(size_t bytes)
Allocate a block of memory of size ByteSize.
void Deallocate(const void *p)
Deallocate memory claimed by an earlier call to allocate.
void * Allocate()
Allocate a block of memory of size ByteSize.
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:1
MemPool & GetMemoryPool()
typename abi< ScalarType >::type simd
Definition: tinysimd.hpp:83