1 /*
2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 #include "precompiled.hpp"
25 #include "runtime/mutex.hpp"
26 #include "runtime/semaphore.hpp"
27 #include "runtime/thread.hpp"
28 #include "runtime/vmThread.hpp"
29 #include "runtime/vmOperations.hpp"
30 #include "utilities/concurrentHashTable.inline.hpp"
31 #include "utilities/concurrentHashTableTasks.inline.hpp"
32 #include "utilitiesHelper.inline.hpp"
33 #include "unittest.hpp"
34
35 // NOTE: On win32 gtest asserts are not mt-safe.
36 // Amusingly as long as they do not assert they are mt-safe.
37 #define SIZE_32 5
38
39 struct Pointer;
40
41 typedef ConcurrentHashTable<uintptr_t, Pointer, mtInternal> SimpleTestTable;
42 typedef ConcurrentHashTable<uintptr_t, Pointer, mtInternal>::MultiGetHandle SimpleTestGetHandle;
43
44 // Simplest working CRPT implementation for the hash-table.
45 struct Pointer : public SimpleTestTable::BaseConfig {
get_hashPointer46 static uintx get_hash(const uintptr_t& value, bool* dead_hash) {
47 return (uintx)value;
48 }
notfoundPointer49 static const uintptr_t& notfound() {
50 static uintptr_t notfound = 0;
51 return notfound;
52 }
allocate_nodePointer53 static void* allocate_node(size_t size, const uintptr_t& value) {
54 return ::malloc(size);
55 }
free_nodePointer56 static void free_node(void* memory, const uintptr_t& value) {
57 ::free(memory);
58 }
59 };
60
61 struct SimpleTestLookup {
62 uintptr_t _val;
SimpleTestLookupSimpleTestLookup63 SimpleTestLookup(uintptr_t val) : _val(val) {}
get_hashSimpleTestLookup64 uintx get_hash() {
65 return Pointer::get_hash(_val, NULL);
66 }
equalsSimpleTestLookup67 bool equals(const uintptr_t* value, bool* is_dead) {
68 return _val == *value;
69 }
70 };
71
cht_insert(Thread * thr)72 static void cht_insert(Thread* thr) {
73 uintptr_t val = 0x2;
74 SimpleTestLookup stl(val);
75 SimpleTestTable* cht = new SimpleTestTable();
76 EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";
77 EXPECT_EQ(cht->get_copy(thr, stl), val) << "Getting an existing value failed.";
78 EXPECT_TRUE(cht->remove(thr, stl)) << "Removing an existing value failed.";
79 EXPECT_FALSE(cht->remove(thr, stl)) << "Removing an already removed item succeeded.";
80 EXPECT_NE(cht->get_copy(thr, stl), val) << "Getting a removed value succeeded.";
81 delete cht;
82 }
83
84 struct ValVerify {
85 uintptr_t _val;
86 bool called_get;
87 bool called_insert;
ValVerifyValVerify88 ValVerify(uintptr_t val) : called_get(false), called_insert(false), _val(val) {}
operator ()ValVerify89 void operator()(bool inserted, uintptr_t* val) {
90 EXPECT_EQ(_val, *val) << "The value inserted is not correct.";
91 if (inserted) {
92 called_insert = true;
93 } else {
94 called_get = true;
95 }
96 }
verifyValVerify97 void verify(bool get, bool insert) {
98 EXPECT_EQ(called_get, get) << "Get unexpected";
99 EXPECT_EQ(called_insert, insert) << "Insert unexpected";
100 }
101 };
102
cht_get_insert_helper(Thread * thr,SimpleTestTable * cht,uintptr_t val)103 static void cht_get_insert_helper(Thread* thr, SimpleTestTable* cht, uintptr_t val) {
104 {
105 SimpleTestLookup stl(val);
106 ValVerify vv(val);
107 EXPECT_EQ(cht->get_insert(thr, stl, val, vv), false) << "Inserting an unique value failed.";
108 vv.verify(false, true);
109 }
110
111 {
112 SimpleTestLookup stl(val);
113 ValVerify vv(val);
114 EXPECT_EQ(cht->get_insert(thr, stl, val, vv), true) << "Getting an old value failed.";
115 vv.verify(true, false);
116 }
117 }
118
cht_get_insert(Thread * thr)119 static void cht_get_insert(Thread* thr) {
120 uintptr_t val = 0x2;
121 SimpleTestLookup stl(val);
122 SimpleTestTable* cht = new SimpleTestTable();
123
124 {
125 SCOPED_TRACE("First");
126 cht_get_insert_helper(thr, cht, val);
127 }
128 EXPECT_EQ(cht->get_copy(thr, stl), val) << "Get an old value failed";
129 EXPECT_TRUE(cht->remove(thr, stl)) << "Removing existing value failed.";
130 EXPECT_NE(cht->get_copy(thr, stl), val) << "Got an already removed item.";
131
132 {
133 SCOPED_TRACE("Second");
134 cht_get_insert_helper(thr, cht, val);
135 }
136
137 delete cht;
138 }
139
getinsert_bulkdelete_eval(uintptr_t * val)140 static bool getinsert_bulkdelete_eval(uintptr_t* val) {
141 EXPECT_TRUE(*val > 0 && *val < 4) << "Val wrong for this test.";
142 return (*val & 0x1); // Delete all values ending with first bit set.
143 }
144
getinsert_bulkdelete_del(uintptr_t * val)145 static void getinsert_bulkdelete_del(uintptr_t* val) {
146 EXPECT_EQ(*val & 0x1, (uintptr_t)1) << "Deleting wrong value.";
147 }
148
cht_getinsert_bulkdelete_insert_verified(Thread * thr,SimpleTestTable * cht,uintptr_t val,bool verify_expect_get,bool verify_expect_inserted)149 static void cht_getinsert_bulkdelete_insert_verified(Thread* thr, SimpleTestTable* cht, uintptr_t val,
150 bool verify_expect_get, bool verify_expect_inserted) {
151 ValVerify vv(val);
152 SimpleTestLookup stl(val);
153 EXPECT_EQ(cht->get_insert(thr, stl, val, vv), verify_expect_get) << "Inserting an unique value failed.";
154 vv.verify(verify_expect_get, verify_expect_inserted);
155 }
156
cht_getinsert_bulkdelete(Thread * thr)157 static void cht_getinsert_bulkdelete(Thread* thr) {
158 uintptr_t val1 = 1;
159 uintptr_t val2 = 2;
160 uintptr_t val3 = 3;
161 SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);
162
163 SimpleTestTable* cht = new SimpleTestTable();
164 cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, false, true);
165 cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true);
166 cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, false, true);
167
168 EXPECT_TRUE(cht->remove(thr, stl2)) << "Remove did not find value.";
169
170 cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, true, false); // val1 should be present
171 cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true); // val2 should be inserted
172 cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, true, false); // val3 should be present
173
174 EXPECT_EQ(cht->get_copy(thr, stl1), val1) << "Get did not find value.";
175 EXPECT_EQ(cht->get_copy(thr, stl2), val2) << "Get did not find value.";
176 EXPECT_EQ(cht->get_copy(thr, stl3), val3) << "Get did not find value.";
177
178 // Removes all odd values.
179 cht->bulk_delete(thr, getinsert_bulkdelete_eval, getinsert_bulkdelete_del);
180
181 EXPECT_EQ(cht->get_copy(thr, stl1), (uintptr_t)0) << "Odd value should not exist.";
182 EXPECT_FALSE(cht->remove(thr, stl1)) << "Odd value should not exist.";
183 EXPECT_EQ(cht->get_copy(thr, stl2), val2) << "Even value should not have been removed.";
184 EXPECT_EQ(cht->get_copy(thr, stl3), (uintptr_t)0) << "Add value should not exists.";
185 EXPECT_FALSE(cht->remove(thr, stl3)) << "Odd value should not exists.";
186
187 delete cht;
188 }
189
cht_getinsert_bulkdelete_task(Thread * thr)190 static void cht_getinsert_bulkdelete_task(Thread* thr) {
191 uintptr_t val1 = 1;
192 uintptr_t val2 = 2;
193 uintptr_t val3 = 3;
194 SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);
195
196 SimpleTestTable* cht = new SimpleTestTable();
197 cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, false, true);
198 cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true);
199 cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, false, true);
200
201 EXPECT_TRUE(cht->remove(thr, stl2)) << "Remove did not find value.";
202
203 cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, true, false); // val1 should be present
204 cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true); // val2 should be inserted
205 cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, true, false); // val3 should be present
206
207 EXPECT_EQ(cht->get_copy(thr, stl1), val1) << "Get did not find value.";
208 EXPECT_EQ(cht->get_copy(thr, stl2), val2) << "Get did not find value.";
209 EXPECT_EQ(cht->get_copy(thr, stl3), val3) << "Get did not find value.";
210
211 // Removes all odd values.
212 SimpleTestTable::BulkDeleteTask bdt(cht);
213 if (bdt.prepare(thr)) {
214 while(bdt.do_task(thr, getinsert_bulkdelete_eval, getinsert_bulkdelete_del)) {
215 bdt.pause(thr);
216 bdt.cont(thr);
217 }
218 bdt.done(thr);
219 }
220
221 EXPECT_EQ(cht->get_copy(thr, stl1), (uintptr_t)0) << "Odd value should not exist.";
222 EXPECT_FALSE(cht->remove(thr, stl1)) << "Odd value should not exist.";
223 EXPECT_EQ(cht->get_copy(thr, stl2), val2) << "Even value should not have been removed.";
224 EXPECT_EQ(cht->get_copy(thr, stl3), (uintptr_t)0) << "Add value should not exists.";
225 EXPECT_FALSE(cht->remove(thr, stl3)) << "Odd value should not exists.";
226
227 delete cht;
228 }
229
cht_scope(Thread * thr)230 static void cht_scope(Thread* thr) {
231 uintptr_t val = 0x2;
232 SimpleTestLookup stl(val);
233 SimpleTestTable* cht = new SimpleTestTable();
234 EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";
235 {
236 SimpleTestGetHandle get_handle(thr, cht);
237 EXPECT_EQ(*get_handle.get(stl), val) << "Getting a pre-existing value failed.";
238 }
239 // We do remove here to make sure the value-handle 'unlocked' the table when leaving the scope.
240 EXPECT_TRUE(cht->remove(thr, stl)) << "Removing a pre-existing value failed.";
241 EXPECT_FALSE(cht->get_copy(thr, stl) == val) << "Got a removed value.";
242 delete cht;
243 }
244
245 struct ChtScan {
246 size_t _count;
ChtScanChtScan247 ChtScan() : _count(0) {}
operator ()ChtScan248 bool operator()(uintptr_t* val) {
249 EXPECT_EQ(*val, (uintptr_t)0x2) << "Got an unknown value.";
250 EXPECT_EQ(_count, 0u) << "Only one value should be in table.";
251 _count++;
252 return true; /* continue scan */
253 }
254 };
255
cht_scan(Thread * thr)256 static void cht_scan(Thread* thr) {
257 uintptr_t val = 0x2;
258 SimpleTestLookup stl(val);
259 ChtScan scan;
260 SimpleTestTable* cht = new SimpleTestTable();
261 EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";
262 EXPECT_EQ(cht->try_scan(thr, scan), true) << "Scanning an non-growing/shrinking table should work.";
263 EXPECT_TRUE(cht->remove(thr, stl)) << "Removing a pre-existing value failed.";
264 EXPECT_FALSE(cht->get_copy(thr, stl) == val) << "Got a removed value.";
265 delete cht;
266 }
267
268 struct ChtCountScan {
269 size_t _count;
ChtCountScanChtCountScan270 ChtCountScan() : _count(0) {}
operator ()ChtCountScan271 bool operator()(uintptr_t* val) {
272 _count++;
273 return true; /* continue scan */
274 }
275 };
276
cht_move_to(Thread * thr)277 static void cht_move_to(Thread* thr) {
278 uintptr_t val1 = 0x2;
279 uintptr_t val2 = 0xe0000002;
280 uintptr_t val3 = 0x3;
281 SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);
282 SimpleTestTable* from_cht = new SimpleTestTable();
283 EXPECT_TRUE(from_cht->insert(thr, stl1, val1)) << "Insert unique value failed.";
284 EXPECT_TRUE(from_cht->insert(thr, stl2, val2)) << "Insert unique value failed.";
285 EXPECT_TRUE(from_cht->insert(thr, stl3, val3)) << "Insert unique value failed.";
286
287 SimpleTestTable* to_cht = new SimpleTestTable();
288 EXPECT_TRUE(from_cht->try_move_nodes_to(thr, to_cht)) << "Moving nodes to new table failed";
289
290 ChtCountScan scan_old;
291 EXPECT_TRUE(from_cht->try_scan(thr, scan_old)) << "Scanning table should work.";
292 EXPECT_EQ(scan_old._count, (size_t)0) << "All items should be moved";
293
294 ChtCountScan scan_new;
295 EXPECT_TRUE(to_cht->try_scan(thr, scan_new)) << "Scanning table should work.";
296 EXPECT_EQ(scan_new._count, (size_t)3) << "All items should be moved";
297 EXPECT_TRUE(to_cht->get_copy(thr, stl1) == val1) << "Getting an inserted value should work.";
298 EXPECT_TRUE(to_cht->get_copy(thr, stl2) == val2) << "Getting an inserted value should work.";
299 EXPECT_TRUE(to_cht->get_copy(thr, stl3) == val3) << "Getting an inserted value should work.";
300 }
301
cht_grow(Thread * thr)302 static void cht_grow(Thread* thr) {
303 uintptr_t val = 0x2;
304 uintptr_t val2 = 0x22;
305 uintptr_t val3 = 0x222;
306 SimpleTestLookup stl(val), stl2(val2), stl3(val3);
307 SimpleTestTable* cht = new SimpleTestTable();
308
309 EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";
310 EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";
311 EXPECT_TRUE(cht->insert(thr, stl3, val3)) << "Insert unique value failed.";
312 EXPECT_FALSE(cht->insert(thr, stl3, val3)) << "Insert duplicate value should have failed.";
313 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an inserted value should work.";
314 EXPECT_TRUE(cht->get_copy(thr, stl2) == val2) << "Getting an inserted value should work.";
315 EXPECT_TRUE(cht->get_copy(thr, stl3) == val3) << "Getting an inserted value should work.";
316
317 EXPECT_TRUE(cht->remove(thr, stl2)) << "Removing an inserted value should work.";
318
319 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an inserted value should work.";
320 EXPECT_FALSE(cht->get_copy(thr, stl2) == val2) << "Getting a removed value should have failed.";
321 EXPECT_TRUE(cht->get_copy(thr, stl3) == val3) << "Getting an inserted value should work.";
322
323
324 EXPECT_TRUE(cht->grow(thr)) << "Growing uncontended should not fail.";
325
326 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an item after grow failed.";
327 EXPECT_FALSE(cht->get_copy(thr, stl2) == val2) << "Getting a removed value after grow should have failed.";
328 EXPECT_TRUE(cht->get_copy(thr, stl3) == val3) << "Getting an item after grow failed.";
329
330 EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";
331 EXPECT_TRUE(cht->remove(thr, stl3)) << "Removing an inserted value should work.";
332
333 EXPECT_TRUE(cht->shrink(thr)) << "Shrinking uncontended should not fail.";
334
335 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an item after shrink failed.";
336 EXPECT_TRUE(cht->get_copy(thr, stl2) == val2) << "Getting an item after shrink failed.";
337 EXPECT_FALSE(cht->get_copy(thr, stl3) == val3) << "Getting a removed value after shrink should have failed.";
338
339 delete cht;
340 }
341
cht_task_grow(Thread * thr)342 static void cht_task_grow(Thread* thr) {
343 uintptr_t val = 0x2;
344 uintptr_t val2 = 0x22;
345 uintptr_t val3 = 0x222;
346 SimpleTestLookup stl(val), stl2(val2), stl3(val3);
347 SimpleTestTable* cht = new SimpleTestTable();
348
349 EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";
350 EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";
351 EXPECT_TRUE(cht->insert(thr, stl3, val3)) << "Insert unique value failed.";
352 EXPECT_FALSE(cht->insert(thr, stl3, val3)) << "Insert duplicate value should have failed.";
353 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an inserted value should work.";
354 EXPECT_TRUE(cht->get_copy(thr, stl2) == val2) << "Getting an inserted value should work.";
355 EXPECT_TRUE(cht->get_copy(thr, stl3) == val3) << "Getting an inserted value should work.";
356
357 EXPECT_TRUE(cht->remove(thr, stl2)) << "Removing an inserted value should work.";
358
359 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an inserted value should work.";
360 EXPECT_FALSE(cht->get_copy(thr, stl2) == val2) << "Getting a removed value should have failed.";
361 EXPECT_TRUE(cht->get_copy(thr, stl3) == val3) << "Getting an inserted value should work.";
362
363 SimpleTestTable::GrowTask gt(cht);
364 EXPECT_TRUE(gt.prepare(thr)) << "Growing uncontended should not fail.";
365 while(gt.do_task(thr)) { /* grow */ }
366 gt.done(thr);
367
368 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an item after grow failed.";
369 EXPECT_FALSE(cht->get_copy(thr, stl2) == val2) << "Getting a removed value after grow should have failed.";
370 EXPECT_TRUE(cht->get_copy(thr, stl3) == val3) << "Getting an item after grow failed.";
371
372 EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";
373 EXPECT_TRUE(cht->remove(thr, stl3)) << "Removing an inserted value should work.";
374
375 EXPECT_TRUE(cht->shrink(thr)) << "Shrinking uncontended should not fail.";
376
377 EXPECT_TRUE(cht->get_copy(thr, stl) == val) << "Getting an item after shrink failed.";
378 EXPECT_TRUE(cht->get_copy(thr, stl2) == val2) << "Getting an item after shrink failed.";
379 EXPECT_FALSE(cht->get_copy(thr, stl3) == val3) << "Getting a removed value after shrink should have failed.";
380
381 delete cht;
382 }
383
TEST_VM(ConcurrentHashTable,basic_insert)384 TEST_VM(ConcurrentHashTable, basic_insert) {
385 nomt_test_doer(cht_insert);
386 }
387
TEST_VM(ConcurrentHashTable,basic_get_insert)388 TEST_VM(ConcurrentHashTable, basic_get_insert) {
389 nomt_test_doer(cht_get_insert);
390 }
391
TEST_VM(ConcurrentHashTable,basic_scope)392 TEST_VM(ConcurrentHashTable, basic_scope) {
393 nomt_test_doer(cht_scope);
394 }
395
TEST_VM(ConcurrentHashTable,basic_get_insert_bulk_delete)396 TEST_VM(ConcurrentHashTable, basic_get_insert_bulk_delete) {
397 nomt_test_doer(cht_getinsert_bulkdelete);
398 }
399
TEST_VM(ConcurrentHashTable,basic_get_insert_bulk_delete_task)400 TEST_VM(ConcurrentHashTable, basic_get_insert_bulk_delete_task) {
401 nomt_test_doer(cht_getinsert_bulkdelete_task);
402 }
403
TEST_VM(ConcurrentHashTable,basic_scan)404 TEST_VM(ConcurrentHashTable, basic_scan) {
405 nomt_test_doer(cht_scan);
406 }
407
TEST_VM(ConcurrentHashTable,basic_move_to)408 TEST_VM(ConcurrentHashTable, basic_move_to) {
409 nomt_test_doer(cht_move_to);
410 }
411
TEST_VM(ConcurrentHashTable,basic_grow)412 TEST_VM(ConcurrentHashTable, basic_grow) {
413 nomt_test_doer(cht_grow);
414 }
415
TEST_VM(ConcurrentHashTable,task_grow)416 TEST_VM(ConcurrentHashTable, task_grow) {
417 nomt_test_doer(cht_task_grow);
418 }
419
420 //#############################################################################################
421
422 class TestInterface;
423
424 typedef ConcurrentHashTable<uintptr_t, TestInterface, mtInternal> TestTable;
425 typedef ConcurrentHashTable<uintptr_t, TestInterface, mtInternal>::MultiGetHandle TestGetHandle;
426
427 class TestInterface : public TestTable::BaseConfig {
428 public:
get_hash(const uintptr_t & value,bool * dead_hash)429 static uintx get_hash(const uintptr_t& value, bool* dead_hash) {
430 return (uintx)(value + 18446744073709551557ul) * 18446744073709551557ul;
431 }
notfound()432 static const uintptr_t& notfound() {
433 static uintptr_t notfound = 0;
434 return notfound;
435 }
436 };
437
438 struct TestLookup {
439 uintptr_t _val;
TestLookupTestLookup440 TestLookup(uintptr_t val) : _val(val) {}
get_hashTestLookup441 uintx get_hash() {
442 return TestInterface::get_hash(_val, NULL);
443 }
equalsTestLookup444 bool equals(const uintptr_t* value, bool* is_dead) {
445 return _val == *value;
446 }
447 };
448
449 class CHTTestThread : public JavaTestThread {
450 public:
451 uintptr_t _start;
452 uintptr_t _stop;
453 TestTable *_cht;
454 jlong _stop_ms;
CHTTestThread(uintptr_t start,uintptr_t stop,TestTable * cht,Semaphore * post)455 CHTTestThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post)
456 : JavaTestThread(post), _start(start), _stop(stop), _cht(cht) {}
premain()457 virtual void premain() {}
main_run()458 void main_run() {
459 premain();
460 _stop_ms = os::javaTimeMillis() + 2000; // 2 seconds max test time
461 while (keep_looping() && test_loop()) { /* */ }
462 postmain();
463 }
postmain()464 virtual void postmain() {}
keep_looping()465 virtual bool keep_looping() {
466 return _stop_ms > os::javaTimeMillis();
467 };
468 virtual bool test_loop() = 0;
~CHTTestThread()469 virtual ~CHTTestThread() {}
470 };
471
472 class ValueSaver {
473 uintptr_t* _vals;
474 size_t _it;
475 size_t _size;
476 public:
ValueSaver()477 ValueSaver() : _it(0), _size(1024) {
478 _vals = NEW_C_HEAP_ARRAY(uintptr_t, _size, mtInternal);
479 }
480
operator ()(uintptr_t * val)481 bool operator()(uintptr_t* val) {
482 _vals[_it++] = *val;
483 if (_it == _size) {
484 _size *= 2;
485 _vals = REALLOC_RESOURCE_ARRAY(uintptr_t, _vals, _size/2, _size);
486 }
487 return true;
488 }
489
check()490 void check() {
491 for (size_t i = 0; i < _it; i++) {
492 size_t count = 0;
493 for (size_t j = (i + 1u); j < _it; j++) {
494 if (_vals[i] == _vals[j]) {
495 count++;
496 }
497 }
498 EXPECT_EQ(count, 0u);
499 }
500 }
501 };
502
integrity_check(Thread * thr,TestTable * cht)503 static void integrity_check(Thread* thr, TestTable* cht)
504 {
505 ValueSaver vs;
506 cht->do_scan(thr, vs);
507 vs.check();
508 }
509
510 //#############################################################################################
511 // All threads are working on different items
512 // This item should only be delete by this thread
513 // Thus get_unsafe is safe for this test.
514
515 class SimpleInserterThread : public CHTTestThread {
516 public:
517 static volatile bool _exit;
518
SimpleInserterThread(uintptr_t start,uintptr_t stop,TestTable * cht,Semaphore * post)519 SimpleInserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post)
520 : CHTTestThread(start, stop, cht, post) {};
~SimpleInserterThread()521 virtual ~SimpleInserterThread(){}
522
keep_looping()523 bool keep_looping() {
524 return !_exit;
525 }
526
test_loop()527 bool test_loop() {
528 bool grow;
529 for (uintptr_t v = _start; v <= _stop; v++) {
530 TestLookup tl(v);
531 EXPECT_TRUE(_cht->insert(this, tl, v, &grow)) << "Inserting an unique value should work.";
532 }
533 for (uintptr_t v = _start; v <= _stop; v++) {
534 TestLookup tl(v);
535 EXPECT_TRUE(_cht->get_copy(this, tl) == v) << "Getting an previously inserted value unsafe failed.";
536 }
537 for (uintptr_t v = _start; v <= _stop; v++) {
538 TestLookup tl(v);
539 EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";
540 }
541 for (uintptr_t v = _start; v <= _stop; v++) {
542 TestLookup tl(v);
543 EXPECT_TRUE(_cht->get_copy(this, tl) == TestInterface::notfound()) << "Got a removed value.";
544 }
545 return true;
546 }
547 };
548
549 volatile bool SimpleInserterThread::_exit = false;
550
551 class RunnerSimpleInserterThread : public CHTTestThread {
552 public:
553 Semaphore _done;
554
RunnerSimpleInserterThread(Semaphore * post)555 RunnerSimpleInserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {
556 _cht = new TestTable(SIZE_32, SIZE_32);
557 };
~RunnerSimpleInserterThread()558 virtual ~RunnerSimpleInserterThread(){}
559
premain()560 void premain() {
561
562 SimpleInserterThread* ins1 = new SimpleInserterThread((uintptr_t)0x100, (uintptr_t) 0x1FF, _cht, &_done);
563 SimpleInserterThread* ins2 = new SimpleInserterThread((uintptr_t)0x200, (uintptr_t) 0x2FF, _cht, &_done);
564 SimpleInserterThread* ins3 = new SimpleInserterThread((uintptr_t)0x300, (uintptr_t) 0x3FF, _cht, &_done);
565 SimpleInserterThread* ins4 = new SimpleInserterThread((uintptr_t)0x400, (uintptr_t) 0x4FF, _cht, &_done);
566
567 for (uintptr_t v = 0x500; v < 0x5FF; v++ ) {
568 TestLookup tl(v);
569 EXPECT_TRUE(_cht->insert(this, tl, v)) << "Inserting an unique value should work.";
570 }
571
572 ins1->doit();
573 ins2->doit();
574 ins3->doit();
575 ins4->doit();
576
577 }
578
test_loop()579 bool test_loop() {
580 for (uintptr_t v = 0x500; v < 0x5FF; v++ ) {
581 TestLookup tl(v);
582 EXPECT_TRUE(_cht->get_copy(this, tl) == v) << "Getting an previously inserted value unsafe failed.";;
583 }
584 return true;
585 }
586
postmain()587 void postmain() {
588 SimpleInserterThread::_exit = true;
589 for (int i = 0; i < 4; i++) {
590 _done.wait();
591 }
592 for (uintptr_t v = 0x500; v < 0x5FF; v++ ) {
593 TestLookup tl(v);
594 EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";
595 }
596 integrity_check(this, _cht);
597 delete _cht;
598 }
599 };
600
601
TEST_VM(ConcurrentHashTable,concurrent_simple)602 TEST_VM(ConcurrentHashTable, concurrent_simple) {
603 SimpleInserterThread::_exit = false;
604 mt_test_doer<RunnerSimpleInserterThread>();
605 }
606
607 //#############################################################################################
608 // In this test we try to get a 'bad' value
609 class DeleteInserterThread : public CHTTestThread {
610 public:
611 static volatile bool _exit;
612
DeleteInserterThread(uintptr_t start,uintptr_t stop,TestTable * cht,Semaphore * post)613 DeleteInserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post) : CHTTestThread(start, stop, cht, post) {};
~DeleteInserterThread()614 virtual ~DeleteInserterThread(){}
615
keep_looping()616 bool keep_looping() {
617 return !_exit;
618 }
619
test_loop()620 bool test_loop() {
621 for (uintptr_t v = _start; v <= _stop; v++) {
622 TestLookup tl(v);
623 _cht->insert(this, tl, v);
624 }
625 for (uintptr_t v = _start; v <= _stop; v++) {
626 TestLookup tl(v);
627 _cht->remove(this, tl);
628 }
629 return true;
630 }
631 };
632
633 volatile bool DeleteInserterThread::_exit = true;
634
635 class RunnerDeleteInserterThread : public CHTTestThread {
636 public:
637 Semaphore _done;
638
RunnerDeleteInserterThread(Semaphore * post)639 RunnerDeleteInserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {
640 _cht = new TestTable(SIZE_32, SIZE_32);
641 };
~RunnerDeleteInserterThread()642 virtual ~RunnerDeleteInserterThread(){}
643
premain()644 void premain() {
645 DeleteInserterThread* ins1 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);
646 DeleteInserterThread* ins2 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);
647 DeleteInserterThread* ins3 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);
648 DeleteInserterThread* ins4 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);
649
650 ins1->doit();
651 ins2->doit();
652 ins3->doit();
653 ins4->doit();
654 }
655
test_loop()656 bool test_loop() {
657 for (uintptr_t v = 0x1; v < 0xFFF; v++ ) {
658 uintptr_t tv;
659 if (v & 0x1) {
660 TestLookup tl(v);
661 tv = _cht->get_copy(this, tl);
662 } else {
663 TestLookup tl(v);
664 TestGetHandle value_handle(this, _cht);
665 uintptr_t* tmp = value_handle.get(tl);
666 tv = tmp != NULL ? *tmp : 0;
667 }
668 EXPECT_TRUE(tv == 0 || tv == v) << "Got unknown value.";
669 }
670 return true;
671 }
672
postmain()673 void postmain() {
674 DeleteInserterThread::_exit = true;
675 for (int i = 0; i < 4; i++) {
676 _done.wait();
677 }
678 integrity_check(this, _cht);
679 delete _cht;
680 }
681 };
682
TEST_VM(ConcurrentHashTable,concurrent_deletes)683 TEST_VM(ConcurrentHashTable, concurrent_deletes) {
684 DeleteInserterThread::_exit = false;
685 mt_test_doer<RunnerDeleteInserterThread>();
686 }
687
688 //#############################################################################################
689
690 #define START_SIZE 13
691 #define END_SIZE 17
692 #define START (uintptr_t)0x10000
693 #define RANGE (uintptr_t)0xFFFF
694
695 #define GSTEST_THREAD_COUNT 5
696
697
698 class GSInserterThread: public CHTTestThread {
699 public:
700 static volatile bool _shrink;
GSInserterThread(uintptr_t start,uintptr_t stop,TestTable * cht,Semaphore * post)701 GSInserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post) : CHTTestThread(start, stop, cht, post) {};
~GSInserterThread()702 virtual ~GSInserterThread(){}
keep_looping()703 bool keep_looping() {
704 return !(_shrink && _cht->get_size_log2(this) == START_SIZE);
705 }
test_loop()706 bool test_loop() {
707 bool grow;
708 for (uintptr_t v = _start; v <= _stop; v++) {
709 TestLookup tl(v);
710 EXPECT_TRUE(_cht->insert(this, tl, v, &grow)) << "Inserting an unique value should work.";
711 if (grow && !_shrink) {
712 _cht->grow(this);
713 }
714 }
715 for (uintptr_t v = _start; v <= _stop; v++) {
716 TestLookup tl(v);
717 EXPECT_TRUE(_cht->get_copy(this, tl) == v) << "Getting an previously inserted value unsafe failed.";
718 }
719 for (uintptr_t v = _start; v <= _stop; v++) {
720 TestLookup tl(v);
721 EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";
722 }
723 if (_shrink) {
724 _cht->shrink(this);
725 }
726 for (uintptr_t v = _start; v <= _stop; v++) {
727 TestLookup tl(v);
728 EXPECT_FALSE(_cht->get_copy(this, tl) == v) << "Getting a removed value should have failed.";
729 }
730 if (!_shrink && _cht->get_size_log2(this) == END_SIZE) {
731 _shrink = true;
732 }
733 return true;
734 }
735 };
736
737 volatile bool GSInserterThread::_shrink = false;
738
739 class GSScannerThread : public CHTTestThread {
740 public:
GSScannerThread(uintptr_t start,uintptr_t stop,TestTable * cht,Semaphore * post)741 GSScannerThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post) : CHTTestThread(start, stop, cht, post) {};
~GSScannerThread()742 virtual ~GSScannerThread(){}
743
operator ()(uintptr_t * val)744 bool operator()(uintptr_t* val) {
745 if (*val >= this->_start && *val <= this->_stop) {
746 return false;
747 }
748 // continue scan
749 return true;
750 }
751
test_loop()752 bool test_loop() {
753 _cht->try_scan(this, *this);
754 os::naked_short_sleep(5);
755 return true;
756 }
757 };
758
759 class RunnerGSInserterThread : public CHTTestThread {
760 public:
761 uintptr_t _start;
762 uintptr_t _range;
763 Semaphore _done;
764
RunnerGSInserterThread(Semaphore * post)765 RunnerGSInserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {
766 _cht = new TestTable(START_SIZE, END_SIZE, 2);
767 };
~RunnerGSInserterThread()768 virtual ~RunnerGSInserterThread(){}
769
premain()770 void premain() {
771 volatile bool timeout = false;
772 _start = START;
773 _range = RANGE;
774 CHTTestThread* tt[GSTEST_THREAD_COUNT];
775 tt[0] = new GSInserterThread(_start, _start + _range, _cht, &_done);
776 _start += _range + 1;
777 tt[1] = new GSInserterThread(_start, _start + _range, _cht, &_done);
778 _start += _range + 1;
779 tt[2] = new GSInserterThread(_start, _start + _range, _cht, &_done);
780 _start += _range + 1;
781 tt[3] = new GSInserterThread(_start, _start + _range, _cht, &_done);
782 tt[4] = new GSScannerThread(_start, _start + _range, _cht, &_done);
783 _start += _range + 1;
784
785
786 for (uintptr_t v = _start; v <= (_start + _range); v++ ) {
787 TestLookup tl(v);
788 EXPECT_TRUE(_cht->insert(this, tl, v)) << "Inserting an unique value should work.";
789 }
790
791 for (int i = 0; i < GSTEST_THREAD_COUNT; i++) {
792 tt[i]->doit();
793 }
794 }
795
test_loop()796 bool test_loop() {
797 for (uintptr_t v = _start; v <= (_start + _range); v++ ) {
798 TestLookup tl(v);
799 EXPECT_TRUE(_cht->get_copy(this, tl) == v) << "Getting an previously inserted value unsafe failed.";
800 }
801 return true;
802 }
803
postmain()804 void postmain() {
805 GSInserterThread::_shrink = true;
806 for (uintptr_t v = _start; v <= (_start + _range); v++ ) {
807 TestLookup tl(v);
808 EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";
809 }
810 for (int i = 0; i < GSTEST_THREAD_COUNT; i++) {
811 _done.wait();
812 }
813 EXPECT_TRUE(_cht->get_size_log2(this) == START_SIZE) << "Not at start size.";
814 Count cnt;
815 _cht->do_scan(this, cnt);
816 EXPECT_TRUE(cnt._cnt == 0) << "Items still in table";
817 delete _cht;
818 }
819
820 struct Count {
CountRunnerGSInserterThread::Count821 Count() : _cnt(0) {}
822 size_t _cnt;
operator ()RunnerGSInserterThread::Count823 bool operator()(uintptr_t*) { _cnt++; return true; };
824 };
825 };
826
TEST_VM(ConcurrentHashTable,concurrent_scan_grow_shrink)827 TEST_VM(ConcurrentHashTable, concurrent_scan_grow_shrink) {
828 GSInserterThread::_shrink = false;
829 mt_test_doer<RunnerGSInserterThread>();
830 }
831
832
833 //#############################################################################################
834
835 #define GI_BD_GI_BD_START_SIZE 13
836 #define GI_BD_END_SIZE 17
837 #define GI_BD_START (uintptr_t)0x1
838 #define GI_BD_RANGE (uintptr_t)0x3FFFF
839
840 #define GI_BD_TEST_THREAD_COUNT 4
841
842
843 class GI_BD_InserterThread: public CHTTestThread {
844 public:
845 static volatile bool _shrink;
846 uintptr_t _br;
GI_BD_InserterThread(uintptr_t start,uintptr_t stop,TestTable * cht,Semaphore * post,uintptr_t br)847 GI_BD_InserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post, uintptr_t br)
848 : CHTTestThread(start, stop, cht, post), _br(br) {};
~GI_BD_InserterThread()849 virtual ~GI_BD_InserterThread(){}
850
keep_looping()851 bool keep_looping() {
852 return !(_shrink && _cht->get_size_log2(this) == GI_BD_GI_BD_START_SIZE);
853 }
854
test_loop()855 bool test_loop() {
856 bool grow;
857 MyDel del(_br);
858 for (uintptr_t v = _start; v <= _stop; v++) {
859 ValVerify vv(v);
860 TestLookup tl(v);
861 _cht->get_insert(this, tl, v, vv, &grow);
862 EXPECT_NE(vv.called_get, vv.called_insert) << "Non or both callbacks was called.";
863 if (grow && !_shrink) {
864 _cht->grow(this);
865 }
866 }
867 if (_shrink) {
868 _cht->shrink(this);
869 }
870 _cht->try_bulk_delete(this, *this, del);
871 if (!_shrink && _cht->is_max_size_reached()) {
872 _shrink = true;
873 }
874 _cht->bulk_delete(this, *this, del);
875 return true;
876 }
877
operator ()(uintptr_t * val)878 bool operator()(uintptr_t* val) {
879 return (*val & _br) == 1;
880 }
881
882 struct MyDel {
MyDelGI_BD_InserterThread::MyDel883 MyDel(uintptr_t &br) : _br(br) {};
884 uintptr_t &_br;
operator ()GI_BD_InserterThread::MyDel885 void operator()(uintptr_t* val) {
886 EXPECT_EQ((*val & _br), _br) << "Removing an item that should not have been removed.";
887 }
888 };
889 };
890
891 volatile bool GI_BD_InserterThread::_shrink = false;
892
893 class RunnerGI_BD_InserterThread : public CHTTestThread {
894 public:
895 Semaphore _done;
896 uintptr_t _start;
897 uintptr_t _range;
RunnerGI_BD_InserterThread(Semaphore * post)898 RunnerGI_BD_InserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {
899 _cht = new TestTable(GI_BD_GI_BD_START_SIZE, GI_BD_END_SIZE, 2);
900 };
~RunnerGI_BD_InserterThread()901 virtual ~RunnerGI_BD_InserterThread(){}
902
premain()903 void premain() {
904 _start = GI_BD_START;
905 _range = GI_BD_RANGE;
906 CHTTestThread* tt[GI_BD_TEST_THREAD_COUNT];
907 tt[0] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x1);
908 tt[1] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x2);
909 tt[2] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x4);
910 tt[3] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x8);
911
912 for (uintptr_t v = _start; v <= (_start + _range); v++ ) {
913 TestLookup tl(v);
914 EXPECT_TRUE(_cht->insert(this, tl, v)) << "Inserting an unique value should work.";
915 }
916
917 for (int i =0; i < GI_BD_TEST_THREAD_COUNT; i++) {
918 tt[i]->doit();
919 }
920 }
921
test_loop()922 bool test_loop() {
923 for (uintptr_t v = _start; v <= (_start + _range); v++ ) {
924 TestLookup tl(v);
925 if (v & 0xF) {
926 _cht->get_copy(this, tl);
927 } else {
928 EXPECT_EQ(_cht->get_copy(this, tl), v) << "Item ending with 0xX0 should never be removed.";
929 }
930 }
931 return true;
932 }
933
postmain()934 void postmain() {
935 GI_BD_InserterThread::_shrink = true;
936 for (uintptr_t v = _start; v <= (_start + _range); v++ ) {
937 TestLookup tl(v);
938 if (v & 0xF) {
939 _cht->remove(this, tl);
940 } else {
941 EXPECT_TRUE(_cht->remove(this, tl)) << "Removing item ending with 0xX0 should always work.";
942 }
943 }
944 for (int i = 0; i < GI_BD_TEST_THREAD_COUNT; i++) {
945 _done.wait();
946 }
947 EXPECT_TRUE(_cht->get_size_log2(this) == GI_BD_GI_BD_START_SIZE) << "We have not shrunk back to start size.";
948 delete _cht;
949 }
950 };
951
TEST_VM(ConcurrentHashTable,concurrent_get_insert_bulk_delete)952 TEST_VM(ConcurrentHashTable, concurrent_get_insert_bulk_delete) {
953 GI_BD_InserterThread::_shrink = false;
954 mt_test_doer<RunnerGI_BD_InserterThread>();
955 }
956
957 //#############################################################################################
958
959 class MT_BD_Thread : public JavaTestThread {
960 TestTable::BulkDeleteTask* _bd;
961 public:
MT_BD_Thread(Semaphore * post,TestTable::BulkDeleteTask * bd)962 MT_BD_Thread(Semaphore* post, TestTable::BulkDeleteTask* bd)
963 : JavaTestThread(post), _bd(bd){}
~MT_BD_Thread()964 virtual ~MT_BD_Thread() {}
main_run()965 void main_run() {
966 MyDel del;
967 while(_bd->do_task(this, *this, del));
968 }
969
operator ()(uintptr_t * val)970 bool operator()(uintptr_t* val) {
971 return true;
972 }
973
974 struct MyDel {
operator ()MT_BD_Thread::MyDel975 void operator()(uintptr_t* val) {
976 }
977 };
978 };
979
980 class Driver_BD_Thread : public JavaTestThread {
981 public:
982 Semaphore _done;
Driver_BD_Thread(Semaphore * post)983 Driver_BD_Thread(Semaphore* post) : JavaTestThread(post) {
984 };
~Driver_BD_Thread()985 virtual ~Driver_BD_Thread(){}
986
main_run()987 void main_run() {
988 Semaphore done(0);
989 TestTable* cht = new TestTable(16, 16, 2);
990 for (uintptr_t v = 1; v < 99999; v++ ) {
991 TestLookup tl(v);
992 EXPECT_TRUE(cht->insert(this, tl, v)) << "Inserting an unique value should work.";
993 }
994 TestTable::BulkDeleteTask bdt(cht, true /* mt */ );
995 EXPECT_TRUE(bdt.prepare(this)) << "Uncontended prepare must work.";
996
997 MT_BD_Thread* tt[4];
998 for (int i = 0; i < 4; i++) {
999 tt[i] = new MT_BD_Thread(&done, &bdt);
1000 tt[i]->doit();
1001 }
1002
1003 for (uintptr_t v = 1; v < 99999; v++ ) {
1004 TestLookup tl(v);
1005 cht->get_copy(this, tl);
1006 }
1007
1008 for (int i = 0; i < 4; i++) {
1009 done.wait();
1010 }
1011
1012 bdt.done(this);
1013
1014 cht->do_scan(this, *this);
1015 }
1016
operator ()(uintptr_t * val)1017 bool operator()(uintptr_t* val) {
1018 EXPECT_TRUE(false) << "No items should left";
1019 return true;
1020 }
1021 };
1022
TEST_VM(ConcurrentHashTable,concurrent_mt_bulk_delete)1023 TEST_VM(ConcurrentHashTable, concurrent_mt_bulk_delete) {
1024 mt_test_doer<Driver_BD_Thread>();
1025 }
1026