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 | |
31 | template<typename A> |
32 | struct is_zero_filling { |
33 | static const bool value = false; |
34 | }; |
35 | |
36 | int NumberOfFoo; |
37 | |
38 | template<typename T, size_t N> |
39 | struct 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 | |
54 | inline 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. |
63 | class 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 | } |
71 | public: |
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 |
86 | template<typename T, typename A> |
87 | void 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 |
202 | template<typename A> |
203 | struct 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 |
249 | template<typename U, typename A> |
250 | void 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 | |
263 | template<typename Allocator> |
264 | int 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 | |