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