1/*
2 Copyright (c) 2005-2019 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// Test whether scalable_allocator complies with the requirements in 20.1.5 of ISO C++ Standard (1998).
18
19#define __TBB_EXTRA_DEBUG 1 // enables additional checks
20#define TBB_PREVIEW_MEMORY_POOL 1
21
22#include "harness_assert.h"
23#if !__TBB_SOURCE_DIRECTLY_INCLUDED
24#include "harness_tbb_independence.h" // because harness_allocator.h requires atomics
25#endif
26#include "tbb/memory_pool.h"
27#include "tbb/scalable_allocator.h"
28
29#define HARNESS_TBBMALLOC_THREAD_SHUTDOWN 1
30// the actual body of the test is there:
31#include "test_allocator.h"
32#include "harness_allocator.h"
33
34#if _MSC_VER
35#include "tbb/machine/windows_api.h"
36#endif /* _MSC_VER */
37
38typedef static_counting_allocator<tbb::memory_pool_allocator<char> > cnt_alloc_t;
39typedef local_counting_allocator<std::allocator<char> > cnt_provider_t;
40class MinimalAllocator : cnt_provider_t {
41public:
42 typedef char value_type;
43 MinimalAllocator() {
44 REMARK("%p::ctor\n", this);
45 }
46 MinimalAllocator(const MinimalAllocator&s) : cnt_provider_t(s) {
47 REMARK("%p::ctor(%p)\n", this, &s);
48 }
49 ~MinimalAllocator() {
50 REMARK("%p::dtor: alloc=%u/%u free=%u/%u\n", this,
51 unsigned(items_allocated),unsigned(allocations),
52 unsigned(items_freed), unsigned(frees) );
53 ASSERT(allocations==frees && items_allocated==items_freed,0);
54 if( allocations ) { // non-temporal copy
55 // TODO: describe consumption requirements
56 ASSERT(items_allocated>cnt_alloc_t::items_allocated, 0);
57 }
58 }
59 void *allocate(size_t sz) {
60 void *p = cnt_provider_t::allocate(sz);
61 REMARK("%p::allocate(%u) = %p\n", this, unsigned(sz), p);
62 return p;
63 }
64 void deallocate(void *p, size_t sz) {
65 ASSERT(allocations>frees,0);
66 REMARK("%p::deallocate(%p, %u)\n", this, p, unsigned(sz));
67 cnt_provider_t::deallocate(cnt_provider_t::pointer(p), sz);
68 }
69};
70
71class NullAllocator {
72public:
73 typedef char value_type;
74 NullAllocator() { }
75 NullAllocator(const NullAllocator&) { }
76 ~NullAllocator() { }
77 void *allocate(size_t) { return NULL; }
78 void deallocate(void *, size_t) { ASSERT(0, NULL); }
79};
80
81void TestZeroSpaceMemoryPool()
82{
83 tbb::memory_pool<NullAllocator> pool;
84 bool allocated = pool.malloc(16) || pool.malloc(9*1024);
85 ASSERT(!allocated, "Allocator with no memory must not allocate anything.");
86}
87
88#if !TBB_USE_EXCEPTIONS
89struct FixedPool {
90 void *buf;
91 size_t size;
92 bool used;
93 FixedPool(void *a_buf, size_t a_size) : buf(a_buf), size(a_size), used(false) {}
94};
95
96static void *fixedBufGetMem(intptr_t pool_id, size_t &bytes)
97{
98 if (((FixedPool*)pool_id)->used)
99 return NULL;
100
101 ((FixedPool*)pool_id)->used = true;
102 bytes = ((FixedPool*)pool_id)->size;
103 return bytes? ((FixedPool*)pool_id)->buf : NULL;
104}
105#endif
106
107/* test that pools in small space are either usable or not created
108 (i.e., exception raised) */
109void TestSmallFixedSizePool()
110{
111 char *buf;
112 bool allocated = false;
113
114 for (size_t sz = 0; sz < 64*1024; sz = sz? 3*sz : 3) {
115 buf = (char*)malloc(sz);
116#if TBB_USE_EXCEPTIONS
117 try {
118 tbb::fixed_pool pool(buf, sz);
119/* Check that pool is usable, i.e. such an allocation exists,
120 that can be fulfilled from the pool. 16B allocation fits in 16KB slabs,
121 so it requires at least 16KB. Requirement of 9KB allocation is more modest.
122*/
123 allocated = pool.malloc( 16 ) || pool.malloc( 9*1024 );
124 } catch (std::invalid_argument&) {
125 ASSERT(!sz, "expect std::invalid_argument for zero-sized pool only");
126 } catch (...) {
127 ASSERT(0, "wrong exception type;");
128 }
129#else
130/* Do not test high-level pool interface because pool ctor emit exception
131 on creation failure. Instead test same functionality via low-level interface.
132 TODO: add support for configuration with disabled exceptions to pools.
133*/
134 rml::MemPoolPolicy pol(fixedBufGetMem, NULL, 0, /*fixedSizePool=*/true,
135 /*keepMemTillDestroy=*/false);
136 rml::MemoryPool *pool;
137 FixedPool fixedPool(buf, sz);
138
139 rml::MemPoolError ret = pool_create_v1((intptr_t)&fixedPool, &pol, &pool);
140
141 if (ret == rml::POOL_OK) {
142 allocated = pool_malloc(pool, 16) || pool_malloc(pool, 9*1024);
143 pool_destroy(pool);
144 } else
145 ASSERT(ret == rml::NO_MEMORY, "Expected that pool either valid "
146 "or have no memory to be created");
147#endif
148 free(buf);
149 }
150 ASSERT(allocated, "Maximal buf size should be enough to create working fixed_pool");
151#if TBB_USE_EXCEPTIONS
152 try {
153 tbb::fixed_pool pool(NULL, 10*1024*1024);
154 ASSERT(0, "Useless allocator with no memory must not be created");
155 } catch (std::invalid_argument&) {
156 } catch (...) {
157 ASSERT(0, "wrong exception type; expected invalid_argument");
158 }
159#endif
160}
161
162int TestMain () {
163#if _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE && !__TBB_WIN8UI_SUPPORT
164 #ifdef _DEBUG
165 ASSERT(!GetModuleHandle("tbbmalloc.dll") && GetModuleHandle("tbbmalloc_debug.dll"),
166 "test linked with wrong (non-debug) tbbmalloc library");
167 #else
168 ASSERT(!GetModuleHandle("tbbmalloc_debug.dll") && GetModuleHandle("tbbmalloc.dll"),
169 "test linked with wrong (debug) tbbmalloc library");
170 #endif
171#endif /* _MSC_VER && !__TBBMALLOC_NO_IMPLICIT_LINKAGE */
172 int result = TestMain<tbb::scalable_allocator<void> >();
173 {
174 tbb::memory_pool<tbb::scalable_allocator<int> > pool;
175 result += TestMain(tbb::memory_pool_allocator<void>(pool) );
176 }{
177 tbb::memory_pool<MinimalAllocator> pool;
178 cnt_alloc_t alloc(( tbb::memory_pool_allocator<char>(pool) )); // double parentheses to avoid function declaration
179 result += TestMain(alloc);
180 }{
181 static char buf[1024*1024*4];
182 tbb::fixed_pool pool(buf, sizeof(buf));
183 const char *text = "this is a test";// 15 bytes
184 char *p1 = (char*)pool.malloc( 16 );
185 ASSERT(p1, NULL);
186 strcpy(p1, text);
187 char *p2 = (char*)pool.realloc( p1, 15 );
188 ASSERT( p2 && !strcmp(p2, text), "realloc broke memory" );
189
190 result += TestMain(tbb::memory_pool_allocator<void>(pool) );
191
192 // try allocate almost entire buf keeping some reasonable space for internals
193 char *p3 = (char*)pool.realloc( p2, sizeof(buf)-128*1024 );
194 ASSERT( p3, "defragmentation failed" );
195 ASSERT( !strcmp(p3, text), "realloc broke memory" );
196 for( size_t sz = 10; sz < sizeof(buf); sz *= 2) {
197 ASSERT( pool.malloc( sz ), NULL);
198 pool.recycle();
199 }
200
201 result += TestMain(tbb::memory_pool_allocator<void>(pool) );
202 }{
203 // Two nested level allocators case with fixed pool allocator as an underlying layer
204 // serving allocRawMem requests for the top level scalable allocator
205 typedef tbb::memory_pool<tbb::memory_pool_allocator<char, tbb::fixed_pool> > NestedPool;
206
207 static char buffer[8*1024*1024];
208 tbb::fixed_pool fixedPool(buffer, sizeof(buffer));
209 // Underlying fixed pool allocator
210 tbb::memory_pool_allocator<char, tbb::fixed_pool> fixedPoolAllocator(fixedPool);
211 // Memory pool that handles fixed pool allocator
212 NestedPool nestedPool(fixedPoolAllocator);
213 // Top level memory pool allocator
214 tbb::memory_pool_allocator<char, NestedPool> nestedAllocator(nestedPool);
215
216 result += TestMain(nestedAllocator);
217 }
218 TestSmallFixedSizePool();
219 TestZeroSpaceMemoryPool();
220
221 ASSERT( !result, NULL );
222 return Harness::Done;
223}
224