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