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_cache_aligned_allocator_H
1849e08aacStbbdev #define __TBB_cache_aligned_allocator_H
1949e08aacStbbdev 
2049e08aacStbbdev #include "detail/_utils.h"
2149e08aacStbbdev #include "detail/_namespace_injection.h"
2249e08aacStbbdev #include <cstdlib>
2349e08aacStbbdev #include <utility>
2449e08aacStbbdev 
2549e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
2649e08aacStbbdev #include <memory_resource>
2749e08aacStbbdev #endif
2849e08aacStbbdev 
2949e08aacStbbdev namespace tbb {
3049e08aacStbbdev namespace detail {
3149e08aacStbbdev 
3249e08aacStbbdev namespace r1 {
338827ea7dSLong Nguyen TBB_EXPORT void*       __TBB_EXPORTED_FUNC cache_aligned_allocate(std::size_t size);
348827ea7dSLong Nguyen TBB_EXPORT void        __TBB_EXPORTED_FUNC cache_aligned_deallocate(void* p);
358827ea7dSLong Nguyen TBB_EXPORT std::size_t __TBB_EXPORTED_FUNC cache_line_size();
3649e08aacStbbdev }
3749e08aacStbbdev 
3849e08aacStbbdev namespace d1 {
3949e08aacStbbdev 
4049e08aacStbbdev template<typename T>
4149e08aacStbbdev class cache_aligned_allocator {
4249e08aacStbbdev public:
4349e08aacStbbdev     using value_type = T;
4449e08aacStbbdev     using propagate_on_container_move_assignment = std::true_type;
4549e08aacStbbdev 
4649e08aacStbbdev     //! Always defined for TBB containers (supported since C++17 for std containers)
4749e08aacStbbdev     using is_always_equal = std::true_type;
4849e08aacStbbdev 
4949e08aacStbbdev     cache_aligned_allocator() = default;
cache_aligned_allocator(const cache_aligned_allocator<U> &)5049e08aacStbbdev     template<typename U> cache_aligned_allocator(const cache_aligned_allocator<U>&) noexcept {}
5149e08aacStbbdev 
5249e08aacStbbdev     //! Allocate space for n objects, starting on a cache/sector line.
allocate(std::size_t n)53b15aabb3Stbbdev     __TBB_nodiscard T* allocate(std::size_t n) {
5449e08aacStbbdev         return static_cast<T*>(r1::cache_aligned_allocate(n * sizeof(value_type)));
5549e08aacStbbdev     }
5649e08aacStbbdev 
5749e08aacStbbdev     //! Free block of memory that starts on a cache line
deallocate(T * p,std::size_t)5849e08aacStbbdev     void deallocate(T* p, std::size_t) {
5949e08aacStbbdev         r1::cache_aligned_deallocate(p);
6049e08aacStbbdev     }
6149e08aacStbbdev 
6249e08aacStbbdev     //! Largest value for which method allocate might succeed.
max_size()6349e08aacStbbdev     std::size_t max_size() const noexcept {
6449e08aacStbbdev         return (~std::size_t(0) - r1::cache_line_size()) / sizeof(value_type);
6549e08aacStbbdev     }
6649e08aacStbbdev 
6749e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN
6849e08aacStbbdev     using pointer = value_type*;
6949e08aacStbbdev     using const_pointer = const value_type*;
7049e08aacStbbdev     using reference = value_type&;
7149e08aacStbbdev     using const_reference = const value_type&;
7249e08aacStbbdev     using difference_type = std::ptrdiff_t;
7349e08aacStbbdev     using size_type = std::size_t;
7449e08aacStbbdev     template<typename U> struct rebind {
7549e08aacStbbdev         using other = cache_aligned_allocator<U>;
7649e08aacStbbdev     };
7749e08aacStbbdev     template<typename U, typename... Args>
construct(U * p,Args &&...args)7849e08aacStbbdev     void construct(U *p, Args&&... args)
7949e08aacStbbdev         { ::new (p) U(std::forward<Args>(args)...); }
destroy(pointer p)8049e08aacStbbdev     void destroy(pointer p) { p->~value_type(); }
address(reference x)8149e08aacStbbdev     pointer address(reference x) const { return &x; }
address(const_reference x)8249e08aacStbbdev     const_pointer address(const_reference x) const { return &x; }
8349e08aacStbbdev #endif // TBB_ALLOCATOR_TRAITS_BROKEN
8449e08aacStbbdev };
8549e08aacStbbdev 
8649e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN
8749e08aacStbbdev     template<>
8849e08aacStbbdev     class cache_aligned_allocator<void> {
8949e08aacStbbdev     public:
9049e08aacStbbdev         using pointer = void*;
9149e08aacStbbdev         using const_pointer = const void*;
9249e08aacStbbdev         using value_type = void;
9349e08aacStbbdev         template<typename U> struct rebind {
9449e08aacStbbdev             using other = cache_aligned_allocator<U>;
9549e08aacStbbdev         };
9649e08aacStbbdev     };
9749e08aacStbbdev #endif
9849e08aacStbbdev 
9949e08aacStbbdev template<typename T, typename U>
10049e08aacStbbdev bool operator==(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return true; }
10149e08aacStbbdev 
102b15aabb3Stbbdev #if !__TBB_CPP20_COMPARISONS_PRESENT
10349e08aacStbbdev template<typename T, typename U>
10449e08aacStbbdev bool operator!=(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return false; }
105b15aabb3Stbbdev #endif
10649e08aacStbbdev 
10749e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
10849e08aacStbbdev 
10949e08aacStbbdev //! C++17 memory resource wrapper to ensure cache line size alignment
11049e08aacStbbdev class cache_aligned_resource : public std::pmr::memory_resource {
11149e08aacStbbdev public:
cache_aligned_resource()11249e08aacStbbdev     cache_aligned_resource() : cache_aligned_resource(std::pmr::get_default_resource()) {}
cache_aligned_resource(std::pmr::memory_resource * upstream)11349e08aacStbbdev     explicit cache_aligned_resource(std::pmr::memory_resource* upstream) : m_upstream(upstream) {}
11449e08aacStbbdev 
upstream_resource()11549e08aacStbbdev     std::pmr::memory_resource* upstream_resource() const {
11649e08aacStbbdev         return m_upstream;
11749e08aacStbbdev     }
11849e08aacStbbdev 
11949e08aacStbbdev private:
12049e08aacStbbdev     //! We don't know what memory resource set. Use padding to guarantee alignment
do_allocate(std::size_t bytes,std::size_t alignment)12149e08aacStbbdev     void* do_allocate(std::size_t bytes, std::size_t alignment) override {
12249e08aacStbbdev         // TODO: make it common with tbb_allocator.cpp
12349e08aacStbbdev         std::size_t cache_line_alignment = correct_alignment(alignment);
12449e08aacStbbdev         std::size_t space = correct_size(bytes) + cache_line_alignment;
12549e08aacStbbdev         std::uintptr_t base = reinterpret_cast<std::uintptr_t>(m_upstream->allocate(space));
12657f524caSIlya Isaev         __TBB_ASSERT(base != 0, "Upstream resource returned nullptr.");
12749e08aacStbbdev 
12849e08aacStbbdev         // Round up to the next cache line (align the base address)
12949e08aacStbbdev         std::uintptr_t result = (base + cache_line_alignment) & ~(cache_line_alignment - 1);
13049e08aacStbbdev         __TBB_ASSERT((result - base) >= sizeof(std::uintptr_t), "Can`t store a base pointer to the header");
13149e08aacStbbdev         __TBB_ASSERT(space - (result - base) >= bytes, "Not enough space for the storage");
13249e08aacStbbdev 
13349e08aacStbbdev         // Record where block actually starts.
13449e08aacStbbdev         (reinterpret_cast<std::uintptr_t*>(result))[-1] = base;
13549e08aacStbbdev         return reinterpret_cast<void*>(result);
13649e08aacStbbdev     }
13749e08aacStbbdev 
do_deallocate(void * ptr,std::size_t bytes,std::size_t alignment)13849e08aacStbbdev     void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override {
13949e08aacStbbdev         if (ptr) {
14049e08aacStbbdev             // Recover where block actually starts
14149e08aacStbbdev             std::uintptr_t base = (reinterpret_cast<std::uintptr_t*>(ptr))[-1];
14249e08aacStbbdev             m_upstream->deallocate(reinterpret_cast<void*>(base), correct_size(bytes) + correct_alignment(alignment));
14349e08aacStbbdev         }
14449e08aacStbbdev     }
14549e08aacStbbdev 
do_is_equal(const std::pmr::memory_resource & other)14649e08aacStbbdev     bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
14749e08aacStbbdev         if (this == &other) { return true; }
14849e08aacStbbdev #if __TBB_USE_OPTIONAL_RTTI
14949e08aacStbbdev         const cache_aligned_resource* other_res = dynamic_cast<const cache_aligned_resource*>(&other);
15049e08aacStbbdev         return other_res && (upstream_resource() == other_res->upstream_resource());
15149e08aacStbbdev #else
15249e08aacStbbdev         return false;
15349e08aacStbbdev #endif
15449e08aacStbbdev     }
15549e08aacStbbdev 
correct_alignment(std::size_t alignment)15649e08aacStbbdev     std::size_t correct_alignment(std::size_t alignment) {
15749e08aacStbbdev         __TBB_ASSERT(tbb::detail::is_power_of_two(alignment), "Alignment is not a power of 2");
15849e08aacStbbdev #if __TBB_CPP17_HW_INTERFERENCE_SIZE_PRESENT
15949e08aacStbbdev         std::size_t cache_line_size = std::hardware_destructive_interference_size;
16049e08aacStbbdev #else
16149e08aacStbbdev         std::size_t cache_line_size = r1::cache_line_size();
16249e08aacStbbdev #endif
16349e08aacStbbdev         return alignment < cache_line_size ? cache_line_size : alignment;
16449e08aacStbbdev     }
16549e08aacStbbdev 
correct_size(std::size_t bytes)16649e08aacStbbdev     std::size_t correct_size(std::size_t bytes) {
16749e08aacStbbdev         // To handle the case, when small size requested. There could be not
16849e08aacStbbdev         // enough space to store the original pointer.
16949e08aacStbbdev         return bytes < sizeof(std::uintptr_t) ? sizeof(std::uintptr_t) : bytes;
17049e08aacStbbdev     }
17149e08aacStbbdev 
17249e08aacStbbdev     std::pmr::memory_resource* m_upstream;
17349e08aacStbbdev };
17449e08aacStbbdev 
17549e08aacStbbdev #endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT
17649e08aacStbbdev 
17749e08aacStbbdev } // namespace d1
17849e08aacStbbdev } // namespace detail
17949e08aacStbbdev 
18049e08aacStbbdev inline namespace v1 {
18149e08aacStbbdev using detail::d1::cache_aligned_allocator;
18249e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
18349e08aacStbbdev using detail::d1::cache_aligned_resource;
18449e08aacStbbdev #endif
18549e08aacStbbdev } // namespace v1
18649e08aacStbbdev } // namespace tbb
18749e08aacStbbdev 
18849e08aacStbbdev #endif /* __TBB_cache_aligned_allocator_H */
18949e08aacStbbdev 
190