Committing TBB 2019 Update 9 source code
[platform/upstream/tbb.git] / src / test / test_allocator.h
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         //Internal call of assignment
48     }
49     Foo& operator=( const Foo& x ) {
50         for (size_t i = 0; i < N; i++)
51             foo_array[i] = x.foo_array[i];
52         ++NumberOfFoo;
53         return *this;
54     }
55
56     ~Foo() {
57         --NumberOfFoo;
58     }
59 };
60
61 inline char PseudoRandomValue( size_t j, size_t k ) {
62     return char(j*3 ^ j>>4 ^ k);
63 }
64
65 #if __APPLE__
66 #include <fcntl.h>
67 #include <unistd.h>
68
69 // A RAII class to disable stderr in a certain scope. It's not thread-safe.
70 class DisableStderr {
71     int stderrCopy;
72     static void dupToStderrAndClose(int fd) {
73         int ret = dup2(fd, STDERR_FILENO); // close current stderr
74         ASSERT(ret != -1, NULL);
75         ret = close(fd);
76         ASSERT(ret != -1, NULL);
77     }
78 public:
79     DisableStderr() {
80         int devNull = open("/dev/null", O_WRONLY);
81         ASSERT(devNull != -1, NULL);
82         stderrCopy = dup(STDERR_FILENO);
83         ASSERT(stderrCopy != -1, NULL);
84         dupToStderrAndClose(devNull);
85     }
86     ~DisableStderr() {
87         dupToStderrAndClose(stderrCopy);
88     }
89 };
90 #endif
91
92 //! T is type and A is allocator for that type
93 template<typename T, typename A>
94 void TestBasic( A& a ) {
95     T x;
96     const T cx = T();
97
98     // See Table 32 in ISO ++ Standard
99     typename A::pointer px = &x;
100     typename A::const_pointer pcx = &cx;
101
102     typename A::reference rx = x;
103     ASSERT( &rx==&x, NULL );
104
105     typename A::const_reference rcx = cx;
106     ASSERT( &rcx==&cx, NULL );
107
108     typename A::value_type v = x;
109
110     typename A::size_type size;
111     size = 0;
112     --size;
113     ASSERT( size>0, "not an unsigned integral type?" );
114
115     typename A::difference_type difference;
116     difference = 0;
117     --difference;
118     ASSERT( difference<0, "not an signed integral type?" );
119
120     // "rebind" tested by our caller
121
122     ASSERT( a.address(rx)==px, NULL );
123
124     ASSERT( a.address(rcx)==pcx, NULL );
125
126     typename A::pointer array[100];
127     size_t sizeof_T = sizeof(T);
128     for( size_t k=0; k<100; ++k ) {
129         array[k] = k&1 ? a.allocate(k,array[0]) : a.allocate(k);
130         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
131         for( size_t j=0; j<k*sizeof_T; ++j )
132             s[j] = PseudoRandomValue(j,k);
133     }
134
135     // Test hint argument. This can't be compiled when hint is void*, It should be const void*
136     typename A::pointer a_ptr;
137     const void * const_hint = NULL;
138     a_ptr = a.allocate (1, const_hint);
139     a.deallocate(a_ptr, 1);
140
141     // Test "a.deallocate(p,n)
142     for( size_t k=0; k<100; ++k ) {
143         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
144         for( size_t j=0; j<k*sizeof_T; ++j )
145             ASSERT( s[j] == PseudoRandomValue(j,k), NULL );
146         a.deallocate(array[k],k);
147     }
148
149     // Test "a.max_size()"
150     AssertSameType( a.max_size(), typename A::size_type(0) );
151     // Following assertion catches case where max_size() is so large that computation of
152     // number of bytes for such an allocation would overflow size_type.
153     ASSERT( a.max_size()*typename A::size_type(sizeof(T))>=a.max_size(), "max_size larger than reasonable" );
154
155     // Test "a.construct(p,t)"
156     int n = NumberOfFoo;
157     typename A::pointer p = a.allocate(1);
158     a.construct( p, cx );
159     ASSERT( NumberOfFoo==n+1, "constructor for Foo not called?" );
160
161     // Test "a.destroy(p)"
162     a.destroy( p );
163     ASSERT( NumberOfFoo==n, "destructor for Foo not called?" );
164     a.deallocate(p,1);
165
166 #if TBB_USE_EXCEPTIONS
167     volatile size_t too_big = (~size_t(0) - 1024*1024)/sizeof(T);
168     bool exception_caught = false;
169     typename A::pointer p1 = NULL;
170     try {
171 #if __APPLE__
172         // On macOS*, failure to map memory results in messages to stderr;
173         // suppress them.
174         DisableStderr disableStderr;
175 #endif
176         p1 = a.allocate(too_big);
177     } catch ( std::bad_alloc& ) {
178         exception_caught = true;
179     }
180     ASSERT( exception_caught, "allocate expected to throw bad_alloc" );
181     a.deallocate(p1, too_big);
182 #endif // TBB_USE_EXCEPTIONS
183
184     #if __TBB_ALLOCATOR_CONSTRUCT_VARIADIC
185     {
186         typedef typename A:: template rebind<std::pair<typename A::value_type, typename A::value_type> >::other pair_allocator_type;
187         pair_allocator_type pair_allocator(a);
188         int NumberOfFooBeforeConstruct= NumberOfFoo;
189         typename pair_allocator_type::pointer pair_pointer = pair_allocator.allocate(1);
190         pair_allocator.construct( pair_pointer, cx, cx);
191         ASSERT( NumberOfFoo==NumberOfFooBeforeConstruct+2, "constructor for Foo not called appropriate number of times?" );
192
193         pair_allocator.destroy( pair_pointer );
194         ASSERT( NumberOfFoo==NumberOfFooBeforeConstruct, "destructor for Foo not called appropriate number of times?" );
195         pair_allocator.deallocate(pair_pointer,1);
196     }
197     #endif
198
199 }
200
201 #include "tbb/blocked_range.h"
202
203 #if _MSC_VER && !defined(__INTEL_COMPILER)
204     // Workaround for erroneous "conditional expression is constant" warning in method check_allocate.
205     #pragma warning (disable: 4127)
206 #endif
207
208 // A is an allocator for some type
209 template<typename A>
210 struct Body: NoAssign {
211     static const size_t max_k = 100000;
212     A &a;
213     Body(A &a_) : a(a_) {}
214     void check_allocate( typename A::pointer array[], size_t i, size_t t ) const
215     {
216         ASSERT(array[i] == 0, NULL);
217         size_t size = i * (i&3);
218         array[i] = i&1 ? a.allocate(size, array[i>>3]) : a.allocate(size);
219         ASSERT(array[i] != 0, "allocator returned null");
220         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
221         for( size_t j=0; j<size*sizeof(typename A::value_type); ++j ) {
222             if(is_zero_filling<typename A::template rebind<void>::other>::value)
223                 ASSERT( !s[j], NULL);
224             s[j] = PseudoRandomValue(i, t);
225         }
226     }
227
228     void check_deallocate( typename A::pointer array[], size_t i, size_t t ) const
229     {
230         ASSERT(array[i] != 0, NULL);
231         size_t size = i * (i&3);
232         char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
233         for( size_t j=0; j<size*sizeof(typename A::value_type); ++j )
234             ASSERT( s[j] == PseudoRandomValue(i, t), "Thread safety test failed" );
235         a.deallocate(array[i], size);
236         array[i] = 0;
237     }
238
239     void operator()( size_t thread_id ) const {
240         typename A::pointer array[256];
241
242         for( size_t k=0; k<256; ++k )
243             array[k] = 0;
244         for( size_t k=0; k<max_k; ++k ) {
245             size_t i = static_cast<unsigned char>(PseudoRandomValue(k,thread_id));
246             if(!array[i]) check_allocate(array, i, thread_id);
247             else check_deallocate(array, i, thread_id);
248         }
249         for( size_t k=0; k<256; ++k )
250             if(array[k])
251                 check_deallocate(array, k, thread_id);
252     }
253 };
254
255 // A is an allocator for some type, and U is another type
256 template<typename U, typename A>
257 void Test(A &a) {
258     typename A::template rebind<U>::other b(a);
259     TestBasic<U>(b);
260     TestBasic<typename A::value_type>(a);
261
262     // thread safety
263     NativeParallelFor( 4, Body<A>(a) );
264     ASSERT( NumberOfFoo==0, "Allocate/deallocate count mismatched" );
265
266     ASSERT( a==b, NULL );
267     ASSERT( !(a!=b), NULL );
268 }
269
270 template<typename Allocator>
271 int TestMain(const Allocator &a = Allocator()) {
272     NumberOfFoo = 0;
273     typename Allocator::template rebind<Foo<char,1> >::other a1(a);
274     typename Allocator::template rebind<Foo<double,1> >::other a2(a);
275     Test<Foo<int,17> >( a1 );
276     Test<Foo<float,23> >( a2 );
277     return 0;
278 }