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