1 /* 2 Copyright (c) 2005-2021 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 18 //! \file test_scalable_allocator.cpp 19 //! \brief Test for [memory_allocation.scalable_allocator] functionality 20 21 // Test whether scalable_allocator complies with the requirements in 20.1.5 of ISO C++ Standard (1998). 22 23 #define __TBB_NO_IMPLICIT_LINKAGE 1 24 25 #if _WIN32 || _WIN64 26 #define _CRT_SECURE_NO_WARNINGS 27 #endif 28 29 #include "common/test.h" 30 #include "common/utils.h" 31 #include "common/utils_assert.h" 32 #include "common/custom_allocators.h" 33 34 #define __TBB_EXTRA_DEBUG 1 // enables additional checks 35 #define TBB_PREVIEW_MEMORY_POOL 1 36 37 #include "tbb/memory_pool.h" 38 #include "tbb/scalable_allocator.h" 39 40 // The actual body of the tests 41 #include "common/allocator_test_common.h" 42 #include "common/allocator_stl_test_common.h" 43 44 #define HARNESS_TBBMALLOC_THREAD_SHUTDOWN 1 45 // #include "harness_allocator.h" 46 47 #if _MSC_VER 48 #include <windows.h> 49 #endif /* _MSC_VER */ 50 51 typedef StaticCountingAllocator<tbb::memory_pool_allocator<char>> cnt_alloc_t; 52 typedef LocalCountingAllocator<std::allocator<char> > cnt_provider_t; 53 54 class MinimalAllocator : cnt_provider_t { 55 public: 56 typedef char value_type; 57 MinimalAllocator() { 58 // REMARK("%p::ctor\n", this); 59 } 60 MinimalAllocator(const MinimalAllocator&s) : cnt_provider_t(s) { 61 // REMARK("%p::ctor(%p)\n", this, &s); 62 } 63 ~MinimalAllocator() { 64 /* REMARK("%p::dtor: alloc=%u/%u free=%u/%u\n", this, unsigned(items_allocated),unsigned(allocations), 65 unsigned(items_freed), unsigned(frees) ); */ 66 REQUIRE((allocations==frees && items_allocated==items_freed)); 67 if( allocations ) { // non-temporal copy 68 // TODO: describe consumption requirements 69 REQUIRE(items_allocated>cnt_alloc_t::items_allocated); 70 } 71 } 72 void *allocate(size_t sz) { 73 void *p = cnt_provider_t::allocate(sz); 74 // REMARK("%p::allocate(%u) = %p\n", this, unsigned(sz), p); 75 return p; 76 } 77 void deallocate(void *p, size_t sz) { 78 REQUIRE(allocations>frees); 79 // REMARK("%p::deallocate(%p, %u)\n", this, p, unsigned(sz)); 80 cnt_provider_t::deallocate(std::allocator_traits<cnt_provider_t>::pointer(p), sz); 81 } 82 }; 83 84 class NullAllocator { 85 public: 86 typedef char value_type; 87 NullAllocator() { } 88 NullAllocator(const NullAllocator&) { } 89 ~NullAllocator() { } 90 void *allocate(size_t) { return NULL; } 91 void deallocate(void *, size_t) { REQUIRE(false); } 92 }; 93 94 void TestZeroSpaceMemoryPool() 95 { 96 tbb::memory_pool<NullAllocator> pool; 97 bool allocated = pool.malloc(16) || pool.malloc(9*1024); 98 REQUIRE_MESSAGE(!allocated, "Allocator with no memory must not allocate anything."); 99 } 100 101 #if !TBB_USE_EXCEPTIONS 102 struct FixedPool { 103 void *buf; 104 size_t size; 105 bool used; 106 FixedPool(void *a_buf, size_t a_size) : buf(a_buf), size(a_size), used(false) {} 107 }; 108 109 static void *fixedBufGetMem(intptr_t pool_id, size_t &bytes) 110 { 111 if (((FixedPool*)pool_id)->used) 112 return NULL; 113 114 ((FixedPool*)pool_id)->used = true; 115 bytes = ((FixedPool*)pool_id)->size; 116 return bytes? ((FixedPool*)pool_id)->buf : NULL; 117 } 118 #endif 119 120 /* test that pools in small space are either usable or not created 121 (i.e., exception raised) */ 122 void TestSmallFixedSizePool() 123 { 124 char *buf; 125 bool allocated = false; 126 127 for (size_t sz = 0; sz < 64*1024; sz = sz? 3*sz : 3) { 128 buf = (char*)malloc(sz); 129 #if TBB_USE_EXCEPTIONS 130 try { 131 tbb::fixed_pool pool(buf, sz); 132 /* Check that pool is usable, i.e. such an allocation exists, 133 that can be fulfilled from the pool. 16B allocation fits in 16KB slabs, 134 so it requires at least 16KB. Requirement of 9KB allocation is more modest. 135 */ 136 allocated = pool.malloc( 16 ) || pool.malloc( 9*1024 ); 137 } catch (std::invalid_argument&) { 138 REQUIRE_MESSAGE(!sz, "expect std::invalid_argument for zero-sized pool only"); 139 } catch (...) { 140 REQUIRE_MESSAGE(false, "wrong exception type;"); 141 } 142 #else 143 /* Do not test high-level pool interface because pool ctor emit exception 144 on creation failure. Instead test same functionality via low-level interface. 145 TODO: add support for configuration with disabled exceptions to pools. 146 */ 147 rml::MemPoolPolicy pol(fixedBufGetMem, NULL, 0, /*fixedSizePool=*/true, 148 /*keepMemTillDestroy=*/false); 149 rml::MemoryPool *pool; 150 FixedPool fixedPool(buf, sz); 151 152 rml::MemPoolError ret = pool_create_v1((intptr_t)&fixedPool, &pol, &pool); 153 154 if (ret == rml::POOL_OK) { 155 allocated = pool_malloc(pool, 16) || pool_malloc(pool, 9*1024); 156 pool_destroy(pool); 157 } else 158 REQUIRE_MESSAGE(ret == rml::NO_MEMORY, "Expected that pool either valid or have no memory to be created"); 159 #endif 160 free(buf); 161 } 162 REQUIRE_MESSAGE(allocated, "Maximal buf size should be enough to create working fixed_pool"); 163 #if TBB_USE_EXCEPTIONS 164 try { 165 tbb::fixed_pool pool(NULL, 10*1024*1024); 166 REQUIRE_MESSAGE(false, "Useless allocator with no memory must not be created"); 167 } catch (std::invalid_argument&) { 168 } catch (...) { 169 REQUIRE_MESSAGE(false, "wrong exception type; expected invalid_argument"); 170 } 171 #endif 172 } 173 174 //! Testing ISO C++ allocator requirements 175 //! \brief \ref interface \ref requirement 176 TEST_CASE("Allocator concept") { 177 #if _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE && !__TBB_WIN8UI_SUPPORT 178 #ifdef _DEBUG 179 REQUIRE_MESSAGE((!GetModuleHandle("tbbmalloc.dll") && GetModuleHandle("tbbmalloc_debug.dll")), 180 "test linked with wrong (non-debug) tbbmalloc library"); 181 #else 182 REQUIRE_MESSAGE((!GetModuleHandle("tbbmalloc_debug.dll") && GetModuleHandle("tbbmalloc.dll")), 183 "test linked with wrong (debug) tbbmalloc library"); 184 #endif // _DEBUG 185 #endif // _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE 186 // allocate/deallocate 187 TestAllocator<tbb::scalable_allocator<void>>(Concept); 188 { 189 tbb::memory_pool<tbb::scalable_allocator<int>> pool; 190 TestAllocator(Concept, tbb::memory_pool_allocator<void>(pool)); 191 }{ 192 // tbb::memory_pool<MinimalAllocator> pool; 193 // cnt_alloc_t alloc( tbb::memory_pool_allocator<char>(pool) ); // double parentheses to avoid function declaration 194 // TestAllocator(Concept, alloc); 195 }{ 196 static char buf[1024*1024*4]; 197 tbb::fixed_pool pool(buf, sizeof(buf)); 198 const char *text = "this is a test";// 15 bytes 199 char *p1 = (char*)pool.malloc( 16 ); 200 REQUIRE(p1); 201 strcpy(p1, text); 202 char *p2 = (char*)pool.realloc( p1, 15 ); 203 REQUIRE_MESSAGE( (p2 && !strcmp(p2, text)), "realloc broke memory" ); 204 205 TestAllocator(Concept, tbb::memory_pool_allocator<void>(pool) ); 206 207 // try allocate almost entire buf keeping some reasonable space for internals 208 char *p3 = (char*)pool.realloc( p2, sizeof(buf)-128*1024 ); 209 REQUIRE_MESSAGE( p3, "defragmentation failed" ); 210 REQUIRE_MESSAGE( !strcmp(p3, text), "realloc broke memory" ); 211 for( size_t sz = 10; sz < sizeof(buf); sz *= 2) { 212 REQUIRE( pool.malloc( sz ) ); 213 pool.recycle(); 214 } 215 216 TestAllocator(Concept, tbb::memory_pool_allocator<void>(pool) ); 217 }{ 218 // Two nested level allocators case with fixed pool allocator as an underlying layer 219 // serving allocRawMem requests for the top level scalable allocator 220 typedef tbb::memory_pool<tbb::memory_pool_allocator<char, tbb::fixed_pool> > NestedPool; 221 222 static char buffer[8*1024*1024]; 223 tbb::fixed_pool fixedPool(buffer, sizeof(buffer)); 224 // Underlying fixed pool allocator 225 tbb::memory_pool_allocator<char, tbb::fixed_pool> fixedPoolAllocator(fixedPool); 226 // Memory pool that handles fixed pool allocator 227 NestedPool nestedPool(fixedPoolAllocator); 228 // Top level memory pool allocator 229 tbb::memory_pool_allocator<char, NestedPool> nestedAllocator(nestedPool); 230 231 TestAllocator(Concept, nestedAllocator); 232 } 233 234 // operator== 235 TestAllocator<tbb::scalable_allocator<void>>(Comparison); 236 } 237 238 #if TBB_USE_EXCEPTIONS 239 //! Testing exception guarantees 240 //! \brief \ref requirement 241 TEST_CASE("Exceptions") { 242 TestAllocator<tbb::scalable_allocator<void>>(Exceptions); 243 } 244 #endif /* TBB_USE_EXCEPTIONS */ 245 246 //! Testing allocators thread safety (should not introduce data races) 247 //! \brief \ref requirements 248 TEST_CASE("Thread safety") { 249 TestAllocator<tbb::scalable_allocator<void>>(ThreadSafety); 250 } 251 252 //! Test that pools in small space are either usable or not created (i.e., exception raised) 253 //! \brief \ref error_guessing 254 TEST_CASE("Small fixed pool") { 255 TestSmallFixedSizePool(); 256 } 257 258 //! Test that allocator with no memory must not allocate anything. 259 //! \brief \ref error_guessing 260 TEST_CASE("Zero space pool") { 261 TestZeroSpaceMemoryPool(); 262 } 263 264 //! Testing allocators compatibility with STL containers 265 //! \brief \ref interface 266 TEST_CASE("Integration with STL containers") { 267 TestAllocatorWithSTL<tbb::scalable_allocator<void> >(); 268 tbb::memory_pool<tbb::scalable_allocator<int> > mpool; 269 TestAllocatorWithSTL(tbb::memory_pool_allocator<void>(mpool) ); 270 static char buf[1024*1024*4]; 271 tbb::fixed_pool fpool(buf, sizeof(buf)); 272 TestAllocatorWithSTL(tbb::memory_pool_allocator<void>(fpool) ); 273 } 274 275 #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT 276 //! Testing memory resources compatibility with STL containers through std::pmr::polymorphic_allocator 277 //! \brief \ref interface 278 TEST_CASE("polymorphic_allocator test") { 279 REQUIRE_MESSAGE(!tbb::scalable_memory_resource()->is_equal(*std::pmr::get_default_resource()), 280 "Scalable resource shouldn't be equal to standard resource." ); 281 REQUIRE_MESSAGE(tbb::scalable_memory_resource()->is_equal(*tbb::scalable_memory_resource()), 282 "Memory that was allocated by one scalable resource should be deallocated by any other instance."); 283 284 typedef std::pmr::polymorphic_allocator<void> pmr_alloc_t; 285 TestAllocatorWithSTL(pmr_alloc_t(tbb::scalable_memory_resource())); 286 } 287 #endif 288 289