24 #include <system_error> 25 #include <type_traits> 27 #include <boost/type_traits/has_trivial_destructor.hpp> 135 typename Hash = std::hash<Key>,
136 typename KeyEqual = std::equal_to<Key>,
137 bool SkipKeyValueDeletion =
140 template <typename>
class Atom = std::atomic,
156 : owner_(owner), slot_(slot) {}
162 return owner_.slots_[slot_].keyValue();
166 return &owner_.slots_[slot_].keyValue();
173 if (owner_.slots_[slot_].state() == LINKED) {
188 return slot_ == rhs.
slot_;
191 return !(*
this ==
rhs);
209 float maxLoadFactor = 0.8
f,
210 const Allocator& alloc = Allocator())
211 : allocator_(alloc) {
212 size_t capacity = size_t(maxSize /
std::min(1.0
f, maxLoadFactor) + 128);
213 size_t avail =
size_t{1} << (8 *
sizeof(IndexType) - 2);
214 if (capacity > avail && maxSize < avail) {
218 if (capacity < maxSize || capacity > avail) {
219 throw std::invalid_argument(
220 "AtomicUnorderedInsertMap capacity must fit in IndexType with 2 bits " 224 numSlots_ = capacity;
226 mmapRequested_ =
sizeof(
Slot) * capacity;
227 slots_ =
reinterpret_cast<Slot*
>(allocator_.allocate(mmapRequested_));
231 slots_[0].stateUpdate(EMPTY, CONSTRUCTING);
235 if (!SkipKeyValueDeletion) {
236 for (
size_t i = 1;
i < numSlots_; ++
i) {
240 allocator_.deallocate(reinterpret_cast<char*>(slots_), mmapRequested_);
262 template <
typename Func>
264 auto const slot = keyToSlotIdx(key);
265 auto prev = slots_[slot].headAndState_.load(std::memory_order_acquire);
267 auto existing = find(key, slot);
269 return std::make_pair(
ConstIterator(*
this, existing),
false);
272 auto idx = allocateNear(slot);
273 new (&slots_[idx].keyValue().first)
Key(key);
274 func(static_cast<void*>(&slots_[idx].keyValue().second));
277 slots_[idx].next_ = prev >> 2;
281 auto after = idx << 2;
288 if (slots_[slot].headAndState_.compare_exchange_strong(prev, after)) {
291 slots_[idx].stateUpdate(CONSTRUCTING, LINKED);
298 existing = find(key, slot);
301 slots_[idx].keyValue().first.~Key();
302 slots_[idx].keyValue().second.~Value();
303 slots_[idx].stateUpdate(CONSTRUCTING, EMPTY);
305 return std::make_pair(
ConstIterator(*
this, existing),
false);
314 template <
class K,
class V>
316 return findOrConstruct(
317 key, [&](
void* raw) {
new (raw)
Value(std::forward<V>(
value)); });
325 IndexType slot = numSlots_ - 1;
326 while (slot > 0 && slots_[slot].
state() != LINKED) {
338 kMaxAllocationTries = 1000,
370 assert(
s == EMPTY ||
s == LINKED);
372 keyValue().first.~Key();
373 keyValue().second.~Value();
378 return BucketState(headAndState_.load(std::memory_order_acquire) & 3);
382 assert(
state() == before);
383 headAndState_ += (after - before);
387 assert(
state() != EMPTY);
388 return *
static_cast<value_type*
>(
static_cast<void*
>(&raw_));
392 assert(
state() != EMPTY);
393 return *
static_cast<const value_type*
>(
static_cast<const void*
>(&raw_));
411 size_t h = hasher()(key);
413 while (h >= numSlots_) {
419 IndexType
find(
const Key& key, IndexType slot)
const {
421 auto hs = slots_[slot].
headAndState_.load(std::memory_order_acquire);
422 for (slot = hs >> 2; slot != 0; slot = slots_[slot].
next_) {
423 if (ke(key, slots_[slot].keyValue().
first)) {
433 for (IndexType tries = 0; tries < kMaxAllocationTries; ++tries) {
434 auto slot = allocationAttempt(start, tries);
435 auto prev = slots_[slot].
headAndState_.load(std::memory_order_acquire);
436 if ((prev & 3) == EMPTY &&
437 slots_[slot].headAndState_.compare_exchange_strong(
438 prev, prev + CONSTRUCTING - EMPTY)) {
442 throw std::bad_alloc();
449 if (
LIKELY(tries < 8 && start + tries < numSlots_)) {
450 return IndexType(start + tries);
453 if (
sizeof(IndexType) <= 4) {
458 assert(rv < numSlots_);
466 memset(slots_, 0, mmapRequested_);
478 typename Hash = std::hash<Key>,
479 typename KeyEqual = std::equal_to<Key>,
480 bool SkipKeyValueDeletion =
483 template <typename>
class Atom = std::atomic,
490 SkipKeyValueDeletion,
499 template <
typename T,
template <
typename>
class Atom = std::atomic>
509 template <
typename T>
const value_type * operator->() const
const value_type & const_reference
bool operator!=(const ConstIterator &rhs) const
IndexType keyToSlotIdx(const Key &key) const
constexpr T nextPowTwo(T const v)
void stateUpdate(BucketState before, BucketState after)
internal::KeyMatcher< M > Key(M inner_matcher)
const value_type & operator*() const
—— Concurrent Priority Queue Implementation ——
const_iterator cend() const
FOLLY_PUSH_WARNING RHS rhs
MutableData(const T &init)
void init(int *argc, char ***argv, bool removeFlags)
MutableAtom(const T &init)
std::pair< const_iterator, bool > findOrConstruct(const Key &key, Func &&func)
bool Value(const T &value, M matcher)
ConstIterator operator++(int)
const_iterator cbegin() const
IndexType allocateNear(IndexType start)
BucketState state() const
static const char *const value
IndexType allocationAttempt(IndexType start, IndexType tries) const
Atom< IndexType > headAndState_
~AtomicUnorderedInsertMap()
std::aligned_storage< sizeof(value_type), alignof(value_type)>::type raw_
Key and Value.
bool operator==(const ConstIterator &rhs) const
std::pair< const_iterator, bool > emplace(const K &key, V &&value)
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
AtomicUnorderedInsertMap(size_t maxSize, float maxLoadFactor=0.8f, const Allocator &alloc=Allocator())
const ConstIterator & operator++()
const value_type & keyValue() const
ConstIterator(const AtomicUnorderedInsertMap &owner, IndexType slot)
IndexType next_
The next bucket in the chain.
std::ptrdiff_t difference_type
std::pair< Key, Value > value_type
IndexType find(const Key &key, IndexType slot) const
size_t slotMask_
tricky, see keyToSlodIdx
const_iterator find(const Key &key) const
constexpr detail::First first
const AtomicUnorderedInsertMap & owner_