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
1851c0b2f7Stbbdev //! \file test_scalable_allocator.cpp
1951c0b2f7Stbbdev //! \brief Test for [memory_allocation.scalable_allocator] functionality
2051c0b2f7Stbbdev
2151c0b2f7Stbbdev // Test whether scalable_allocator complies with the requirements in 20.1.5 of ISO C++ Standard (1998).
2251c0b2f7Stbbdev
2351c0b2f7Stbbdev #define __TBB_NO_IMPLICIT_LINKAGE 1
2451c0b2f7Stbbdev
25478de5b1Stbbdev #if _WIN32 || _WIN64
26478de5b1Stbbdev #define _CRT_SECURE_NO_WARNINGS
27478de5b1Stbbdev #endif
28478de5b1Stbbdev
29112076d0SIlya Isaev #define __TBB_EXTRA_DEBUG 1 // enables additional checks
30112076d0SIlya Isaev #define TBB_PREVIEW_MEMORY_POOL 1
31112076d0SIlya Isaev #define HARNESS_TBBMALLOC_THREAD_SHUTDOWN 1
32112076d0SIlya Isaev
3351c0b2f7Stbbdev #include "common/test.h"
3451c0b2f7Stbbdev #include "common/utils.h"
3551c0b2f7Stbbdev #include "common/utils_assert.h"
3651c0b2f7Stbbdev #include "common/custom_allocators.h"
3751c0b2f7Stbbdev
3851c0b2f7Stbbdev
3951c0b2f7Stbbdev #include "tbb/memory_pool.h"
4051c0b2f7Stbbdev #include "tbb/scalable_allocator.h"
4151c0b2f7Stbbdev
4251c0b2f7Stbbdev // The actual body of the tests
4351c0b2f7Stbbdev #include "common/allocator_test_common.h"
4451c0b2f7Stbbdev #include "common/allocator_stl_test_common.h"
4551c0b2f7Stbbdev
4651c0b2f7Stbbdev // #include "harness_allocator.h"
4751c0b2f7Stbbdev
4851c0b2f7Stbbdev #if _MSC_VER
4951c0b2f7Stbbdev #include <windows.h>
5051c0b2f7Stbbdev #endif /* _MSC_VER */
5151c0b2f7Stbbdev
5251c0b2f7Stbbdev typedef StaticCountingAllocator<tbb::memory_pool_allocator<char>> cnt_alloc_t;
5351c0b2f7Stbbdev typedef LocalCountingAllocator<std::allocator<char> > cnt_provider_t;
5451c0b2f7Stbbdev
5551c0b2f7Stbbdev class MinimalAllocator : cnt_provider_t {
5651c0b2f7Stbbdev public:
5751c0b2f7Stbbdev typedef char value_type;
MinimalAllocator()5851c0b2f7Stbbdev MinimalAllocator() {
5951c0b2f7Stbbdev // REMARK("%p::ctor\n", this);
6051c0b2f7Stbbdev }
MinimalAllocator(const MinimalAllocator & s)6151c0b2f7Stbbdev MinimalAllocator(const MinimalAllocator&s) : cnt_provider_t(s) {
6251c0b2f7Stbbdev // REMARK("%p::ctor(%p)\n", this, &s);
6351c0b2f7Stbbdev }
~MinimalAllocator()6451c0b2f7Stbbdev ~MinimalAllocator() {
6551c0b2f7Stbbdev /* REMARK("%p::dtor: alloc=%u/%u free=%u/%u\n", this, unsigned(items_allocated),unsigned(allocations),
6651c0b2f7Stbbdev unsigned(items_freed), unsigned(frees) ); */
6751c0b2f7Stbbdev REQUIRE((allocations==frees && items_allocated==items_freed));
6851c0b2f7Stbbdev if( allocations ) { // non-temporal copy
6951c0b2f7Stbbdev // TODO: describe consumption requirements
7051c0b2f7Stbbdev REQUIRE(items_allocated>cnt_alloc_t::items_allocated);
7151c0b2f7Stbbdev }
7251c0b2f7Stbbdev }
allocate(size_t sz)7351c0b2f7Stbbdev void *allocate(size_t sz) {
7451c0b2f7Stbbdev void *p = cnt_provider_t::allocate(sz);
7551c0b2f7Stbbdev // REMARK("%p::allocate(%u) = %p\n", this, unsigned(sz), p);
7651c0b2f7Stbbdev return p;
7751c0b2f7Stbbdev }
deallocate(void * p,size_t sz)7851c0b2f7Stbbdev void deallocate(void *p, size_t sz) {
7951c0b2f7Stbbdev REQUIRE(allocations>frees);
8051c0b2f7Stbbdev // REMARK("%p::deallocate(%p, %u)\n", this, p, unsigned(sz));
81b15aabb3Stbbdev cnt_provider_t::deallocate(std::allocator_traits<cnt_provider_t>::pointer(p), sz);
8251c0b2f7Stbbdev }
8351c0b2f7Stbbdev };
8451c0b2f7Stbbdev
8551c0b2f7Stbbdev class NullAllocator {
8651c0b2f7Stbbdev public:
8751c0b2f7Stbbdev typedef char value_type;
NullAllocator()8851c0b2f7Stbbdev NullAllocator() { }
NullAllocator(const NullAllocator &)8951c0b2f7Stbbdev NullAllocator(const NullAllocator&) { }
~NullAllocator()9051c0b2f7Stbbdev ~NullAllocator() { }
allocate(size_t)9157f524caSIlya Isaev void *allocate(size_t) { return nullptr; }
deallocate(void *,size_t)9251c0b2f7Stbbdev void deallocate(void *, size_t) { REQUIRE(false); }
9351c0b2f7Stbbdev };
9451c0b2f7Stbbdev
TestZeroSpaceMemoryPool()9551c0b2f7Stbbdev void TestZeroSpaceMemoryPool()
9651c0b2f7Stbbdev {
9751c0b2f7Stbbdev tbb::memory_pool<NullAllocator> pool;
9851c0b2f7Stbbdev bool allocated = pool.malloc(16) || pool.malloc(9*1024);
9951c0b2f7Stbbdev REQUIRE_MESSAGE(!allocated, "Allocator with no memory must not allocate anything.");
10051c0b2f7Stbbdev }
10151c0b2f7Stbbdev
10251c0b2f7Stbbdev #if !TBB_USE_EXCEPTIONS
10351c0b2f7Stbbdev struct FixedPool {
10451c0b2f7Stbbdev void *buf;
10551c0b2f7Stbbdev size_t size;
10651c0b2f7Stbbdev bool used;
FixedPoolFixedPool10751c0b2f7Stbbdev FixedPool(void *a_buf, size_t a_size) : buf(a_buf), size(a_size), used(false) {}
10851c0b2f7Stbbdev };
10951c0b2f7Stbbdev
fixedBufGetMem(intptr_t pool_id,size_t & bytes)11051c0b2f7Stbbdev static void *fixedBufGetMem(intptr_t pool_id, size_t &bytes)
11151c0b2f7Stbbdev {
11251c0b2f7Stbbdev if (((FixedPool*)pool_id)->used)
11357f524caSIlya Isaev return nullptr;
11451c0b2f7Stbbdev
11551c0b2f7Stbbdev ((FixedPool*)pool_id)->used = true;
11651c0b2f7Stbbdev bytes = ((FixedPool*)pool_id)->size;
11757f524caSIlya Isaev return bytes? ((FixedPool*)pool_id)->buf : nullptr;
11851c0b2f7Stbbdev }
11951c0b2f7Stbbdev #endif
12051c0b2f7Stbbdev
12151c0b2f7Stbbdev /* test that pools in small space are either usable or not created
12251c0b2f7Stbbdev (i.e., exception raised) */
TestSmallFixedSizePool()12351c0b2f7Stbbdev void TestSmallFixedSizePool()
12451c0b2f7Stbbdev {
12551c0b2f7Stbbdev char *buf;
12651c0b2f7Stbbdev bool allocated = false;
12751c0b2f7Stbbdev
12851c0b2f7Stbbdev for (size_t sz = 0; sz < 64*1024; sz = sz? 3*sz : 3) {
12951c0b2f7Stbbdev buf = (char*)malloc(sz);
13051c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
13151c0b2f7Stbbdev try {
13251c0b2f7Stbbdev tbb::fixed_pool pool(buf, sz);
13351c0b2f7Stbbdev /* Check that pool is usable, i.e. such an allocation exists,
13451c0b2f7Stbbdev that can be fulfilled from the pool. 16B allocation fits in 16KB slabs,
13551c0b2f7Stbbdev so it requires at least 16KB. Requirement of 9KB allocation is more modest.
13651c0b2f7Stbbdev */
13751c0b2f7Stbbdev allocated = pool.malloc( 16 ) || pool.malloc( 9*1024 );
13851c0b2f7Stbbdev } catch (std::invalid_argument&) {
13951c0b2f7Stbbdev REQUIRE_MESSAGE(!sz, "expect std::invalid_argument for zero-sized pool only");
14051c0b2f7Stbbdev } catch (...) {
14151c0b2f7Stbbdev REQUIRE_MESSAGE(false, "wrong exception type;");
14251c0b2f7Stbbdev }
14351c0b2f7Stbbdev #else
14451c0b2f7Stbbdev /* Do not test high-level pool interface because pool ctor emit exception
14551c0b2f7Stbbdev on creation failure. Instead test same functionality via low-level interface.
14651c0b2f7Stbbdev TODO: add support for configuration with disabled exceptions to pools.
14751c0b2f7Stbbdev */
14857f524caSIlya Isaev rml::MemPoolPolicy pol(fixedBufGetMem, nullptr, 0, /*fixedSizePool=*/true,
14951c0b2f7Stbbdev /*keepMemTillDestroy=*/false);
15051c0b2f7Stbbdev rml::MemoryPool *pool;
15151c0b2f7Stbbdev FixedPool fixedPool(buf, sz);
15251c0b2f7Stbbdev
15351c0b2f7Stbbdev rml::MemPoolError ret = pool_create_v1((intptr_t)&fixedPool, &pol, &pool);
15451c0b2f7Stbbdev
15551c0b2f7Stbbdev if (ret == rml::POOL_OK) {
15651c0b2f7Stbbdev allocated = pool_malloc(pool, 16) || pool_malloc(pool, 9*1024);
15751c0b2f7Stbbdev pool_destroy(pool);
15851c0b2f7Stbbdev } else
15951c0b2f7Stbbdev REQUIRE_MESSAGE(ret == rml::NO_MEMORY, "Expected that pool either valid or have no memory to be created");
16051c0b2f7Stbbdev #endif
16151c0b2f7Stbbdev free(buf);
16251c0b2f7Stbbdev }
16351c0b2f7Stbbdev REQUIRE_MESSAGE(allocated, "Maximal buf size should be enough to create working fixed_pool");
16451c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
16551c0b2f7Stbbdev try {
16657f524caSIlya Isaev tbb::fixed_pool pool(nullptr, 10*1024*1024);
16751c0b2f7Stbbdev REQUIRE_MESSAGE(false, "Useless allocator with no memory must not be created");
16851c0b2f7Stbbdev } catch (std::invalid_argument&) {
16951c0b2f7Stbbdev } catch (...) {
17051c0b2f7Stbbdev REQUIRE_MESSAGE(false, "wrong exception type; expected invalid_argument");
17151c0b2f7Stbbdev }
17251c0b2f7Stbbdev #endif
17351c0b2f7Stbbdev }
17451c0b2f7Stbbdev
17551c0b2f7Stbbdev //! Testing ISO C++ allocator requirements
17651c0b2f7Stbbdev //! \brief \ref interface \ref requirement
17751c0b2f7Stbbdev TEST_CASE("Allocator concept") {
17851c0b2f7Stbbdev #if _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE && !__TBB_WIN8UI_SUPPORT
17951c0b2f7Stbbdev #ifdef _DEBUG
18051c0b2f7Stbbdev REQUIRE_MESSAGE((!GetModuleHandle("tbbmalloc.dll") && GetModuleHandle("tbbmalloc_debug.dll")),
18151c0b2f7Stbbdev "test linked with wrong (non-debug) tbbmalloc library");
18251c0b2f7Stbbdev #else
18351c0b2f7Stbbdev REQUIRE_MESSAGE((!GetModuleHandle("tbbmalloc_debug.dll") && GetModuleHandle("tbbmalloc.dll")),
18451c0b2f7Stbbdev "test linked with wrong (debug) tbbmalloc library");
18551c0b2f7Stbbdev #endif // _DEBUG
18651c0b2f7Stbbdev #endif // _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE
18751c0b2f7Stbbdev // allocate/deallocate
18851c0b2f7Stbbdev TestAllocator<tbb::scalable_allocator<void>>(Concept);
18951c0b2f7Stbbdev {
19051c0b2f7Stbbdev tbb::memory_pool<tbb::scalable_allocator<int>> pool;
19151c0b2f7Stbbdev TestAllocator(Concept, tbb::memory_pool_allocator<void>(pool));
19251c0b2f7Stbbdev }{
19351c0b2f7Stbbdev // tbb::memory_pool<MinimalAllocator> pool;
19451c0b2f7Stbbdev // cnt_alloc_t alloc( tbb::memory_pool_allocator<char>(pool) ); // double parentheses to avoid function declaration
19551c0b2f7Stbbdev // TestAllocator(Concept, alloc);
19651c0b2f7Stbbdev }{
19751c0b2f7Stbbdev static char buf[1024*1024*4];
19851c0b2f7Stbbdev tbb::fixed_pool pool(buf, sizeof(buf));
19951c0b2f7Stbbdev const char *text = "this is a test";// 15 bytes
20051c0b2f7Stbbdev char *p1 = (char*)pool.malloc( 16 );
20151c0b2f7Stbbdev REQUIRE(p1);
20251c0b2f7Stbbdev strcpy(p1, text);
20351c0b2f7Stbbdev char *p2 = (char*)pool.realloc( p1, 15 );
20451c0b2f7Stbbdev REQUIRE_MESSAGE( (p2 && !strcmp(p2, text)), "realloc broke memory" );
20551c0b2f7Stbbdev
20651c0b2f7Stbbdev TestAllocator(Concept, tbb::memory_pool_allocator<void>(pool) );
20751c0b2f7Stbbdev
20851c0b2f7Stbbdev // try allocate almost entire buf keeping some reasonable space for internals
20951c0b2f7Stbbdev char *p3 = (char*)pool.realloc( p2, sizeof(buf)-128*1024 );
21051c0b2f7Stbbdev REQUIRE_MESSAGE( p3, "defragmentation failed" );
21151c0b2f7Stbbdev REQUIRE_MESSAGE( !strcmp(p3, text), "realloc broke memory" );
21251c0b2f7Stbbdev for( size_t sz = 10; sz < sizeof(buf); sz *= 2) {
21351c0b2f7Stbbdev REQUIRE( pool.malloc( sz ) );
21451c0b2f7Stbbdev pool.recycle();
21551c0b2f7Stbbdev }
21651c0b2f7Stbbdev
21751c0b2f7Stbbdev TestAllocator(Concept, tbb::memory_pool_allocator<void>(pool) );
21851c0b2f7Stbbdev }{
21951c0b2f7Stbbdev // Two nested level allocators case with fixed pool allocator as an underlying layer
22051c0b2f7Stbbdev // serving allocRawMem requests for the top level scalable allocator
22151c0b2f7Stbbdev typedef tbb::memory_pool<tbb::memory_pool_allocator<char, tbb::fixed_pool> > NestedPool;
22251c0b2f7Stbbdev
22351c0b2f7Stbbdev static char buffer[8*1024*1024];
22451c0b2f7Stbbdev tbb::fixed_pool fixedPool(buffer, sizeof(buffer));
22551c0b2f7Stbbdev // Underlying fixed pool allocator
22651c0b2f7Stbbdev tbb::memory_pool_allocator<char, tbb::fixed_pool> fixedPoolAllocator(fixedPool);
22751c0b2f7Stbbdev // Memory pool that handles fixed pool allocator
22851c0b2f7Stbbdev NestedPool nestedPool(fixedPoolAllocator);
22951c0b2f7Stbbdev // Top level memory pool allocator
23051c0b2f7Stbbdev tbb::memory_pool_allocator<char, NestedPool> nestedAllocator(nestedPool);
23151c0b2f7Stbbdev
23251c0b2f7Stbbdev TestAllocator(Concept, nestedAllocator);
23351c0b2f7Stbbdev }
234112076d0SIlya Isaev tbb::memory_pool<tbb::scalable_allocator<int>> mpool;
235112076d0SIlya Isaev
236112076d0SIlya Isaev tbb::memory_pool_allocator<int> mpalloc(mpool);
237112076d0SIlya Isaev
238112076d0SIlya Isaev TestAllocator<tbb::memory_pool_allocator<int>>(Concept, mpalloc);
239112076d0SIlya Isaev TestAllocator<tbb::memory_pool_allocator<void>>(Concept, mpalloc);
24051c0b2f7Stbbdev
24151c0b2f7Stbbdev // operator==
24251c0b2f7Stbbdev TestAllocator<tbb::scalable_allocator<void>>(Comparison);
243112076d0SIlya Isaev TestAllocator<tbb::memory_pool_allocator<void>>(Comparison, tbb::memory_pool_allocator<void>(mpool));
244112076d0SIlya Isaev TestAllocator<tbb::memory_pool_allocator<int>>(Comparison, mpalloc);
245112076d0SIlya Isaev TestAllocator<tbb::memory_pool_allocator<void>>(Comparison, mpalloc);
24651c0b2f7Stbbdev }
24751c0b2f7Stbbdev
24851c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
24951c0b2f7Stbbdev //! Testing exception guarantees
25051c0b2f7Stbbdev //! \brief \ref requirement
25151c0b2f7Stbbdev TEST_CASE("Exceptions") {
25251c0b2f7Stbbdev TestAllocator<tbb::scalable_allocator<void>>(Exceptions);
25351c0b2f7Stbbdev }
25451c0b2f7Stbbdev #endif /* TBB_USE_EXCEPTIONS */
25551c0b2f7Stbbdev
25651c0b2f7Stbbdev //! Testing allocators thread safety (should not introduce data races)
25751c0b2f7Stbbdev //! \brief \ref requirements
25851c0b2f7Stbbdev TEST_CASE("Thread safety") {
25951c0b2f7Stbbdev TestAllocator<tbb::scalable_allocator<void>>(ThreadSafety);
26051c0b2f7Stbbdev }
26151c0b2f7Stbbdev
26251c0b2f7Stbbdev //! Test that pools in small space are either usable or not created (i.e., exception raised)
26351c0b2f7Stbbdev //! \brief \ref error_guessing
26451c0b2f7Stbbdev TEST_CASE("Small fixed pool") {
26551c0b2f7Stbbdev TestSmallFixedSizePool();
26651c0b2f7Stbbdev }
26751c0b2f7Stbbdev
26851c0b2f7Stbbdev //! Test that allocator with no memory must not allocate anything.
26951c0b2f7Stbbdev //! \brief \ref error_guessing
27051c0b2f7Stbbdev TEST_CASE("Zero space pool") {
27151c0b2f7Stbbdev TestZeroSpaceMemoryPool();
27251c0b2f7Stbbdev }
27351c0b2f7Stbbdev
274112076d0SIlya Isaev #if TBB_ALLOCATOR_TRAITS_BROKEN
275112076d0SIlya Isaev //! Testing allocator traits is broken
276112076d0SIlya Isaev //! \brief \ref error_guessing
277112076d0SIlya Isaev TEST_CASE("Broken allocator concept") {
278112076d0SIlya Isaev TestAllocator<tbb::scalable_allocator<void>>(Broken);
279112076d0SIlya Isaev
280112076d0SIlya Isaev tbb::memory_pool<tbb::scalable_allocator<int>> mpool;
281112076d0SIlya Isaev TestAllocator<tbb::memory_pool_allocator<void>>(Broken, tbb::memory_pool_allocator<void>(mpool));
282112076d0SIlya Isaev }
283112076d0SIlya Isaev #endif
284112076d0SIlya Isaev
285112076d0SIlya Isaev
28651c0b2f7Stbbdev //! Testing allocators compatibility with STL containers
28751c0b2f7Stbbdev //! \brief \ref interface
28851c0b2f7Stbbdev TEST_CASE("Integration with STL containers") {
28951c0b2f7Stbbdev TestAllocatorWithSTL<tbb::scalable_allocator<void> >();
29051c0b2f7Stbbdev tbb::memory_pool<tbb::scalable_allocator<int> > mpool;
29151c0b2f7Stbbdev TestAllocatorWithSTL(tbb::memory_pool_allocator<void>(mpool) );
29251c0b2f7Stbbdev static char buf[1024*1024*4];
29351c0b2f7Stbbdev tbb::fixed_pool fpool(buf, sizeof(buf));
29451c0b2f7Stbbdev TestAllocatorWithSTL(tbb::memory_pool_allocator<void>(fpool) );
29551c0b2f7Stbbdev }
29651c0b2f7Stbbdev
29751c0b2f7Stbbdev #if __TBB_CPP17_MEMORY_RESOURCE_PRESENT
29851c0b2f7Stbbdev //! Testing memory resources compatibility with STL containers through std::pmr::polymorphic_allocator
29951c0b2f7Stbbdev //! \brief \ref interface
30051c0b2f7Stbbdev TEST_CASE("polymorphic_allocator test") {
30151c0b2f7Stbbdev REQUIRE_MESSAGE(!tbb::scalable_memory_resource()->is_equal(*std::pmr::get_default_resource()),
30251c0b2f7Stbbdev "Scalable resource shouldn't be equal to standard resource." );
30351c0b2f7Stbbdev REQUIRE_MESSAGE(tbb::scalable_memory_resource()->is_equal(*tbb::scalable_memory_resource()),
30451c0b2f7Stbbdev "Memory that was allocated by one scalable resource should be deallocated by any other instance.");
30551c0b2f7Stbbdev
30651c0b2f7Stbbdev typedef std::pmr::polymorphic_allocator<void> pmr_alloc_t;
30751c0b2f7Stbbdev TestAllocatorWithSTL(pmr_alloc_t(tbb::scalable_memory_resource()));
30851c0b2f7Stbbdev }
30951c0b2f7Stbbdev #endif
31051c0b2f7Stbbdev
311