Nektar++
NekMemoryManager.hpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: NekMemoryManager.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 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description:
33 //
34 // A memory manager that allocates memory from thread specific pools.
35 //
36 ////////////////////////////////////////////////////////////////////////////////
37 
38 #ifndef NEKTAR_LIB_UTILITIES_NEK_MEMORY_MANAGER_H
39 #define NEKTAR_LIB_UTILITIES_NEK_MEMORY_MANAGER_H
40 
44 
45 #include <boost/mpl/contains.hpp>
46 #include <boost/mpl/list_c.hpp>
47 #include <boost/shared_ptr.hpp>
48 #include <boost/utility/enable_if.hpp>
49 #include <boost/mpl/integral_c.hpp>
50 #include <boost/bind.hpp>
51 #include <boost/type_traits.hpp>
52 
53 #include <boost/preprocessor/repetition/enum_params.hpp>
54 #include <boost/preprocessor/repetition/repeat_from_to.hpp>
55 #include <boost/preprocessor/repetition/enum_trailing.hpp>
56 #include <boost/preprocessor/repetition/enum_binary_params.hpp>
57 #include <boost/preprocessor/repetition/enum_trailing_params.hpp>
58 #include <boost/preprocessor/control/if.hpp>
59 #include <boost/preprocessor/facilities/empty.hpp>
60 #include <boost/preprocessor/comparison/equal.hpp>
61 #include <boost/preprocessor/punctuation/comma.hpp>
62 #include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
63 
64 #include <vector>
65 
66 #ifdef max
67 #undef max
68 #endif
69 
70 namespace Nektar
71 {
72 
73 #ifndef NEKTAR_MAX_MEMORY_MANAGER_CONSTRUCTOR_ARGS
74 #define NEKTAR_MAX_MEMORY_MANAGER_CONSTRUCTOR_ARGS 15
75 #endif //NEKTAR_MAX_MEMORY_MANAGER_CONSTRUCTOR_ARGS
76 
77  /// @brief General purpose memory allocation routines with the ability
78  /// to allocate from thread specific memory pools.
79  ///
80  /// If compiled with NEKTAR_MEMORY_POOL_ENABLED, the MemoryManager
81  /// allocates from thread specific memory pools for small objects.
82  /// Large objects are managed with the system supplied new/delete.
83  /// These memory pools provide faster allocation and deallocation
84  /// of small objects (particularly useful for shared pointers which
85  /// allocate many 4 byte objects).
86  ///
87  /// @warning All memory allocated from the memory manager must be returned
88  /// to the memory manager. Calling delete on memory allocated from the
89  /// manager will likely cause undefined behavior. A particularly subtle
90  /// violation of this rule occurs when giving memory allocated from the
91  /// manager to a shared pointer.
92  /// @code
93  /// boost::shared_ptr<Obj> f(MemoryManager<Obj>::Allocate());
94  /// @endcode
95  /// Shared pointers call delete when they go out of scope, so this line of
96  /// code will cause problems. Instead, you should call the
97  /// AllocateSharedPtr method:
98  /// @code
99  /// boost::shared_ptr<Obj> f = MemoryManager<Obj>::AllocateSharedPtr();
100  /// @endcode
101  template<typename DataType>
103  {
104  private:
105  template<typename ObjectType, typename CustomDeallocator>
107  {
108  public:
109  explicit DeallocateSharedPtr(const CustomDeallocator& d) :
110  m_dealloc(d)
111  {
112  }
113 
114  void operator()(ObjectType*& m) const
115  {
116  m_dealloc();
118  }
119 
120  private:
121  CustomDeallocator m_dealloc;
122 
123  };
124 
126  {
127  public:
128  void operator()() const
129  {
130  }
131  };
132 
133  public:
134  /// @brief Deallocate a pointer allocated by
135  /// MemoryManager::Allocate.
136  /// @note Results are undefined if called with a pointer to
137  /// something that was not allocated with the memory manager.
138  ///
139  /// Use this method to deallocate a pointer you have allocated from
140  /// the MemoryManager using the Allocate method.
141  ///
142  /// Example:
143  /// @code
144  /// CustObj* c = MemoryManager::Allocate<CustObj>();
145  /// MemoryManager::Deallocate(c);
146  /// @endcode
147  static void Deallocate(DataType*& data)
148  {
149  #ifdef NEKTAR_MEMORY_POOL_ENABLED
150  data->~DataType();
151  GetMemoryPool().Deallocate(data, sizeof(DataType));
152  #else //NEKTAR_MEMORY_POOL_ENABLED
153  delete data;
154  #endif //NEKTAR_MEMORY_POOL_ENABLED
155 
156  data = NULL;
157  }
158 
159  #ifdef NEKTAR_MEMORY_POOL_ENABLED
160  /// @brief Allocates a single object from the memory pool.
161  /// @throws unknown If the object throws an exception during
162  /// construction, this method will catch it, release the memory
163  /// back to the pool, then rethrow it.
164  ///
165  /// The allocated object must be returned to the memory pool
166  /// via Deallocate.
167  static DataType* Allocate()
168  {
169  DataType* result = static_cast<DataType*>(GetMemoryPool().Allocate(sizeof(DataType)));
170 
171  if( result )
172  {
173  try
174  {
175  new (result) DataType();
176  }
177  catch(...)
178  {
179  GetMemoryPool().Deallocate(result,
180  sizeof(DataType));
181  throw;
182  }
183  }
184 
185  return result;
186  }
187  #define ALLOCATE_METHOD_GENERATOR(z, i, methodName) \
188  template<BOOST_PP_ENUM_PARAMS(i, typename Arg)> \
189  static DataType* methodName(BOOST_PP_ENUM_BINARY_PARAMS(i, const Arg, & arg)) \
190  { \
191  DataType* result = static_cast<DataType*>(GetMemoryPool().Allocate(sizeof(DataType))); \
192  \
193  if( result ) \
194  { \
195  try \
196  { \
197  new (result) DataType(BOOST_PP_ENUM_PARAMS(i, arg)); \
198  } \
199  catch(...) \
200  { \
201  GetMemoryPool().Deallocate(result, sizeof(DataType)); \
202  throw; \
203  } \
204  } \
205  \
206  return result; \
207  }
208  #else //NEKTAR_MEMORY_POOL_ENABLED
209  /// @brief Allocates a single object from the memory pool.
210  /// @throws unknown Any exception thrown by DataType's default
211  /// constructor will propogate through this method.
212  ///
213  /// The allocated object must be returned to the memory pool
214  /// via Deallocate.
215  static DataType* Allocate()
216  {
217  return new DataType();
218  }
219 
220  #define ALLOCATE_METHOD_GENERATOR(z, i, methodName) \
221  template<BOOST_PP_ENUM_PARAMS(i, typename Arg)> \
222  static DataType* methodName(BOOST_PP_ENUM_BINARY_PARAMS(i, const Arg, & arg)) \
223  { \
224  return new DataType(BOOST_PP_ENUM_PARAMS(i, arg)); \
225  }
226  #endif //NEKTAR_MEMORY_POOL_ENABLED
227 
229 
230  /// @brief Allocate a shared pointer from the memory pool.
231  ///
232  /// The shared pointer does not need to be returned to the memory
233  /// pool. When the reference count to this object reaches 0, the
234  /// shared pointer will automatically return the memory.
235  static boost::shared_ptr<DataType> AllocateSharedPtr()
236  {
237  return AllocateSharedPtrD(DefaultCustomDeallocator());
238  }
239 
240  /// @def ALLOCATE_SHARED_PTR_METHOD_GENERATOR
241  /// @brief Generator for allocating shared pointers to objects with
242  /// constructors having a variable number of parameters.
243  ///
244  /// Uses the Boost preprocessor macros
245  /// - BOOST_PP_ENUM_PARAMS(idx, text) which generates a list of
246  /// parameters, in this case: "typename Arg0, typename Arg1, ..."
247  /// used for the template definition.
248  /// - BOOST_PP_ENUM_BINARY_PARAMS(idx, type, name) which generates
249  /// a list of parameters and variables, in this case:
250  /// "Arg0& arg0, Arg1& arg1, ..." used for the function prototype.
251  ///
252  /// @note All the parameter lists are references so whenever the
253  /// MemoryManager::AllocateSharedPtr(...) is used, all parameters
254  /// must be passed <b>by reference</b>. Consequently, fundamental
255  /// datatype parameters must be defined locally first. So
256  /// @code
257  /// MemoryManager::AllocateSharedPtr(Var, true);
258  /// @endcode
259  /// must be replaced by
260  /// @code
261  /// bool flag = true;
262  /// MemoryManager::AllocateSharedPtr(Var, flag);
263  /// @endcode
264  #define ALLOCATE_SHARED_PTR_METHOD_GENERATOR(z, i, methodName) \
265  template<BOOST_PP_ENUM_PARAMS(i, typename Arg)> \
266  static boost::shared_ptr<DataType> methodName(BOOST_PP_ENUM_BINARY_PARAMS(i, const Arg, & arg)) \
267  { \
268  return AllocateSharedPtrD(DefaultCustomDeallocator(), BOOST_PP_ENUM_PARAMS(i, arg)); \
269  }
270 
271  #define ALLOCATE_SHARED_PTR_METHOD_WITH_DEALLOCATOR_GENERATOR(z, i, methodName) \
272  template<typename DeallocatorType BOOST_PP_ENUM_TRAILING_PARAMS(i, typename Arg)> \
273  static boost::shared_ptr<DataType> methodName(const DeallocatorType& d BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(i, Arg, & arg)) \
274  { \
275  DataType* data = Allocate(BOOST_PP_ENUM_PARAMS(i, arg)); \
276  return boost::shared_ptr<DataType>(data, DeallocateSharedPtr<DataType, DeallocatorType>(d)); \
277  }
278 
281 
282 
283  /// \brief Allocates a chunk of raw, uninitialized memory, capable of holding NumberOfElements objects.
284  /// \param NumberOfElements The number of elements the array should be capable of holding.
285  ///
286  /// This method is not meant to be called by client code. Use Array instead.
287  /// Any memory allocated from this method must be returned to the memory pool
288  /// via RawDeallocate. Failure to do so will result in memory leaks and undefined
289  /// behavior.
290  static DataType* RawAllocate(unsigned int NumberOfElements)
291  {
292  #ifdef NEKTAR_MEMORY_POOL_ENABLED
293  return static_cast<DataType*>(GetMemoryPool().Allocate(sizeof(DataType)*NumberOfElements));
294  #else //NEKTAR_MEMORY_POOL_ENABLED
295  return static_cast<DataType*>(::operator new(NumberOfElements * sizeof(DataType)));
296  #endif //NEKTAR_MEMORY_POOL_ENABLED
297  }
298 
299 
300  /// \brief Deallocates memory allocated from RawAllocate.
301  /// \param array A pointer to the memory returned from RawAllocate.
302  /// \param NumberOfElements The number of object held in the array.
303  ///
304  /// This method is not meant to be called by client code. Use Array instead.
305  /// Only memory allocated via RawAllocate should be returned to the pool here.
306  static void RawDeallocate(DataType* array, unsigned int NumberOfElements)
307  {
308  #ifdef NEKTAR_MEMORY_POOL_ENABLED
309  GetMemoryPool().Deallocate(array, sizeof(DataType)*NumberOfElements);
310  #else //NEKTAR_MEMORY_POOL_ENABLED
311  ::operator delete(array);
312  #endif //NEKTAR_MEMORY_POOL_ENABLED
313 
314  }
315 
316  /////////////////////////////////////////////////////////////////
317  ///\name Allocator Interface
318  /// The allocator interface allows a MemoryManager object to be used
319  /// in any object that allows an allocator parameter, such as STL
320  /// containers.
321  /////////////////////////////////////////////////////////////////
322  typedef DataType value_type;
323  typedef size_t size_type;
324  typedef ptrdiff_t difference_type;
325  typedef DataType* pointer;
326  typedef const DataType* const_pointer;
327  typedef DataType& reference;
328  typedef const DataType& const_reference;
329 
331  template<typename T>
334 
335  pointer address(reference r) const { return &r; }
336  const_pointer address(const_reference r) const { return &r; }
337 
338  pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)//typename MemoryManager<void>::pointer hint = 0)
339  {
340  return RawAllocate(n);
341  }
342 
343  void deallocate(pointer p, size_type n)
344  {
345  return RawDeallocate(p, n);
346  }
347 
348  void construct(pointer p, const_reference val)
349  {
350  new(p) DataType(val);
351  }
352 
353  void destroy(pointer p)
354  {
355  p->~DataType();
356  }
357 
358  size_type max_size()
359  {
360  return std::numeric_limits<size_type>::max()/sizeof(DataType);
361  }
362 
363  template<typename U>
364  struct rebind
365  {
367  };
368 
369  /////////////////////////////////////////////////////////////////
370  ///@}
371  /////////////////////////////////////////////////////////////////
372 
373  private:
374 
375  };
376 
377  template<typename DataType>
379  {
380  return true;
381  }
382 
383  template<typename DataType>
385  {
386  return !(lhs == rhs);
387  }
388 
389 }
390 
391 #endif //NEKTAR_LIB_UTILITIES_NEK_MEMORY_MANAGER_H
392 
393 
static boost::shared_ptr< DataType > AllocateSharedPtr()
Allocate a shared pointer from the memory pool.
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
#define ALLOCATE_SHARED_PTR_METHOD_GENERATOR(z, i, methodName)
Generator for allocating shared pointers to objects with constructors having a variable number of par...
void Deallocate(void *p, size_t bytes)
Deallocate memory claimed by an earlier call to allocate.
bool operator==(const Array< OneD, NekDouble > &lhs, const Array< OneD, NekDouble > &rhs)
DeallocateSharedPtr(const CustomDeallocator &d)
pointer address(reference r) const
const DataType * const_pointer
#define ALLOCATE_METHOD_GENERATOR(z, i, methodName)
StandardMatrixTag & lhs
void * Allocate(size_t bytes)
Allocate a block of memory of size ByteSize.
void deallocate(pointer p, size_type n)
static void RawDeallocate(DataType *array, unsigned int NumberOfElements)
Deallocates memory allocated from RawAllocate.
static DataType * Allocate()
Allocates a single object from the memory pool.
bool operator!=(const Array< OneD, T1 > &lhs, const Array< OneD, T2 > &rhs)
pointer allocate(size_type n, std::allocator< void >::const_pointer hint=0)
void construct(pointer p, const_reference val)
#define NEKTAR_MAX_MEMORY_MANAGER_CONSTRUCTOR_ARGS
MemPool & GetMemoryPool()
const DataType & const_reference
MemoryManager(const MemoryManager< T > &rhs)
static void Deallocate(DataType *&data)
Deallocate a pointer allocated by MemoryManager::Allocate.
BOOST_PP_REPEAT_FROM_TO(1, NEKTAR_MAX_MEMORY_MANAGER_CONSTRUCTOR_ARGS, ALLOCATE_METHOD_GENERATOR, Allocate)
static DataType * RawAllocate(unsigned int NumberOfElements)
Allocates a chunk of raw, uninitialized memory, capable of holding NumberOfElements objects...
#define ALLOCATE_SHARED_PTR_METHOD_WITH_DEALLOCATOR_GENERATOR(z, i, methodName)
const_pointer address(const_reference r) const