Path: blob/master/test/hotspot/gtest/utilities/test_concurrentHashtable.cpp
41145 views
/*1* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223#include "precompiled.hpp"24#include "runtime/mutex.hpp"25#include "runtime/semaphore.hpp"26#include "runtime/thread.hpp"27#include "runtime/vmThread.hpp"28#include "runtime/vmOperations.hpp"29#include "utilities/concurrentHashTable.inline.hpp"30#include "utilities/concurrentHashTableTasks.inline.hpp"31#include "threadHelper.inline.hpp"32#include "unittest.hpp"3334// NOTE: On win32 gtest asserts are not mt-safe.35// Amusingly as long as they do not assert they are mt-safe.36#define SIZE_32 53738struct Pointer : public AllStatic {39typedef uintptr_t Value;40static uintx get_hash(const Value& value, bool* dead_hash) {41return (uintx)value;42}43static void* allocate_node(void* context, size_t size, const Value& value) {44return ::malloc(size);45}46static void free_node(void* context, void* memory, const Value& value) {47::free(memory);48}49};5051struct Allocator {52struct TableElement{53TableElement * volatile _next;54uintptr_t _value;55};5657const uint nelements = 5;58TableElement* elements;59uint cur_index;6061Allocator() : cur_index(0) {62elements = (TableElement*)::malloc(nelements * sizeof(TableElement));63}6465void* allocate_node() {66return (void*)&elements[cur_index++];67}6869void free_node(void* value) { /* Arena allocator. Ignore freed nodes*/ }7071void reset() {72cur_index = 0;73}7475~Allocator() {76::free(elements);77}78};7980struct Config : public AllStatic {81typedef uintptr_t Value;8283static uintx get_hash(const Value& value, bool* dead_hash) {84return (uintx)value;85}86static void* allocate_node(void* context, size_t size, const Value& value) {87Allocator* mm = (Allocator*)context;88return mm->allocate_node();89}9091static void free_node(void* context, void* memory, const Value& value) {92Allocator* mm = (Allocator*)context;93mm->free_node(memory);94}95};9697typedef ConcurrentHashTable<Pointer, mtInternal> SimpleTestTable;98typedef ConcurrentHashTable<Pointer, mtInternal>::MultiGetHandle SimpleTestGetHandle;99typedef ConcurrentHashTable<Config, mtInternal> CustomTestTable;100101struct SimpleTestLookup {102uintptr_t _val;103SimpleTestLookup(uintptr_t val) : _val(val) {}104uintx get_hash() {105return Pointer::get_hash(_val, NULL);106}107bool equals(const uintptr_t* value, bool* is_dead) {108return _val == *value;109}110};111112struct ValueGet {113uintptr_t _return;114ValueGet() : _return(0) {}115void operator()(uintptr_t* value) {116EXPECT_NE(value, (uintptr_t*)NULL) << "expected valid value";117_return = *value;118}119uintptr_t get_value() const {120return _return;121}122};123124template <typename T=SimpleTestTable>125static uintptr_t cht_get_copy(T* cht, Thread* thr, SimpleTestLookup stl) {126ValueGet vg;127cht->get(thr, stl, vg);128return vg.get_value();129}130131template <typename T=SimpleTestTable>132static void cht_find(Thread* thr, T* cht, uintptr_t val) {133SimpleTestLookup stl(val);134ValueGet vg;135EXPECT_EQ(cht->get(thr, stl, vg), true) << "Getting an old value failed.";136EXPECT_EQ(val, vg.get_value()) << "Getting an old value failed.";137}138139template <typename T=SimpleTestTable>140static void cht_insert_and_find(Thread* thr, T* cht, uintptr_t val) {141SimpleTestLookup stl(val);142EXPECT_EQ(cht->insert(thr, stl, val), true) << "Inserting an unique value failed.";143cht_find(thr, cht, val);144}145146static void cht_insert(Thread* thr) {147uintptr_t val = 0x2;148SimpleTestLookup stl(val);149SimpleTestTable* cht = new SimpleTestTable();150EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";151EXPECT_EQ(cht_get_copy(cht, thr, stl), val) << "Getting an existing value failed.";152EXPECT_TRUE(cht->remove(thr, stl)) << "Removing an existing value failed.";153EXPECT_FALSE(cht->remove(thr, stl)) << "Removing an already removed item succeeded.";154EXPECT_NE(cht_get_copy(cht, thr, stl), val) << "Getting a removed value succeeded.";155delete cht;156}157158static void cht_insert_get(Thread* thr) {159uintptr_t val = 0x2;160SimpleTestLookup stl(val);161SimpleTestTable* cht = new SimpleTestTable();162ValueGet vg;163EXPECT_TRUE(cht->insert_get(thr, stl, val, vg)) << "Insert unique value failed.";164EXPECT_EQ(val, vg.get_value()) << "Getting an inserted value failed.";165ValueGet vg_dup;166EXPECT_FALSE(cht->insert_get(thr, stl, val, vg_dup)) << "Insert duplicate value succeeded.";167EXPECT_EQ(val, vg_dup.get_value()) << "Getting an existing value failed.";168delete cht;169}170171static void cht_get_insert(Thread* thr) {172uintptr_t val = 0x2;173SimpleTestLookup stl(val);174SimpleTestTable* cht = new SimpleTestTable();175176{177SCOPED_TRACE("First");178cht_insert_and_find(thr, cht, val);179}180EXPECT_EQ(cht_get_copy(cht, thr, stl), val) << "Get an old value failed";181EXPECT_TRUE(cht->remove(thr, stl)) << "Removing existing value failed.";182EXPECT_NE(cht_get_copy(cht, thr, stl), val) << "Got an already removed item.";183184{185SCOPED_TRACE("Second");186cht_insert_and_find(thr, cht, val);187}188189delete cht;190}191192static bool getinsert_bulkdelete_eval(uintptr_t* val) {193EXPECT_TRUE(*val > 0 && *val < 4) << "Val wrong for this test.";194return (*val & 0x1); // Delete all values ending with first bit set.195}196197static void getinsert_bulkdelete_del(uintptr_t* val) {198EXPECT_EQ(*val & 0x1, (uintptr_t)1) << "Deleting wrong value.";199}200201static void cht_getinsert_bulkdelete_insert_verified(Thread* thr, SimpleTestTable* cht, uintptr_t val,202bool verify_expect_get, bool verify_expect_inserted) {203SimpleTestLookup stl(val);204if (verify_expect_inserted) {205cht_insert_and_find(thr, cht, val);206}207if (verify_expect_get) {208cht_find(thr, cht, val);209}210}211212static void cht_getinsert_bulkdelete(Thread* thr) {213uintptr_t val1 = 1;214uintptr_t val2 = 2;215uintptr_t val3 = 3;216SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);217218SimpleTestTable* cht = new SimpleTestTable();219cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, false, true);220cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true);221cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, false, true);222223EXPECT_TRUE(cht->remove(thr, stl2)) << "Remove did not find value.";224225cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, true, false); // val1 should be present226cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true); // val2 should be inserted227cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, true, false); // val3 should be present228229EXPECT_EQ(cht_get_copy(cht, thr, stl1), val1) << "Get did not find value.";230EXPECT_EQ(cht_get_copy(cht, thr, stl2), val2) << "Get did not find value.";231EXPECT_EQ(cht_get_copy(cht, thr, stl3), val3) << "Get did not find value.";232233// Removes all odd values.234cht->bulk_delete(thr, getinsert_bulkdelete_eval, getinsert_bulkdelete_del);235236EXPECT_EQ(cht_get_copy(cht, thr, stl1), (uintptr_t)0) << "Odd value should not exist.";237EXPECT_FALSE(cht->remove(thr, stl1)) << "Odd value should not exist.";238EXPECT_EQ(cht_get_copy(cht, thr, stl2), val2) << "Even value should not have been removed.";239EXPECT_EQ(cht_get_copy(cht, thr, stl3), (uintptr_t)0) << "Add value should not exists.";240EXPECT_FALSE(cht->remove(thr, stl3)) << "Odd value should not exists.";241242delete cht;243}244245static void cht_getinsert_bulkdelete_task(Thread* thr) {246uintptr_t val1 = 1;247uintptr_t val2 = 2;248uintptr_t val3 = 3;249SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);250251SimpleTestTable* cht = new SimpleTestTable();252cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, false, true);253cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true);254cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, false, true);255256EXPECT_TRUE(cht->remove(thr, stl2)) << "Remove did not find value.";257258cht_getinsert_bulkdelete_insert_verified(thr, cht, val1, true, false); // val1 should be present259cht_getinsert_bulkdelete_insert_verified(thr, cht, val2, false, true); // val2 should be inserted260cht_getinsert_bulkdelete_insert_verified(thr, cht, val3, true, false); // val3 should be present261262EXPECT_EQ(cht_get_copy(cht, thr, stl1), val1) << "Get did not find value.";263EXPECT_EQ(cht_get_copy(cht, thr, stl2), val2) << "Get did not find value.";264EXPECT_EQ(cht_get_copy(cht, thr, stl3), val3) << "Get did not find value.";265266// Removes all odd values.267SimpleTestTable::BulkDeleteTask bdt(cht);268if (bdt.prepare(thr)) {269while(bdt.do_task(thr, getinsert_bulkdelete_eval, getinsert_bulkdelete_del)) {270bdt.pause(thr);271bdt.cont(thr);272}273bdt.done(thr);274}275276EXPECT_EQ(cht_get_copy(cht, thr, stl1), (uintptr_t)0) << "Odd value should not exist.";277EXPECT_FALSE(cht->remove(thr, stl1)) << "Odd value should not exist.";278EXPECT_EQ(cht_get_copy(cht, thr, stl2), val2) << "Even value should not have been removed.";279EXPECT_EQ(cht_get_copy(cht, thr, stl3), (uintptr_t)0) << "Add value should not exists.";280EXPECT_FALSE(cht->remove(thr, stl3)) << "Odd value should not exists.";281282delete cht;283}284285static void cht_reset_shrink(Thread* thr) {286uintptr_t val1 = 1;287uintptr_t val2 = 2;288uintptr_t val3 = 3;289SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);290291Allocator mem_allocator;292const uint initial_log_table_size = 4;293CustomTestTable* cht = new CustomTestTable(&mem_allocator);294295cht_insert_and_find(thr, cht, val1);296cht_insert_and_find(thr, cht, val2);297cht_insert_and_find(thr, cht, val3);298299cht->unsafe_reset();300mem_allocator.reset();301302EXPECT_EQ(cht_get_copy(cht, thr, stl1), (uintptr_t)0) << "Table should have been reset";303// Re-inserted values should not be considered duplicates; table was reset.304cht_insert_and_find(thr, cht, val1);305cht_insert_and_find(thr, cht, val2);306cht_insert_and_find(thr, cht, val3);307308cht->unsafe_reset();309delete cht;310}311312static void cht_scope(Thread* thr) {313uintptr_t val = 0x2;314SimpleTestLookup stl(val);315SimpleTestTable* cht = new SimpleTestTable();316EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";317{318SimpleTestGetHandle get_handle(thr, cht);319EXPECT_EQ(*get_handle.get(stl), val) << "Getting a pre-existing value failed.";320}321// We do remove here to make sure the value-handle 'unlocked' the table when leaving the scope.322EXPECT_TRUE(cht->remove(thr, stl)) << "Removing a pre-existing value failed.";323EXPECT_FALSE(cht_get_copy(cht, thr, stl) == val) << "Got a removed value.";324delete cht;325}326327struct ChtScan {328size_t _count;329ChtScan() : _count(0) {}330bool operator()(uintptr_t* val) {331EXPECT_EQ(*val, (uintptr_t)0x2) << "Got an unknown value.";332EXPECT_EQ(_count, 0u) << "Only one value should be in table.";333_count++;334return true; /* continue scan */335}336};337338static void cht_scan(Thread* thr) {339uintptr_t val = 0x2;340SimpleTestLookup stl(val);341ChtScan scan;342SimpleTestTable* cht = new SimpleTestTable();343EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";344EXPECT_EQ(cht->try_scan(thr, scan), true) << "Scanning an non-growing/shrinking table should work.";345EXPECT_TRUE(cht->remove(thr, stl)) << "Removing a pre-existing value failed.";346EXPECT_FALSE(cht_get_copy(cht, thr, stl) == val) << "Got a removed value.";347delete cht;348}349350struct ChtCountScan {351size_t _count;352ChtCountScan() : _count(0) {}353bool operator()(uintptr_t* val) {354_count++;355return true; /* continue scan */356}357};358359static void cht_move_to(Thread* thr) {360uintptr_t val1 = 0x2;361uintptr_t val2 = 0xe0000002;362uintptr_t val3 = 0x3;363SimpleTestLookup stl1(val1), stl2(val2), stl3(val3);364SimpleTestTable* from_cht = new SimpleTestTable();365EXPECT_TRUE(from_cht->insert(thr, stl1, val1)) << "Insert unique value failed.";366EXPECT_TRUE(from_cht->insert(thr, stl2, val2)) << "Insert unique value failed.";367EXPECT_TRUE(from_cht->insert(thr, stl3, val3)) << "Insert unique value failed.";368369SimpleTestTable* to_cht = new SimpleTestTable();370EXPECT_TRUE(from_cht->try_move_nodes_to(thr, to_cht)) << "Moving nodes to new table failed";371372ChtCountScan scan_old;373EXPECT_TRUE(from_cht->try_scan(thr, scan_old)) << "Scanning table should work.";374EXPECT_EQ(scan_old._count, (size_t)0) << "All items should be moved";375376ChtCountScan scan_new;377EXPECT_TRUE(to_cht->try_scan(thr, scan_new)) << "Scanning table should work.";378EXPECT_EQ(scan_new._count, (size_t)3) << "All items should be moved";379EXPECT_TRUE(cht_get_copy(to_cht, thr, stl1) == val1) << "Getting an inserted value should work.";380EXPECT_TRUE(cht_get_copy(to_cht, thr, stl2) == val2) << "Getting an inserted value should work.";381EXPECT_TRUE(cht_get_copy(to_cht, thr, stl3) == val3) << "Getting an inserted value should work.";382}383384static void cht_grow(Thread* thr) {385uintptr_t val = 0x2;386uintptr_t val2 = 0x22;387uintptr_t val3 = 0x222;388SimpleTestLookup stl(val), stl2(val2), stl3(val3);389SimpleTestTable* cht = new SimpleTestTable();390391EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";392EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";393EXPECT_TRUE(cht->insert(thr, stl3, val3)) << "Insert unique value failed.";394EXPECT_FALSE(cht->insert(thr, stl3, val3)) << "Insert duplicate value should have failed.";395EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an inserted value should work.";396EXPECT_TRUE(cht_get_copy(cht, thr, stl2) == val2) << "Getting an inserted value should work.";397EXPECT_TRUE(cht_get_copy(cht, thr, stl3) == val3) << "Getting an inserted value should work.";398399EXPECT_TRUE(cht->remove(thr, stl2)) << "Removing an inserted value should work.";400401EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an inserted value should work.";402EXPECT_FALSE(cht_get_copy(cht, thr, stl2) == val2) << "Getting a removed value should have failed.";403EXPECT_TRUE(cht_get_copy(cht, thr, stl3) == val3) << "Getting an inserted value should work.";404405406EXPECT_TRUE(cht->grow(thr)) << "Growing uncontended should not fail.";407408EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an item after grow failed.";409EXPECT_FALSE(cht_get_copy(cht, thr, stl2) == val2) << "Getting a removed value after grow should have failed.";410EXPECT_TRUE(cht_get_copy(cht, thr, stl3) == val3) << "Getting an item after grow failed.";411412EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";413EXPECT_TRUE(cht->remove(thr, stl3)) << "Removing an inserted value should work.";414415EXPECT_TRUE(cht->shrink(thr)) << "Shrinking uncontended should not fail.";416417EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an item after shrink failed.";418EXPECT_TRUE(cht_get_copy(cht, thr, stl2) == val2) << "Getting an item after shrink failed.";419EXPECT_FALSE(cht_get_copy(cht, thr, stl3) == val3) << "Getting a removed value after shrink should have failed.";420421delete cht;422}423424static void cht_task_grow(Thread* thr) {425uintptr_t val = 0x2;426uintptr_t val2 = 0x22;427uintptr_t val3 = 0x222;428SimpleTestLookup stl(val), stl2(val2), stl3(val3);429SimpleTestTable* cht = new SimpleTestTable();430431EXPECT_TRUE(cht->insert(thr, stl, val)) << "Insert unique value failed.";432EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";433EXPECT_TRUE(cht->insert(thr, stl3, val3)) << "Insert unique value failed.";434EXPECT_FALSE(cht->insert(thr, stl3, val3)) << "Insert duplicate value should have failed.";435EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an inserted value should work.";436EXPECT_TRUE(cht_get_copy(cht, thr, stl2) == val2) << "Getting an inserted value should work.";437EXPECT_TRUE(cht_get_copy(cht, thr, stl3) == val3) << "Getting an inserted value should work.";438439EXPECT_TRUE(cht->remove(thr, stl2)) << "Removing an inserted value should work.";440441EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an inserted value should work.";442EXPECT_FALSE(cht_get_copy(cht, thr, stl2) == val2) << "Getting a removed value should have failed.";443EXPECT_TRUE(cht_get_copy(cht, thr, stl3) == val3) << "Getting an inserted value should work.";444445SimpleTestTable::GrowTask gt(cht);446EXPECT_TRUE(gt.prepare(thr)) << "Growing uncontended should not fail.";447while(gt.do_task(thr)) { /* grow */ }448gt.done(thr);449450EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an item after grow failed.";451EXPECT_FALSE(cht_get_copy(cht, thr, stl2) == val2) << "Getting a removed value after grow should have failed.";452EXPECT_TRUE(cht_get_copy(cht, thr, stl3) == val3) << "Getting an item after grow failed.";453454EXPECT_TRUE(cht->insert(thr, stl2, val2)) << "Insert unique value failed.";455EXPECT_TRUE(cht->remove(thr, stl3)) << "Removing an inserted value should work.";456457EXPECT_TRUE(cht->shrink(thr)) << "Shrinking uncontended should not fail.";458459EXPECT_TRUE(cht_get_copy(cht, thr, stl) == val) << "Getting an item after shrink failed.";460EXPECT_TRUE(cht_get_copy(cht, thr, stl2) == val2) << "Getting an item after shrink failed.";461EXPECT_FALSE(cht_get_copy(cht, thr, stl3) == val3) << "Getting a removed value after shrink should have failed.";462463delete cht;464}465466TEST_VM(ConcurrentHashTable, basic_insert) {467nomt_test_doer(cht_insert);468}469470TEST_VM(ConcurrentHashTable, basic_get_insert) {471nomt_test_doer(cht_get_insert);472}473474TEST_VM(ConcurrentHashTable, basic_insert_get) {475nomt_test_doer(cht_insert_get);476}477478TEST_VM(ConcurrentHashTable, basic_scope) {479nomt_test_doer(cht_scope);480}481482TEST_VM(ConcurrentHashTable, basic_get_insert_bulk_delete) {483nomt_test_doer(cht_getinsert_bulkdelete);484}485486TEST_VM(ConcurrentHashTable, basic_get_insert_bulk_delete_task) {487nomt_test_doer(cht_getinsert_bulkdelete_task);488}489490TEST_VM(ConcurrentHashTable, basic_reset_shrink) {491nomt_test_doer(cht_reset_shrink);492}493494TEST_VM(ConcurrentHashTable, basic_scan) {495nomt_test_doer(cht_scan);496}497498TEST_VM(ConcurrentHashTable, basic_move_to) {499nomt_test_doer(cht_move_to);500}501502TEST_VM(ConcurrentHashTable, basic_grow) {503nomt_test_doer(cht_grow);504}505506TEST_VM(ConcurrentHashTable, task_grow) {507nomt_test_doer(cht_task_grow);508}509510//#############################################################################################511512class TestInterface : public AllStatic {513public:514typedef uintptr_t Value;515static uintx get_hash(const Value& value, bool* dead_hash) {516return (uintx)(value + 18446744073709551557ul) * 18446744073709551557ul;517}518static void* allocate_node(void* context, size_t size, const Value& value) {519return AllocateHeap(size, mtInternal);520}521static void free_node(void* context, void* memory, const Value& value) {522FreeHeap(memory);523}524};525526typedef ConcurrentHashTable<TestInterface, mtInternal> TestTable;527typedef ConcurrentHashTable<TestInterface, mtInternal>::MultiGetHandle TestGetHandle;528529struct TestLookup {530uintptr_t _val;531TestLookup(uintptr_t val) : _val(val) {}532uintx get_hash() {533return TestInterface::get_hash(_val, NULL);534}535bool equals(const uintptr_t* value, bool* is_dead) {536return _val == *value;537}538};539540static uintptr_t cht_get_copy(TestTable* cht, Thread* thr, TestLookup tl) {541ValueGet vg;542cht->get(thr, tl, vg);543return vg.get_value();544}545546class CHTTestThread : public JavaTestThread {547public:548uintptr_t _start;549uintptr_t _stop;550TestTable *_cht;551jlong _stop_ms;552CHTTestThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post)553: JavaTestThread(post), _start(start), _stop(stop), _cht(cht) {}554virtual void premain() {}555void main_run() {556premain();557_stop_ms = os::javaTimeMillis() + 2000; // 2 seconds max test time558while (keep_looping() && test_loop()) { /* */ }559postmain();560}561virtual void postmain() {}562virtual bool keep_looping() {563return _stop_ms > os::javaTimeMillis();564};565virtual bool test_loop() = 0;566virtual ~CHTTestThread() {}567};568569class ValueSaver {570uintptr_t* _vals;571size_t _it;572size_t _size;573public:574ValueSaver() : _it(0), _size(1024) {575_vals = NEW_C_HEAP_ARRAY(uintptr_t, _size, mtInternal);576}577578bool operator()(uintptr_t* val) {579_vals[_it++] = *val;580if (_it == _size) {581_size *= 2;582_vals = REALLOC_RESOURCE_ARRAY(uintptr_t, _vals, _size/2, _size);583}584return true;585}586587void check() {588for (size_t i = 0; i < _it; i++) {589size_t count = 0;590for (size_t j = (i + 1u); j < _it; j++) {591if (_vals[i] == _vals[j]) {592count++;593}594}595EXPECT_EQ(count, 0u);596}597}598};599600static void integrity_check(Thread* thr, TestTable* cht)601{602ValueSaver vs;603cht->do_scan(thr, vs);604vs.check();605}606607//#############################################################################################608// All threads are working on different items609// This item should only be delete by this thread610// Thus get_unsafe is safe for this test.611612class SimpleInserterThread : public CHTTestThread {613public:614static volatile bool _exit;615616SimpleInserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post)617: CHTTestThread(start, stop, cht, post) {};618virtual ~SimpleInserterThread(){}619620bool keep_looping() {621return !_exit;622}623624bool test_loop() {625bool grow;626for (uintptr_t v = _start; v <= _stop; v++) {627TestLookup tl(v);628EXPECT_TRUE(_cht->insert(this, tl, v, &grow)) << "Inserting an unique value should work.";629}630for (uintptr_t v = _start; v <= _stop; v++) {631TestLookup tl(v);632EXPECT_TRUE(cht_get_copy(_cht, this, tl) == v) << "Getting an previously inserted value unsafe failed.";633}634for (uintptr_t v = _start; v <= _stop; v++) {635TestLookup tl(v);636EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";637}638for (uintptr_t v = _start; v <= _stop; v++) {639TestLookup tl(v);640EXPECT_TRUE(cht_get_copy(_cht, this, tl) == 0) << "Got a removed value.";641}642return true;643}644};645646volatile bool SimpleInserterThread::_exit = false;647648class RunnerSimpleInserterThread : public CHTTestThread {649public:650Semaphore _done;651652RunnerSimpleInserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {653_cht = new TestTable(SIZE_32, SIZE_32);654};655virtual ~RunnerSimpleInserterThread(){}656657void premain() {658659SimpleInserterThread* ins1 = new SimpleInserterThread((uintptr_t)0x100, (uintptr_t) 0x1FF, _cht, &_done);660SimpleInserterThread* ins2 = new SimpleInserterThread((uintptr_t)0x200, (uintptr_t) 0x2FF, _cht, &_done);661SimpleInserterThread* ins3 = new SimpleInserterThread((uintptr_t)0x300, (uintptr_t) 0x3FF, _cht, &_done);662SimpleInserterThread* ins4 = new SimpleInserterThread((uintptr_t)0x400, (uintptr_t) 0x4FF, _cht, &_done);663664for (uintptr_t v = 0x500; v < 0x5FF; v++ ) {665TestLookup tl(v);666EXPECT_TRUE(_cht->insert(this, tl, v)) << "Inserting an unique value should work.";667}668669ins1->doit();670ins2->doit();671ins3->doit();672ins4->doit();673674}675676bool test_loop() {677for (uintptr_t v = 0x500; v < 0x5FF; v++ ) {678TestLookup tl(v);679EXPECT_TRUE(cht_get_copy(_cht, this, tl) == v) << "Getting an previously inserted value unsafe failed.";;680}681return true;682}683684void postmain() {685SimpleInserterThread::_exit = true;686for (int i = 0; i < 4; i++) {687_done.wait();688}689for (uintptr_t v = 0x500; v < 0x5FF; v++ ) {690TestLookup tl(v);691EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";692}693integrity_check(this, _cht);694delete _cht;695}696};697698699TEST_VM(ConcurrentHashTable, concurrent_simple) {700SimpleInserterThread::_exit = false;701mt_test_doer<RunnerSimpleInserterThread>();702}703704//#############################################################################################705// In this test we try to get a 'bad' value706class DeleteInserterThread : public CHTTestThread {707public:708static volatile bool _exit;709710DeleteInserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post) : CHTTestThread(start, stop, cht, post) {};711virtual ~DeleteInserterThread(){}712713bool keep_looping() {714return !_exit;715}716717bool test_loop() {718for (uintptr_t v = _start; v <= _stop; v++) {719TestLookup tl(v);720_cht->insert(this, tl, v);721}722for (uintptr_t v = _start; v <= _stop; v++) {723TestLookup tl(v);724_cht->remove(this, tl);725}726return true;727}728};729730volatile bool DeleteInserterThread::_exit = true;731732class RunnerDeleteInserterThread : public CHTTestThread {733public:734Semaphore _done;735736RunnerDeleteInserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {737_cht = new TestTable(SIZE_32, SIZE_32);738};739virtual ~RunnerDeleteInserterThread(){}740741void premain() {742DeleteInserterThread* ins1 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);743DeleteInserterThread* ins2 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);744DeleteInserterThread* ins3 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);745DeleteInserterThread* ins4 = new DeleteInserterThread((uintptr_t)0x1, (uintptr_t) 0xFFF, _cht, &_done);746747ins1->doit();748ins2->doit();749ins3->doit();750ins4->doit();751}752753bool test_loop() {754for (uintptr_t v = 0x1; v < 0xFFF; v++ ) {755uintptr_t tv;756if (v & 0x1) {757TestLookup tl(v);758tv = cht_get_copy(_cht, this, tl);759} else {760TestLookup tl(v);761TestGetHandle value_handle(this, _cht);762uintptr_t* tmp = value_handle.get(tl);763tv = tmp != NULL ? *tmp : 0;764}765EXPECT_TRUE(tv == 0 || tv == v) << "Got unknown value.";766}767return true;768}769770void postmain() {771DeleteInserterThread::_exit = true;772for (int i = 0; i < 4; i++) {773_done.wait();774}775integrity_check(this, _cht);776delete _cht;777}778};779780TEST_VM(ConcurrentHashTable, concurrent_deletes) {781DeleteInserterThread::_exit = false;782mt_test_doer<RunnerDeleteInserterThread>();783}784785//#############################################################################################786787#define START_SIZE 13788#define END_SIZE 17789#define START (uintptr_t)0x10000790#define RANGE (uintptr_t)0xFFFF791792#define GSTEST_THREAD_COUNT 5793794795class GSInserterThread: public CHTTestThread {796public:797static volatile bool _shrink;798GSInserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post) : CHTTestThread(start, stop, cht, post) {};799virtual ~GSInserterThread(){}800bool keep_looping() {801return !(_shrink && _cht->get_size_log2(this) == START_SIZE);802}803bool test_loop() {804bool grow;805for (uintptr_t v = _start; v <= _stop; v++) {806TestLookup tl(v);807EXPECT_TRUE(_cht->insert(this, tl, v, &grow)) << "Inserting an unique value should work.";808if (grow && !_shrink) {809_cht->grow(this);810}811}812for (uintptr_t v = _start; v <= _stop; v++) {813TestLookup tl(v);814EXPECT_TRUE(cht_get_copy(_cht, this, tl) == v) << "Getting an previously inserted value unsafe failed.";815}816for (uintptr_t v = _start; v <= _stop; v++) {817TestLookup tl(v);818EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";819}820if (_shrink) {821_cht->shrink(this);822}823for (uintptr_t v = _start; v <= _stop; v++) {824TestLookup tl(v);825EXPECT_FALSE(cht_get_copy(_cht, this, tl) == v) << "Getting a removed value should have failed.";826}827if (!_shrink && _cht->get_size_log2(this) == END_SIZE) {828_shrink = true;829}830return true;831}832};833834volatile bool GSInserterThread::_shrink = false;835836class GSScannerThread : public CHTTestThread {837public:838GSScannerThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post) : CHTTestThread(start, stop, cht, post) {};839virtual ~GSScannerThread(){}840841bool operator()(uintptr_t* val) {842if (*val >= this->_start && *val <= this->_stop) {843return false;844}845// continue scan846return true;847}848849bool test_loop() {850_cht->try_scan(this, *this);851os::naked_short_sleep(5);852return true;853}854};855856class RunnerGSInserterThread : public CHTTestThread {857public:858uintptr_t _start;859uintptr_t _range;860Semaphore _done;861862RunnerGSInserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {863_cht = new TestTable(START_SIZE, END_SIZE, 2);864};865virtual ~RunnerGSInserterThread(){}866867void premain() {868volatile bool timeout = false;869_start = START;870_range = RANGE;871CHTTestThread* tt[GSTEST_THREAD_COUNT];872tt[0] = new GSInserterThread(_start, _start + _range, _cht, &_done);873_start += _range + 1;874tt[1] = new GSInserterThread(_start, _start + _range, _cht, &_done);875_start += _range + 1;876tt[2] = new GSInserterThread(_start, _start + _range, _cht, &_done);877_start += _range + 1;878tt[3] = new GSInserterThread(_start, _start + _range, _cht, &_done);879tt[4] = new GSScannerThread(_start, _start + _range, _cht, &_done);880_start += _range + 1;881882883for (uintptr_t v = _start; v <= (_start + _range); v++ ) {884TestLookup tl(v);885EXPECT_TRUE(_cht->insert(this, tl, v)) << "Inserting an unique value should work.";886}887888for (int i = 0; i < GSTEST_THREAD_COUNT; i++) {889tt[i]->doit();890}891}892893bool test_loop() {894for (uintptr_t v = _start; v <= (_start + _range); v++ ) {895TestLookup tl(v);896EXPECT_TRUE(cht_get_copy(_cht, this, tl) == v) << "Getting an previously inserted value unsafe failed.";897}898return true;899}900901void postmain() {902GSInserterThread::_shrink = true;903for (uintptr_t v = _start; v <= (_start + _range); v++ ) {904TestLookup tl(v);905EXPECT_TRUE(_cht->remove(this, tl)) << "Removing an existing value failed.";906}907for (int i = 0; i < GSTEST_THREAD_COUNT; i++) {908_done.wait();909}910EXPECT_TRUE(_cht->get_size_log2(this) == START_SIZE) << "Not at start size.";911Count cnt;912_cht->do_scan(this, cnt);913EXPECT_TRUE(cnt._cnt == 0) << "Items still in table";914delete _cht;915}916917struct Count {918Count() : _cnt(0) {}919size_t _cnt;920bool operator()(uintptr_t*) { _cnt++; return true; };921};922};923924TEST_VM(ConcurrentHashTable, concurrent_scan_grow_shrink) {925GSInserterThread::_shrink = false;926mt_test_doer<RunnerGSInserterThread>();927}928929930//#############################################################################################931932#define GI_BD_GI_BD_START_SIZE 13933#define GI_BD_END_SIZE 17934#define GI_BD_START (uintptr_t)0x1935#define GI_BD_RANGE (uintptr_t)0x3FFFF936937#define GI_BD_TEST_THREAD_COUNT 4938939940class GI_BD_InserterThread: public CHTTestThread {941public:942static volatile bool _shrink;943uintptr_t _br;944GI_BD_InserterThread(uintptr_t start, uintptr_t stop, TestTable* cht, Semaphore* post, uintptr_t br)945: CHTTestThread(start, stop, cht, post), _br(br) {};946virtual ~GI_BD_InserterThread(){}947948bool keep_looping() {949return !(_shrink && _cht->get_size_log2(this) == GI_BD_GI_BD_START_SIZE);950}951952bool test_loop() {953bool grow;954MyDel del(_br);955for (uintptr_t v = _start; v <= _stop; v++) {956{957TestLookup tl(v);958ValueGet vg;959do {960if (_cht->get(this, tl, vg, &grow)) {961EXPECT_EQ(v, vg.get_value()) << "Getting an old value failed.";962break;963}964if (_cht->insert(this, tl, v, &grow)) {965break;966}967} while(true);968}969if (grow && !_shrink) {970_cht->grow(this);971}972}973if (_shrink) {974_cht->shrink(this);975}976_cht->try_bulk_delete(this, *this, del);977if (!_shrink && _cht->is_max_size_reached()) {978_shrink = true;979}980_cht->bulk_delete(this, *this, del);981return true;982}983984bool operator()(uintptr_t* val) {985return (*val & _br) == 1;986}987988struct MyDel {989MyDel(uintptr_t &br) : _br(br) {};990uintptr_t &_br;991void operator()(uintptr_t* val) {992EXPECT_EQ((*val & _br), _br) << "Removing an item that should not have been removed.";993}994};995};996997volatile bool GI_BD_InserterThread::_shrink = false;998999class RunnerGI_BD_InserterThread : public CHTTestThread {1000public:1001Semaphore _done;1002uintptr_t _start;1003uintptr_t _range;1004RunnerGI_BD_InserterThread(Semaphore* post) : CHTTestThread(0, 0, NULL, post) {1005_cht = new TestTable(GI_BD_GI_BD_START_SIZE, GI_BD_END_SIZE, 2);1006};1007virtual ~RunnerGI_BD_InserterThread(){}10081009void premain() {1010_start = GI_BD_START;1011_range = GI_BD_RANGE;1012CHTTestThread* tt[GI_BD_TEST_THREAD_COUNT];1013tt[0] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x1);1014tt[1] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x2);1015tt[2] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x4);1016tt[3] = new GI_BD_InserterThread(_start, _start + _range, _cht, &_done, (uintptr_t)0x8);10171018for (uintptr_t v = _start; v <= (_start + _range); v++ ) {1019TestLookup tl(v);1020EXPECT_TRUE(_cht->insert(this, tl, v)) << "Inserting an unique value should work.";1021}10221023for (int i =0; i < GI_BD_TEST_THREAD_COUNT; i++) {1024tt[i]->doit();1025}1026}10271028bool test_loop() {1029for (uintptr_t v = _start; v <= (_start + _range); v++ ) {1030TestLookup tl(v);1031if (v & 0xF) {1032cht_get_copy(_cht, this, tl);1033} else {1034EXPECT_EQ(cht_get_copy(_cht, this, tl), v) << "Item ending with 0xX0 should never be removed.";1035}1036}1037return true;1038}10391040void postmain() {1041GI_BD_InserterThread::_shrink = true;1042for (uintptr_t v = _start; v <= (_start + _range); v++ ) {1043TestLookup tl(v);1044if (v & 0xF) {1045_cht->remove(this, tl);1046} else {1047EXPECT_TRUE(_cht->remove(this, tl)) << "Removing item ending with 0xX0 should always work.";1048}1049}1050for (int i = 0; i < GI_BD_TEST_THREAD_COUNT; i++) {1051_done.wait();1052}1053EXPECT_TRUE(_cht->get_size_log2(this) == GI_BD_GI_BD_START_SIZE) << "We have not shrunk back to start size.";1054delete _cht;1055}1056};10571058TEST_VM(ConcurrentHashTable, concurrent_get_insert_bulk_delete) {1059GI_BD_InserterThread::_shrink = false;1060mt_test_doer<RunnerGI_BD_InserterThread>();1061}10621063//#############################################################################################10641065class MT_BD_Thread : public JavaTestThread {1066TestTable::BulkDeleteTask* _bd;1067public:1068MT_BD_Thread(Semaphore* post, TestTable::BulkDeleteTask* bd)1069: JavaTestThread(post), _bd(bd){}1070virtual ~MT_BD_Thread() {}1071void main_run() {1072MyDel del;1073while(_bd->do_task(this, *this, del));1074}10751076bool operator()(uintptr_t* val) {1077return true;1078}10791080struct MyDel {1081void operator()(uintptr_t* val) {1082}1083};1084};10851086class Driver_BD_Thread : public JavaTestThread {1087public:1088Semaphore _done;1089Driver_BD_Thread(Semaphore* post) : JavaTestThread(post) {1090};1091virtual ~Driver_BD_Thread(){}10921093void main_run() {1094Semaphore done(0);1095TestTable* cht = new TestTable(16, 16, 2);1096for (uintptr_t v = 1; v < 99999; v++ ) {1097TestLookup tl(v);1098EXPECT_TRUE(cht->insert(this, tl, v)) << "Inserting an unique value should work.";1099}1100TestTable::BulkDeleteTask bdt(cht, true /* mt */ );1101EXPECT_TRUE(bdt.prepare(this)) << "Uncontended prepare must work.";11021103MT_BD_Thread* tt[4];1104for (int i = 0; i < 4; i++) {1105tt[i] = new MT_BD_Thread(&done, &bdt);1106tt[i]->doit();1107}11081109for (uintptr_t v = 1; v < 99999; v++ ) {1110TestLookup tl(v);1111cht_get_copy(cht, this, tl);1112}11131114for (int i = 0; i < 4; i++) {1115done.wait();1116}11171118bdt.done(this);11191120cht->do_scan(this, *this);1121}11221123bool operator()(uintptr_t* val) {1124EXPECT_TRUE(false) << "No items should left";1125return true;1126}1127};11281129TEST_VM(ConcurrentHashTable, concurrent_mt_bulk_delete) {1130mt_test_doer<Driver_BD_Thread>();1131}113211331134