xref: /oneTBB/include/oneapi/tbb/memory_pool.h (revision 6caecf96)
1 /*
2     Copyright (c) 2005-2021 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)
48     void recycle() { rml::pool_reset(my_pool); }
49 
50     //! The "malloc" analogue to allocate block of memory of size bytes
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.
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
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
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 
100     explicit memory_pool_allocator(pool_type &pool) throw() : my_pool(&pool) {}
101     memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
102     template<typename U>
103     memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
104 
105     pointer address(reference x) const { return &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*/ = 0) {
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.
116     void deallocate( pointer p, size_type ) {
117         my_pool->free(p);
118     }
119     //! Largest value for which method allocate might succeed.
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>
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.
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 
152     explicit memory_pool_allocator( pool_type &pool) throw() : my_pool(&pool) {}
153     memory_pool_allocator( const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
154     template<typename U>
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
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
197     ~fixed_pool() { destroy(); }
198 };
199 
200 //////////////// Implementation ///////////////
201 
202 template <typename Alloc>
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>
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, NULL);
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 0;
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>
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, NULL);
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
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, 0, 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 }
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