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