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 // Basic testing of an allocator
18*51c0b2f7Stbbdev // Tests against requirements in 20.1.5 of ISO C++ Standard (1998).
19*51c0b2f7Stbbdev // Does not check for thread safety or false sharing issues.
20*51c0b2f7Stbbdev //
21*51c0b2f7Stbbdev // Tests for compatibility with the host's STL are in
22*51c0b2f7Stbbdev // test_Allocator_STL.h.  Those tests are in a separate file
23*51c0b2f7Stbbdev // because they bring in lots of STL headers, and the tests here
24*51c0b2f7Stbbdev // are supposed to work in the abscense of STL.
25*51c0b2f7Stbbdev 
26*51c0b2f7Stbbdev #include "common/test.h"
27*51c0b2f7Stbbdev #include "common/utils.h"
28*51c0b2f7Stbbdev #include <utility> //for std::pair
29*51c0b2f7Stbbdev #include <cstring>
30*51c0b2f7Stbbdev 
31*51c0b2f7Stbbdev //! Compile-time error if x and y have different types
32*51c0b2f7Stbbdev template<typename T>
33*51c0b2f7Stbbdev void AssertSameType( const T& /*x*/, const T& /*y*/ ) {}
34*51c0b2f7Stbbdev 
35*51c0b2f7Stbbdev //! The function to zero-initialize arrays; useful to avoid warnings
36*51c0b2f7Stbbdev template <typename T>
37*51c0b2f7Stbbdev void zero_fill(void* array, size_t n) {
38*51c0b2f7Stbbdev     memset(array, 0, sizeof(T)*n);
39*51c0b2f7Stbbdev }
40*51c0b2f7Stbbdev 
41*51c0b2f7Stbbdev template<typename A>
42*51c0b2f7Stbbdev struct is_zero_filling {
43*51c0b2f7Stbbdev     static const bool value = false;
44*51c0b2f7Stbbdev };
45*51c0b2f7Stbbdev 
46*51c0b2f7Stbbdev int NumberOfFoo;
47*51c0b2f7Stbbdev 
48*51c0b2f7Stbbdev template<typename T, size_t N>
49*51c0b2f7Stbbdev struct Foo {
50*51c0b2f7Stbbdev     T foo_array[N];
51*51c0b2f7Stbbdev     Foo() {
52*51c0b2f7Stbbdev         zero_fill<T>(foo_array, N);
53*51c0b2f7Stbbdev         ++NumberOfFoo;
54*51c0b2f7Stbbdev     }
55*51c0b2f7Stbbdev     Foo( const Foo& x ) {
56*51c0b2f7Stbbdev         *this = x;
57*51c0b2f7Stbbdev         //Internal call of assignment
58*51c0b2f7Stbbdev     }
59*51c0b2f7Stbbdev     Foo& operator=( const Foo& x ) {
60*51c0b2f7Stbbdev         for (size_t i = 0; i < N; i++)
61*51c0b2f7Stbbdev             foo_array[i] = x.foo_array[i];
62*51c0b2f7Stbbdev         ++NumberOfFoo;
63*51c0b2f7Stbbdev         return *this;
64*51c0b2f7Stbbdev     }
65*51c0b2f7Stbbdev 
66*51c0b2f7Stbbdev     ~Foo() {
67*51c0b2f7Stbbdev         --NumberOfFoo;
68*51c0b2f7Stbbdev     }
69*51c0b2f7Stbbdev };
70*51c0b2f7Stbbdev 
71*51c0b2f7Stbbdev inline char PseudoRandomValue( size_t j, size_t k ) {
72*51c0b2f7Stbbdev     return char(j*3 ^ j>>4 ^ k);
73*51c0b2f7Stbbdev }
74*51c0b2f7Stbbdev 
75*51c0b2f7Stbbdev #if __APPLE__
76*51c0b2f7Stbbdev #include <fcntl.h>
77*51c0b2f7Stbbdev #include <unistd.h>
78*51c0b2f7Stbbdev 
79*51c0b2f7Stbbdev // A RAII class to disable stderr in a certain scope. It's not thread-safe.
80*51c0b2f7Stbbdev class DisableStderr {
81*51c0b2f7Stbbdev     int stderrCopy;
82*51c0b2f7Stbbdev     static void dupToStderrAndClose(int fd) {
83*51c0b2f7Stbbdev         int ret = dup2(fd, STDERR_FILENO); // close current stderr
84*51c0b2f7Stbbdev         REQUIRE(ret != -1);
85*51c0b2f7Stbbdev         ret = close(fd);
86*51c0b2f7Stbbdev         REQUIRE(ret != -1);
87*51c0b2f7Stbbdev     }
88*51c0b2f7Stbbdev public:
89*51c0b2f7Stbbdev     DisableStderr() {
90*51c0b2f7Stbbdev         int devNull = open("/dev/null", O_WRONLY);
91*51c0b2f7Stbbdev         REQUIRE(devNull != -1);
92*51c0b2f7Stbbdev         stderrCopy = dup(STDERR_FILENO);
93*51c0b2f7Stbbdev         REQUIRE(stderrCopy != -1);
94*51c0b2f7Stbbdev         dupToStderrAndClose(devNull);
95*51c0b2f7Stbbdev     }
96*51c0b2f7Stbbdev     ~DisableStderr() {
97*51c0b2f7Stbbdev         dupToStderrAndClose(stderrCopy);
98*51c0b2f7Stbbdev     }
99*51c0b2f7Stbbdev };
100*51c0b2f7Stbbdev #endif
101*51c0b2f7Stbbdev 
102*51c0b2f7Stbbdev //! T is type and A is allocator for that type
103*51c0b2f7Stbbdev template<typename T, typename A>
104*51c0b2f7Stbbdev void TestBrokenAllocator(A& a) {
105*51c0b2f7Stbbdev     T x;
106*51c0b2f7Stbbdev     const T cx = T();
107*51c0b2f7Stbbdev     // See Table 32 in ISO ++ Standard
108*51c0b2f7Stbbdev     typename A::pointer px = &x;
109*51c0b2f7Stbbdev     typename A::const_pointer pcx = &cx;
110*51c0b2f7Stbbdev 
111*51c0b2f7Stbbdev     typename A::reference rx = x;
112*51c0b2f7Stbbdev     REQUIRE(&rx == &x);
113*51c0b2f7Stbbdev 
114*51c0b2f7Stbbdev     typename A::const_reference rcx = cx;
115*51c0b2f7Stbbdev     REQUIRE(&rcx==&cx);
116*51c0b2f7Stbbdev 
117*51c0b2f7Stbbdev     typename A::value_type v = x;
118*51c0b2f7Stbbdev 
119*51c0b2f7Stbbdev     typename A::size_type size;
120*51c0b2f7Stbbdev     size = 0;
121*51c0b2f7Stbbdev     --size;
122*51c0b2f7Stbbdev     REQUIRE_MESSAGE(size > 0, "not an unsigned integral type?");
123*51c0b2f7Stbbdev 
124*51c0b2f7Stbbdev     typename A::difference_type difference;
125*51c0b2f7Stbbdev     difference = 0;
126*51c0b2f7Stbbdev     --difference;
127*51c0b2f7Stbbdev     REQUIRE_MESSAGE(difference<0, "not an signed integral type?");
128*51c0b2f7Stbbdev 
129*51c0b2f7Stbbdev     // "rebind" tested by our caller
130*51c0b2f7Stbbdev 
131*51c0b2f7Stbbdev     REQUIRE(a.address(rx) == px);
132*51c0b2f7Stbbdev 
133*51c0b2f7Stbbdev     REQUIRE(a.address(rcx) == pcx);
134*51c0b2f7Stbbdev 
135*51c0b2f7Stbbdev     // Test "a.max_size()"
136*51c0b2f7Stbbdev     AssertSameType(a.max_size(), typename A::size_type(0));
137*51c0b2f7Stbbdev     // Following assertion catches case where max_size() is so large that computation of
138*51c0b2f7Stbbdev     // number of bytes for such an allocation would overflow size_type.
139*51c0b2f7Stbbdev     REQUIRE_MESSAGE((a.max_size() * typename A::size_type(sizeof(T)) >= a.max_size()), "max_size larger than reasonable");
140*51c0b2f7Stbbdev 
141*51c0b2f7Stbbdev     // Test "a.construct(p,t)"
142*51c0b2f7Stbbdev     int n = NumberOfFoo;
143*51c0b2f7Stbbdev     typename A::pointer p = a.allocate(1);
144*51c0b2f7Stbbdev     a.construct(p, cx);
145*51c0b2f7Stbbdev     REQUIRE_MESSAGE(NumberOfFoo == n + 1, "constructor for Foo not called?");
146*51c0b2f7Stbbdev 
147*51c0b2f7Stbbdev     // Test "a.destroy(p)"
148*51c0b2f7Stbbdev     a.destroy(p);
149*51c0b2f7Stbbdev     REQUIRE_MESSAGE(NumberOfFoo == n, "destructor for Foo not called?");
150*51c0b2f7Stbbdev     a.deallocate(p, 1);
151*51c0b2f7Stbbdev 
152*51c0b2f7Stbbdev     {
153*51c0b2f7Stbbdev         typedef typename A::template rebind<std::pair<typename A::value_type, typename A::value_type> >::other pair_allocator_type;
154*51c0b2f7Stbbdev         pair_allocator_type pair_allocator(a);
155*51c0b2f7Stbbdev         int NumberOfFooBeforeConstruct = NumberOfFoo;
156*51c0b2f7Stbbdev         typename pair_allocator_type::pointer pair_pointer = pair_allocator.allocate(1);
157*51c0b2f7Stbbdev         pair_allocator.construct(pair_pointer, cx, cx);
158*51c0b2f7Stbbdev         REQUIRE_MESSAGE(NumberOfFoo == NumberOfFooBeforeConstruct+2, "constructor for Foo not called appropriate number of times?");
159*51c0b2f7Stbbdev 
160*51c0b2f7Stbbdev         pair_allocator.destroy(pair_pointer);
161*51c0b2f7Stbbdev         REQUIRE_MESSAGE(NumberOfFoo == NumberOfFooBeforeConstruct, "destructor for Foo not called appropriate number of times?");
162*51c0b2f7Stbbdev         pair_allocator.deallocate(pair_pointer, 1);
163*51c0b2f7Stbbdev     }
164*51c0b2f7Stbbdev }
165*51c0b2f7Stbbdev 
166*51c0b2f7Stbbdev //! T is type and A is allocator for that type
167*51c0b2f7Stbbdev template<typename T, typename A>
168*51c0b2f7Stbbdev void TestAllocatorConcept(A& a) {
169*51c0b2f7Stbbdev     // Test "a.allocate(p,n)
170*51c0b2f7Stbbdev     typename std::allocator_traits<A>::pointer array[100];
171*51c0b2f7Stbbdev     std::size_t sizeof_T = sizeof(T);
172*51c0b2f7Stbbdev     for(std::size_t k = 0; k < 100; ++k) {
173*51c0b2f7Stbbdev         array[k] = a.allocate(k);
174*51c0b2f7Stbbdev         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
175*51c0b2f7Stbbdev         for(std::size_t j=0; j < k * sizeof_T; ++j)
176*51c0b2f7Stbbdev             s[j] = PseudoRandomValue(j, k);
177*51c0b2f7Stbbdev     }
178*51c0b2f7Stbbdev 
179*51c0b2f7Stbbdev     // Test "a.deallocate(p,n)
180*51c0b2f7Stbbdev     for(std::size_t k = 0; k < 100; ++k) {
181*51c0b2f7Stbbdev         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
182*51c0b2f7Stbbdev         for(std::size_t j = 0; j < k * sizeof_T; ++j)
183*51c0b2f7Stbbdev             REQUIRE(s[j] == PseudoRandomValue(j, k));
184*51c0b2f7Stbbdev         a.deallocate(array[k], k);
185*51c0b2f7Stbbdev     }
186*51c0b2f7Stbbdev }
187*51c0b2f7Stbbdev 
188*51c0b2f7Stbbdev //! T is type and A is allocator for that type
189*51c0b2f7Stbbdev template<typename T, typename A>
190*51c0b2f7Stbbdev void TestAllocatorExceptions(A& a) {
191*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
192*51c0b2f7Stbbdev     volatile size_t too_big = (~std::size_t(0) - 1024 * 1024) / sizeof(T);
193*51c0b2f7Stbbdev     bool exception_caught = false;
194*51c0b2f7Stbbdev     typename std::allocator_traits<A>::pointer p1 = nullptr;
195*51c0b2f7Stbbdev     try {
196*51c0b2f7Stbbdev #if __APPLE__
197*51c0b2f7Stbbdev         // On macOS*, failure to map memory results in messages to stderr;
198*51c0b2f7Stbbdev         // suppress them.
199*51c0b2f7Stbbdev         DisableStderr disableStderr;
200*51c0b2f7Stbbdev #endif
201*51c0b2f7Stbbdev         p1 = a.allocate(too_big);
202*51c0b2f7Stbbdev     } catch (std::bad_alloc&) {
203*51c0b2f7Stbbdev         exception_caught = true;
204*51c0b2f7Stbbdev     }
205*51c0b2f7Stbbdev     REQUIRE_MESSAGE(exception_caught, "allocate expected to throw bad_alloc");
206*51c0b2f7Stbbdev     a.deallocate(p1, too_big);
207*51c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS
208*51c0b2f7Stbbdev     utils::suppress_unused_warning(a);
209*51c0b2f7Stbbdev }
210*51c0b2f7Stbbdev 
211*51c0b2f7Stbbdev #if _MSC_VER && !defined(__INTEL_COMPILER)
212*51c0b2f7Stbbdev     // Workaround for erroneous "conditional expression is constant" warning in method check_allocate.
213*51c0b2f7Stbbdev     #pragma warning (disable: 4127)
214*51c0b2f7Stbbdev #endif
215*51c0b2f7Stbbdev 
216*51c0b2f7Stbbdev // A is an allocator for some type
217*51c0b2f7Stbbdev template<typename A>
218*51c0b2f7Stbbdev struct Body: utils::NoAssign {
219*51c0b2f7Stbbdev     using pointer_type = typename std::allocator_traits<A>::pointer;
220*51c0b2f7Stbbdev     using value_type = typename std::allocator_traits<A>::value_type;
221*51c0b2f7Stbbdev     // For the int types and above this test runs too long
222*51c0b2f7Stbbdev     static const std::size_t max_k = sizeof(value_type) < sizeof(int) ? 100000 : 5000;
223*51c0b2f7Stbbdev     A &a;
224*51c0b2f7Stbbdev     Body(A &a_) : a(a_) {}
225*51c0b2f7Stbbdev     void check_allocate(pointer_type array[], std::size_t i, std::size_t t) const
226*51c0b2f7Stbbdev     {
227*51c0b2f7Stbbdev         REQUIRE(array[i] == nullptr);
228*51c0b2f7Stbbdev         std::size_t size = i * (i & 3);
229*51c0b2f7Stbbdev         array[i] = a.allocate(size);
230*51c0b2f7Stbbdev         REQUIRE_MESSAGE(array[i] != nullptr, "allocator returned null");
231*51c0b2f7Stbbdev         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
232*51c0b2f7Stbbdev         for(std::size_t j = 0; j < size * sizeof(value_type); ++j) {
233*51c0b2f7Stbbdev             if(is_zero_filling<typename std::allocator_traits<A>::template rebind_alloc<void>>::value)
234*51c0b2f7Stbbdev                 REQUIRE(!s[j]);
235*51c0b2f7Stbbdev             s[j] = PseudoRandomValue(i, t);
236*51c0b2f7Stbbdev         }
237*51c0b2f7Stbbdev     }
238*51c0b2f7Stbbdev 
239*51c0b2f7Stbbdev     void check_deallocate(pointer_type array[], std::size_t i, std::size_t t) const
240*51c0b2f7Stbbdev     {
241*51c0b2f7Stbbdev         REQUIRE(array[i] != nullptr);
242*51c0b2f7Stbbdev         size_t size = i * (i & 3);
243*51c0b2f7Stbbdev         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
244*51c0b2f7Stbbdev         for(std::size_t j=0; j < size * sizeof(value_type); ++j)
245*51c0b2f7Stbbdev             REQUIRE_MESSAGE(s[j] == PseudoRandomValue(i, t), "Thread safety test failed");
246*51c0b2f7Stbbdev         a.deallocate(array[i], size);
247*51c0b2f7Stbbdev         array[i] = 0;
248*51c0b2f7Stbbdev     }
249*51c0b2f7Stbbdev 
250*51c0b2f7Stbbdev     void operator()(std::size_t thread_id) const {
251*51c0b2f7Stbbdev         pointer_type array[256];
252*51c0b2f7Stbbdev 
253*51c0b2f7Stbbdev         for(std::size_t k = 0; k < 256; ++k)
254*51c0b2f7Stbbdev             array[k] = 0;
255*51c0b2f7Stbbdev         for(std::size_t k = 0; k < max_k; ++k) {
256*51c0b2f7Stbbdev             std::size_t i = static_cast<unsigned char>(PseudoRandomValue(k, thread_id));
257*51c0b2f7Stbbdev             if(!array[i]) check_allocate(array, i, thread_id);
258*51c0b2f7Stbbdev             else check_deallocate(array, i, thread_id);
259*51c0b2f7Stbbdev         }
260*51c0b2f7Stbbdev         for(std::size_t k = 0; k < 256; ++k)
261*51c0b2f7Stbbdev             if(array[k])
262*51c0b2f7Stbbdev                 check_deallocate(array, k, thread_id);
263*51c0b2f7Stbbdev     }
264*51c0b2f7Stbbdev };
265*51c0b2f7Stbbdev 
266*51c0b2f7Stbbdev template<typename A>
267*51c0b2f7Stbbdev void TestThreadSafety(A &a) {
268*51c0b2f7Stbbdev     utils::NativeParallelFor(4, Body<A>(a));
269*51c0b2f7Stbbdev }
270*51c0b2f7Stbbdev 
271*51c0b2f7Stbbdev enum TestName { Concept, Broken, Exceptions, ThreadSafety, Comparison };
272*51c0b2f7Stbbdev 
273*51c0b2f7Stbbdev template<typename Allocator>
274*51c0b2f7Stbbdev void TestAllocator(TestName name, const Allocator &a = Allocator()) {
275*51c0b2f7Stbbdev 
276*51c0b2f7Stbbdev     using FooChar = Foo<char, 1>;
277*51c0b2f7Stbbdev     using FooDouble = Foo<double, 1>;
278*51c0b2f7Stbbdev     using FooInt = Foo<int, 17>;
279*51c0b2f7Stbbdev     using FooFloat = Foo<float, 23>;
280*51c0b2f7Stbbdev     #if TBB_ALLOCATOR_TRAITS_BROKEN
281*51c0b2f7Stbbdev         using AllocatorFooChar = typename Allocator::template rebind<FooChar>::other;
282*51c0b2f7Stbbdev         using AllocatorFooDouble = typename Allocator::template rebind<FooDouble>::other;
283*51c0b2f7Stbbdev         using AllocatorFooInt = typename AllocatorFooChar::template rebind<FooInt>::other;
284*51c0b2f7Stbbdev         using AllocatorFooFloat = typename AllocatorFooDouble::template rebind<FooFloat>::other;
285*51c0b2f7Stbbdev     #else
286*51c0b2f7Stbbdev         using AllocatorFooChar = typename std::allocator_traits<Allocator>::template rebind_alloc<FooChar>;
287*51c0b2f7Stbbdev         using AllocatorFooDouble = typename std::allocator_traits<Allocator>::template rebind_alloc<FooDouble>;
288*51c0b2f7Stbbdev         using AllocatorFooInt = typename std::allocator_traits<AllocatorFooChar>::template rebind_alloc<FooInt>;
289*51c0b2f7Stbbdev         using AllocatorFooFloat = typename std::allocator_traits<AllocatorFooDouble>::template rebind_alloc<FooFloat>;
290*51c0b2f7Stbbdev     #endif
291*51c0b2f7Stbbdev 
292*51c0b2f7Stbbdev     NumberOfFoo = 0;
293*51c0b2f7Stbbdev     AllocatorFooChar a1(a);
294*51c0b2f7Stbbdev     AllocatorFooDouble a2(a);
295*51c0b2f7Stbbdev     AllocatorFooInt b1(a1);
296*51c0b2f7Stbbdev     AllocatorFooFloat b2(a2);
297*51c0b2f7Stbbdev 
298*51c0b2f7Stbbdev     switch(name) {
299*51c0b2f7Stbbdev     case Comparison:
300*51c0b2f7Stbbdev         REQUIRE(a1 == b1);
301*51c0b2f7Stbbdev         REQUIRE(!(a2 != b2));
302*51c0b2f7Stbbdev         break;
303*51c0b2f7Stbbdev     case Concept:
304*51c0b2f7Stbbdev         TestAllocatorConcept<FooInt>(b1);
305*51c0b2f7Stbbdev         TestAllocatorConcept<typename AllocatorFooChar::value_type>(a1);
306*51c0b2f7Stbbdev         TestAllocatorConcept<FooFloat>(b2);
307*51c0b2f7Stbbdev         TestAllocatorConcept<typename AllocatorFooDouble::value_type>(a2);
308*51c0b2f7Stbbdev         break;
309*51c0b2f7Stbbdev     case Broken:
310*51c0b2f7Stbbdev     #if TBB_ALLOCATOR_TRAITS_BROKEN
311*51c0b2f7Stbbdev         TestBrokenAllocator<FooInt>(b1);
312*51c0b2f7Stbbdev         TestBrokenAllocator<typename AllocatorFooChar::value_type>(a1);
313*51c0b2f7Stbbdev         TestBrokenAllocator<FooFloat>(b2);
314*51c0b2f7Stbbdev         TestBrokenAllocator<typename AllocatorFooDouble::value_type>(a2);
315*51c0b2f7Stbbdev     #endif
316*51c0b2f7Stbbdev         break;
317*51c0b2f7Stbbdev     case Exceptions:
318*51c0b2f7Stbbdev         TestAllocatorExceptions<FooInt>(b1);
319*51c0b2f7Stbbdev         TestAllocatorExceptions<typename AllocatorFooChar::value_type>(a1);
320*51c0b2f7Stbbdev         TestAllocatorExceptions<FooFloat>(b2);
321*51c0b2f7Stbbdev         TestAllocatorExceptions<typename AllocatorFooDouble::value_type>(a2);
322*51c0b2f7Stbbdev         break;
323*51c0b2f7Stbbdev     case ThreadSafety:
324*51c0b2f7Stbbdev         TestThreadSafety(a1);
325*51c0b2f7Stbbdev         TestThreadSafety(a2);
326*51c0b2f7Stbbdev         break;
327*51c0b2f7Stbbdev     }
328*51c0b2f7Stbbdev     REQUIRE_MESSAGE(NumberOfFoo == 0, "Allocate/deallocate count mismatched");
329*51c0b2f7Stbbdev }
330