151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
1751c0b2f7Stbbdev // Basic testing of an allocator
1851c0b2f7Stbbdev // Tests against requirements in 20.1.5 of ISO C++ Standard (1998).
1951c0b2f7Stbbdev // Does not check for thread safety or false sharing issues.
2051c0b2f7Stbbdev //
2151c0b2f7Stbbdev // Tests for compatibility with the host's STL are in
2251c0b2f7Stbbdev // test_Allocator_STL.h. Those tests are in a separate file
2351c0b2f7Stbbdev // because they bring in lots of STL headers, and the tests here
2451c0b2f7Stbbdev // are supposed to work in the abscense of STL.
2551c0b2f7Stbbdev
264523a761Stbbdev #ifndef __TBB_test_common_allocator_test_common_H_
274523a761Stbbdev #define __TBB_test_common_allocator_test_common_H_
284523a761Stbbdev
2951c0b2f7Stbbdev #include "common/test.h"
3051c0b2f7Stbbdev #include "common/utils.h"
3151c0b2f7Stbbdev #include <utility> //for std::pair
3251c0b2f7Stbbdev #include <cstring>
3351c0b2f7Stbbdev
3451c0b2f7Stbbdev //! Compile-time error if x and y have different types
3551c0b2f7Stbbdev template<typename T>
AssertSameType(const T &,const T &)3651c0b2f7Stbbdev void AssertSameType( const T& /*x*/, const T& /*y*/ ) {}
3751c0b2f7Stbbdev
3851c0b2f7Stbbdev //! The function to zero-initialize arrays; useful to avoid warnings
3951c0b2f7Stbbdev template <typename T>
zero_fill(void * array,size_t n)4051c0b2f7Stbbdev void zero_fill(void* array, size_t n) {
4151c0b2f7Stbbdev memset(array, 0, sizeof(T)*n);
4251c0b2f7Stbbdev }
4351c0b2f7Stbbdev
4451c0b2f7Stbbdev template<typename A>
4551c0b2f7Stbbdev struct is_zero_filling {
4651c0b2f7Stbbdev static const bool value = false;
4751c0b2f7Stbbdev };
4851c0b2f7Stbbdev
4951c0b2f7Stbbdev int NumberOfFoo;
5051c0b2f7Stbbdev
5151c0b2f7Stbbdev template<typename T, size_t N>
5251c0b2f7Stbbdev struct Foo {
5351c0b2f7Stbbdev T foo_array[N];
FooFoo5451c0b2f7Stbbdev Foo() {
5551c0b2f7Stbbdev zero_fill<T>(foo_array, N);
5651c0b2f7Stbbdev ++NumberOfFoo;
5751c0b2f7Stbbdev }
FooFoo5851c0b2f7Stbbdev Foo( const Foo& x ) {
5951c0b2f7Stbbdev *this = x;
6051c0b2f7Stbbdev //Internal call of assignment
6151c0b2f7Stbbdev }
6251c0b2f7Stbbdev Foo& operator=( const Foo& x ) {
6351c0b2f7Stbbdev for (size_t i = 0; i < N; i++)
6451c0b2f7Stbbdev foo_array[i] = x.foo_array[i];
6551c0b2f7Stbbdev ++NumberOfFoo;
6651c0b2f7Stbbdev return *this;
6751c0b2f7Stbbdev }
6851c0b2f7Stbbdev
~FooFoo6951c0b2f7Stbbdev ~Foo() {
7051c0b2f7Stbbdev --NumberOfFoo;
7151c0b2f7Stbbdev }
7251c0b2f7Stbbdev };
7351c0b2f7Stbbdev
PseudoRandomValue(size_t j,size_t k)7451c0b2f7Stbbdev inline char PseudoRandomValue( size_t j, size_t k ) {
7551c0b2f7Stbbdev return char(j*3 ^ j>>4 ^ k);
7651c0b2f7Stbbdev }
7751c0b2f7Stbbdev
7851c0b2f7Stbbdev #if __APPLE__
7951c0b2f7Stbbdev #include <fcntl.h>
8051c0b2f7Stbbdev #include <unistd.h>
8151c0b2f7Stbbdev
8251c0b2f7Stbbdev // A RAII class to disable stderr in a certain scope. It's not thread-safe.
8351c0b2f7Stbbdev class DisableStderr {
8451c0b2f7Stbbdev int stderrCopy;
dupToStderrAndClose(int fd)8551c0b2f7Stbbdev static void dupToStderrAndClose(int fd) {
8651c0b2f7Stbbdev int ret = dup2(fd, STDERR_FILENO); // close current stderr
8751c0b2f7Stbbdev REQUIRE(ret != -1);
8851c0b2f7Stbbdev ret = close(fd);
8951c0b2f7Stbbdev REQUIRE(ret != -1);
9051c0b2f7Stbbdev }
9151c0b2f7Stbbdev public:
DisableStderr()9251c0b2f7Stbbdev DisableStderr() {
9351c0b2f7Stbbdev int devNull = open("/dev/null", O_WRONLY);
9451c0b2f7Stbbdev REQUIRE(devNull != -1);
9551c0b2f7Stbbdev stderrCopy = dup(STDERR_FILENO);
9651c0b2f7Stbbdev REQUIRE(stderrCopy != -1);
9751c0b2f7Stbbdev dupToStderrAndClose(devNull);
9851c0b2f7Stbbdev }
~DisableStderr()9951c0b2f7Stbbdev ~DisableStderr() {
10051c0b2f7Stbbdev dupToStderrAndClose(stderrCopy);
10151c0b2f7Stbbdev }
10251c0b2f7Stbbdev };
10351c0b2f7Stbbdev #endif
10451c0b2f7Stbbdev
10551c0b2f7Stbbdev //! T is type and A is allocator for that type
10651c0b2f7Stbbdev template<typename T, typename A>
TestBrokenAllocator(A & a)10751c0b2f7Stbbdev void TestBrokenAllocator(A& a) {
10851c0b2f7Stbbdev T x;
10951c0b2f7Stbbdev const T cx = T();
11051c0b2f7Stbbdev // See Table 32 in ISO ++ Standard
11151c0b2f7Stbbdev typename A::pointer px = &x;
11251c0b2f7Stbbdev typename A::const_pointer pcx = &cx;
11351c0b2f7Stbbdev
11451c0b2f7Stbbdev typename A::reference rx = x;
11551c0b2f7Stbbdev REQUIRE(&rx == &x);
11651c0b2f7Stbbdev
11751c0b2f7Stbbdev typename A::const_reference rcx = cx;
11851c0b2f7Stbbdev REQUIRE(&rcx==&cx);
11951c0b2f7Stbbdev
12051c0b2f7Stbbdev typename A::value_type v = x;
12151c0b2f7Stbbdev
12251c0b2f7Stbbdev typename A::size_type size;
12351c0b2f7Stbbdev size = 0;
12451c0b2f7Stbbdev --size;
12551c0b2f7Stbbdev REQUIRE_MESSAGE(size > 0, "not an unsigned integral type?");
12651c0b2f7Stbbdev
12751c0b2f7Stbbdev typename A::difference_type difference;
12851c0b2f7Stbbdev difference = 0;
12951c0b2f7Stbbdev --difference;
13051c0b2f7Stbbdev REQUIRE_MESSAGE(difference<0, "not an signed integral type?");
13151c0b2f7Stbbdev
13251c0b2f7Stbbdev // "rebind" tested by our caller
13351c0b2f7Stbbdev
13451c0b2f7Stbbdev REQUIRE(a.address(rx) == px);
13551c0b2f7Stbbdev
13651c0b2f7Stbbdev REQUIRE(a.address(rcx) == pcx);
13751c0b2f7Stbbdev
13851c0b2f7Stbbdev // Test "a.max_size()"
13951c0b2f7Stbbdev AssertSameType(a.max_size(), typename A::size_type(0));
14051c0b2f7Stbbdev // Following assertion catches case where max_size() is so large that computation of
14151c0b2f7Stbbdev // number of bytes for such an allocation would overflow size_type.
14251c0b2f7Stbbdev REQUIRE_MESSAGE((a.max_size() * typename A::size_type(sizeof(T)) >= a.max_size()), "max_size larger than reasonable");
14351c0b2f7Stbbdev
14451c0b2f7Stbbdev // Test "a.construct(p,t)"
14551c0b2f7Stbbdev int n = NumberOfFoo;
14651c0b2f7Stbbdev typename A::pointer p = a.allocate(1);
14751c0b2f7Stbbdev a.construct(p, cx);
14851c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == n + 1, "constructor for Foo not called?");
14951c0b2f7Stbbdev
15051c0b2f7Stbbdev // Test "a.destroy(p)"
15151c0b2f7Stbbdev a.destroy(p);
15251c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == n, "destructor for Foo not called?");
15351c0b2f7Stbbdev a.deallocate(p, 1);
15451c0b2f7Stbbdev
15551c0b2f7Stbbdev {
15651c0b2f7Stbbdev typedef typename A::template rebind<std::pair<typename A::value_type, typename A::value_type> >::other pair_allocator_type;
15751c0b2f7Stbbdev pair_allocator_type pair_allocator(a);
15851c0b2f7Stbbdev int NumberOfFooBeforeConstruct = NumberOfFoo;
15951c0b2f7Stbbdev typename pair_allocator_type::pointer pair_pointer = pair_allocator.allocate(1);
16051c0b2f7Stbbdev pair_allocator.construct(pair_pointer, cx, cx);
16151c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == NumberOfFooBeforeConstruct+2, "constructor for Foo not called appropriate number of times?");
16251c0b2f7Stbbdev
16351c0b2f7Stbbdev pair_allocator.destroy(pair_pointer);
16451c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == NumberOfFooBeforeConstruct, "destructor for Foo not called appropriate number of times?");
16551c0b2f7Stbbdev pair_allocator.deallocate(pair_pointer, 1);
16651c0b2f7Stbbdev }
16751c0b2f7Stbbdev }
16851c0b2f7Stbbdev
16951c0b2f7Stbbdev //! T is type and A is allocator for that type
17051c0b2f7Stbbdev template<typename T, typename A>
TestAllocatorConcept(A & a)17151c0b2f7Stbbdev void TestAllocatorConcept(A& a) {
17251c0b2f7Stbbdev // Test "a.allocate(p,n)
17351c0b2f7Stbbdev typename std::allocator_traits<A>::pointer array[100];
17451c0b2f7Stbbdev std::size_t sizeof_T = sizeof(T);
17551c0b2f7Stbbdev for(std::size_t k = 0; k < 100; ++k) {
17651c0b2f7Stbbdev array[k] = a.allocate(k);
17751c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
17851c0b2f7Stbbdev for(std::size_t j=0; j < k * sizeof_T; ++j)
17951c0b2f7Stbbdev s[j] = PseudoRandomValue(j, k);
18051c0b2f7Stbbdev }
18151c0b2f7Stbbdev
18251c0b2f7Stbbdev // Test "a.deallocate(p,n)
18351c0b2f7Stbbdev for(std::size_t k = 0; k < 100; ++k) {
18451c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[k]));
18551c0b2f7Stbbdev for(std::size_t j = 0; j < k * sizeof_T; ++j)
18651c0b2f7Stbbdev REQUIRE(s[j] == PseudoRandomValue(j, k));
18751c0b2f7Stbbdev a.deallocate(array[k], k);
18851c0b2f7Stbbdev }
18951c0b2f7Stbbdev }
19051c0b2f7Stbbdev
19151c0b2f7Stbbdev //! T is type and A is allocator for that type
19251c0b2f7Stbbdev template<typename T, typename A>
TestAllocatorExceptions(A & a)19351c0b2f7Stbbdev void TestAllocatorExceptions(A& a) {
19451c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
19551c0b2f7Stbbdev volatile size_t too_big = (~std::size_t(0) - 1024 * 1024) / sizeof(T);
19651c0b2f7Stbbdev bool exception_caught = false;
19751c0b2f7Stbbdev typename std::allocator_traits<A>::pointer p1 = nullptr;
19851c0b2f7Stbbdev try {
19951c0b2f7Stbbdev #if __APPLE__
20051c0b2f7Stbbdev // On macOS*, failure to map memory results in messages to stderr;
20151c0b2f7Stbbdev // suppress them.
20251c0b2f7Stbbdev DisableStderr disableStderr;
20351c0b2f7Stbbdev #endif
20451c0b2f7Stbbdev p1 = a.allocate(too_big);
20551c0b2f7Stbbdev } catch (std::bad_alloc&) {
20651c0b2f7Stbbdev exception_caught = true;
20751c0b2f7Stbbdev }
20851c0b2f7Stbbdev REQUIRE_MESSAGE(exception_caught, "allocate expected to throw bad_alloc");
20951c0b2f7Stbbdev a.deallocate(p1, too_big);
21051c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS
21151c0b2f7Stbbdev utils::suppress_unused_warning(a);
21251c0b2f7Stbbdev }
21351c0b2f7Stbbdev
21451c0b2f7Stbbdev #if _MSC_VER && !defined(__INTEL_COMPILER)
21551c0b2f7Stbbdev // Workaround for erroneous "conditional expression is constant" warning in method check_allocate.
21651c0b2f7Stbbdev #pragma warning (disable: 4127)
21751c0b2f7Stbbdev #endif
21851c0b2f7Stbbdev
21951c0b2f7Stbbdev // A is an allocator for some type
22051c0b2f7Stbbdev template<typename A>
22151c0b2f7Stbbdev struct Body: utils::NoAssign {
22251c0b2f7Stbbdev using pointer_type = typename std::allocator_traits<A>::pointer;
22351c0b2f7Stbbdev using value_type = typename std::allocator_traits<A>::value_type;
22451c0b2f7Stbbdev // For the int types and above this test runs too long
22551c0b2f7Stbbdev static const std::size_t max_k = sizeof(value_type) < sizeof(int) ? 100000 : 5000;
22651c0b2f7Stbbdev A &a;
BodyBody22751c0b2f7Stbbdev Body(A &a_) : a(a_) {}
check_allocateBody22851c0b2f7Stbbdev void check_allocate(pointer_type array[], std::size_t i, std::size_t t) const
22951c0b2f7Stbbdev {
23051c0b2f7Stbbdev REQUIRE(array[i] == nullptr);
23151c0b2f7Stbbdev std::size_t size = i * (i & 3);
23251c0b2f7Stbbdev array[i] = a.allocate(size);
23351c0b2f7Stbbdev REQUIRE_MESSAGE(array[i] != nullptr, "allocator returned null");
23451c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
23551c0b2f7Stbbdev for(std::size_t j = 0; j < size * sizeof(value_type); ++j) {
23651c0b2f7Stbbdev if(is_zero_filling<typename std::allocator_traits<A>::template rebind_alloc<void>>::value)
23751c0b2f7Stbbdev REQUIRE(!s[j]);
23851c0b2f7Stbbdev s[j] = PseudoRandomValue(i, t);
23951c0b2f7Stbbdev }
24051c0b2f7Stbbdev }
24151c0b2f7Stbbdev
check_deallocateBody24251c0b2f7Stbbdev void check_deallocate(pointer_type array[], std::size_t i, std::size_t t) const
24351c0b2f7Stbbdev {
24451c0b2f7Stbbdev REQUIRE(array[i] != nullptr);
24551c0b2f7Stbbdev size_t size = i * (i & 3);
24651c0b2f7Stbbdev char* s = reinterpret_cast<char*>(reinterpret_cast<void*>(array[i]));
24751c0b2f7Stbbdev for(std::size_t j=0; j < size * sizeof(value_type); ++j)
24851c0b2f7Stbbdev REQUIRE_MESSAGE(s[j] == PseudoRandomValue(i, t), "Thread safety test failed");
24951c0b2f7Stbbdev a.deallocate(array[i], size);
25057f524caSIlya Isaev array[i] = nullptr;
25151c0b2f7Stbbdev }
25251c0b2f7Stbbdev
operatorBody25351c0b2f7Stbbdev void operator()(std::size_t thread_id) const {
25451c0b2f7Stbbdev pointer_type array[256];
25551c0b2f7Stbbdev
25651c0b2f7Stbbdev for(std::size_t k = 0; k < 256; ++k)
25757f524caSIlya Isaev array[k] = nullptr;
25851c0b2f7Stbbdev for(std::size_t k = 0; k < max_k; ++k) {
25951c0b2f7Stbbdev std::size_t i = static_cast<unsigned char>(PseudoRandomValue(k, thread_id));
26051c0b2f7Stbbdev if(!array[i]) check_allocate(array, i, thread_id);
26151c0b2f7Stbbdev else check_deallocate(array, i, thread_id);
26251c0b2f7Stbbdev }
26351c0b2f7Stbbdev for(std::size_t k = 0; k < 256; ++k)
26451c0b2f7Stbbdev if(array[k])
26551c0b2f7Stbbdev check_deallocate(array, k, thread_id);
26651c0b2f7Stbbdev }
26751c0b2f7Stbbdev };
26851c0b2f7Stbbdev
26951c0b2f7Stbbdev template<typename A>
TestThreadSafety(A & a)27051c0b2f7Stbbdev void TestThreadSafety(A &a) {
27151c0b2f7Stbbdev utils::NativeParallelFor(4, Body<A>(a));
27251c0b2f7Stbbdev }
27351c0b2f7Stbbdev
27451c0b2f7Stbbdev enum TestName { Concept, Broken, Exceptions, ThreadSafety, Comparison };
27551c0b2f7Stbbdev
27651c0b2f7Stbbdev template<typename Allocator>
27751c0b2f7Stbbdev void TestAllocator(TestName name, const Allocator &a = Allocator()) {
27851c0b2f7Stbbdev
27951c0b2f7Stbbdev using FooChar = Foo<char, 1>;
28051c0b2f7Stbbdev using FooDouble = Foo<double, 1>;
28151c0b2f7Stbbdev using FooInt = Foo<int, 17>;
28251c0b2f7Stbbdev using FooFloat = Foo<float, 23>;
28351c0b2f7Stbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN
28451c0b2f7Stbbdev using AllocatorFooChar = typename Allocator::template rebind<FooChar>::other;
28551c0b2f7Stbbdev using AllocatorFooDouble = typename Allocator::template rebind<FooDouble>::other;
28651c0b2f7Stbbdev using AllocatorFooInt = typename AllocatorFooChar::template rebind<FooInt>::other;
28751c0b2f7Stbbdev using AllocatorFooFloat = typename AllocatorFooDouble::template rebind<FooFloat>::other;
28851c0b2f7Stbbdev #else
28951c0b2f7Stbbdev using AllocatorFooChar = typename std::allocator_traits<Allocator>::template rebind_alloc<FooChar>;
29051c0b2f7Stbbdev using AllocatorFooDouble = typename std::allocator_traits<Allocator>::template rebind_alloc<FooDouble>;
29151c0b2f7Stbbdev using AllocatorFooInt = typename std::allocator_traits<AllocatorFooChar>::template rebind_alloc<FooInt>;
29251c0b2f7Stbbdev using AllocatorFooFloat = typename std::allocator_traits<AllocatorFooDouble>::template rebind_alloc<FooFloat>;
29351c0b2f7Stbbdev #endif
29451c0b2f7Stbbdev
29551c0b2f7Stbbdev NumberOfFoo = 0;
296112076d0SIlya Isaev Allocator a_cpy(a);
29751c0b2f7Stbbdev AllocatorFooChar a1(a);
29851c0b2f7Stbbdev AllocatorFooDouble a2(a);
29951c0b2f7Stbbdev AllocatorFooInt b1(a1);
30051c0b2f7Stbbdev AllocatorFooFloat b2(a2);
30151c0b2f7Stbbdev
30251c0b2f7Stbbdev switch(name) {
30351c0b2f7Stbbdev case Comparison:
304112076d0SIlya Isaev REQUIRE(a_cpy == a);
30551c0b2f7Stbbdev REQUIRE(a1 == b1);
30651c0b2f7Stbbdev REQUIRE(!(a2 != b2));
30751c0b2f7Stbbdev break;
30851c0b2f7Stbbdev case Concept:
30951c0b2f7Stbbdev TestAllocatorConcept<FooInt>(b1);
31051c0b2f7Stbbdev TestAllocatorConcept<typename AllocatorFooChar::value_type>(a1);
31151c0b2f7Stbbdev TestAllocatorConcept<FooFloat>(b2);
31251c0b2f7Stbbdev TestAllocatorConcept<typename AllocatorFooDouble::value_type>(a2);
31351c0b2f7Stbbdev break;
31451c0b2f7Stbbdev case Broken:
31551c0b2f7Stbbdev #if TBB_ALLOCATOR_TRAITS_BROKEN
31651c0b2f7Stbbdev TestBrokenAllocator<FooInt>(b1);
31751c0b2f7Stbbdev TestBrokenAllocator<typename AllocatorFooChar::value_type>(a1);
31851c0b2f7Stbbdev TestBrokenAllocator<FooFloat>(b2);
31951c0b2f7Stbbdev TestBrokenAllocator<typename AllocatorFooDouble::value_type>(a2);
32051c0b2f7Stbbdev #endif
32151c0b2f7Stbbdev break;
32251c0b2f7Stbbdev case Exceptions:
32351c0b2f7Stbbdev TestAllocatorExceptions<FooInt>(b1);
32451c0b2f7Stbbdev TestAllocatorExceptions<typename AllocatorFooChar::value_type>(a1);
32551c0b2f7Stbbdev TestAllocatorExceptions<FooFloat>(b2);
32651c0b2f7Stbbdev TestAllocatorExceptions<typename AllocatorFooDouble::value_type>(a2);
32751c0b2f7Stbbdev break;
32851c0b2f7Stbbdev case ThreadSafety:
32951c0b2f7Stbbdev TestThreadSafety(a1);
33051c0b2f7Stbbdev TestThreadSafety(a2);
33151c0b2f7Stbbdev break;
33251c0b2f7Stbbdev }
33351c0b2f7Stbbdev REQUIRE_MESSAGE(NumberOfFoo == 0, "Allocate/deallocate count mismatched");
33451c0b2f7Stbbdev }
3354523a761Stbbdev #endif // __TBB_test_common_allocator_test_common_H_
336