151c0b2f7Stbbdev /* 2b15aabb3Stbbdev Copyright (c) 2005-2021 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> 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> 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]; 5451c0b2f7Stbbdev Foo() { 5551c0b2f7Stbbdev zero_fill<T>(foo_array, N); 5651c0b2f7Stbbdev ++NumberOfFoo; 5751c0b2f7Stbbdev } 5851c0b2f7Stbbdev 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 6951c0b2f7Stbbdev ~Foo() { 7051c0b2f7Stbbdev --NumberOfFoo; 7151c0b2f7Stbbdev } 7251c0b2f7Stbbdev }; 7351c0b2f7Stbbdev 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; 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: 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 } 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> 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> 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> 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; 22751c0b2f7Stbbdev Body(A &a_) : a(a_) {} 22851c0b2f7Stbbdev 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 24251c0b2f7Stbbdev 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); 25051c0b2f7Stbbdev array[i] = 0; 25151c0b2f7Stbbdev } 25251c0b2f7Stbbdev 25351c0b2f7Stbbdev void operator()(std::size_t thread_id) const { 25451c0b2f7Stbbdev pointer_type array[256]; 25551c0b2f7Stbbdev 25651c0b2f7Stbbdev for(std::size_t k = 0; k < 256; ++k) 25751c0b2f7Stbbdev array[k] = 0; 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> 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; 296*112076d0SIlya 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: 304*112076d0SIlya 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