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 | |
40 | namespace tbb { |
41 | namespace interface6 { |
42 | //! @cond INTERNAL |
43 | namespace internal { |
44 | |
45 | //! Base of thread-safe pool allocator for variable-size requests |
46 | class 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. |
49 | public: |
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 | |
65 | protected: |
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 */ |
83 | template<typename T, typename P = internal::pool_base> |
84 | class memory_pool_allocator { |
85 | protected: |
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); |
94 | public: |
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 */ |
153 | template<typename P> |
154 | class memory_pool_allocator<void, P> { |
155 | public: |
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 | |
169 | protected: |
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 | |
179 | template<typename T, typename U, typename P> |
180 | inline bool operator==( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool==b.my_pool;} |
181 | |
182 | template<typename T, typename U, typename P> |
183 | inline 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 |
187 | template <typename Alloc> |
188 | class 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 | |
193 | public: |
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 | |
202 | class 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 | |
207 | public: |
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 | |
216 | template <typename Alloc> |
217 | memory_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 | } |
224 | template <typename Alloc> |
225 | void *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 |
240 | template <typename Alloc> |
241 | int 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 |
251 | inline 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 | } |
260 | inline 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 |
269 | using interface6::memory_pool_allocator; |
270 | using interface6::memory_pool; |
271 | using interface6::fixed_pool; |
272 | } //namespace tbb |
273 | |
274 | #undef __TBBMALLOC_ASSERT |
275 | #endif// __TBB_memory_pool_H |
276 | |