xref: /oneTBB/include/oneapi/tbb/memory_pool.h (revision c21e688a)
149e08aacStbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 Intel Corporation
349e08aacStbbdev 
449e08aacStbbdev     Licensed under the Apache License, Version 2.0 (the "License");
549e08aacStbbdev     you may not use this file except in compliance with the License.
649e08aacStbbdev     You may obtain a copy of the License at
749e08aacStbbdev 
849e08aacStbbdev         http://www.apache.org/licenses/LICENSE-2.0
949e08aacStbbdev 
1049e08aacStbbdev     Unless required by applicable law or agreed to in writing, software
1149e08aacStbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1249e08aacStbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349e08aacStbbdev     See the License for the specific language governing permissions and
1449e08aacStbbdev     limitations under the License.
1549e08aacStbbdev */
1649e08aacStbbdev 
1749e08aacStbbdev #ifndef __TBB_memory_pool_H
1849e08aacStbbdev #define __TBB_memory_pool_H
1949e08aacStbbdev 
2049e08aacStbbdev #if !TBB_PREVIEW_MEMORY_POOL
2149e08aacStbbdev #error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h
2249e08aacStbbdev #endif
2349e08aacStbbdev /** @file */
2449e08aacStbbdev 
2549e08aacStbbdev #include "scalable_allocator.h"
2649e08aacStbbdev 
2749e08aacStbbdev #include <new> // std::bad_alloc
2849e08aacStbbdev #include <stdexcept> // std::runtime_error, std::invalid_argument
2949e08aacStbbdev #include <utility> // std::forward
3049e08aacStbbdev 
3149e08aacStbbdev 
3249e08aacStbbdev #if __TBB_EXTRA_DEBUG
3349e08aacStbbdev #define __TBBMALLOC_ASSERT ASSERT
3449e08aacStbbdev #else
3549e08aacStbbdev #define __TBBMALLOC_ASSERT(a,b) ((void)0)
3649e08aacStbbdev #endif
3749e08aacStbbdev 
3849e08aacStbbdev namespace tbb {
3949e08aacStbbdev namespace detail {
4049e08aacStbbdev namespace d1 {
4149e08aacStbbdev 
4249e08aacStbbdev //! Base of thread-safe pool allocator for variable-size requests
4349e08aacStbbdev class pool_base : no_copy {
4449e08aacStbbdev     // Pool interface is separate from standard allocator classes because it has
4549e08aacStbbdev     // to maintain internal state, no copy or assignment. Move and swap are possible.
4649e08aacStbbdev public:
4749e08aacStbbdev     //! Reset pool to reuse its memory (free all objects at once)
recycle()4849e08aacStbbdev     void recycle() { rml::pool_reset(my_pool); }
4949e08aacStbbdev 
5049e08aacStbbdev     //! The "malloc" analogue to allocate block of memory of size bytes
malloc(size_t size)5149e08aacStbbdev     void *malloc(size_t size) { return rml::pool_malloc(my_pool, size); }
5249e08aacStbbdev 
5349e08aacStbbdev     //! The "free" analogue to discard a previously allocated piece of memory.
free(void * ptr)5449e08aacStbbdev     void free(void* ptr) { rml::pool_free(my_pool, ptr); }
5549e08aacStbbdev 
5649e08aacStbbdev     //! The "realloc" analogue complementing pool_malloc.
5749e08aacStbbdev     // Enables some low-level optimization possibilities
realloc(void * ptr,size_t size)5849e08aacStbbdev     void *realloc(void* ptr, size_t size) {
5949e08aacStbbdev         return rml::pool_realloc(my_pool, ptr, size);
6049e08aacStbbdev     }
6149e08aacStbbdev 
6249e08aacStbbdev protected:
6349e08aacStbbdev     //! destroy pool - must be called in a child class
destroy()6449e08aacStbbdev     void destroy() { rml::pool_destroy(my_pool); }
6549e08aacStbbdev 
6649e08aacStbbdev     rml::MemoryPool *my_pool;
6749e08aacStbbdev };
6849e08aacStbbdev 
6949e08aacStbbdev #if _MSC_VER && !defined(__INTEL_COMPILER)
7049e08aacStbbdev     // Workaround for erroneous "unreferenced parameter" warning in method destroy.
7149e08aacStbbdev     #pragma warning (push)
7249e08aacStbbdev     #pragma warning (disable: 4100)
7349e08aacStbbdev #endif
7449e08aacStbbdev 
7549e08aacStbbdev //! Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5
7649e08aacStbbdev /** @ingroup memory_allocation */
7749e08aacStbbdev template<typename T, typename P = pool_base>
7849e08aacStbbdev class memory_pool_allocator {
7949e08aacStbbdev protected:
8049e08aacStbbdev     typedef P pool_type;
8149e08aacStbbdev     pool_type *my_pool;
8249e08aacStbbdev     template<typename U, typename R>
8349e08aacStbbdev     friend class memory_pool_allocator;
8449e08aacStbbdev     template<typename V, typename U, typename R>
8549e08aacStbbdev     friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
8649e08aacStbbdev     template<typename V, typename U, typename R>
8749e08aacStbbdev     friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
8849e08aacStbbdev public:
8949e08aacStbbdev     typedef T value_type;
9049e08aacStbbdev     typedef value_type* pointer;
9149e08aacStbbdev     typedef const value_type* const_pointer;
9249e08aacStbbdev     typedef value_type& reference;
9349e08aacStbbdev     typedef const value_type& const_reference;
9449e08aacStbbdev     typedef size_t size_type;
9549e08aacStbbdev     typedef ptrdiff_t difference_type;
9649e08aacStbbdev     template<typename U> struct rebind {
9749e08aacStbbdev         typedef memory_pool_allocator<U, P> other;
9849e08aacStbbdev     };
9949e08aacStbbdev 
memory_pool_allocator(pool_type & pool)10049e08aacStbbdev     explicit memory_pool_allocator(pool_type &pool) throw() : my_pool(&pool) {}
throw()10149e08aacStbbdev     memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
10249e08aacStbbdev     template<typename U>
memory_pool_allocator(const memory_pool_allocator<U,P> & src)10349e08aacStbbdev     memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
10449e08aacStbbdev 
address(reference x)10549e08aacStbbdev     pointer address(reference x) const { return &x; }
address(const_reference x)10649e08aacStbbdev     const_pointer address(const_reference x) const { return &x; }
10749e08aacStbbdev 
10849e08aacStbbdev     //! Allocate space for n objects.
10957f524caSIlya Isaev     pointer allocate( size_type n, const void* /*hint*/ = nullptr) {
11049e08aacStbbdev         pointer p = static_cast<pointer>( my_pool->malloc( n*sizeof(value_type) ) );
11149e08aacStbbdev         if (!p)
11249e08aacStbbdev             throw_exception(std::bad_alloc());
11349e08aacStbbdev         return p;
11449e08aacStbbdev     }
11549e08aacStbbdev     //! Free previously allocated block of memory.
deallocate(pointer p,size_type)11649e08aacStbbdev     void deallocate( pointer p, size_type ) {
11749e08aacStbbdev         my_pool->free(p);
11849e08aacStbbdev     }
11949e08aacStbbdev     //! Largest value for which method allocate might succeed.
max_size()12049e08aacStbbdev     size_type max_size() const throw() {
12149e08aacStbbdev         size_type max = static_cast<size_type>(-1) / sizeof (value_type);
12249e08aacStbbdev         return (max > 0 ? max : 1);
12349e08aacStbbdev     }
12449e08aacStbbdev     //! Copy-construct value at location pointed to by p.
12549e08aacStbbdev 
12649e08aacStbbdev     template<typename U, typename... Args>
construct(U * p,Args &&...args)12749e08aacStbbdev     void construct(U *p, Args&&... args)
12849e08aacStbbdev         { ::new((void *)p) U(std::forward<Args>(args)...); }
12949e08aacStbbdev 
13049e08aacStbbdev     //! Destroy value at location pointed to by p.
destroy(pointer p)13149e08aacStbbdev     void destroy( pointer p ) { p->~value_type(); }
13249e08aacStbbdev 
13349e08aacStbbdev };
13449e08aacStbbdev 
13549e08aacStbbdev #if _MSC_VER && !defined(__INTEL_COMPILER)
13649e08aacStbbdev     #pragma warning (pop)
13749e08aacStbbdev #endif // warning 4100 is back
13849e08aacStbbdev 
13949e08aacStbbdev //! Analogous to std::allocator<void>, as defined in ISO C++ Standard, Section 20.4.1
14049e08aacStbbdev /** @ingroup memory_allocation */
14149e08aacStbbdev template<typename P>
14249e08aacStbbdev class memory_pool_allocator<void, P> {
14349e08aacStbbdev public:
14449e08aacStbbdev     typedef P pool_type;
14549e08aacStbbdev     typedef void* pointer;
14649e08aacStbbdev     typedef const void* const_pointer;
14749e08aacStbbdev     typedef void value_type;
14849e08aacStbbdev     template<typename U> struct rebind {
14949e08aacStbbdev         typedef memory_pool_allocator<U, P> other;
15049e08aacStbbdev     };
15149e08aacStbbdev 
memory_pool_allocator(pool_type & pool)15249e08aacStbbdev     explicit memory_pool_allocator( pool_type &pool) throw() : my_pool(&pool) {}
throw()15349e08aacStbbdev     memory_pool_allocator( const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {}
15449e08aacStbbdev     template<typename U>
memory_pool_allocator(const memory_pool_allocator<U,P> & src)15549e08aacStbbdev     memory_pool_allocator(const memory_pool_allocator<U,P>& src) throw() : my_pool(src.my_pool) {}
15649e08aacStbbdev 
15749e08aacStbbdev protected:
15849e08aacStbbdev     pool_type *my_pool;
15949e08aacStbbdev     template<typename U, typename R>
16049e08aacStbbdev     friend class memory_pool_allocator;
16149e08aacStbbdev     template<typename V, typename U, typename R>
16249e08aacStbbdev     friend bool operator==( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
16349e08aacStbbdev     template<typename V, typename U, typename R>
16449e08aacStbbdev     friend bool operator!=( const memory_pool_allocator<V,R>& a, const memory_pool_allocator<U,R>& b);
16549e08aacStbbdev };
16649e08aacStbbdev 
16749e08aacStbbdev template<typename T, typename U, typename P>
16849e08aacStbbdev inline bool operator==( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool==b.my_pool;}
16949e08aacStbbdev 
17049e08aacStbbdev template<typename T, typename U, typename P>
17149e08aacStbbdev inline bool operator!=( const memory_pool_allocator<T,P>& a, const memory_pool_allocator<U,P>& b) {return a.my_pool!=b.my_pool;}
17249e08aacStbbdev 
17349e08aacStbbdev //! Thread-safe growable pool allocator for variable-size requests
17449e08aacStbbdev template <typename Alloc>
17549e08aacStbbdev class memory_pool : public pool_base {
17649e08aacStbbdev     Alloc my_alloc; // TODO: base-class optimization
17749e08aacStbbdev     static void *allocate_request(intptr_t pool_id, size_t & bytes);
17849e08aacStbbdev     static int deallocate_request(intptr_t pool_id, void*, size_t raw_bytes);
17949e08aacStbbdev 
18049e08aacStbbdev public:
18149e08aacStbbdev     //! construct pool with underlying allocator
18249e08aacStbbdev     explicit memory_pool(const Alloc &src = Alloc());
18349e08aacStbbdev 
18449e08aacStbbdev     //! destroy pool
~memory_pool()18549e08aacStbbdev     ~memory_pool() { destroy(); } // call the callbacks first and destroy my_alloc latter
18649e08aacStbbdev };
18749e08aacStbbdev 
18849e08aacStbbdev class fixed_pool : public pool_base {
18949e08aacStbbdev     void *my_buffer;
19049e08aacStbbdev     size_t my_size;
19149e08aacStbbdev     inline static void *allocate_request(intptr_t pool_id, size_t & bytes);
19249e08aacStbbdev 
19349e08aacStbbdev public:
19449e08aacStbbdev     //! construct pool with underlying allocator
19549e08aacStbbdev     inline fixed_pool(void *buf, size_t size);
19649e08aacStbbdev     //! destroy pool
~fixed_pool()19749e08aacStbbdev     ~fixed_pool() { destroy(); }
19849e08aacStbbdev };
19949e08aacStbbdev 
20049e08aacStbbdev //////////////// Implementation ///////////////
20149e08aacStbbdev 
20249e08aacStbbdev template <typename Alloc>
memory_pool(const Alloc & src)20349e08aacStbbdev memory_pool<Alloc>::memory_pool(const Alloc &src) : my_alloc(src) {
20449e08aacStbbdev     rml::MemPoolPolicy args(allocate_request, deallocate_request,
20549e08aacStbbdev                             sizeof(typename Alloc::value_type));
20649e08aacStbbdev     rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool);
20749e08aacStbbdev     if (res!=rml::POOL_OK)
20849e08aacStbbdev         throw_exception(std::runtime_error("Can't create pool"));
20949e08aacStbbdev }
21049e08aacStbbdev template <typename Alloc>
allocate_request(intptr_t pool_id,size_t & bytes)21149e08aacStbbdev void *memory_pool<Alloc>::allocate_request(intptr_t pool_id, size_t & bytes) {
21249e08aacStbbdev     memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id);
21349e08aacStbbdev     const size_t unit_size = sizeof(typename Alloc::value_type);
21457f524caSIlya Isaev     __TBBMALLOC_ASSERT( 0 == bytes%unit_size, nullptr);
21549e08aacStbbdev     void *ptr;
21649e08aacStbbdev #if TBB_USE_EXCEPTIONS
21749e08aacStbbdev     try {
21849e08aacStbbdev #endif
21949e08aacStbbdev         ptr = self.my_alloc.allocate( bytes/unit_size );
22049e08aacStbbdev #if TBB_USE_EXCEPTIONS
22149e08aacStbbdev     } catch(...) {
22257f524caSIlya Isaev         return nullptr;
22349e08aacStbbdev     }
22449e08aacStbbdev #endif
22549e08aacStbbdev     return ptr;
22649e08aacStbbdev }
22749e08aacStbbdev #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
22849e08aacStbbdev     // Workaround for erroneous "unreachable code" warning in the template below.
22949e08aacStbbdev     // Specific for VC++ 17-18 compiler
23049e08aacStbbdev     #pragma warning (push)
23149e08aacStbbdev     #pragma warning (disable: 4702)
23249e08aacStbbdev #endif
23349e08aacStbbdev template <typename Alloc>
deallocate_request(intptr_t pool_id,void * raw_ptr,size_t raw_bytes)23449e08aacStbbdev int memory_pool<Alloc>::deallocate_request(intptr_t pool_id, void* raw_ptr, size_t raw_bytes) {
23549e08aacStbbdev     memory_pool<Alloc> &self = *reinterpret_cast<memory_pool<Alloc>*>(pool_id);
23649e08aacStbbdev     const size_t unit_size = sizeof(typename Alloc::value_type);
23757f524caSIlya Isaev     __TBBMALLOC_ASSERT( 0 == raw_bytes%unit_size, nullptr);
23849e08aacStbbdev     self.my_alloc.deallocate( static_cast<typename Alloc::value_type*>(raw_ptr), raw_bytes/unit_size );
23949e08aacStbbdev     return 0;
24049e08aacStbbdev }
24149e08aacStbbdev #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
24249e08aacStbbdev     #pragma warning (pop)
24349e08aacStbbdev #endif
fixed_pool(void * buf,size_t size)24449e08aacStbbdev inline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) {
24549e08aacStbbdev     if (!buf || !size)
24649e08aacStbbdev         // TODO: improve support for mode with exceptions disabled
24749e08aacStbbdev         throw_exception(std::invalid_argument("Zero in parameter is invalid"));
24857f524caSIlya Isaev     rml::MemPoolPolicy args(allocate_request, nullptr, size, /*fixedPool=*/true);
24949e08aacStbbdev     rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool);
25049e08aacStbbdev     if (res!=rml::POOL_OK)
25149e08aacStbbdev         throw_exception(std::runtime_error("Can't create pool"));
25249e08aacStbbdev }
allocate_request(intptr_t pool_id,size_t & bytes)25349e08aacStbbdev inline void *fixed_pool::allocate_request(intptr_t pool_id, size_t & bytes) {
25449e08aacStbbdev     fixed_pool &self = *reinterpret_cast<fixed_pool*>(pool_id);
25549e08aacStbbdev     __TBBMALLOC_ASSERT(0 != self.my_size, "The buffer must not be used twice.");
25649e08aacStbbdev     bytes = self.my_size;
25749e08aacStbbdev     self.my_size = 0; // remember that buffer has been used
25849e08aacStbbdev     return self.my_buffer;
25949e08aacStbbdev }
26049e08aacStbbdev 
26149e08aacStbbdev } // namespace d1
26249e08aacStbbdev } // namespace detail
26349e08aacStbbdev 
26449e08aacStbbdev inline namespace v1 {
26549e08aacStbbdev using detail::d1::memory_pool_allocator;
26649e08aacStbbdev using detail::d1::memory_pool;
26749e08aacStbbdev using detail::d1::fixed_pool;
26849e08aacStbbdev } // inline namepspace v1
26949e08aacStbbdev } // namespace tbb
27049e08aacStbbdev 
27149e08aacStbbdev #undef __TBBMALLOC_ASSERT
27249e08aacStbbdev #endif// __TBB_memory_pool_H
273