Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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 // 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 //
35 ////////////////////////////////////////////////////////////////////////////////
36 
37 
38 #ifndef NEKATAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
39 #define NEKATAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
40 
41 #include <boost/thread/tss.hpp>
42 #include <boost/pool/pool.hpp>
43 #include <boost/thread/mutex.hpp>
44 
45 #include <loki/Singleton.h>
46 #include <map>
49 
50 #include <cstring>
51 
52 namespace Nektar
53 {
54  namespace detail
55  {
56  /// \internal
57  /// \brief A memory pool which exists on a thread by thread basis.
58  /// \param ByteSize The number of bytes in each chunk allocated by the pool.
59  ///
60  /// Provides a simple, thread specific memory pool that is based on byte size.
61  /// The pool allocates and deallocates raw memory - the user is responsible for
62  /// calling appropriate constructors/destructors when allocating objects.
63  ///
64  /// Example:
65  ///
66  /// \code
67  /// ThreadSpecificPool<sizeof(TestClass)> pool;
68  /// void* memory = pool.allocate();
69  ///
70  /// // Construct the object in the memory returned by the pool.
71  /// TestClass* t = new (memory) TestClass;
72  ///
73  /// // Do stuff with t.
74  ///
75  /// // Destruct t and return it.
76  /// t->~TestClass();
77  /// pool.deallocate(t);
78  /// \endcode
80  {
81  public:
82  ThreadSpecificPool(size_t ByteSize) :
83  m_pool(),
84  m_blockSize(ByteSize),
85  m_mutex()
86  {
87  // We can do the new in the constructor list because the thread specific
88  // pointer doesn't have a supporting constructor.
89  m_pool = new boost::pool<>(m_blockSize);
90  }
91 
93  {
94  // The documentation isn't particularly clear if delete needs to be called manually
95  // or if the thread specific pointer will call delete for me. Looking through the
96  // boost code doesn't make it any clearer.
97  }
98 
99  /// \brief Allocate a block of memory of size ByteSize.
100  /// \throw std::bad_alloc if memory is exhausted.
101  void* Allocate()
102  {
103  boost::mutex::scoped_lock l(m_mutex);
104  void* result = m_pool->malloc();
105 
106 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
107  memset(result, 0, m_blockSize);
108 #endif //defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
109 
110  return result;
111  }
112 
113  /// \brief Deallocate memory claimed by an earlier call to allocate.
114  ///
115  /// \attention It is an error to deallocate memory not allocated
116  /// from this pool. Doing this will result in undefined behavior.
117  void Deallocate(const void* p)
118  {
119  boost::mutex::scoped_lock l(m_mutex);
120 #if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
121  // The idea here is to fill the returned memory with some known
122  // pattern, then detect that pattern on the allocate. If the
123  // pattern is no longer there then some memory corruption has
124  // occurred. However, I'm not sure how to distinguish between first
125  // time allocations and repeat allocations.
126 
127  //memset(p, '+', m_pool->get_requested_size());
128 #endif //defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
129 
130  m_pool->free(const_cast<void*>(p));
131  }
132 
133 
134  private:
135  //boost::thread_specific_ptr<boost::pool<> > m_pool;
136  boost::pool<>* m_pool;
137  size_t m_blockSize;
138  boost::mutex m_mutex;
139  };
140  }
141 
142  class MemPool
143  {
144  public:
145  typedef std::map<size_t, boost::shared_ptr<detail::ThreadSpecificPool> > PoolMapType;
146 
147  public:
149  m_fourBytePool(4),
150  m_pools(),
151  m_upperBound(1024)
152  {
153  // The m_pools data member stores a collection of thread specific pools of varying size. All memory requests
154  // up to and including the largest pool size will be allocated from a pool (note that this means you may receive
155  // more memory than you asked for). For example, if there is a pool for 8 bytes and the next largest pool is 32
156  // bytes, then a request for 10 bytes will return a 32 byte chunk of memory from the 32 byte pool.
157 
158  typedef PoolMapType::value_type PairType;
159  m_pools.insert(PairType(8, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(8))));
160  m_pools.insert(PairType(16, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(16))));
161  m_pools.insert(PairType(32, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(32))));
162  m_pools.insert(PairType(64, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(64))));
163  m_pools.insert(PairType(128, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(128))));
164  m_pools.insert(PairType(256, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(256))));
165  m_pools.insert(PairType(512, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(512))));
166  m_pools.insert(PairType(1024, boost::shared_ptr<detail::ThreadSpecificPool>(new detail::ThreadSpecificPool(1024))));
167  }
168 
170  {
171  }
172 
173  /// \brief Allocate a block of memory of size ByteSize.
174  /// \throw std::bad_alloc if memory is exhausted.
175  /// \param bytes The number of bytes to allocate.
176  ///
177  /// If the bytes parameter specifies a size that is handled by memory pools then the memory
178  /// is allocated from the pool. Otherwise the memory is allocated with a call to new.
179  ///
180  /// Important: All memory allocated from this method must be returned to the pool
181  /// via the Deallocate method. Deleting pointers allocated from the memory pool with the
182  /// delete operator will result in undefined behavior.
183  void* Allocate(size_t bytes)
184  {
185  if( bytes <= 4 )
186  {
187  return m_fourBytePool.Allocate();
188  }
189  else if( bytes > m_upperBound )
190  {
191  return ::operator new(bytes);
192  }
193  else
194  {
195  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
196  ASSERTL1(iter != m_pools.end(), "The memory manager is mishandling a memory request for " +
197  boost::lexical_cast<std::string>(bytes) + " bytes of memory.");
198 
199  return (*iter).second->Allocate();
200  }
201  }
202 
203  /// \brief Deallocate memory claimed by an earlier call to allocate.
204  ///
205  /// \attention It is an error to deallocate memory not allocated
206  /// from this pool. Doing this will result in undefined behavior.
207  void Deallocate(void* p, size_t bytes)
208  {
209  if( bytes <= 4 )
210  {
212  }
213  else if( bytes > m_upperBound )
214  {
215  ::operator delete(p);
216  }
217  else
218  {
219  PoolMapType::iterator iter = m_pools.lower_bound(bytes);
220  ASSERTL1(iter != m_pools.end(), "The memory manager is mishandling a memory request for " +
221  boost::lexical_cast<std::string>(bytes) + " bytes of memory.");
222 
223  (*iter).second->Deallocate(p);
224  }
225  }
226 
227  private:
229  std::map<size_t, boost::shared_ptr<detail::ThreadSpecificPool> > m_pools;
230  size_t m_upperBound;
231  };
232 
234 
235 }
236 
237 
238 
239 #endif //NEKATAR_LIB_UTILITES_THREAD_SPECIFIC_POOL_HPP
240