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 
43 #include <map>
44 #include <memory>
45 
46 #ifdef NEKTAR_USE_THREAD_SAFETY
47 #include <boost/thread/mutex.hpp>
48 #endif
49 
50 #ifdef NEKTAR_USE_ALIGNED_MEM
53 #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) : m_pool(), m_blockSize(ByteSize)
89  {
90  // We can't do the new in the constructor list because the
91  // thread specific pointer doesn't have a supporting
92  // constructor.
93  m_pool = new boost::pool<>(m_blockSize);
94  }
95 
97  {
98  // Need to call delete manually, otherwise memory is leaking.
99  delete m_pool;
100  }
101 
102  /// \brief Allocate a block of memory of size ByteSize.
103  /// \throw std::bad_alloc if memory is exhausted.
104  void *Allocate()
105  {
106 #ifdef NEKTAR_USE_THREAD_SAFETY
107  boost::mutex::scoped_lock l(m_mutex);
108 #endif
109  void *result = m_pool->malloc();
110 
111 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
112  memset(result, 0, m_blockSize);
113 #endif // defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
114 
115  return result;
116  }
117 
118  /// \brief Deallocate memory claimed by an earlier call to allocate.
119  ///
120  /// \attention It is an error to deallocate memory not allocated
121  /// from this pool. Doing this will result in undefined behavior.
122  void Deallocate(const void *p)
123  {
124 #ifdef NEKTAR_USE_THREAD_SAFETY
125  boost::mutex::scoped_lock l(m_mutex);
126 #endif
127 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
128  // The idea here is to fill the returned memory with some known
129  // pattern, then detect that pattern on the allocate. If the
130  // pattern is no longer there then some memory corruption has
131  // occurred. However, I'm not sure how to distinguish between first
132  // time allocations and repeat allocations.
133 
134  // memset(p, '+', m_pool->get_requested_size());
135 #endif // defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
136 
137  m_pool->free(const_cast<void *>(p));
138  }
139 
140 private:
141  // boost::thread_specific_ptr<boost::pool<> > m_pool;
142  boost::pool<> *m_pool;
143  size_t m_blockSize;
144 #ifdef NEKTAR_USE_THREAD_SAFETY
145  boost::mutex m_mutex;
146 #endif
147 };
148 } // namespace detail
149 
150 class MemPool
151 {
152 public:
153  typedef std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool>>
155 
156 public:
158  {
159  // The m_pools data member stores a collection of thread specific pools
160  // of varying size. All memory requests up to and including the largest
161  // pool size will be allocated from a pool (note that this means you may
162  // receive more memory than you asked for). For example, if there is a
163  // pool for 8 bytes and the next largest pool is 32 bytes, then a
164  // request for 10 bytes will return a 32 byte chunk of memory from the
165  // 32 byte pool.
166 
167  typedef PoolMapType::value_type PairType;
168  m_pools.insert(PairType(8, std::shared_ptr<detail::ThreadSpecificPool>(
169  new detail::ThreadSpecificPool(8))));
170  m_pools.insert(PairType(16, std::shared_ptr<detail::ThreadSpecificPool>(
171  new detail::ThreadSpecificPool(16))));
172  m_pools.insert(PairType(32, std::shared_ptr<detail::ThreadSpecificPool>(
173  new detail::ThreadSpecificPool(32))));
174  m_pools.insert(PairType(64, std::shared_ptr<detail::ThreadSpecificPool>(
175  new detail::ThreadSpecificPool(64))));
176  m_pools.insert(
177  PairType(128, std::shared_ptr<detail::ThreadSpecificPool>(
178  new detail::ThreadSpecificPool(128))));
179  m_pools.insert(
180  PairType(256, std::shared_ptr<detail::ThreadSpecificPool>(
181  new detail::ThreadSpecificPool(256))));
182  m_pools.insert(
183  PairType(512, std::shared_ptr<detail::ThreadSpecificPool>(
184  new detail::ThreadSpecificPool(512))));
185  m_pools.insert(
186  PairType(1024, std::shared_ptr<detail::ThreadSpecificPool>(
187  new detail::ThreadSpecificPool(1024))));
188  }
189 
191  {
192  }
193 
194  /// \brief Allocate a block of memory of size ByteSize.
195  /// \throw std::bad_alloc if memory is exhausted.
196  /// \param bytes The number of bytes to allocate.
197  ///
198  /// If the bytes parameter specifies a size that is handled by memory pools
199  /// then the memory is allocated from the pool. Otherwise the memory is
200  /// allocated with a call to new.
201  ///
202  /// Important: All memory allocated from this method must be returned to the
203  /// pool via the Deallocate method. Deleting pointers allocated from the
204  /// memory pool with the delete operator will result in undefined behavior.
205  void *Allocate(size_t bytes)
206  {
207  if (bytes <= 4)
208  {
209  return m_fourBytePool.Allocate();
210  }
211  else if (bytes > m_upperBound)
212  {
213 #ifdef NEKTAR_USE_ALIGNED_MEM
214  return boost::alignment::aligned_alloc(
216 #else
217  return ::operator new(bytes);
218 #endif
219  }
220  else
221  {
222  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
223  ASSERTL1(iter != m_pools.end(),
224  "The memory manager is mishandling a memory request for " +
225  std::to_string(bytes) + " bytes of memory.");
226 
227  return (*iter).second->Allocate();
228  }
229  }
230 
231  /// \brief Deallocate memory claimed by an earlier call to allocate.
232  ///
233  /// \attention It is an error to deallocate memory not allocated
234  /// from this pool. Doing this will result in undefined behavior.
235  void Deallocate(void *p, size_t bytes)
236  {
237  if (bytes <= 4)
238  {
240  }
241  else if (bytes > m_upperBound)
242  {
243 #ifdef NEKTAR_USE_ALIGNED_MEM
244  boost::alignment::aligned_free(p);
245 #else
246  ::operator delete(p);
247 #endif
248  }
249  else
250  {
251  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
252  ASSERTL1(iter != m_pools.end(),
253  "The memory manager is mishandling a memory request for " +
254  std::to_string(bytes) + " bytes of memory.");
255 
256  (*iter).second->Deallocate(p);
257  }
258  }
259 
260 private:
262  std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool>> m_pools;
263  size_t m_upperBound;
264 };
265 
267 } // namespace Nektar
268 
269 #endif // NEKATAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
Definition: ErrorUtil.hpp:249
#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:2
MemPool & GetMemoryPool()
typename abi< ScalarType, width >::type simd
Definition: tinysimd.hpp:80