1 /*
2 Copyright 2005-2014 Intel Corporation. All Rights Reserved.
3
4 This file is part of Threading Building Blocks. Threading Building Blocks is free software;
5 you can redistribute it and/or modify it under the terms of the GNU General Public License
6 version 2 as published by the Free Software Foundation. Threading Building Blocks is
7 distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9 See the GNU General Public License for more details. You should have received a copy of
10 the GNU General Public License along with Threading Building Blocks; if not, write to the
11 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12
13 As a special exception, you may use this file as part of a free software library without
14 restriction. Specifically, if other files instantiate templates or use macros or inline
15 functions from this file, or you compile this file and link it with other files to produce
16 an executable, this file does not by itself cause the resulting executable to be covered
17 by the GNU General Public License. This exception does not however invalidate any other
18 reasons why the executable file might be covered by the GNU General Public License.
19 */
20
21 #ifndef __TBB_memory_pool_H
22 #define __TBB_memory_pool_H
23
24 #if !TBB_PREVIEW_MEMORY_POOL
25 #error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h
26 #endif
27 /** @file */
28
29 #include "scalable_allocator.h"
30 #include <new> // std::bad_alloc
31 #if __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
32 #include <utility> // std::forward
33 #endif
34
35 #if __TBB_EXTRA_DEBUG
36 #define __TBBMALLOC_ASSERT ASSERT
37 #else
38 #define __TBBMALLOC_ASSERT(a,b) ((void)0)
39 #endif
40
41 namespace tbb {
42 namespace interface6 {
43 //! @cond INTERNAL
44 namespace internal {
45
46 //! Base of thread-safe pool allocator for variable-size requests
47 class pool_base : tbb::internal::no_copy {
48 // Pool interface is separate from standard allocator classes because it has
49 // to maintain internal state, no copy or assignment. Move and swap are possible.
50 public:
51 //! Reset pool to reuse its memory (free all objects at once)
recycle()52 void recycle() { rml::pool_reset(my_pool); }
53
54 //! The "malloc" analogue to allocate block of memory of size bytes
malloc(size_t size)55 void *malloc(size_t size) { return rml::pool_malloc(my_pool, size); }
56
57 //! The "free" analogue to discard a previously allocated piece of memory.
free(void * ptr)58 void free(void* ptr) { rml::pool_free(my_pool, ptr); }
59
60 //! The "realloc" analogue complementing pool_malloc.
61 // Enables some low-level optimization possibilities
realloc(void * ptr,size_t size)62 void *realloc(void* ptr, size_t size) {
63 return rml::pool_realloc(my_pool, ptr, size);
64 }
65
66 protected:
67 //! destroy pool - must be called in a child class
destroy()68 void destroy() { rml::pool_destroy(my_pool); }
69
70 rml::MemoryPool *my_pool;
71 };
72
73 } // namespace internal
74 //! @endcond
75
76 #if _MSC_VER && !defined(__INTEL_COMPILER)
77 // Workaround for erroneous "unreferenced parameter" warning in method destroy.
78 #pragma warning (push)
79 #pragma warning (disable: 4100)
80 #endif
81
82 //! Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5
83 /** @ingroup memory_allocation */
84 template<typename T, typename P = internal::pool_base>
85 class memory_pool_allocator {
86 protected:
87 typedef P pool_type;
88 pool_type *my_pool;
89 template<typename U, typename R>
90 friend class memory_pool_allocator;
91 template<typename V, typename U, typename R>
92 friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
93 template<typename V, typename U, typename R>
94 friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
95 public:
96 typedef typename tbb::internal::allocator_type<T>::value_type value_type;
97 typedef value_type* pointer;
98 typedef const value_type* const_pointer;
99 typedef value_type& reference;
100 typedef const value_type& const_reference;
101 typedef size_t size_type;
102 typedef ptrdiff_t difference_type;
103 template<typename U> struct rebind {
104 typedef memory_pool_allocator<U, P> other;
105 };
106
throw()107 memory_pool_allocator(pool_type &pool) throw() : my_pool(&pool) {}
throw()108 memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
109 template<typename U>
memory_pool_allocator(const memory_pool_allocator<U,P> & src)110 memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
111
address(reference x)112 pointer address(reference x) const { return &x; }
address(const_reference x)113 const_pointer address(const_reference x) const { return &x; }
114
115 //! Allocate space for n objects.
116 pointer allocate( size_type n, const void* /*hint*/ = 0) {
117 return static_cast<pointer>( my_pool->malloc( n*sizeof(value_type) ) );
118 }
119 //! Free previously allocated block of memory.
deallocate(pointer p,size_type)120 void deallocate( pointer p, size_type ) {
121 my_pool->free(p);
122 }
123 //! Largest value for which method allocate might succeed.
max_size()124 size_type max_size() const throw() {
125 size_type max = static_cast<size_type>(-1) / sizeof (value_type);
126 return (max > 0 ? max : 1);
127 }
128 //! Copy-construct value at location pointed to by p.
129 #if __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
130 template<typename U, typename... Args>
construct(U * p,Args &&...args)131 void construct(U *p, Args&&... args)
132 { ::new((void *)p) U(std::forward<Args>(args)...); }
133 #else // __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
134 #if __TBB_CPP11_RVALUE_REF_PRESENT
construct(pointer p,value_type && value)135 void construct( pointer p, value_type&& value ) {::new((void*)(p)) value_type(std::move(value));}
136 #endif
construct(pointer p,const value_type & value)137 void construct( pointer p, const value_type& value ) { ::new((void*)(p)) value_type(value); }
138 #endif // __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
139
140 //! Destroy value at location pointed to by p.
destroy(pointer p)141 void destroy( pointer p ) { p->~value_type(); }
142
143 };
144
145 #if _MSC_VER && !defined(__INTEL_COMPILER)
146 #pragma warning (pop)
147 #endif // warning 4100 is back
148
149 //! Analogous to std::allocator<void>, as defined in ISO C++ Standard, Section 20.4.1
150 /** @ingroup memory_allocation */
151 template<typename P>
152 class memory_pool_allocator<void, P> {
153 public:
154 typedef P pool_type;
155 typedef void* pointer;
156 typedef const void* const_pointer;
157 typedef void value_type;
158 template<typename U> struct rebind {
159 typedef memory_pool_allocator<U, P> other;
160 };
161
throw()162 memory_pool_allocator( pool_type &pool) throw() : my_pool(&pool) {}
throw()163 memory_pool_allocator( const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
164 template<typename U>
memory_pool_allocator(const memory_pool_allocator<U,P> & src)165 memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
166
167 protected:
168 pool_type *my_pool;
169 template<typename U, typename R>
170 friend class memory_pool_allocator;
171 template<typename V, typename U, typename R>
172 friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
173 template<typename V, typename U, typename R>
174 friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
175 };
176
177 template<typename T, typename U, typename P>
178 inline bool operator==( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool==b.my_pool;}
179
180 template<typename T, typename U, typename P>
181 inline bool operator!=( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool!=b.my_pool;}
182
183
184 //! Thread-safe growable pool allocator for variable-size requests
185 template <typename Alloc>
186 class memory_pool : public internal::pool_base {
187 Alloc my_alloc; // TODO: base-class optimization
188 static void *allocate_request(intptr_t pool_id, size_t & bytes);
189 static int deallocate_request(intptr_t pool_id, void*, size_t raw_bytes);
190
191 public:
192 //! construct pool with underlying allocator
193 memory_pool(const Alloc &src = Alloc());
194
195 //! destroy pool
~memory_pool()196 ~memory_pool() { destroy(); } // call the callbacks first and destroy my_alloc latter
197
198 };
199
200 class fixed_pool : public internal::pool_base {
201 void *my_buffer;
202 size_t my_size;
203 inline static void *allocate_request(intptr_t pool_id, size_t & bytes);
204
205 public:
206 //! construct pool with underlying allocator
207 inline fixed_pool(void *buf, size_t size);
208 //! destroy pool
~fixed_pool()209 ~fixed_pool() { destroy(); }
210 };
211
212 //////////////// Implementation ///////////////
213
214 template <typename Alloc>
memory_pool(const Alloc & src)215 memory_pool<Alloc>::memory_pool(const Alloc &src) : my_alloc(src) {
216 rml::MemPoolPolicy args(allocate_request, deallocate_request,
217 sizeof(typename Alloc::value_type));
218 rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool);
219 if( res!=rml::POOL_OK ) __TBB_THROW(std::bad_alloc());
220 }
221 template <typename Alloc>
allocate_request(intptr_t pool_id,size_t & bytes)222 void *memory_pool<Alloc>::allocate_request(intptr_t pool_id, size_t & bytes) {
223 memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id);
224 const size_t unit_size = sizeof(typename Alloc::value_type);
225 __TBBMALLOC_ASSERT( 0 == bytes%unit_size, NULL);
226 void *ptr;
227 __TBB_TRY { ptr = self.my_alloc.allocate( bytes/unit_size ); }
228 __TBB_CATCH(...) { return 0; }
229 return ptr;
230 }
231 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
232 // Workaround for erroneous "unreachable code" warning in the template below.
233 // Specific for VC++ 17-18 compiler
234 #pragma warning (push)
235 #pragma warning (disable: 4702)
236 #endif
237 template <typename Alloc>
deallocate_request(intptr_t pool_id,void * raw_ptr,size_t raw_bytes)238 int memory_pool<Alloc>::deallocate_request(intptr_t pool_id, void* raw_ptr, size_t raw_bytes) {
239 memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id);
240 const size_t unit_size = sizeof(typename Alloc::value_type);
241 __TBBMALLOC_ASSERT( 0 == raw_bytes%unit_size, NULL);
242 self.my_alloc.deallocate( static_cast<typename Alloc::value_type*>(raw_ptr), raw_bytes/unit_size );
243 return 0;
244 }
245 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
246 #pragma warning (pop)
247 #endif
fixed_pool(void * buf,size_t size)248 inline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) {
249 if( !buf || !size ) __TBB_THROW(std::bad_alloc());
250 rml::MemPoolPolicy args(allocate_request, 0, size, /*fixedPool=*/true);
251 rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool);
252 if( res!=rml::POOL_OK ) __TBB_THROW(std::bad_alloc());
253 }
allocate_request(intptr_t pool_id,size_t & bytes)254 inline void *fixed_pool::allocate_request(intptr_t pool_id, size_t & bytes) {
255 fixed_pool &self = *reinterpret_cast<fixed_pool*>(pool_id);
256 __TBBMALLOC_ASSERT(0 != self.my_size, "The buffer must not be used twice.");
257 bytes = self.my_size;
258 self.my_size = 0; // remember that buffer has been used
259 return self.my_buffer;
260 }
261
262 } //namespace interface6
263 using interface6::memory_pool_allocator;
264 using interface6::memory_pool;
265 using interface6::fixed_pool;
266 } //namespace tbb
267
268 #undef __TBBMALLOC_ASSERT
269 #endif// __TBB_memory_pool_H
270