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 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 | |
38 | typedef static_counting_allocator<tbb::memory_pool_allocator<char> > cnt_alloc_t; |
39 | typedef local_counting_allocator<std::allocator<char> > cnt_provider_t; |
40 | class MinimalAllocator : cnt_provider_t { |
41 | public: |
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 | |
71 | class NullAllocator { |
72 | public: |
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 | |
81 | void 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 |
89 | struct 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 | |
96 | static 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) */ |
109 | void 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 | |
162 | int 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 | |