Skip to content

Commit

Permalink
Support ReadWriteLock in more parallel map APIs. (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
greg7mdp authored Nov 19, 2023
1 parent f8cdc5c commit 355a6ef
Showing 1 changed file with 73 additions and 52 deletions.
125 changes: 73 additions & 52 deletions include/gtl/phmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3626,14 +3626,6 @@ class parallel_hash_set {
// ----------------------------------
// same as emplace, but hashval is provided
// --------------------------------------------------------------------
template<class K, class... Args>
std::pair<iterator, bool> emplace_decomposable_with_hash(const K& key, size_t hashval, Args&&... args) {
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
UniqueLock m(inner);
return make_rv(&inner, set.emplace_decomposable(key, hashval, std::forward<Args>(args)...));
}

struct EmplaceDecomposableHashval {
template<class K, class... Args>
std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
Expand Down Expand Up @@ -3681,25 +3673,34 @@ class parallel_hash_set {
return emplace_with_hash(hashval, std::forward<Args>(args)...).first;
}

template<class K = key_type, class F>
iterator lazy_emplace_with_hash(const key_arg<K>& key, size_t hashval, F&& f) {
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
UniqueLock m(inner);
return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward<F>(f)));
}

// --------------------------------------------------------------------
// end of phmap expension
// --------------------------------------------------------------------

template<class K, class... Args>
std::pair<iterator, bool> emplace_decomposable_with_hash(const K& key, size_t hashval, Args&&... args) {
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
ReadWriteLock m(inner);

size_t offset = set._find_key(key, hashval);
if (offset == (size_t)-1 && m.switch_to_unique()) {
// we did an unlock/lock, and another thread could have inserted the same key, so we need to
// do a find() again.
offset = set._find_key(key, hashval);
}
if (offset == (size_t)-1) {
offset = set.prepare_insert(hashval);
set.emplace_at(offset, std::forward<Args>(args)...);
set.set_ctrl(offset, H2(hashval));
return make_rv(&inner, { set.iterator_at(offset), true });
}
return make_rv(&inner, { set.iterator_at(offset), false });
}

template<class K, class... Args>
std::pair<iterator, bool> emplace_decomposable(const K& key, Args&&... args) {
size_t hashval = this->hash(key);
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
UniqueLock m(inner);
return make_rv(&inner, set.emplace_decomposable(key, hashval, std::forward<Args>(args)...));
return emplace_decomposable_with_hash(key, this->hash(key), std::forward<Args>(args)...);
}

struct EmplaceDecomposable {
Expand Down Expand Up @@ -3736,10 +3737,11 @@ class parallel_hash_set {
size_t hashval = this->hash(PolicyTraits::key(slot));

PolicyTraits::construct(&alloc_ref(), slot, std::forward<Args>(args)...);
const auto& elem = PolicyTraits::element(slot);
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
UniqueLock m(inner);
const auto& elem = PolicyTraits::element(slot);
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
UniqueLock m(inner);

typename EmbeddedSet::template InsertSlotWithHash<true> f{ inner, std::move(*slot), hashval };
return make_rv(PolicyTraits::apply(f, elem));
}
Expand All @@ -3761,13 +3763,28 @@ class parallel_hash_set {

// lazy_emplace
// ------------
template<class K = key_type, class F>
template <class K = key_type, class F>
iterator lazy_emplace_with_hash(const key_arg<K>& key, size_t hashval, F&& f) {
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
ReadWriteLock m(inner);
size_t offset = set._find_key(key, hashval);
if (offset == (size_t)-1 && m.switch_to_unique()) {
// we did an unlock/lock, and another thread could have inserted the same key, so we need to
// do a find() again.
offset = set._find_key(key, hashval);
}
if (offset == (size_t)-1) {
offset = set.prepare_insert(hashval);
set.lazy_emplace_at(offset, std::forward<F>(f));
set.set_ctrl(offset, H2(hashval));
}
return iterator_at(offset);
}

template <class K = key_type, class F>
iterator lazy_emplace(const key_arg<K>& key, F&& f) {
auto hashval = this->hash(key);
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
UniqueLock m(inner);
return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward<F>(f)));
return lazy_emplace_with_hash(key, this->hash(key), std::forward<F>(f));
}

// emplace_single
Expand Down Expand Up @@ -3837,18 +3854,25 @@ class parallel_hash_set {
// ----------------------------------------------------------------------------------------------------
template<class K = key_type, class F>
bool erase_if(const key_arg<K>& key, F&& f) {
return erase_if_impl<K, F, UniqueLock>(key, std::forward<F>(f));
return erase_if_impl<K, F, ReadWriteLock>(key, std::forward<F>(f));
}

template<class K = key_type, class F, class L>
template <class K = key_type, class F, class L>
bool erase_if_impl(const key_arg<K>& key, F&& f) {
static_assert(std::is_invocable_v<F, value_type&>);
static_assert(std::is_invocable<F, value_type&>::value);
auto hashval = this->hash(key);
Inner& inner = sets_[subidx(hashval)];
auto& set = inner.set_;
L m(inner);
auto it = set.find(key, hashval);
if (it == set.end()) return false;
if (it == set.end())
return false;
if (m.switch_to_unique()) {
// we did an unlock/lock, need to call `find()` again
it = set.find(key, hashval);
if (it == set.end())
return false;
}
if (std::forward<F>(f)(const_cast<value_type &>(*it)))
{
set._erase(it);
Expand All @@ -3870,6 +3894,7 @@ class parallel_hash_set {
auto res = this->find_or_prepare_insert_with_hash(hashval, key, m);
Inner* inner = std::get<0>(res);
if (std::get<2>(res)) {
// key not found. call fEmplace lambda which should invoke passed constructor
if constexpr (std::is_same_v<gtl::priv::empty, aux_type>) {
inner->set_.lazy_emplace_at(std::get<1>(res), std::forward<FEmplace>(fEmplace));
inner->set_.set_ctrl(std::get<1>(res), H2(hashval));
Expand All @@ -3880,8 +3905,8 @@ class parallel_hash_set {
inner->set_.erase(*del);
}
} else {
// key found. Call fExists lambda. In case of the set, non "key" part of value_type can be changed
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
// in case of the set, non "key" part of value_type can be changed

if constexpr (std::is_same_v<gtl::priv::empty, aux_type>)
std::forward<FExists>(fExists)(const_cast<value_type&>(*it));
Expand Down Expand Up @@ -4326,19 +4351,14 @@ class parallel_hash_set {
auto& set = inner.set_;
mutexlock = std::move(typename Lockable::ReadWriteLock(inner));
size_t offset = set._find_key(key, hashval);
if (offset == (size_t)-1 && mutexlock.switch_to_unique()) {
// we did an unlock/lock, and another thread could have inserted the same key, so we need to
// do a find() again.
offset = set._find_key(key, hashval);
}
if (offset == (size_t)-1) {
if (mutexlock.switch_to_unique()) {
// we did an unlock/lock, and another thread could have inserted the same key, so we need to
// do a find() again.
offset = set._find_key(key, hashval);
if (offset == (size_t)-1) {
offset = set.prepare_insert(hashval);
return std::make_tuple(&inner, offset, true);
}
} else {
offset = set.prepare_insert(hashval);
return std::make_tuple(&inner, offset, true);
}
offset = set.prepare_insert(hashval);
return std::make_tuple(&inner, offset, true);
}
return std::make_tuple(&inner, offset, false);
}
Expand Down Expand Up @@ -4555,9 +4575,10 @@ class parallel_hash_map : public parallel_hash_set<N, RefSet, Mtx_, AuxCont, Pol
template<class K = key_type, class F, class... Args>
bool try_emplace_l(K&& k, F&& f, Args&&... args) {
size_t hashval = this->hash(k);
UniqueLock m;
ReadWriteLock m;
auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
typename Base::Inner* inner = std::get<0>(res);

if (std::get<2>(res)) {
inner->set_.emplace_at(std::get<1>(res),
std::piecewise_construct,
Expand All @@ -4566,8 +4587,8 @@ class parallel_hash_map : public parallel_hash_set<N, RefSet, Mtx_, AuxCont, Pol
inner->set_.set_ctrl(std::get<1>(res), H2(hashval));
} else {
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
std::forward<F>(f)(
const_cast<value_type&>(*it)); // in case of the set, non "key" part of value_type can be changed
// call lambda. in case of the set, non "key" part of value_type can be changed
std::forward<F>(f)(const_cast<value_type&>(*it));
}
return std::get<2>(res);
}
Expand All @@ -4578,7 +4599,7 @@ class parallel_hash_map : public parallel_hash_set<N, RefSet, Mtx_, AuxCont, Pol
template<class K = key_type, class... Args>
std::pair<typename parallel_hash_map::parallel_hash_set::pointer, bool> try_emplace_p(K&& k, Args&&... args) {
size_t hashval = this->hash(k);
UniqueLock m;
ReadWriteLock m;
auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
typename Base::Inner* inner = std::get<0>(res);
if (std::get<2>(res)) {
Expand Down

0 comments on commit 355a6ef

Please sign in to comment.