1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17// Basic testing of an allocator
18// Tests against requirements in 20.1.5 of ISO C++ Standard (1998).
19// Does not check for thread safety or false sharing issues.
20//
21// Tests for compatibility with the host's STL are in
22// test_Allocator_STL.h. Those tests are in a separate file
23// because they bring in lots of STL headers, and the tests here
24// are supposed to work in the abscense of STL.
25
26#include "harness.h"
27#if __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
28 #include <utility> //for std::pair
29#endif
30
31template<typename A>
32struct is_zero_filling {
33 static const bool value = false;
34};
35
36int NumberOfFoo;
37
38template<typename T, size_t N>
39struct Foo {
40 T foo_array[N];
41 Foo() {
42 zero_fill<T>(foo_array, N);
43 ++NumberOfFoo;
44 }
45 Foo( const Foo& x ) {
46 *this = x;
47 ++NumberOfFoo;
48 }
49 ~Foo() {
50 --NumberOfFoo;
51 }
52};
53
54inline char PseudoRandomValue( size_t j, size_t k ) {
55 return char(j*3 ^ j>>4 ^ k);
56}
57
58#if __APPLE__
59#include <fcntl.h>
60#include <unistd.h>
61
62// A RAII class to disable stderr in a certain scope. It's not thread-safe.
63class DisableStderr {
64 int stderrCopy;
65 static void dupToStderrAndClose(int fd) {
66 int ret = dup2(fd, STDERR_FILENO); // close current stderr
67 ASSERT(ret != -1, NULL);
68 ret = close(fd);
69 ASSERT(ret != -1, NULL);
70 }
71public:
72 DisableStderr() {
73 int devNull = open("/dev/null", O_WRONLY);
74 ASSERT(devNull != -1, NULL);
75 stderrCopy = dup(STDERR_FILENO);
76 ASSERT(stderrCopy != -1, NULL);
77 dupToStderrAndClose(devNull);
78 }
79 ~DisableStderr() {
80 dupToStderrAndClose(stderrCopy);
81 }
82};
83#endif
84
85//! T is type and A is allocator for that type
86template<typename T, typename A>
87void TestBasic( A& a ) {
88 T x;
89 const T cx = T();
90
91 // See Table 32 in ISO ++ Standard
92 typename A::pointer px = &x;
93 typename A::const_pointer pcx = &cx;
94
95 typename A::reference rx = x;
96 ASSERT( &rx==&x, NULL );
97
98 typename A::const_reference rcx = cx;
99 ASSERT( &rcx==&cx, NULL );
100
101 typename A::value_type v = x;
102
103 typename A::size_type size;
104 size = 0;
105 --size;
106 ASSERT( size>0, "not an unsigned integral type?" );
107
108 typename A::difference_type difference;
109 difference = 0;
110 --difference;
111 ASSERT( difference<0, "not an signed integral type?" );
112
113 // "rebind" tested by our caller
114
115 ASSERT( a.address(rx)==px, NULL );
116
117 ASSERT( a.address(rcx)==pcx, NULL );
118
119 typename A::pointer array[100];
120 size_t sizeof_T = sizeof(T);
121 for( size_t k=0; k<100; ++k ) {
122 array[k] = k&1 ? a.allocate(k,array[0]) : a.allocate(k);
123 char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
124 for( size_t j=0; j<k*sizeof_T; ++j )
125 s[j] = PseudoRandomValue(j,k);
126 }
127
128 // Test hint argument. This can't be compiled when hint is void*, It should be const void*
129 typename A::pointer a_ptr;
130 const void * const_hint = NULL;
131 a_ptr = a.allocate (1, const_hint);
132 a.deallocate(a_ptr, 1);
133
134 // Test "a.deallocate(p,n)
135 for( size_t k=0; k<100; ++k ) {
136 char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
137 for( size_t j=0; j<k*sizeof_T; ++j )
138 ASSERT( s[j] == PseudoRandomValue(j,k), NULL );
139 a.deallocate(array[k],k);
140 }
141
142 // Test "a.max_size()"
143 AssertSameType( a.max_size(), typename A::size_type(0) );
144 // Following assertion catches case where max_size() is so large that computation of
145 // number of bytes for such an allocation would overflow size_type.
146 ASSERT( a.max_size()*typename A::size_type(sizeof(T))>=a.max_size(), "max_size larger than reasonable" );
147
148 // Test "a.construct(p,t)"
149 int n = NumberOfFoo;
150 typename A::pointer p = a.allocate(1);
151 a.construct( p, cx );
152 ASSERT( NumberOfFoo==n+1, "constructor for Foo not called?" );
153
154 // Test "a.destroy(p)"
155 a.destroy( p );
156 ASSERT( NumberOfFoo==n, "destructor for Foo not called?" );
157 a.deallocate(p,1);
158
159#if TBB_USE_EXCEPTIONS
160 volatile size_t too_big = (~size_t(0) - 1024*1024)/sizeof(T);
161 bool exception_caught = false;
162 typename A::pointer p1 = NULL;
163 try {
164#if __APPLE__
165 // On macOS*, failure to map memory results in messages to stderr;
166 // suppress them.
167 DisableStderr disableStderr;
168#endif
169 p1 = a.allocate(too_big);
170 } catch ( std::bad_alloc& ) {
171 exception_caught = true;
172 }
173 ASSERT( exception_caught, "allocate expected to throw bad_alloc" );
174 a.deallocate(p1, too_big);
175#endif // TBB_USE_EXCEPTIONS
176
177 #if __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
178 {
179 typedef typename A:: template rebind<std::pair<typename A::value_type, typename A::value_type> >::other pair_allocator_type;
180 pair_allocator_type pair_allocator(a);
181 int NumberOfFooBeforeConstruct= NumberOfFoo;
182 typename pair_allocator_type::pointer pair_pointer = pair_allocator.allocate(1);
183 pair_allocator.construct( pair_pointer, cx, cx);
184 ASSERT( NumberOfFoo==NumberOfFooBeforeConstruct+2, "constructor for Foo not called appropriate number of times?" );
185
186 pair_allocator.destroy( pair_pointer );
187 ASSERT( NumberOfFoo==NumberOfFooBeforeConstruct, "destructor for Foo not called appropriate number of times?" );
188 pair_allocator.deallocate(pair_pointer,1);
189 }
190 #endif
191
192}
193
194#include "tbb/blocked_range.h"
195
196#if _MSC_VER && !defined(__INTEL_COMPILER)
197 // Workaround for erroneous "conditional expression is constant" warning in method check_allocate.
198 #pragma warning (disable: 4127)
199#endif
200
201// A is an allocator for some type
202template<typename A>
203struct Body: NoAssign {
204 static const size_t max_k = 100000;
205 A &a;
206 Body(A &a_) : a(a_) {}
207 void check_allocate( typename A::pointer array[], size_t i, size_t t ) const
208 {
209 ASSERT(array[i] == 0, NULL);
210 size_t size = i * (i&3);
211 array[i] = i&1 ? a.allocate(size, array[i>>3]) : a.allocate(size);
212 ASSERT(array[i] != 0, "allocator returned null");
213 char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
214 for( size_t j=0; j<size*sizeof(typename A::value_type); ++j ) {
215 if(is_zero_filling<typename A::template rebind<void>::other>::value)
216 ASSERT( !s[j], NULL);
217 s[j] = PseudoRandomValue(i, t);
218 }
219 }
220
221 void check_deallocate( typename A::pointer array[], size_t i, size_t t ) const
222 {
223 ASSERT(array[i] != 0, NULL);
224 size_t size = i * (i&3);
225 char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
226 for( size_t j=0; j<size*sizeof(typename A::value_type); ++j )
227 ASSERT( s[j] == PseudoRandomValue(i, t), "Thread safety test failed" );
228 a.deallocate(array[i], size);
229 array[i] = 0;
230 }
231
232 void operator()( size_t thread_id ) const {
233 typename A::pointer array[256];
234
235 for( size_t k=0; k<256; ++k )
236 array[k] = 0;
237 for( size_t k=0; k<max_k; ++k ) {
238 size_t i = static_cast<unsigned char>(PseudoRandomValue(k,thread_id));
239 if(!array[i]) check_allocate(array, i, thread_id);
240 else check_deallocate(array, i, thread_id);
241 }
242 for( size_t k=0; k<256; ++k )
243 if(array[k])
244 check_deallocate(array, k, thread_id);
245 }
246};
247
248// A is an allocator for some type, and U is another type
249template<typename U, typename A>
250void Test(A &a) {
251 typename A::template rebind<U>::other b(a);
252 TestBasic<U>(b);
253 TestBasic<typename A::value_type>(a);
254
255 // thread safety
256 NativeParallelFor( 4, Body<A>(a) );
257 ASSERT( NumberOfFoo==0, "Allocate/deallocate count mismatched" );
258
259 ASSERT( a==b, NULL );
260 ASSERT( !(a!=b), NULL );
261}
262
263template<typename Allocator>
264int TestMain(const Allocator &a = Allocator()) {
265 NumberOfFoo = 0;
266 typename Allocator::template rebind<Foo<char,1> >::other a1(a);
267 typename Allocator::template rebind<Foo<double,1> >::other a2(a);
268 Test<Foo<int,17> >( a1 );
269 Test<Foo<float,23> >( a2 );
270 return 0;
271}
272