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