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