26 namespace concurrenthashmap {
29 template <
typename Allocator>
32 template <
typename Node>
35 Allocator().deallocate((
uint8_t*)node,
sizeof(
Node));
39 template <
typename Allocator>
46 template <
typename Bucket>
48 bucket->destroy(count_);
56 typename Enabled =
void>
63 template <
typename Arg,
typename...
Args>
66 std::piecewise_construct,
67 std::forward_as_tuple(
std::forward<Arg>(
k)),
68 std::forward_as_tuple(
std::forward<
Args>(args)...)) {}
80 template <
typename KeyType,
typename ValueType,
typename Allocator>
86 !std::is_nothrow_copy_constructible<ValueType>::value ||
87 !std::is_nothrow_copy_constructible<KeyType>::value>> {
96 template <
typename Arg,
typename...
Args>
98 item_ = (value_type*)Allocator().allocate(
sizeof(value_type));
99 new (item_) value_type(
100 std::piecewise_construct,
101 std::forward_as_tuple(std::forward<Arg>(
k)),
102 std::forward_as_tuple(std::forward<Args>(args)...));
107 item_->~value_type();
108 Allocator().deallocate((
uint8_t*)item_,
sizeof(value_type));
118 mutable bool owned_{
true};
125 template <
typename>
class Atom = std::atomic>
127 NodeT<KeyType, ValueType, Allocator, Atom>,
129 concurrenthashmap::HazptrDeleter<Allocator>> {
136 this->acquire_link_safe();
139 template <
typename Arg,
typename...
Args>
142 std::piecewise_construct,
143 std::forward<Arg>(
k),
147 this->acquire_link_safe();
155 return item_.getItem();
158 template <
typename S>
161 auto p = next_.load(std::memory_order_acquire);
168 Atom<NodeT*> next_{
nullptr};
203 typename HashFn = std::hash<KeyType>,
204 typename KeyEqual = std::equal_to<KeyType>,
205 typename Allocator = std::allocator<uint8_t>,
206 template <
typename>
class Atom = std::atomic,
228 size_t initial_buckets,
231 : load_factor_(load_factor), max_size_(max_size) {
237 auto buckets = Buckets::create(initial_buckets);
238 buckets_.store(buckets, std::memory_order_release);
239 load_factor_nodes_ = initial_buckets * load_factor_;
240 bucket_count_.store(initial_buckets, std::memory_order_relaxed);
244 auto buckets = buckets_.load(std::memory_order_relaxed);
247 auto count = bucket_count_.load(std::memory_order_relaxed);
248 buckets->unlink_and_reclaim_nodes(
count);
249 buckets->destroy(
count);
264 template <
typename Key,
typename Value>
266 auto node = (
Node*)Allocator().allocate(
sizeof(
Node));
267 new (node)
Node(std::forward<Key>(
k), std::forward<Value>(
v));
268 auto res = insert_internal(
270 node->getItem().first,
271 InsertType::DOES_NOT_EXIST,
272 [](
const ValueType&) {
return false; },
277 Allocator().deallocate((
uint8_t*)node,
sizeof(
Node));
282 template <
typename Key,
typename...
Args>
286 return insert_internal(
288 std::forward<Key>(
k),
289 InsertType::DOES_NOT_EXIST,
290 [](
const ValueType&) {
return false; },
292 std::forward<Key>(
k),
293 std::forward<Args>(args)...);
296 template <
typename...
Args>
298 return insert_internal(
301 InsertType::DOES_NOT_EXIST,
302 [](
const ValueType&) {
return false; },
307 template <
typename Key,
typename Value>
309 auto node = (
Node*)Allocator().allocate(
sizeof(
Node));
310 new (node)
Node(std::forward<Key>(
k), std::forward<Value>(
v));
311 auto res = insert_internal(
313 node->getItem().first,
315 [](
const ValueType&) {
return false; },
320 Allocator().deallocate((
uint8_t*)node,
sizeof(
Node));
325 template <
typename Key,
typename Value>
327 auto node = (
Node*)Allocator().allocate(
sizeof(
Node));
328 new (node)
Node(std::forward<Key>(
k), std::forward<Value>(
v));
329 auto res = insert_internal(
331 node->getItem().first,
332 InsertType::MUST_EXIST,
333 [](
const ValueType&) {
return false; },
338 Allocator().deallocate((
uint8_t*)node,
sizeof(
Node));
343 template <
typename Key,
typename Value>
347 const ValueType& expected,
349 auto node = (
Node*)Allocator().allocate(
sizeof(
Node));
350 new (node)
Node(std::forward<Key>(
k), std::forward<Value>(desired));
351 auto res = insert_internal(
353 node->getItem().first,
355 [&expected](
const ValueType&
v) {
return v == expected; },
360 Allocator().deallocate((
uint8_t*)node,
sizeof(
Node));
365 template <
typename MatchFunc,
typename...
Args>
373 auto h = HashFn()(
k);
374 std::unique_lock<Mutex>
g(m_);
376 size_t bcount = bucket_count_.load(std::memory_order_relaxed);
377 auto buckets = buckets_.load(std::memory_order_relaxed);
379 if (size_ >= load_factor_nodes_ && type == InsertType::DOES_NOT_EXIST) {
380 if (max_size_ && size_ << 1 > max_size_) {
382 throw std::bad_alloc();
385 buckets = buckets_.load(std::memory_order_relaxed);
386 bcount = bucket_count_.load(std::memory_order_relaxed);
389 auto idx = getIdx(bcount,
h);
390 auto head = &buckets->buckets_[idx]();
391 auto node = head->load(std::memory_order_relaxed);
392 auto headnode = node;
396 hazbuckets.reset(buckets);
399 if (KeyEqual()(k, node->getItem().first)) {
400 it.
setNode(node, buckets, bcount, idx);
402 if (type == InsertType::MATCH) {
403 if (!match(node->getItem().second)) {
407 if (type == InsertType::DOES_NOT_EXIST) {
411 cur = (
Node*)Allocator().allocate(
sizeof(
Node));
412 new (cur)
Node(std::forward<Args>(args)...);
414 auto next = node->next_.load(std::memory_order_relaxed);
415 cur->
next_.store(
next, std::memory_order_relaxed);
417 next->acquire_link();
419 prev->store(cur, std::memory_order_release);
428 node = node->next_.load(std::memory_order_relaxed);
430 if (type != InsertType::DOES_NOT_EXIST && type != InsertType::ANY) {
436 if (size_ >= load_factor_nodes_ && type == InsertType::ANY) {
437 if (max_size_ && size_ << 1 > max_size_) {
439 throw std::bad_alloc();
444 buckets = buckets_.load(std::memory_order_relaxed);
446 hazbuckets.reset(buckets);
447 idx = getIdx(bcount,
h);
448 head = &buckets->buckets_[idx]();
449 headnode = head->load(std::memory_order_relaxed);
457 DCHECK(type == InsertType::ANY || type == InsertType::DOES_NOT_EXIST);
458 cur = (
Node*)Allocator().allocate(
sizeof(
Node));
459 new (cur)
Node(std::forward<Args>(args)...);
461 cur->
next_.store(headnode, std::memory_order_relaxed);
462 head->store(cur, std::memory_order_release);
463 it.
setNode(cur, buckets, bcount, idx);
469 auto buckets = buckets_.load(std::memory_order_relaxed);
470 auto newbuckets = Buckets::create(bucket_count);
472 load_factor_nodes_ = bucket_count * load_factor_;
474 auto oldcount = bucket_count_.load(std::memory_order_relaxed);
475 for (
size_t i = 0;
i < oldcount;
i++) {
476 auto bucket = &buckets->buckets_[
i]();
477 auto node = bucket->load(std::memory_order_relaxed);
481 auto h = HashFn()(node->getItem().first);
482 auto idx = getIdx(bucket_count,
h);
490 auto last = node->next_.load(std::memory_order_relaxed);
491 for (; last !=
nullptr;
492 last = last->next_.load(std::memory_order_relaxed)) {
493 auto k = getIdx(bucket_count, HashFn()(last->getItem().first));
502 lastrun->acquire_link();
503 newbuckets->buckets_[lastidx]().store(lastrun, std::memory_order_relaxed);
505 for (; node != lastrun;
506 node = node->next_.load(std::memory_order_relaxed)) {
507 auto newnode = (
Node*)Allocator().allocate(
sizeof(
Node));
508 new (newnode)
Node(node);
509 auto k = getIdx(bucket_count, HashFn()(node->getItem().first));
510 auto prevhead = &newbuckets->buckets_[
k]();
511 newnode->next_.store(prevhead->load(std::memory_order_relaxed));
512 prevhead->store(newnode, std::memory_order_relaxed);
516 auto oldbuckets = buckets_.load(std::memory_order_relaxed);
517 seqlock_.fetch_add(1, std::memory_order_release);
518 bucket_count_.store(bucket_count, std::memory_order_release);
519 buckets_.store(newbuckets, std::memory_order_release);
520 seqlock_.fetch_add(1, std::memory_order_release);
528 auto h = HashFn()(
k);
531 getBucketsAndCount(bcount, buckets, res.
hazptrs_[0]);
533 auto idx = getIdx(bcount,
h);
534 auto prev = &buckets->
buckets_[idx]();
535 auto node = hazcurr.get_protected(*prev);
537 if (KeyEqual()(k, node->getItem().first)) {
538 res.
setNode(node, buckets, bcount, idx);
541 node = haznext.get_protected(node->next_);
542 hazcurr.swap(haznext);
548 size_type
erase(
const key_type& key) {
549 return erase_internal(key,
nullptr);
554 auto h = HashFn()(key);
556 std::lock_guard<Mutex>
g(m_);
558 size_t bcount = bucket_count_.load(std::memory_order_relaxed);
559 auto buckets = buckets_.load(std::memory_order_relaxed);
560 auto idx = getIdx(bcount,
h);
561 auto head = &buckets->buckets_[idx]();
562 node = head->load(std::memory_order_relaxed);
563 Node* prev =
nullptr;
565 if (KeyEqual()(key, node->getItem().first)) {
566 auto next = node->next_.load(std::memory_order_relaxed);
568 next->acquire_link();
571 prev->
next_.store(
next, std::memory_order_release);
574 head->store(
next, std::memory_order_release);
580 node->next_.load(std::memory_order_acquire),
590 node = node->
next_.load(std::memory_order_relaxed);
609 erase_internal(pos->first, &res);
615 size_t bcount = bucket_count_.load(std::memory_order_relaxed);
617 auto newbuckets = Buckets::create(bcount);
619 std::lock_guard<Mutex>
g(m_);
620 buckets = buckets_.load(std::memory_order_relaxed);
621 buckets_.store(newbuckets, std::memory_order_release);
628 std::lock_guard<Mutex>
g(m_);
629 load_factor_ = factor;
631 bucket_count_.load(std::memory_order_relaxed) * load_factor_;
638 getBucketsAndCount(bcount, buckets, res.
hazptrs_[0]);
639 res.
setNode(
nullptr, buckets, bcount, 0);
653 concurrenthashmap::HazptrBucketDeleter<Allocator>> {
663 auto buckets =
new (buf)
Buckets();
664 for (
size_t i = 0;
i <
count;
i++) {
671 for (
size_t i = 0;
i <
count;
i++) {
672 buckets_[
i].~BucketRoot();
675 Allocator().deallocate(
680 for (
size_t i = 0;
i <
count;
i++) {
681 auto node = buckets_[
i]().
load(std::memory_order_relaxed);
683 buckets_[
i]().store(
nullptr, std::memory_order_relaxed);
685 auto next = node->next_.load(std::memory_order_relaxed);
687 node->next_.store(
nullptr, std::memory_order_relaxed);
689 node->unlink_and_reclaim_unchecked();
711 bucket_count_ = bucket_count;
716 return node_->getItem();
721 return &(node_->getItem());
726 node_ = hazptrs_[2].get_protected(node_->next_);
727 hazptrs_[1].swap(hazptrs_[2]);
737 if (idx_ >= bucket_count_) {
741 node_ = hazptrs_[1].get_protected(buckets_->buckets_[idx_]());
750 return node_ == o.
node_;
754 return !(*
this == o);
773 : hazptrs_(
std::
move(o.hazptrs_)),
785 size_t bucket_count_{0};
793 return (hash >> ShardBits) & (bucket_count - 1);
800 auto seqlock = seqlock_.load(std::memory_order_acquire);
801 bcount = bucket_count_.load(std::memory_order_acquire);
803 auto seqlock2 = seqlock_.load(std::memory_order_acquire);
804 if (!(seqlock & 1) && (seqlock == seqlock2)) {
818 alignas(64) Atom<Buckets*> buckets_{
nullptr};
819 std::atomic<uint64_t> seqlock_{0};
constexpr unsigned int popcount(T const v)
FOLLY_ALWAYS_INLINE Iterator(std::nullptr_t)
bool insert_or_assign(Iterator &it, Key &&k, Value &&v)
std::pair< const KeyType, ValueType > value_type
~ConcurrentHashMapSegment()
ConcurrentHashMapSegment(size_t initial_buckets, float load_factor, size_t max_size)
void max_load_factor(float factor)
ValueHolder(std::piecewise_construct_t, Arg &&k, Args &&...args)
#define FOLLY_ALWAYS_INLINE
FOLLY_ALWAYS_INLINE T * get_protected(const Atom< T * > &src) noexcept
void getBucketsAndCount(size_t &bcount, Buckets *&buckets, hazptr_holder< Atom > &hazptr)
constexpr T nextPowTwo(T const v)
bool find(Iterator &res, const KeyType &k)
Atom< size_t > bucket_count_
constexpr detail::Map< Move > move
bool assign_if_equal(Iterator &it, Key &&k, const ValueType &expected, Value &&desired)
static Buckets * create(size_t count)
internal::KeyMatcher< M > Key(M inner_matcher)
void retire(D deleter={}, hazptr_domain< Atom > &domain=default_hazptr_domain< Atom >())
void push_links(bool m, S &s)
bool try_emplace(Iterator &it, Key &&k, Args &&...args)
bool assign(Iterator &it, Key &&k, Value &&v)
ValueHolder(const ValueHolder &other)
bool emplace(Iterator &it, const KeyType &k, Node *node)
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
NodeT(Arg &&k, Args &&...args)
requires E e noexcept(noexcept(s.error(std::move(e))))
bool insert(Iterator &it, Key &&k, Value &&v)
bool operator==(const Iterator &o) const
constexpr bool isPowTwo(T const v)
hazptr_array< 3, Atom > hazptrs_
void erase(Iterator &res, Iterator &pos)
constexpr auto size(C const &c) -> decltype(c.size())
FOLLY_ALWAYS_INLINE ~Iterator()
size_type erase_internal(const key_type &key, Iterator *iter)
bool Value(const T &value, M matcher)
std::pair< const KeyType, ValueType > value_type
Iterator(Iterator &&o) noexcept
void rehash(size_t bucket_count)
static map< string, int > m
std::pair< const KeyType, ValueType > value_type
void operator()(Bucket *bucket)
void operator()(Node *node)
ValueHolder< KeyType, ValueType, Allocator > item_
void destroy(size_t count)
void setNode(Node *node, Buckets *buckets, size_t bucket_count, uint64_t idx)
Iterator & operator=(Iterator &&o) noexcept
T exchange(T &obj, U &&new_value)
ValueHolder(std::piecewise_construct_t, Arg &&k, Args &&...args)
ValueHolder(const ValueHolder &other)
bool insert_internal(Iterator &it, const KeyType &k, InsertType type, MatchFunc match, Node *cur, Args &&...args)
size_type erase(const key_type &key)
const value_type & operator*() const
bool insert(Iterator &it, std::pair< key_type, mapped_type > &&foo)
void unlink_and_reclaim_nodes(size_t count)
const Iterator & operator++()
const value_type * operator->() const
bool operator!=(const Iterator &o) const
FOLLY_ALWAYS_INLINE Iterator()
uint64_t getIdx(size_t bucket_count, size_t hash)
Iterator< typename Container::const_iterator > cend(const Container &c)
std::pair< const KeyType, ValueType > value_type
size_t load_factor_nodes_
HazptrBucketDeleter(size_t count)