149e08aacStbbdev /*
2*dc883a59SMarian Klymov Copyright (c) 2005-2023 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_scalable_allocator_H
1849e08aacStbbdev #define __TBB_scalable_allocator_H
1949e08aacStbbdev
2049e08aacStbbdev #ifdef __cplusplus
2149e08aacStbbdev #include "oneapi/tbb/detail/_config.h"
2249e08aacStbbdev #include "oneapi/tbb/detail/_utils.h"
23*dc883a59SMarian Klymov #include "oneapi/tbb/detail/_namespace_injection.h"
2449e08aacStbbdev #include <cstdlib>
2549e08aacStbbdev #include <utility>
268827ea7dSLong Nguyen #include <new> /* std::bad_alloc() */
2749e08aacStbbdev #else
288827ea7dSLong Nguyen #include "oneapi/tbb/detail/_export.h"
2949e08aacStbbdev #include <stddef.h> /* Need ptrdiff_t and size_t from here. */
30dbccbee9SIlya Mishin #if !defined(_MSC_VER) || defined(__clang__)
3149e08aacStbbdev #include <stdint.h> /* Need intptr_t from here. */
3249e08aacStbbdev #endif
3349e08aacStbbdev #endif
3449e08aacStbbdev
3549e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
3649e08aacStbbdev #include <memory_resource>
3749e08aacStbbdev #endif
3849e08aacStbbdev
3949e08aacStbbdev #ifdef __cplusplus
4049e08aacStbbdev extern "C" {
4149e08aacStbbdev #endif /* __cplusplus */
4249e08aacStbbdev
4349e08aacStbbdev #if _MSC_VER
4449e08aacStbbdev #define __TBB_EXPORTED_FUNC __cdecl
4549e08aacStbbdev #else
4649e08aacStbbdev #define __TBB_EXPORTED_FUNC
4749e08aacStbbdev #endif
4849e08aacStbbdev
4949e08aacStbbdev /** The "malloc" analogue to allocate block of memory of size bytes.
5049e08aacStbbdev * @ingroup memory_allocation */
518827ea7dSLong Nguyen TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_malloc(size_t size);
5249e08aacStbbdev
5349e08aacStbbdev /** The "free" analogue to discard a previously allocated piece of memory.
5449e08aacStbbdev @ingroup memory_allocation */
558827ea7dSLong Nguyen TBBMALLOC_EXPORT void __TBB_EXPORTED_FUNC scalable_free(void* ptr);
5649e08aacStbbdev
5749e08aacStbbdev /** The "realloc" analogue complementing scalable_malloc.
5849e08aacStbbdev @ingroup memory_allocation */
598827ea7dSLong Nguyen TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_realloc(void* ptr, size_t size);
6049e08aacStbbdev
6149e08aacStbbdev /** The "calloc" analogue complementing scalable_malloc.
6249e08aacStbbdev @ingroup memory_allocation */
638827ea7dSLong Nguyen TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_calloc(size_t nobj, size_t size);
6449e08aacStbbdev
6549e08aacStbbdev /** The "posix_memalign" analogue.
6649e08aacStbbdev @ingroup memory_allocation */
678827ea7dSLong Nguyen TBBMALLOC_EXPORT int __TBB_EXPORTED_FUNC scalable_posix_memalign(void** memptr, size_t alignment, size_t size);
6849e08aacStbbdev
6949e08aacStbbdev /** The "_aligned_malloc" analogue.
7049e08aacStbbdev @ingroup memory_allocation */
718827ea7dSLong Nguyen TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_aligned_malloc(size_t size, size_t alignment);
7249e08aacStbbdev
7349e08aacStbbdev /** The "_aligned_realloc" analogue.
7449e08aacStbbdev @ingroup memory_allocation */
758827ea7dSLong Nguyen TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_aligned_realloc(void* ptr, size_t size, size_t alignment);
7649e08aacStbbdev
7749e08aacStbbdev /** The "_aligned_free" analogue.
7849e08aacStbbdev @ingroup memory_allocation */
798827ea7dSLong Nguyen TBBMALLOC_EXPORT void __TBB_EXPORTED_FUNC scalable_aligned_free(void* ptr);
8049e08aacStbbdev
8149e08aacStbbdev /** The analogue of _msize/malloc_size/malloc_usable_size.
8249e08aacStbbdev Returns the usable size of a memory block previously allocated by scalable_*,
8349e08aacStbbdev or 0 (zero) if ptr does not point to such a block.
8449e08aacStbbdev @ingroup memory_allocation */
858827ea7dSLong Nguyen TBBMALLOC_EXPORT size_t __TBB_EXPORTED_FUNC scalable_msize(void* ptr);
8649e08aacStbbdev
8749e08aacStbbdev /* Results for scalable_allocation_* functions */
8849e08aacStbbdev typedef enum {
8949e08aacStbbdev TBBMALLOC_OK,
9049e08aacStbbdev TBBMALLOC_INVALID_PARAM,
9149e08aacStbbdev TBBMALLOC_UNSUPPORTED,
9249e08aacStbbdev TBBMALLOC_NO_MEMORY,
9349e08aacStbbdev TBBMALLOC_NO_EFFECT
9449e08aacStbbdev } ScalableAllocationResult;
9549e08aacStbbdev
9649e08aacStbbdev /* Setting TBB_MALLOC_USE_HUGE_PAGES environment variable to 1 enables huge pages.
9749e08aacStbbdev scalable_allocation_mode call has priority over environment variable. */
9849e08aacStbbdev typedef enum {
9949e08aacStbbdev TBBMALLOC_USE_HUGE_PAGES, /* value turns using huge pages on and off */
10049e08aacStbbdev /* deprecated, kept for backward compatibility only */
10149e08aacStbbdev USE_HUGE_PAGES = TBBMALLOC_USE_HUGE_PAGES,
10249e08aacStbbdev /* try to limit memory consumption value (Bytes), clean internal buffers
10349e08aacStbbdev if limit is exceeded, but not prevents from requesting memory from OS */
10449e08aacStbbdev TBBMALLOC_SET_SOFT_HEAP_LIMIT,
10549e08aacStbbdev /* Lower bound for the size (Bytes), that is interpreted as huge
10649e08aacStbbdev * and not released during regular cleanup operations. */
10749e08aacStbbdev TBBMALLOC_SET_HUGE_SIZE_THRESHOLD
10849e08aacStbbdev } AllocationModeParam;
10949e08aacStbbdev
11049e08aacStbbdev /** Set TBB allocator-specific allocation modes.
11149e08aacStbbdev @ingroup memory_allocation */
1128827ea7dSLong Nguyen TBBMALLOC_EXPORT int __TBB_EXPORTED_FUNC scalable_allocation_mode(int param, intptr_t value);
11349e08aacStbbdev
11449e08aacStbbdev typedef enum {
11549e08aacStbbdev /* Clean internal allocator buffers for all threads.
11649e08aacStbbdev Returns TBBMALLOC_NO_EFFECT if no buffers cleaned,
11749e08aacStbbdev TBBMALLOC_OK if some memory released from buffers. */
11849e08aacStbbdev TBBMALLOC_CLEAN_ALL_BUFFERS,
11949e08aacStbbdev /* Clean internal allocator buffer for current thread only.
12049e08aacStbbdev Return values same as for TBBMALLOC_CLEAN_ALL_BUFFERS. */
12149e08aacStbbdev TBBMALLOC_CLEAN_THREAD_BUFFERS
12249e08aacStbbdev } ScalableAllocationCmd;
12349e08aacStbbdev
12449e08aacStbbdev /** Call TBB allocator-specific commands.
12549e08aacStbbdev @ingroup memory_allocation */
1268827ea7dSLong Nguyen TBBMALLOC_EXPORT int __TBB_EXPORTED_FUNC scalable_allocation_command(int cmd, void *param);
12749e08aacStbbdev
12849e08aacStbbdev #ifdef __cplusplus
12949e08aacStbbdev } /* extern "C" */
13049e08aacStbbdev #endif /* __cplusplus */
13149e08aacStbbdev
13249e08aacStbbdev #ifdef __cplusplus
13349e08aacStbbdev
13449e08aacStbbdev //! The namespace rml contains components of low-level memory pool interface.
13549e08aacStbbdev namespace rml {
13649e08aacStbbdev class MemoryPool;
13749e08aacStbbdev
13849e08aacStbbdev typedef void *(*rawAllocType)(std::intptr_t pool_id, std::size_t &bytes);
13949e08aacStbbdev // returns non-zero in case of error
14049e08aacStbbdev typedef int (*rawFreeType)(std::intptr_t pool_id, void* raw_ptr, std::size_t raw_bytes);
14149e08aacStbbdev
14249e08aacStbbdev struct MemPoolPolicy {
14349e08aacStbbdev enum {
14449e08aacStbbdev TBBMALLOC_POOL_VERSION = 1
14549e08aacStbbdev };
14649e08aacStbbdev
14749e08aacStbbdev rawAllocType pAlloc;
14849e08aacStbbdev rawFreeType pFree;
14949e08aacStbbdev // granularity of pAlloc allocations. 0 means default used.
15049e08aacStbbdev std::size_t granularity;
15149e08aacStbbdev int version;
15249e08aacStbbdev // all memory consumed at 1st pAlloc call and never returned,
15349e08aacStbbdev // no more pAlloc calls after 1st
15449e08aacStbbdev unsigned fixedPool : 1,
15549e08aacStbbdev // memory consumed but returned only at pool termination
15649e08aacStbbdev keepAllMemory : 1,
15749e08aacStbbdev reserved : 30;
15849e08aacStbbdev
15949e08aacStbbdev MemPoolPolicy(rawAllocType pAlloc_, rawFreeType pFree_,
16049e08aacStbbdev std::size_t granularity_ = 0, bool fixedPool_ = false,
16149e08aacStbbdev bool keepAllMemory_ = false) :
pAllocMemPoolPolicy16249e08aacStbbdev pAlloc(pAlloc_), pFree(pFree_), granularity(granularity_), version(TBBMALLOC_POOL_VERSION),
16349e08aacStbbdev fixedPool(fixedPool_), keepAllMemory(keepAllMemory_),
16449e08aacStbbdev reserved(0) {}
16549e08aacStbbdev };
16649e08aacStbbdev
16749e08aacStbbdev // enums have same values as appropriate enums from ScalableAllocationResult
16849e08aacStbbdev // TODO: use ScalableAllocationResult in pool_create directly
16949e08aacStbbdev enum MemPoolError {
17049e08aacStbbdev // pool created successfully
17149e08aacStbbdev POOL_OK = TBBMALLOC_OK,
17249e08aacStbbdev // invalid policy parameters found
17349e08aacStbbdev INVALID_POLICY = TBBMALLOC_INVALID_PARAM,
17449e08aacStbbdev // requested pool policy is not supported by allocator library
17549e08aacStbbdev UNSUPPORTED_POLICY = TBBMALLOC_UNSUPPORTED,
17649e08aacStbbdev // lack of memory during pool creation
17749e08aacStbbdev NO_MEMORY = TBBMALLOC_NO_MEMORY,
17849e08aacStbbdev // action takes no effect
17949e08aacStbbdev NO_EFFECT = TBBMALLOC_NO_EFFECT
18049e08aacStbbdev };
18149e08aacStbbdev
1828827ea7dSLong Nguyen TBBMALLOC_EXPORT MemPoolError pool_create_v1(std::intptr_t pool_id, const MemPoolPolicy *policy,
18349e08aacStbbdev rml::MemoryPool **pool);
18449e08aacStbbdev
1858827ea7dSLong Nguyen TBBMALLOC_EXPORT bool pool_destroy(MemoryPool* memPool);
1868827ea7dSLong Nguyen TBBMALLOC_EXPORT void *pool_malloc(MemoryPool* memPool, std::size_t size);
1878827ea7dSLong Nguyen TBBMALLOC_EXPORT void *pool_realloc(MemoryPool* memPool, void *object, std::size_t size);
1888827ea7dSLong Nguyen TBBMALLOC_EXPORT void *pool_aligned_malloc(MemoryPool* mPool, std::size_t size, std::size_t alignment);
1898827ea7dSLong Nguyen TBBMALLOC_EXPORT void *pool_aligned_realloc(MemoryPool* mPool, void *ptr, std::size_t size, std::size_t alignment);
1908827ea7dSLong Nguyen TBBMALLOC_EXPORT bool pool_reset(MemoryPool* memPool);
1918827ea7dSLong Nguyen TBBMALLOC_EXPORT bool pool_free(MemoryPool *memPool, void *object);
1928827ea7dSLong Nguyen TBBMALLOC_EXPORT MemoryPool *pool_identify(void *object);
1938827ea7dSLong Nguyen TBBMALLOC_EXPORT std::size_t pool_msize(MemoryPool *memPool, void *object);
19449e08aacStbbdev
19549e08aacStbbdev } // namespace rml
19649e08aacStbbdev
19749e08aacStbbdev namespace tbb {
19849e08aacStbbdev namespace detail {
19949e08aacStbbdev namespace d1 {
20049e08aacStbbdev
20149e08aacStbbdev // keep throw in a separate function to prevent code bloat
20249e08aacStbbdev template<typename E>
throw_exception(const E & e)20349e08aacStbbdev void throw_exception(const E &e) {
20449e08aacStbbdev #if TBB_USE_EXCEPTIONS
20549e08aacStbbdev throw e;
20649e08aacStbbdev #else
20749e08aacStbbdev suppress_unused_warning(e);
20849e08aacStbbdev #endif
20949e08aacStbbdev }
21049e08aacStbbdev
21149e08aacStbbdev template<typename T>
21249e08aacStbbdev class scalable_allocator {
21349e08aacStbbdev public:
21449e08aacStbbdev using value_type = T;
21549e08aacStbbdev using propagate_on_container_move_assignment = std::true_type;
21649e08aacStbbdev
21749e08aacStbbdev //! Always defined for TBB containers
21849e08aacStbbdev using is_always_equal = std::true_type;
21949e08aacStbbdev
22049e08aacStbbdev scalable_allocator() = default;
scalable_allocator(const scalable_allocator<U> &)22149e08aacStbbdev template<typename U> scalable_allocator(const scalable_allocator<U>&) noexcept {}
22249e08aacStbbdev
22349e08aacStbbdev //! Allocate space for n objects.
allocate(std::size_t n)224b15aabb3Stbbdev __TBB_nodiscard T* allocate(std::size_t n) {
22549e08aacStbbdev T* p = static_cast<T*>(scalable_malloc(n * sizeof(value_type)));
22649e08aacStbbdev if (!p) {
22749e08aacStbbdev throw_exception(std::bad_alloc());
22849e08aacStbbdev }
22949e08aacStbbdev return p;
23049e08aacStbbdev }
23149e08aacStbbdev
23249e08aacStbbdev //! Free previously allocated block of memory
deallocate(T * p,std::size_t)23349e08aacStbbdev void deallocate(T* p, std::size_t) {
23449e08aacStbbdev scalable_free(p);
23549e08aacStbbdev }
23649e08aacStbbdev
23749e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN
23849e08aacStbbdev using pointer = value_type*;
23949e08aacStbbdev using const_pointer = const value_type*;
24049e08aacStbbdev using reference = value_type&;
24149e08aacStbbdev using const_reference = const value_type&;
24249e08aacStbbdev using difference_type = std::ptrdiff_t;
24349e08aacStbbdev using size_type = std::size_t;
24449e08aacStbbdev template<typename U> struct rebind {
24549e08aacStbbdev using other = scalable_allocator<U>;
24649e08aacStbbdev };
24749e08aacStbbdev //! Largest value for which method allocate might succeed.
max_size()24849e08aacStbbdev size_type max_size() const noexcept {
24949e08aacStbbdev size_type absolutemax = static_cast<size_type>(-1) / sizeof (value_type);
25049e08aacStbbdev return (absolutemax > 0 ? absolutemax : 1);
25149e08aacStbbdev }
25249e08aacStbbdev template<typename U, typename... Args>
construct(U * p,Args &&...args)25349e08aacStbbdev void construct(U *p, Args&&... args)
25449e08aacStbbdev { ::new((void *)p) U(std::forward<Args>(args)...); }
destroy(pointer p)25549e08aacStbbdev void destroy(pointer p) { p->~value_type(); }
address(reference x)25649e08aacStbbdev pointer address(reference x) const { return &x; }
address(const_reference x)25749e08aacStbbdev const_pointer address(const_reference x) const { return &x; }
25849e08aacStbbdev #endif // TBB_ALLOCATOR_TRAITS_BROKEN
25949e08aacStbbdev
26049e08aacStbbdev };
26149e08aacStbbdev
26249e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN
26349e08aacStbbdev template<>
26449e08aacStbbdev class scalable_allocator<void> {
26549e08aacStbbdev public:
26649e08aacStbbdev using pointer = void*;
26749e08aacStbbdev using const_pointer = const void*;
26849e08aacStbbdev using value_type = void;
26949e08aacStbbdev template<typename U> struct rebind {
27049e08aacStbbdev using other = scalable_allocator<U>;
27149e08aacStbbdev };
27249e08aacStbbdev };
27349e08aacStbbdev #endif
27449e08aacStbbdev
27549e08aacStbbdev template<typename T, typename U>
27649e08aacStbbdev inline bool operator==(const scalable_allocator<T>&, const scalable_allocator<U>&) noexcept { return true; }
27749e08aacStbbdev
278b15aabb3Stbbdev #if !__TBB_CPP20_COMPARISONS_PRESENT
27949e08aacStbbdev template<typename T, typename U>
28049e08aacStbbdev inline bool operator!=(const scalable_allocator<T>&, const scalable_allocator<U>&) noexcept { return false; }
281b15aabb3Stbbdev #endif
28249e08aacStbbdev
28349e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
28449e08aacStbbdev
28549e08aacStbbdev //! C++17 memory resource implementation for scalable allocator
28649e08aacStbbdev //! ISO C++ Section 23.12.2
28749e08aacStbbdev class scalable_resource_impl : public std::pmr::memory_resource {
28849e08aacStbbdev private:
do_allocate(std::size_t bytes,std::size_t alignment)28949e08aacStbbdev void* do_allocate(std::size_t bytes, std::size_t alignment) override {
29049e08aacStbbdev void* p = scalable_aligned_malloc(bytes, alignment);
29149e08aacStbbdev if (!p) {
29249e08aacStbbdev throw_exception(std::bad_alloc());
29349e08aacStbbdev }
29449e08aacStbbdev return p;
29549e08aacStbbdev }
29649e08aacStbbdev
do_deallocate(void * ptr,std::size_t,std::size_t)29749e08aacStbbdev void do_deallocate(void* ptr, std::size_t /*bytes*/, std::size_t /*alignment*/) override {
29849e08aacStbbdev scalable_free(ptr);
29949e08aacStbbdev }
30049e08aacStbbdev
30149e08aacStbbdev //! Memory allocated by one instance of scalable_resource_impl could be deallocated by any
30249e08aacStbbdev //! other instance of this class
do_is_equal(const std::pmr::memory_resource & other)30349e08aacStbbdev bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
30449e08aacStbbdev return this == &other ||
30549e08aacStbbdev #if __TBB_USE_OPTIONAL_RTTI
30649e08aacStbbdev dynamic_cast<const scalable_resource_impl*>(&other) != nullptr;
30749e08aacStbbdev #else
30849e08aacStbbdev false;
30949e08aacStbbdev #endif
31049e08aacStbbdev }
31149e08aacStbbdev };
31249e08aacStbbdev
31349e08aacStbbdev //! Global scalable allocator memory resource provider
scalable_memory_resource()31449e08aacStbbdev inline std::pmr::memory_resource* scalable_memory_resource() noexcept {
31549e08aacStbbdev static tbb::detail::d1::scalable_resource_impl scalable_res;
31649e08aacStbbdev return &scalable_res;
31749e08aacStbbdev }
31849e08aacStbbdev
31949e08aacStbbdev #endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT
32049e08aacStbbdev
32149e08aacStbbdev } // namespace d1
32249e08aacStbbdev } // namespace detail
32349e08aacStbbdev
32449e08aacStbbdev inline namespace v1 {
32549e08aacStbbdev using detail::d1::scalable_allocator;
32649e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
32749e08aacStbbdev using detail::d1::scalable_memory_resource;
32849e08aacStbbdev #endif
32949e08aacStbbdev } // namespace v1
33049e08aacStbbdev
33149e08aacStbbdev } // namespace tbb
33249e08aacStbbdev
33349e08aacStbbdev #endif /* __cplusplus */
33449e08aacStbbdev
33549e08aacStbbdev #endif /* __TBB_scalable_allocator_H */
336