[CP] [CP] [CP] [CP] fix link hashmap core
This commit is contained in:
86
deps/oblib/src/lib/hash/ob_link_hashmap.h
vendored
86
deps/oblib/src/lib/hash/ob_link_hashmap.h
vendored
@ -30,6 +30,12 @@ inline int32_t faa_if_positive(int32_t* addr, int32_t x)
|
||||
return ov;
|
||||
}
|
||||
|
||||
inline static ObQSync &get_global_link_hashmap_qsync()
|
||||
{
|
||||
static ObQSync qsync;
|
||||
return qsync;
|
||||
}
|
||||
|
||||
// DO NOT use me
|
||||
class BaseRefHandle {
|
||||
public:
|
||||
@ -65,42 +71,6 @@ protected:
|
||||
uint64_t qc_slot_;
|
||||
};
|
||||
|
||||
// fast read, slow del, delay reclaim Value/Node
|
||||
class TCRefHandle final : public BaseRefHandle {
|
||||
public:
|
||||
typedef RefNode Node;
|
||||
explicit TCRefHandle(RetireStation& retire_station) : BaseRefHandle(retire_station)
|
||||
{}
|
||||
void born(Node* node)
|
||||
{
|
||||
UNUSED(get_tcref().born(&node->uref_));
|
||||
}
|
||||
int32_t end(Node* node)
|
||||
{
|
||||
UNUSED(get_tcref().end(&node->uref_));
|
||||
return get_tcref().sync(&node->uref_);
|
||||
}
|
||||
bool inc(Node* node)
|
||||
{
|
||||
int32_t ref = 0;
|
||||
if (TCRef::REF_LIMIT == (ref = get_tcref().inc_ref(&node->uref_))) {
|
||||
ref = ATOMIC_LOAD(&node->uref_);
|
||||
}
|
||||
return ref > TCRef::REF_LIMIT / 2;
|
||||
}
|
||||
int32_t dec(Node* node)
|
||||
{
|
||||
return get_tcref().dec_ref(&node->uref_);
|
||||
}
|
||||
|
||||
private:
|
||||
static TCRef& get_tcref()
|
||||
{
|
||||
static TCRef tcref;
|
||||
return tcref;
|
||||
}
|
||||
};
|
||||
|
||||
// balanced read/del performance, realtime reclaim Value, batch/delay reclaim Node.
|
||||
class RefHandle final : public BaseRefHandle {
|
||||
public:
|
||||
@ -125,45 +95,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class DummyRefHandle final : public BaseRefHandle {
|
||||
public:
|
||||
typedef RefNode Node;
|
||||
enum { BORN_REF = 1 };
|
||||
explicit DummyRefHandle(RetireStation& retire_station) : BaseRefHandle(retire_station)
|
||||
{}
|
||||
void enter_critical() override
|
||||
{}
|
||||
void leave_critical() override
|
||||
{}
|
||||
void retire(Node* node, HazardList& reclaim_list) override
|
||||
{
|
||||
reclaim_list.push(&node->retire_link_);
|
||||
}
|
||||
void purge(HazardList& reclaim_list) override
|
||||
{
|
||||
UNUSED(reclaim_list);
|
||||
}
|
||||
void born(Node* node)
|
||||
{
|
||||
UNUSED(node);
|
||||
}
|
||||
int32_t end(Node* node)
|
||||
{
|
||||
UNUSED(node);
|
||||
return 0;
|
||||
}
|
||||
bool inc(Node* node)
|
||||
{
|
||||
UNUSED(node);
|
||||
return true;
|
||||
}
|
||||
int32_t dec(Node* node)
|
||||
{
|
||||
UNUSED(node);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Key, typename Value, typename AllocHandle = AllocHandle<Key, Value>, typename RefHandle = RefHandle,
|
||||
int64_t SHRINK_THRESHOLD = 8>
|
||||
class ObLinkHashMap {
|
||||
@ -264,9 +195,13 @@ public:
|
||||
void purge()
|
||||
{
|
||||
HazardList reclaim_list;
|
||||
{
|
||||
CriticalGuard(get_global_link_hashmap_qsync());
|
||||
get_retire_station().purge(reclaim_list);
|
||||
reclaim_nodes(reclaim_list);
|
||||
}
|
||||
WaitQuiescent(get_global_link_hashmap_qsync());
|
||||
}
|
||||
int64_t count() const
|
||||
{
|
||||
return hash_.count();
|
||||
@ -344,6 +279,7 @@ public:
|
||||
HazardList reclaim_list;
|
||||
HashNode* node = CONTAINER_OF(hash_link, HashNode, hash_link_);
|
||||
end_uref(node);
|
||||
CriticalGuard(get_global_link_hashmap_qsync());
|
||||
ref_handle_.retire(node, reclaim_list);
|
||||
reclaim_nodes(reclaim_list);
|
||||
count_handle_.add(-1);
|
||||
|
||||
@ -66,12 +66,21 @@ static uint64_t node_free CACHE_ALIGNED;
|
||||
|
||||
ObMemAttr attr(1001, ObNewModIds::OB_MEMSTORE);
|
||||
|
||||
static int64_t STEP = 0;
|
||||
|
||||
class TestAllocHandle {
|
||||
typedef LinkHashNode<HashKey> Node;
|
||||
|
||||
public:
|
||||
HashValue* alloc_value()
|
||||
TestAllocHandle() : is_inited_(true)
|
||||
{}
|
||||
~TestAllocHandle()
|
||||
{
|
||||
ATOMIC_STORE(&is_inited_, false);
|
||||
}
|
||||
HashValue *alloc_value()
|
||||
{
|
||||
abort_unless(ATOMIC_LOAD(&is_inited_) == true);
|
||||
ATOMIC_INC(&value_alloc);
|
||||
HashValue* value = (HashValue*)ob_malloc(sizeof(HashValue), attr);
|
||||
new (value) HashValue();
|
||||
@ -79,12 +88,14 @@ public:
|
||||
}
|
||||
void free_value(HashValue* val)
|
||||
{
|
||||
abort_unless(ATOMIC_LOAD(&is_inited_) == true);
|
||||
ATOMIC_INC(&value_free);
|
||||
val->~HashValue();
|
||||
ob_free(val);
|
||||
}
|
||||
Node* alloc_node(HashValue* val)
|
||||
{
|
||||
abort_unless(ATOMIC_LOAD(&is_inited_) == true);
|
||||
UNUSED(val);
|
||||
ATOMIC_INC(&node_alloc);
|
||||
Node* node = (Node*)ob_malloc(sizeof(Node), attr);
|
||||
@ -93,10 +104,18 @@ public:
|
||||
}
|
||||
void free_node(Node* node)
|
||||
{
|
||||
if (ATOMIC_LOAD(&STEP) == 1) {
|
||||
IGNORE_RETURN ATOMIC_BCAS(&STEP, 1, 2);
|
||||
usleep(1 * 1000 * 1000);
|
||||
}
|
||||
abort_unless(ATOMIC_LOAD(&is_inited_) == true);
|
||||
ATOMIC_INC(&node_free);
|
||||
node->~Node();
|
||||
ob_free(node);
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_inited_;
|
||||
};
|
||||
|
||||
class AtomicGetFunctor {
|
||||
@ -118,9 +137,7 @@ static bool print(HashKey& key, HashValue* value)
|
||||
return true;
|
||||
}
|
||||
|
||||
// typedef ObLinkHashMap<HashKey, HashValue, TestAllocHandle> Hashmap;
|
||||
typedef ObLinkHashMap<HashKey, HashValue, TestAllocHandle, TCRefHandle> Hashmap;
|
||||
// typedef ObLinkHashMapWithHazardValue<HashKey, HashValue, TestAllocHandle> Hashmap;
|
||||
typedef ObLinkHashMap<HashKey, HashValue, TestAllocHandle> Hashmap;
|
||||
|
||||
TEST(TestObHashMap, Feature)
|
||||
{
|
||||
@ -326,7 +343,37 @@ TEST(TestObHashMap, Stress)
|
||||
EXPECT_EQ(node_free, node_alloc);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
TEST(TestObHashMap, Retire)
|
||||
{
|
||||
std::thread *t;
|
||||
{
|
||||
Hashmap A;
|
||||
HashKey key;
|
||||
HashValue *val_ptr = nullptr;
|
||||
EXPECT_EQ(OB_SUCCESS, A.init());
|
||||
key.v_ = 1;
|
||||
EXPECT_EQ(OB_SUCCESS, A.create(key, val_ptr));
|
||||
A.revert(val_ptr);
|
||||
EXPECT_EQ(OB_SUCCESS, A.del(key));
|
||||
ATOMIC_INC(&STEP);
|
||||
t = new std::thread([&]() {
|
||||
usleep(10 * 1000);
|
||||
Hashmap B;
|
||||
HashKey key;
|
||||
EXPECT_EQ(OB_SUCCESS, B.init());
|
||||
key.v_ = 1;
|
||||
EXPECT_EQ(OB_SUCCESS, B.create(key, val_ptr));
|
||||
B.revert(val_ptr);
|
||||
});
|
||||
while (ATOMIC_LOAD(&STEP) != 2)
|
||||
;
|
||||
// ~A()
|
||||
}
|
||||
t->join();
|
||||
delete t;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
oceanbase::common::ObLogger::get_logger().set_file_name("test_link_hashmap.log", true);
|
||||
|
||||
Reference in New Issue
Block a user