in folly/concurrency/detail/ConcurrentHashMap-detail.h [1683:1884]
class alignas(64) ConcurrentHashMapSegment {
using ImplT = Impl<
KeyType,
ValueType,
ShardBits,
HashFn,
KeyEqual,
Allocator,
Atom,
Mutex>;
public:
typedef KeyType key_type;
typedef ValueType mapped_type;
typedef std::pair<const KeyType, ValueType> value_type;
typedef std::size_t size_type;
using InsertType = concurrenthashmap::InsertType;
using Iterator = typename ImplT::Iterator;
using Node = typename ImplT::Node;
static constexpr float kDefaultLoadFactor = ImplT::kDefaultLoadFactor;
ConcurrentHashMapSegment(
size_t initial_buckets,
float load_factor,
size_t max_size,
hazptr_obj_cohort<Atom>* cohort)
: impl_(initial_buckets, load_factor, max_size, cohort), cohort_(cohort) {
DCHECK(cohort);
}
~ConcurrentHashMapSegment() = default;
size_t size() { return impl_.size(); }
bool empty() { return impl_.empty(); }
template <typename Key>
bool insert(Iterator& it, std::pair<Key, mapped_type>&& foo) {
return insert(it, std::move(foo.first), std::move(foo.second));
}
template <typename Key, typename Value>
bool insert(Iterator& it, Key&& k, Value&& v) {
auto node = (Node*)Allocator().allocate(sizeof(Node));
new (node) Node(cohort_, std::forward<Key>(k), std::forward<Value>(v));
auto res = insert_internal(
it,
node->getItem().first,
InsertType::DOES_NOT_EXIST,
[](const ValueType&) { return false; },
node);
if (!res) {
node->~Node();
Allocator().deallocate((uint8_t*)node, sizeof(Node));
}
return res;
}
template <typename Key, typename... Args>
bool try_emplace(Iterator& it, Key&& k, Args&&... args) {
// Note: first key is only ever compared. Second is moved in to
// create the node, and the first key is never touched again.
return insert_internal(
it,
std::forward<Key>(k),
InsertType::DOES_NOT_EXIST,
[](const ValueType&) { return false; },
std::forward<Key>(k),
std::forward<Args>(args)...);
}
template <typename... Args>
bool emplace(Iterator& it, const KeyType& k, Node* node) {
return insert_internal(
it,
k,
InsertType::DOES_NOT_EXIST,
[](const ValueType&) { return false; },
node);
}
template <typename Key, typename Value>
bool insert_or_assign(Iterator& it, Key&& k, Value&& v) {
auto node = (Node*)Allocator().allocate(sizeof(Node));
new (node) Node(cohort_, std::forward<Key>(k), std::forward<Value>(v));
auto res = insert_internal(
it,
node->getItem().first,
InsertType::ANY,
[](const ValueType&) { return false; },
node);
if (!res) {
node->~Node();
Allocator().deallocate((uint8_t*)node, sizeof(Node));
}
return res;
}
template <typename Key, typename Value>
bool assign(Iterator& it, Key&& k, Value&& v) {
auto node = (Node*)Allocator().allocate(sizeof(Node));
new (node) Node(cohort_, std::forward<Key>(k), std::forward<Value>(v));
auto res = insert_internal(
it,
node->getItem().first,
InsertType::MUST_EXIST,
[](const ValueType&) { return false; },
node);
if (!res) {
node->~Node();
Allocator().deallocate((uint8_t*)node, sizeof(Node));
}
return res;
}
template <typename Key, typename Value>
bool assign_if_equal(
Iterator& it, Key&& k, const ValueType& expected, Value&& desired) {
auto node = (Node*)Allocator().allocate(sizeof(Node));
new (node)
Node(cohort_, std::forward<Key>(k), std::forward<Value>(desired));
auto res = insert_internal(
it,
node->getItem().first,
InsertType::MATCH,
[&expected](const ValueType& v) { return v == expected; },
node);
if (!res) {
node->~Node();
Allocator().deallocate((uint8_t*)node, sizeof(Node));
}
return res;
}
template <typename MatchFunc, typename K, typename... Args>
bool insert_internal(
Iterator& it,
const K& k,
InsertType type,
MatchFunc match,
Args&&... args) {
return impl_.insert(
it, k, type, match, cohort_, std::forward<Args>(args)...);
}
template <typename MatchFunc, typename K, typename... Args>
bool insert_internal(
Iterator& it, const K& k, InsertType type, MatchFunc match, Node* cur) {
return impl_.insert(it, k, type, match, cur, cohort_);
}
// Must hold lock.
void rehash(size_t bucket_count) {
impl_.rehash(folly::nextPowTwo(bucket_count), cohort_);
}
template <typename K>
bool find(Iterator& res, const K& k) {
return impl_.find(res, k);
}
// Listed separately because we need a prev pointer.
template <typename K>
size_type erase(const K& key) {
return erase_internal(key, nullptr, [](const ValueType&) { return true; });
}
template <typename K, typename Predicate>
size_type erase_key_if(const K& key, Predicate&& predicate) {
return erase_internal(key, nullptr, std::forward<Predicate>(predicate));
}
template <typename K, typename MatchFunc>
size_type erase_internal(const K& key, Iterator* iter, MatchFunc match) {
return impl_.erase(key, iter, match);
}
// Unfortunately because we are reusing nodes on rehash, we can't
// have prev pointers in the bucket chain. We have to start the
// search from the bucket.
//
// This is a small departure from standard stl containers: erase may
// throw if hash or key_eq functions throw.
void erase(Iterator& res, Iterator& pos) {
erase_internal(pos->first, &res, [](const ValueType&) { return true; });
// Invalidate the iterator.
pos = cend();
}
void clear() { impl_.clear(cohort_); }
void max_load_factor(float factor) { impl_.max_load_factor(factor); }
Iterator cbegin() { return impl_.cbegin(); }
Iterator cend() { return impl_.cend(); }
private:
ImplT impl_;
hazptr_obj_cohort<Atom>* cohort_;
};