* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ECMASCRIPT_STRING_TABLE_HASHTRIEMAP_INL_H
#define ECMASCRIPT_STRING_TABLE_HASHTRIEMAP_INL_H
#include "common_components/log/log.h"
#include "objects/readonly_handle.h"
#include "ecmascript/string/base_string.h"
#include "ecmascript/string/line_string-inl.h"
#include "hashtriemap.h"
#include "ecmascript/string/integer_cache.h"
namespace panda::ecmascript {
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <bool IsLock>
HashTrieMapNode* HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::Expand(
Entry* oldEntry, Entry* newEntry, uint32_t oldHash, uint32_t newHash, uint32_t hashShift, Indirect* parent)
{
if (oldHash == newHash) {
newEntry->Overflow().store(oldEntry, std::memory_order_release);
return newEntry;
}
Indirect* newIndirect = new Indirect();
Indirect* top = newIndirect;
while (true) {
#ifndef NDEBUG
if (hashShift == TrieMapConfig::TOTAL_HASH_BITS) {
if constexpr (IsLock) {
hashTrieMap_->GetMutex().Unlock();
}
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while inserting";
UNREACHABLE_CC();
}
#endif
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
uint32_t oldIdx = (oldHash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
uint32_t newIdx = (newHash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
if (oldIdx != newIdx) {
newIndirect->GetChild(oldIdx).store(oldEntry, std::memory_order_release);
newIndirect->GetChild(newIdx).store(newEntry, std::memory_order_release);
break;
}
Indirect* nextIndirect = new Indirect();
newIndirect->GetChild(oldIdx).store(nextIndirect, std::memory_order_release);
newIndirect = nextIndirect;
}
return top;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <bool IsCheck, typename ReadBarrier>
BaseString* HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::Load(ReadBarrier&& readBarrier, const uint32_t key,
BaseString* value)
{
uint32_t hash = key;
Indirect* current = GetRootAndProcessHash(hash);
for (uint32_t hashShift = 0; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift +=
TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
std::atomic<Node*>* slot = ¤t->GetChild(index);
Node* node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
return nullptr;
}
if (!node->IsEntry()) {
current = node->AsIndirect();
continue;
}
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
bool valuesEqual = false;
if (!IsNull(oldValue) && BaseString::StringsAreEqual(std::forward<ReadBarrier>(readBarrier), oldValue,
value)) {
valuesEqual = true;
}
if constexpr (IsCheck) {
if (oldValue->GetMixHashcode() != value->GetMixHashcode()) {
continue;
}
if (!valuesEqual) {
return oldValue;
}
} else if (valuesEqual) {
return oldValue;
}
}
return nullptr;
}
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <bool IsLock, typename LoaderCallback, typename EqualsCallback>
BaseString* HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::LoadOrStore(ThreadHolder* holder,
const uint32_t key,
LoaderCallback loaderCallback,
EqualsCallback equalsCallback)
{
uint32_t hash = key;
uint32_t hashShift = 0;
std::atomic<Node*>* slot = nullptr;
Node* node = nullptr;
[[maybe_unused]] bool haveInsertPoint = false;
common::ReadOnlyHandle<BaseString> str;
bool isStrCreated = false;
Indirect* current = GetRootAndProcessHash(hash);
while (true) {
haveInsertPoint = false;
for (; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift += TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
slot = ¤t->GetChild(index);
node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
haveInsertPoint = true;
break;
}
if (!node->IsEntry()) {
current = node->AsIndirect();
continue;
}
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (std::invoke(std::forward<EqualsCallback>(equalsCallback), oldValue)) {
#if ECMASCRIPT_ENABLE_TRACE_STRING_TABLE
TraceFindSuccessDepth(hashShift);
#endif
return oldValue;
}
}
haveInsertPoint = true;
break;
}
#ifndef NDEBUG
if (!haveInsertPoint) {
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
#endif
if (!isStrCreated) {
str = std::invoke(std::forward<LoaderCallback>(loaderCallback));
isStrCreated = true;
}
if constexpr (IsLock) {
hashTrieMap_->GetMutex().LockWithThreadState(holder);
}
DCHECK_CC(slot != nullptr);
node = slot->load(std::memory_order_acquire);
if (node == nullptr || node->IsEntry()) {
break;
}
if constexpr (IsLock) {
hashTrieMap_->GetMutex().Unlock();
}
current = node->AsIndirect();
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
}
#if ECMASCRIPT_ENABLE_TRACE_STRING_TABLE
TraceFindFail();
#endif
Entry* oldEntry = nullptr;
uint32_t oldHash = key;
if (node != nullptr) {
oldEntry = node->AsEntry();
for (Entry* currentEntry = oldEntry; currentEntry;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (oldValue->GetMixHashcode() != key) {
oldHash = oldValue->GetMixHashcode();
continue;
}
if (std::invoke(std::forward<EqualsCallback>(equalsCallback), oldValue)) {
if constexpr (IsLock) {
hashTrieMap_->GetMutex().Unlock();
}
return oldValue;
}
}
}
BaseString* value = *str;
DCHECK_CC(value != nullptr);
value->SetIsInternString();
IntegerCache::InitIntegerCache(value);
Entry* newEntry = NewEntry(value);
oldEntry = PruneHead(oldEntry);
if (oldEntry == nullptr) {
slot->store(newEntry, std::memory_order_release);
} else {
auto expandedNode = Expand<IsLock>(oldEntry, newEntry,
oldHash >> TrieMapConfig::ROOT_BIT, hash, hashShift, current);
slot->store(expandedNode, std::memory_order_release);
}
if constexpr (IsLock) {
hashTrieMap_->GetMutex().Unlock();
}
return value;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <typename LoaderCallback, typename EqualsCallback>
BaseString* HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::LoadOrStoreForJit(ThreadHolder* holder,
const uint32_t key,
LoaderCallback loaderCallback,
EqualsCallback equalsCallback)
{
uint32_t hash = key;
uint32_t hashShift = 0;
std::atomic<Node*>* slot = nullptr;
Node* node = nullptr;
[[maybe_unused]] bool haveInsertPoint = false;
BaseString* value = nullptr;
Indirect* current = GetRootAndProcessHash(hash);
while (true) {
haveInsertPoint = false;
for (; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift += TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
slot = ¤t->GetChild(index);
node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
haveInsertPoint = true;
break;
}
if (!node->IsEntry()) {
current = node->AsIndirect();
continue;
}
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (std::invoke(std::forward<EqualsCallback>(equalsCallback), oldValue)) {
return oldValue;
}
}
haveInsertPoint = true;
break;
}
#ifndef NDEBUG
if (!haveInsertPoint) {
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
#endif
hashTrieMap_->GetMutex().LockWithThreadState(holder);
value = std::invoke(std::forward<LoaderCallback>(loaderCallback));
DCHECK_CC(slot != nullptr);
node = slot->load(std::memory_order_acquire);
if (node == nullptr || node->IsEntry()) {
break;
}
hashTrieMap_->GetMutex().Unlock();
current = node->AsIndirect();
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
}
Entry* oldEntry = nullptr;
uint32_t oldHash = key;
if (node != nullptr) {
oldEntry = node->AsEntry();
for (Entry* currentEntry = oldEntry; currentEntry;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (oldValue->GetMixHashcode() != key) {
oldHash = oldValue->GetMixHashcode();
continue;
}
if (std::invoke(std::forward<EqualsCallback>(equalsCallback), oldValue)) {
hashTrieMap_->GetMutex().Unlock();
return oldValue;
}
}
}
DCHECK_CC(value != nullptr);
value->SetIsInternString();
IntegerCache::InitIntegerCache(value);
Entry* newEntry = NewEntry(value);
oldEntry = PruneHead(oldEntry);
if (oldEntry == nullptr) {
slot->store(newEntry, std::memory_order_release);
} else {
auto expandedNode = Expand<true>(oldEntry, newEntry,
oldHash >> TrieMapConfig::ROOT_BIT, hash, hashShift, current);
slot->store(expandedNode, std::memory_order_release);
}
hashTrieMap_->GetMutex().Unlock();
return value;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <typename LoaderCallback, typename EqualsCallback>
BaseString* HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::StoreOrLoad(ThreadHolder* holder,
const uint32_t key,
HashTrieMapLoadResult loadResult,
LoaderCallback loaderCallback,
EqualsCallback equalsCallback)
{
uint32_t hash = key;
ProcessHash(hash);
uint32_t hashShift = loadResult.hashShift;
std::atomic<Node*>* slot = loadResult.slot;
Node* node = nullptr;
[[maybe_unused]] bool haveInsertPoint = true;
Indirect* current = loadResult.current;
common::ReadOnlyHandle<BaseString> str = std::invoke(std::forward<LoaderCallback>(loaderCallback));
hashTrieMap_->GetMutex().LockWithThreadState(holder);
node = slot->load(std::memory_order_acquire);
if (node != nullptr && !node->IsEntry()) {
hashTrieMap_->GetMutex().Unlock();
current = node->AsIndirect();
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
while (true) {
haveInsertPoint = false;
for (; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift += TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
slot = ¤t->GetChild(index);
node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
haveInsertPoint = true;
break;
}
if (node->IsEntry()) {
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (!IsNull(oldValue) && std::invoke(std::forward<EqualsCallback>(equalsCallback),
oldValue)) {
return oldValue;
}
}
haveInsertPoint = true;
break;
}
current = node->AsIndirect();
}
#ifndef NDEBUG
if (!haveInsertPoint) {
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
#endif
hashTrieMap_->GetMutex().LockWithThreadState(holder);
node = slot->load(std::memory_order_acquire);
if (node == nullptr || node->IsEntry()) {
break;
}
hashTrieMap_->GetMutex().Unlock();
current = node->AsIndirect();
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
}
}
Entry* oldEntry = nullptr;
uint32_t oldHash = key;
if (node != nullptr) {
oldEntry = node->AsEntry();
for (Entry* currentEntry = oldEntry; currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (oldValue->GetMixHashcode() != key) {
oldHash = oldValue->GetMixHashcode();
continue;
}
if (std::invoke(std::forward<EqualsCallback>(equalsCallback), oldValue)) {
hashTrieMap_->GetMutex().Unlock();
return oldValue;
}
}
}
BaseString* value = *str;
DCHECK_CC(value != nullptr);
value->SetIsInternString();
IntegerCache::InitIntegerCache(value);
Entry* newEntry = NewEntry(value);
oldEntry = PruneHead(oldEntry);
if (oldEntry == nullptr) {
slot->store(newEntry, std::memory_order_release);
} else {
auto expandedNode = Expand<true>(oldEntry, newEntry,
oldHash >> TrieMapConfig::ROOT_BIT, hash, hashShift, current);
slot->store(expandedNode, std::memory_order_release);
}
hashTrieMap_->GetMutex().Unlock();
return value;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <typename ReadBarrier>
HashTrieMapLoadResult HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::Load(ReadBarrier&& readBarrier,
const uint32_t key,
BaseString* value)
{
uint32_t hash = key;
Indirect* current = GetRootAndProcessHash(hash);
for (uint32_t hashShift = 0; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift +=
TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
std::atomic<Node*>* slot = ¤t->GetChild(index);
Node* node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
return {nullptr, current, hashShift, slot};
}
if (node->IsEntry()) {
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (BaseString::StringsAreEqual(std::forward<ReadBarrier>(readBarrier), oldValue, value)) {
return {oldValue, nullptr, hashShift, nullptr};
}
}
return {nullptr, current, hashShift, slot};
}
current = node->AsIndirect();
}
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <typename ReadBarrier>
HashTrieMapLoadResult HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::Load(ReadBarrier&& readBarrier,
const uint32_t key, const common::ReadOnlyHandle<BaseString>& string, uint32_t offset, uint32_t utf8Len)
{
uint32_t hash = key;
Indirect* current = GetRootAndProcessHash(hash);
DCHECK_CC(!string.IsEmpty());
const uint8_t* utf8Data = common::ReadOnlyHandle<LineString>::Cast(string)->GetDataUtf8() + offset;
for (uint32_t hashShift = 0; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift +=
TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
std::atomic<Node*>* slot = ¤t->GetChild(index);
Node* node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
return {nullptr, current, hashShift, slot};
}
if (!node->IsEntry()) {
current = node->AsIndirect();
continue;
}
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
auto oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (BaseString::StringIsEqualUint8Data(std::forward<ReadBarrier>(readBarrier), oldValue, utf8Data, utf8Len,
true)) {
return {oldValue, nullptr, hashShift, nullptr};
}
}
return {nullptr, current, hashShift, slot};
}
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <bool threadState, typename ReadBarrier, typename HandleType>
BaseString* HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::StoreOrLoad(ThreadHolder* holder,
ReadBarrier&& readBarrier,
const uint32_t key,
HashTrieMapLoadResult loadResult,
HandleType str)
{
uint32_t hash = key;
ProcessHash(hash);
uint32_t hashShift = loadResult.hashShift;
std::atomic<Node*>* slot = loadResult.slot;
Node* node = nullptr;
[[maybe_unused]] bool haveInsertPoint = true;
Indirect* current = loadResult.current;
if constexpr (threadState) {
hashTrieMap_->GetMutex().LockWithThreadState(holder);
} else {
hashTrieMap_->GetMutex().Lock();
}
node = slot->load(std::memory_order_acquire);
if (node != nullptr && !node->IsEntry()) {
hashTrieMap_->GetMutex().Unlock();
current = node->AsIndirect();
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
while (true) {
haveInsertPoint = false;
for (; hashShift < TrieMapConfig::TOTAL_HASH_BITS; hashShift += TrieMapConfig::N_CHILDREN_LOG2) {
size_t index = (hash >> hashShift) & TrieMapConfig::N_CHILDREN_MASK;
slot = ¤t->GetChild(index);
node = slot->load(std::memory_order_acquire);
if (node == nullptr) {
haveInsertPoint = true;
break;
}
if (!node->IsEntry()) {
current = node->AsIndirect();
continue;
}
for (Entry* currentEntry = node->AsEntry(); currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
BaseString* oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (BaseString::StringsAreEqual(std::forward<ReadBarrier>(readBarrier), oldValue, *str)) {
return oldValue;
}
}
haveInsertPoint = true;
break;
}
#ifndef NDEBUG
if (!haveInsertPoint) {
LOG_COMMON(FATAL) << "StringTable: ran out of hash bits while iterating";
UNREACHABLE_CC();
}
#endif
if constexpr (threadState) {
hashTrieMap_->GetMutex().LockWithThreadState(holder);
} else {
hashTrieMap_->GetMutex().Lock();
}
node = slot->load(std::memory_order_acquire);
if (node == nullptr || node->IsEntry()) {
break;
}
hashTrieMap_->GetMutex().Unlock();
current = node->AsIndirect();
hashShift += TrieMapConfig::N_CHILDREN_LOG2;
}
}
Entry* oldEntry = nullptr;
uint32_t oldHash = key;
if (node != nullptr) {
oldEntry = node->AsEntry();
for (Entry* currentEntry = oldEntry; currentEntry != nullptr;
currentEntry = currentEntry->Overflow().load(std::memory_order_acquire)) {
BaseString* oldValue = GetValue(currentEntry);
if (IsNull(oldValue)) {
continue;
}
if (oldValue->GetMixHashcode() != key) {
oldHash = oldValue->GetMixHashcode();
continue;
}
if (BaseString::StringsAreEqual(std::forward<ReadBarrier>(readBarrier), oldValue, *str)) {
hashTrieMap_->GetMutex().Unlock();
return oldValue;
}
}
}
BaseString* value = *str;
DCHECK_CC(value != nullptr);
value->SetIsInternString();
IntegerCache::InitIntegerCache(value);
Entry* newEntry = NewEntry(value);
oldEntry = PruneHead(oldEntry);
if (oldEntry == nullptr) {
slot->store(newEntry, std::memory_order_release);
} else {
auto expandedNode = Expand<true>(oldEntry, newEntry,
oldHash >> TrieMapConfig::ROOT_BIT, hash, hashShift, current);
slot->store(expandedNode, std::memory_order_release);
}
hashTrieMap_->GetMutex().Unlock();
return value;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <TrieMapConfig::SlotBarrier Barrier, std::enable_if_t<Barrier != TrieMapConfig::NeedSlotBarrierCMC, int>>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::CheckWeakRef(const WeakRootVisitor& visitor, Entry* entry)
{
if (entry->IsToSpaceObject()) {
return false;
}
panda::ecmascript::TaggedObject* object = reinterpret_cast<panda::ecmascript::TaggedObject*>(entry->Value<
TrieMapConfig::NoSlotBarrier>());
auto fwd = visitor(object);
if (fwd == nullptr) {
if constexpr (SlotBarrier == TrieMapConfig::NeedSlotBarrier) {
entry->SetValue(nullptr);
}
LOG_COMMON(VERBOSE) << "StringTable: delete string " << std::hex << object;
return true;
} else if (fwd != object) {
entry->SetValue(reinterpret_cast<BaseString*>(fwd));
LOG_COMMON(VERBOSE) << "StringTable: forward " << std::hex << object << " -> " << fwd;
}
return false;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <typename ReadBarrier>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::CheckValidity(ReadBarrier&& readBarrier,
BaseString* value,
bool& isValid)
{
if (value->IsTreeString()) {
isValid = false;
return false;
}
uint32_t hashcode = value->GetHashcode(std::forward<ReadBarrier>(readBarrier));
if (this->template Load<true>(std::forward<ReadBarrier>(readBarrier), hashcode, value) != nullptr) {
isValid = false;
return false;
}
return true;
}
template<typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::Iter(Indirect *node, std::function<bool(Node *)> &iter)
{
if (node == nullptr) {
return true;
}
if (!iter(node)) {
return false;
}
for (std::atomic<uint64_t> &temp: node->children_) {
auto &child = reinterpret_cast<std::atomic<HashTrieMapNode *> &>(temp);
Node *childNode = child.load(std::memory_order_acquire);
if (childNode == nullptr)
continue;
if (!(childNode->IsEntry())) {
Iter(childNode->AsIndirect(), iter);
continue;
}
for (Entry *e = childNode->AsEntry(); e != nullptr; e = e->Overflow().load(std::memory_order_acquire)) {
if (!iter(e)) {
return false;
}
}
}
return true;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <TrieMapConfig::SlotBarrier Barrier, std::enable_if_t<Barrier != TrieMapConfig::NeedSlotBarrier, int>>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::CheckWeakRef(const WeakRefFieldVisitor& visitor,
Entry* entry)
{
if (SlotBarrier == TrieMapConfig::NeedSlotBarrierCMC) {
uint64_t str = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(entry->Value<SlotBarrier>()));
bool isAlive = visitor(reinterpret_cast<common::RefField<>&>(str));
entry->SetValue(reinterpret_cast<BaseString*>(static_cast<uintptr_t>(str)));
return isAlive;
}
uint64_t str = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(entry->Value<SlotBarrier>()));
bool isAlive = visitor(reinterpret_cast<common::RefField<>&>(str));
if (!isAlive) {
return true;
}
entry->SetValue(reinterpret_cast<BaseString*>(static_cast<uintptr_t>(str)));
return false;
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <TrieMapConfig::SlotBarrier Barrier, std::enable_if_t<Barrier == TrieMapConfig::NeedSlotBarrierCMC, int>>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::ClearNodeFromGC(Indirect* parent, int index,
const WeakRefFieldVisitor& visitor,
std::vector<Entry*>& waitDeleteEntries)
{
Node* child = parent->GetChild(index).load(std::memory_order_relaxed);
if (child == nullptr)
return true;
if (child->IsEntry()) {
for (Entry *prev = nullptr, *current = child->AsEntry(); current != nullptr; current = current->
Overflow().load(std::memory_order_acquire)) {
if (!CheckWeakRef(visitor, current) && prev != nullptr) {
prev->Overflow().store(current->Overflow().load(std::memory_order_acquire), std::memory_order_release);
waitDeleteEntries.push_back(current);
} else {
prev = current;
}
}
return false;
} else {
Indirect* indirect = child->AsIndirect();
uint32_t cleanCount = 0;
for (uint32_t i = 0; i < TrieMapConfig::INDIRECT_SIZE; ++i) {
if (ClearNodeFromGC(indirect, i, visitor, waitDeleteEntries)) {
cleanCount += 1;
}
}
return false;
}
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <TrieMapConfig::SlotBarrier Barrier, std::enable_if_t<Barrier == TrieMapConfig::NoSlotBarrier, int>>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::ClearNodeFromGC(Indirect* parent, int index,
const WeakRefFieldVisitor& visitor)
{
Node* child = parent->GetChild(index).load(std::memory_order_relaxed);
if (child == nullptr) {
return true;
}
if (child->IsEntry()) {
Entry* entry = child->AsEntry();
for (Entry *prev = nullptr, *current = entry->Overflow().load(std::memory_order_relaxed); current != nullptr
;) {
Entry* next = current->Overflow().load(std::memory_order_relaxed);
if (CheckWeakRef(visitor, current)) {
if (prev) {
prev->Overflow().store(next, std::memory_order_relaxed);
} else {
entry->Overflow().store(next, std::memory_order_relaxed);
}
delete current;
} else {
prev = current;
}
current = next;
}
if (CheckWeakRef(visitor, entry)) {
Entry* e = entry->Overflow().load(std::memory_order_relaxed);
if (e == nullptr) {
delete entry;
parent->GetChild(index).store(nullptr, std::memory_order_relaxed);
return true;
}
delete entry;
parent->GetChild(index).store(e, std::memory_order_relaxed);
}
return false;
} else {
Indirect* indirect = child->AsIndirect();
uint32_t cleanCount = 0;
for (uint32_t i = 0; i < TrieMapConfig::INDIRECT_SIZE; ++i) {
if (ClearNodeFromGC(indirect, i, visitor)) {
cleanCount += 1;
}
}
if (cleanCount == TrieMapConfig::INDIRECT_SIZE) {
delete indirect;
parent->GetChild(index).store(nullptr, std::memory_order_relaxed);
return true;
}
return false;
}
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <TrieMapConfig::SlotBarrier Barrier, std::enable_if_t<Barrier == TrieMapConfig::NeedSlotBarrier, int>>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::ClearNodeFromGC(Indirect* parent, int index,
const WeakRootVisitor& visitor, std::vector<Entry*>& waitDeleteEntries,
std::vector<HashTrieMapSlotCheckInfo>& waitCheckAndFreeHeadEntries)
{
Node* child = parent->GetChild(index).load(std::memory_order_relaxed);
if (child == nullptr) {
return true;
}
if (child->IsEntry()) {
Entry* entry = child->AsEntry();
for (Entry *prev = nullptr, *current = entry; current != nullptr; current = current->
Overflow().load(std::memory_order_acquire)) {
if (CheckWeakRef(visitor, current) && prev != nullptr) {
prev->Overflow().store(current->Overflow().load(std::memory_order_acquire), std::memory_order_release);
waitDeleteEntries.push_back(current);
} else {
prev = current;
}
}
if (entry->Value<TrieMapConfig::NoSlotBarrier>() == nullptr) {
waitCheckAndFreeHeadEntries.push_back(std::make_tuple(parent, index, entry));
}
return false;
} else {
Indirect* indirect = child->AsIndirect();
uint32_t cleanCount = 0;
for (uint32_t i = 0; i < TrieMapConfig::INDIRECT_SIZE; ++i) {
if (ClearNodeFromGC(indirect, i, visitor, waitDeleteEntries, waitCheckAndFreeHeadEntries)) {
cleanCount += 1;
}
}
return false;
}
}
template <typename Mutex, typename ThreadHolder, TrieMapConfig::SlotBarrier SlotBarrier>
template <TrieMapConfig::SlotBarrier Barrier, std::enable_if_t<Barrier == TrieMapConfig::NoSlotBarrier ||
Barrier == TrieMapConfig::NoSlotBarrierDynamic, int>>
bool HashTrieMapOperation<Mutex, ThreadHolder, SlotBarrier>::ClearNodeFromGC(Indirect* parent, int index,
const WeakRootVisitor& visitor)
{
Node* child = parent->GetChild(index).load(std::memory_order_relaxed);
if (child == nullptr)
return true;
if (child->IsEntry()) {
Entry* entry = child->AsEntry();
for (Entry *prev = nullptr, *current = entry->Overflow().load(std::memory_order_relaxed); current != nullptr;) {
Entry* next = current->Overflow().load(std::memory_order_relaxed);
if (CheckWeakRef(visitor, current)) {
if (prev != nullptr) {
prev->Overflow().store(next, std::memory_order_relaxed);
} else {
entry->Overflow().store(next, std::memory_order_relaxed);
}
delete current;
} else {
prev = current;
}
current = next;
}
if (CheckWeakRef(visitor, entry)) {
Entry* e = entry->Overflow().load(std::memory_order_relaxed);
if (e == nullptr) {
delete entry;
parent->GetChild(index).store(nullptr, std::memory_order_relaxed);
return true;
}
delete entry;
parent->GetChild(index).store(e, std::memory_order_relaxed);
}
return false;
} else {
Indirect* indirect = child->AsIndirect();
uint32_t cleanCount = 0;
for (uint32_t i = 0; i < TrieMapConfig::INDIRECT_SIZE; ++i) {
if (ClearNodeFromGC(indirect, i, visitor)) {
cleanCount += 1;
}
}
if (cleanCount == TrieMapConfig::INDIRECT_SIZE && hashTrieMap_->GetInuseCount() == 0) {
delete indirect;
parent->GetChild(index).store(nullptr, std::memory_order_relaxed);
return true;
}
return false;
}
}
}
#endif