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