1 /*
2 Copyright (c) 2005-2022 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #ifndef __TBB_memory_pool_H
18 #define __TBB_memory_pool_H
19
20 #if !TBB_PREVIEW_MEMORY_POOL
21 #error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h
22 #endif
23 /** @file */
24
25 #include "scalable_allocator.h"
26
27 #include <new> // std::bad_alloc
28 #include <stdexcept> // std::runtime_error, std::invalid_argument
29 #include <utility> // std::forward
30
31
32 #if __TBB_EXTRA_DEBUG
33 #define __TBBMALLOC_ASSERT ASSERT
34 #else
35 #define __TBBMALLOC_ASSERT(a,b) ((void)0)
36 #endif
37
38 namespace tbb {
39 namespace detail {
40 namespace d1 {
41
42 //! Base of thread-safe pool allocator for variable-size requests
43 class pool_base : no_copy {
44 // Pool interface is separate from standard allocator classes because it has
45 // to maintain internal state, no copy or assignment. Move and swap are possible.
46 public:
47 //! Reset pool to reuse its memory (free all objects at once)
recycle()48 void recycle() { rml::pool_reset(my_pool); }
49
50 //! The "malloc" analogue to allocate block of memory of size bytes
malloc(size_t size)51 void *malloc(size_t size) { return rml::pool_malloc(my_pool, size); }
52
53 //! The "free" analogue to discard a previously allocated piece of memory.
free(void * ptr)54 void free(void* ptr) { rml::pool_free(my_pool, ptr); }
55
56 //! The "realloc" analogue complementing pool_malloc.
57 // Enables some low-level optimization possibilities
realloc(void * ptr,size_t size)58 void *realloc(void* ptr, size_t size) {
59 return rml::pool_realloc(my_pool, ptr, size);
60 }
61
62 protected:
63 //! destroy pool - must be called in a child class
destroy()64 void destroy() { rml::pool_destroy(my_pool); }
65
66 rml::MemoryPool *my_pool;
67 };
68
69 #if _MSC_VER && !defined(__INTEL_COMPILER)
70 // Workaround for erroneous "unreferenced parameter" warning in method destroy.
71 #pragma warning (push)
72 #pragma warning (disable: 4100)
73 #endif
74
75 //! Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5
76 /** @ingroup memory_allocation */
77 template<typename T, typename P = pool_base>
78 class memory_pool_allocator {
79 protected:
80 typedef P pool_type;
81 pool_type *my_pool;
82 template<typename U, typename R>
83 friend class memory_pool_allocator;
84 template<typename V, typename U, typename R>
85 friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
86 template<typename V, typename U, typename R>
87 friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
88 public:
89 typedef T value_type;
90 typedef value_type* pointer;
91 typedef const value_type* const_pointer;
92 typedef value_type& reference;
93 typedef const value_type& const_reference;
94 typedef size_t size_type;
95 typedef ptrdiff_t difference_type;
96 template<typename U> struct rebind {
97 typedef memory_pool_allocator<U, P> other;
98 };
99
memory_pool_allocator(pool_type & pool)100 explicit memory_pool_allocator(pool_type &pool) throw() : my_pool(&pool) {}
throw()101 memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
102 template<typename U>
memory_pool_allocator(const memory_pool_allocator<U,P> & src)103 memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
104
address(reference x)105 pointer address(reference x) const { return &x; }
address(const_reference x)106 const_pointer address(const_reference x) const { return &x; }
107
108 //! Allocate space for n objects.
109 pointer allocate( size_type n, const void* /*hint*/ = nullptr) {
110 pointer p = static_cast<pointer>( my_pool->malloc( n*sizeof(value_type) ) );
111 if (!p)
112 throw_exception(std::bad_alloc());
113 return p;
114 }
115 //! Free previously allocated block of memory.
deallocate(pointer p,size_type)116 void deallocate( pointer p, size_type ) {
117 my_pool->free(p);
118 }
119 //! Largest value for which method allocate might succeed.
max_size()120 size_type max_size() const throw() {
121 size_type max = static_cast<size_type>(-1) / sizeof (value_type);
122 return (max > 0 ? max : 1);
123 }
124 //! Copy-construct value at location pointed to by p.
125
126 template<typename U, typename... Args>
construct(U * p,Args &&...args)127 void construct(U *p, Args&&... args)
128 { ::new((void *)p) U(std::forward<Args>(args)...); }
129
130 //! Destroy value at location pointed to by p.
destroy(pointer p)131 void destroy( pointer p ) { p->~value_type(); }
132
133 };
134
135 #if _MSC_VER && !defined(__INTEL_COMPILER)
136 #pragma warning (pop)
137 #endif // warning 4100 is back
138
139 //! Analogous to std::allocator<void>, as defined in ISO C++ Standard, Section 20.4.1
140 /** @ingroup memory_allocation */
141 template<typename P>
142 class memory_pool_allocator<void, P> {
143 public:
144 typedef P pool_type;
145 typedef void* pointer;
146 typedef const void* const_pointer;
147 typedef void value_type;
148 template<typename U> struct rebind {
149 typedef memory_pool_allocator<U, P> other;
150 };
151
memory_pool_allocator(pool_type & pool)152 explicit memory_pool_allocator( pool_type &pool) throw() : my_pool(&pool) {}
throw()153 memory_pool_allocator( const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
154 template<typename U>
memory_pool_allocator(const memory_pool_allocator<U,P> & src)155 memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
156
157 protected:
158 pool_type *my_pool;
159 template<typename U, typename R>
160 friend class memory_pool_allocator;
161 template<typename V, typename U, typename R>
162 friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
163 template<typename V, typename U, typename R>
164 friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
165 };
166
167 template<typename T, typename U, typename P>
168 inline bool operator==( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool==b.my_pool;}
169
170 template<typename T, typename U, typename P>
171 inline bool operator!=( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool!=b.my_pool;}
172
173 //! Thread-safe growable pool allocator for variable-size requests
174 template <typename Alloc>
175 class memory_pool : public pool_base {
176 Alloc my_alloc; // TODO: base-class optimization
177 static void *allocate_request(intptr_t pool_id, size_t & bytes);
178 static int deallocate_request(intptr_t pool_id, void*, size_t raw_bytes);
179
180 public:
181 //! construct pool with underlying allocator
182 explicit memory_pool(const Alloc &src = Alloc());
183
184 //! destroy pool
~memory_pool()185 ~memory_pool() { destroy(); } // call the callbacks first and destroy my_alloc latter
186 };
187
188 class fixed_pool : public pool_base {
189 void *my_buffer;
190 size_t my_size;
191 inline static void *allocate_request(intptr_t pool_id, size_t & bytes);
192
193 public:
194 //! construct pool with underlying allocator
195 inline fixed_pool(void *buf, size_t size);
196 //! destroy pool
~fixed_pool()197 ~fixed_pool() { destroy(); }
198 };
199
200 //////////////// Implementation ///////////////
201
202 template <typename Alloc>
memory_pool(const Alloc & src)203 memory_pool<Alloc>::memory_pool(const Alloc &src) : my_alloc(src) {
204 rml::MemPoolPolicy args(allocate_request, deallocate_request,
205 sizeof(typename Alloc::value_type));
206 rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool);
207 if (res!=rml::POOL_OK)
208 throw_exception(std::runtime_error("Can't create pool"));
209 }
210 template <typename Alloc>
allocate_request(intptr_t pool_id,size_t & bytes)211 void *memory_pool<Alloc>::allocate_request(intptr_t pool_id, size_t & bytes) {
212 memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id);
213 const size_t unit_size = sizeof(typename Alloc::value_type);
214 __TBBMALLOC_ASSERT( 0 == bytes%unit_size, nullptr);
215 void *ptr;
216 #if TBB_USE_EXCEPTIONS
217 try {
218 #endif
219 ptr = self.my_alloc.allocate( bytes/unit_size );
220 #if TBB_USE_EXCEPTIONS
221 } catch(...) {
222 return nullptr;
223 }
224 #endif
225 return ptr;
226 }
227 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
228 // Workaround for erroneous "unreachable code" warning in the template below.
229 // Specific for VC++ 17-18 compiler
230 #pragma warning (push)
231 #pragma warning (disable: 4702)
232 #endif
233 template <typename Alloc>
deallocate_request(intptr_t pool_id,void * raw_ptr,size_t raw_bytes)234 int memory_pool<Alloc>::deallocate_request(intptr_t pool_id, void* raw_ptr, size_t raw_bytes) {
235 memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id);
236 const size_t unit_size = sizeof(typename Alloc::value_type);
237 __TBBMALLOC_ASSERT( 0 == raw_bytes%unit_size, nullptr);
238 self.my_alloc.deallocate( static_cast<typename Alloc::value_type*>(raw_ptr), raw_bytes/unit_size );
239 return 0;
240 }
241 #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
242 #pragma warning (pop)
243 #endif
fixed_pool(void * buf,size_t size)244 inline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) {
245 if (!buf || !size)
246 // TODO: improve support for mode with exceptions disabled
247 throw_exception(std::invalid_argument("Zero in parameter is invalid"));
248 rml::MemPoolPolicy args(allocate_request, nullptr, size, /*fixedPool=*/true);
249 rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool);
250 if (res!=rml::POOL_OK)
251 throw_exception(std::runtime_error("Can't create pool"));
252 }
allocate_request(intptr_t pool_id,size_t & bytes)253 inline void *fixed_pool::allocate_request(intptr_t pool_id, size_t & bytes) {
254 fixed_pool &self = *reinterpret_cast<fixed_pool*>(pool_id);
255 __TBBMALLOC_ASSERT(0 != self.my_size, "The buffer must not be used twice.");
256 bytes = self.my_size;
257 self.my_size = 0; // remember that buffer has been used
258 return self.my_buffer;
259 }
260
261 } // namespace d1
262 } // namespace detail
263
264 inline namespace v1 {
265 using detail::d1::memory_pool_allocator;
266 using detail::d1::memory_pool;
267 using detail::d1::fixed_pool;
268 } // inline namepspace v1
269 } // namespace tbb
270
271 #undef __TBBMALLOC_ASSERT
272 #endif// __TBB_memory_pool_H
273