1*49e08aacStbbdev /* 2*49e08aacStbbdev Copyright (c) 2005-2020 Intel Corporation 3*49e08aacStbbdev 4*49e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*49e08aacStbbdev you may not use this file except in compliance with the License. 6*49e08aacStbbdev You may obtain a copy of the License at 7*49e08aacStbbdev 8*49e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0 9*49e08aacStbbdev 10*49e08aacStbbdev Unless required by applicable law or agreed to in writing, software 11*49e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*49e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*49e08aacStbbdev See the License for the specific language governing permissions and 14*49e08aacStbbdev limitations under the License. 15*49e08aacStbbdev */ 16*49e08aacStbbdev 17*49e08aacStbbdev #ifndef __TBB_cache_aligned_allocator_H 18*49e08aacStbbdev #define __TBB_cache_aligned_allocator_H 19*49e08aacStbbdev 20*49e08aacStbbdev #include "detail/_utils.h" 21*49e08aacStbbdev #include "detail/_namespace_injection.h" 22*49e08aacStbbdev #include <cstdlib> 23*49e08aacStbbdev #include <utility> 24*49e08aacStbbdev 25*49e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 26*49e08aacStbbdev #include <memory_resource> 27*49e08aacStbbdev #endif 28*49e08aacStbbdev 29*49e08aacStbbdev namespace tbb { 30*49e08aacStbbdev namespace detail { 31*49e08aacStbbdev 32*49e08aacStbbdev namespace r1 { 33*49e08aacStbbdev void* __TBB_EXPORTED_FUNC cache_aligned_allocate(std::size_t size); 34*49e08aacStbbdev void __TBB_EXPORTED_FUNC cache_aligned_deallocate(void* p); 35*49e08aacStbbdev std::size_t __TBB_EXPORTED_FUNC cache_line_size(); 36*49e08aacStbbdev } 37*49e08aacStbbdev 38*49e08aacStbbdev namespace d1 { 39*49e08aacStbbdev 40*49e08aacStbbdev template<typename T> 41*49e08aacStbbdev class cache_aligned_allocator { 42*49e08aacStbbdev public: 43*49e08aacStbbdev using value_type = T; 44*49e08aacStbbdev using propagate_on_container_move_assignment = std::true_type; 45*49e08aacStbbdev 46*49e08aacStbbdev //! Always defined for TBB containers (supported since C++17 for std containers) 47*49e08aacStbbdev using is_always_equal = std::true_type; 48*49e08aacStbbdev 49*49e08aacStbbdev cache_aligned_allocator() = default; 50*49e08aacStbbdev template<typename U> cache_aligned_allocator(const cache_aligned_allocator<U>&) noexcept {} 51*49e08aacStbbdev 52*49e08aacStbbdev //! Allocate space for n objects, starting on a cache/sector line. 53*49e08aacStbbdev T* allocate(std::size_t n) { 54*49e08aacStbbdev return static_cast<T*>(r1::cache_aligned_allocate(n * sizeof(value_type))); 55*49e08aacStbbdev } 56*49e08aacStbbdev 57*49e08aacStbbdev //! Free block of memory that starts on a cache line 58*49e08aacStbbdev void deallocate(T* p, std::size_t) { 59*49e08aacStbbdev r1::cache_aligned_deallocate(p); 60*49e08aacStbbdev } 61*49e08aacStbbdev 62*49e08aacStbbdev //! Largest value for which method allocate might succeed. 63*49e08aacStbbdev std::size_t max_size() const noexcept { 64*49e08aacStbbdev return (~std::size_t(0) - r1::cache_line_size()) / sizeof(value_type); 65*49e08aacStbbdev } 66*49e08aacStbbdev 67*49e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 68*49e08aacStbbdev using pointer = value_type*; 69*49e08aacStbbdev using const_pointer = const value_type*; 70*49e08aacStbbdev using reference = value_type&; 71*49e08aacStbbdev using const_reference = const value_type&; 72*49e08aacStbbdev using difference_type = std::ptrdiff_t; 73*49e08aacStbbdev using size_type = std::size_t; 74*49e08aacStbbdev template<typename U> struct rebind { 75*49e08aacStbbdev using other = cache_aligned_allocator<U>; 76*49e08aacStbbdev }; 77*49e08aacStbbdev template<typename U, typename... Args> 78*49e08aacStbbdev void construct(U *p, Args&&... args) 79*49e08aacStbbdev { ::new (p) U(std::forward<Args>(args)...); } 80*49e08aacStbbdev void destroy(pointer p) { p->~value_type(); } 81*49e08aacStbbdev pointer address(reference x) const { return &x; } 82*49e08aacStbbdev const_pointer address(const_reference x) const { return &x; } 83*49e08aacStbbdev #endif // TBB_ALLOCATOR_TRAITS_BROKEN 84*49e08aacStbbdev }; 85*49e08aacStbbdev 86*49e08aacStbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN 87*49e08aacStbbdev template<> 88*49e08aacStbbdev class cache_aligned_allocator<void> { 89*49e08aacStbbdev public: 90*49e08aacStbbdev using pointer = void*; 91*49e08aacStbbdev using const_pointer = const void*; 92*49e08aacStbbdev using value_type = void; 93*49e08aacStbbdev template<typename U> struct rebind { 94*49e08aacStbbdev using other = cache_aligned_allocator<U>; 95*49e08aacStbbdev }; 96*49e08aacStbbdev }; 97*49e08aacStbbdev #endif 98*49e08aacStbbdev 99*49e08aacStbbdev template<typename T, typename U> 100*49e08aacStbbdev bool operator==(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return true; } 101*49e08aacStbbdev 102*49e08aacStbbdev template<typename T, typename U> 103*49e08aacStbbdev bool operator!=(const cache_aligned_allocator<T>&, const cache_aligned_allocator<U>&) noexcept { return false; } 104*49e08aacStbbdev 105*49e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 106*49e08aacStbbdev 107*49e08aacStbbdev //! C++17 memory resource wrapper to ensure cache line size alignment 108*49e08aacStbbdev class cache_aligned_resource : public std::pmr::memory_resource { 109*49e08aacStbbdev public: 110*49e08aacStbbdev cache_aligned_resource() : cache_aligned_resource(std::pmr::get_default_resource()) {} 111*49e08aacStbbdev explicit cache_aligned_resource(std::pmr::memory_resource* upstream) : m_upstream(upstream) {} 112*49e08aacStbbdev 113*49e08aacStbbdev std::pmr::memory_resource* upstream_resource() const { 114*49e08aacStbbdev return m_upstream; 115*49e08aacStbbdev } 116*49e08aacStbbdev 117*49e08aacStbbdev private: 118*49e08aacStbbdev //! We don't know what memory resource set. Use padding to guarantee alignment 119*49e08aacStbbdev void* do_allocate(std::size_t bytes, std::size_t alignment) override { 120*49e08aacStbbdev // TODO: make it common with tbb_allocator.cpp 121*49e08aacStbbdev std::size_t cache_line_alignment = correct_alignment(alignment); 122*49e08aacStbbdev std::size_t space = correct_size(bytes) + cache_line_alignment; 123*49e08aacStbbdev std::uintptr_t base = reinterpret_cast<std::uintptr_t>(m_upstream->allocate(space)); 124*49e08aacStbbdev __TBB_ASSERT(base != 0, "Upstream resource returned NULL."); 125*49e08aacStbbdev 126*49e08aacStbbdev // Round up to the next cache line (align the base address) 127*49e08aacStbbdev std::uintptr_t result = (base + cache_line_alignment) & ~(cache_line_alignment - 1); 128*49e08aacStbbdev __TBB_ASSERT((result - base) >= sizeof(std::uintptr_t), "Can`t store a base pointer to the header"); 129*49e08aacStbbdev __TBB_ASSERT(space - (result - base) >= bytes, "Not enough space for the storage"); 130*49e08aacStbbdev 131*49e08aacStbbdev // Record where block actually starts. 132*49e08aacStbbdev (reinterpret_cast<std::uintptr_t*>(result))[-1] = base; 133*49e08aacStbbdev return reinterpret_cast<void*>(result); 134*49e08aacStbbdev } 135*49e08aacStbbdev 136*49e08aacStbbdev void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override { 137*49e08aacStbbdev if (ptr) { 138*49e08aacStbbdev // Recover where block actually starts 139*49e08aacStbbdev std::uintptr_t base = (reinterpret_cast<std::uintptr_t*>(ptr))[-1]; 140*49e08aacStbbdev m_upstream->deallocate(reinterpret_cast<void*>(base), correct_size(bytes) + correct_alignment(alignment)); 141*49e08aacStbbdev } 142*49e08aacStbbdev } 143*49e08aacStbbdev 144*49e08aacStbbdev bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { 145*49e08aacStbbdev if (this == &other) { return true; } 146*49e08aacStbbdev #if __TBB_USE_OPTIONAL_RTTI 147*49e08aacStbbdev const cache_aligned_resource* other_res = dynamic_cast<const cache_aligned_resource*>(&other); 148*49e08aacStbbdev return other_res && (upstream_resource() == other_res->upstream_resource()); 149*49e08aacStbbdev #else 150*49e08aacStbbdev return false; 151*49e08aacStbbdev #endif 152*49e08aacStbbdev } 153*49e08aacStbbdev 154*49e08aacStbbdev std::size_t correct_alignment(std::size_t alignment) { 155*49e08aacStbbdev __TBB_ASSERT(tbb::detail::is_power_of_two(alignment), "Alignment is not a power of 2"); 156*49e08aacStbbdev #if __TBB_CPP17_HW_INTERFERENCE_SIZE_PRESENT 157*49e08aacStbbdev std::size_t cache_line_size = std::hardware_destructive_interference_size; 158*49e08aacStbbdev #else 159*49e08aacStbbdev std::size_t cache_line_size = r1::cache_line_size(); 160*49e08aacStbbdev #endif 161*49e08aacStbbdev return alignment < cache_line_size ? cache_line_size : alignment; 162*49e08aacStbbdev } 163*49e08aacStbbdev 164*49e08aacStbbdev std::size_t correct_size(std::size_t bytes) { 165*49e08aacStbbdev // To handle the case, when small size requested. There could be not 166*49e08aacStbbdev // enough space to store the original pointer. 167*49e08aacStbbdev return bytes < sizeof(std::uintptr_t) ? sizeof(std::uintptr_t) : bytes; 168*49e08aacStbbdev } 169*49e08aacStbbdev 170*49e08aacStbbdev std::pmr::memory_resource* m_upstream; 171*49e08aacStbbdev }; 172*49e08aacStbbdev 173*49e08aacStbbdev #endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT 174*49e08aacStbbdev 175*49e08aacStbbdev } // namespace d1 176*49e08aacStbbdev } // namespace detail 177*49e08aacStbbdev 178*49e08aacStbbdev inline namespace v1 { 179*49e08aacStbbdev using detail::d1::cache_aligned_allocator; 180*49e08aacStbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 181*49e08aacStbbdev using detail::d1::cache_aligned_resource; 182*49e08aacStbbdev #endif 183*49e08aacStbbdev } // namespace v1 184*49e08aacStbbdev } // namespace tbb 185*49e08aacStbbdev 186*49e08aacStbbdev #endif /* __TBB_cache_aligned_allocator_H */ 187*49e08aacStbbdev 188