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 <mutex>
48#endif
49
50#ifdef NEKTAR_USE_ALIGNED_MEM
53#include <boost/align/aligned_alloc.hpp>
54#endif
55
56#include <cstring>
57
58namespace Nektar
59{
60namespace 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{
87public:
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 std::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 std::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
140private:
141 // boost::thread_specific_ptr<boost::pool<> > m_pool;
142 boost::pool<> *m_pool;
144#ifdef NEKTAR_USE_THREAD_SAFETY
145 std::mutex m_mutex;
146#endif
147};
148} // namespace detail
149
151{
152public:
153 typedef std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool>>
155
156public:
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>(
170 m_pools.insert(PairType(16, std::shared_ptr<detail::ThreadSpecificPool>(
172 m_pools.insert(PairType(32, std::shared_ptr<detail::ThreadSpecificPool>(
174 m_pools.insert(PairType(64, std::shared_ptr<detail::ThreadSpecificPool>(
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
260private:
262 std::map<size_t, std::shared_ptr<detail::ThreadSpecificPool>> m_pools;
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:242
#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.
MemPool & GetMemoryPool()
typename abi< ScalarType, width >::type simd
Definition: tinysimd.hpp:80