mirror of
https://github.com/greg7mdp/parallel-hashmap.git
synced 2026-08-29 08:34:39 +08:00
Add support for read/write locks
This commit is contained in:
Vendored
+61
-38
@@ -1547,12 +1547,13 @@ public:
|
|||||||
|
|
||||||
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) {
|
iterator lazy_emplace_with_hash(const key_arg<K>& key, size_t hashval, F&& f) {
|
||||||
auto res = find_or_prepare_insert(key, hashval);
|
size_t offset = _find_key(key, hashval);
|
||||||
if (res.second) {
|
if (offset == (size_t)-1) {
|
||||||
lazy_emplace_at(res.first, std::forward<F>(f));
|
offset = prepare_insert(hashval);
|
||||||
this->set_ctrl(res.first, H2(hashval));
|
lazy_emplace_at(offset, std::forward<F>(f));
|
||||||
|
this->set_ctrl(offset, H2(hashval));
|
||||||
}
|
}
|
||||||
return iterator_at(res.first);
|
return iterator_at(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class K = key_type, class F>
|
template <class K = key_type, class F>
|
||||||
@@ -1564,12 +1565,13 @@ public:
|
|||||||
|
|
||||||
template <class K = key_type, class F>
|
template <class K = key_type, class F>
|
||||||
void emplace_single_with_hash(const key_arg<K>& key, size_t hashval, F&& f) {
|
void emplace_single_with_hash(const key_arg<K>& key, size_t hashval, F&& f) {
|
||||||
auto res = find_or_prepare_insert(key, hashval);
|
size_t offset = _find_key(key, hashval);
|
||||||
if (res.second) {
|
if (offset == (size_t)-1) {
|
||||||
lazy_emplace_at(res.first, std::forward<F>(f));
|
offset = prepare_insert(hashval);
|
||||||
this->set_ctrl(res.first, H2(hashval));
|
lazy_emplace_at(offset, std::forward<F>(f));
|
||||||
|
this->set_ctrl(offset, H2(hashval));
|
||||||
} else
|
} else
|
||||||
_erase(iterator_at(res.first));
|
_erase(iterator_at(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1897,12 +1899,14 @@ private:
|
|||||||
std::pair<iterator, bool> emplace_decomposable(const K& key, size_t hashval,
|
std::pair<iterator, bool> emplace_decomposable(const K& key, size_t hashval,
|
||||||
Args&&... args)
|
Args&&... args)
|
||||||
{
|
{
|
||||||
auto res = find_or_prepare_insert(key, hashval);
|
size_t offset = _find_key(key, hashval);
|
||||||
if (res.second) {
|
if (offset == (size_t)-1) {
|
||||||
emplace_at(res.first, std::forward<Args>(args)...);
|
offset = prepare_insert(hashval);
|
||||||
this->set_ctrl(res.first, H2(hashval));
|
emplace_at(offset, std::forward<Args>(args)...);
|
||||||
|
this->set_ctrl(offset, H2(hashval));
|
||||||
|
return {iterator_at(offset), true};
|
||||||
}
|
}
|
||||||
return {iterator_at(res.first), res.second};
|
return {iterator_at(offset), false};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmplaceDecomposable
|
struct EmplaceDecomposable
|
||||||
@@ -2194,7 +2198,7 @@ private:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <class K>
|
template <class K>
|
||||||
std::pair<size_t, bool> find_or_prepare_insert(const K& key, size_t hashval) {
|
size_t _find_key(const K& key, size_t hashval) {
|
||||||
auto seq = probe(hashval);
|
auto seq = probe(hashval);
|
||||||
while (true) {
|
while (true) {
|
||||||
Group g{ctrl_ + seq.offset()};
|
Group g{ctrl_ + seq.offset()};
|
||||||
@@ -2202,12 +2206,21 @@ protected:
|
|||||||
if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
|
if (PHMAP_PREDICT_TRUE(PolicyTraits::apply(
|
||||||
EqualElement<K>{key, eq_ref()},
|
EqualElement<K>{key, eq_ref()},
|
||||||
PolicyTraits::element(slots_ + seq.offset((size_t)i)))))
|
PolicyTraits::element(slots_ + seq.offset((size_t)i)))))
|
||||||
return {seq.offset((size_t)i), false};
|
return seq.offset((size_t)i);
|
||||||
}
|
}
|
||||||
if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) break;
|
if (PHMAP_PREDICT_TRUE(g.MatchEmpty())) break;
|
||||||
seq.next();
|
seq.next();
|
||||||
}
|
}
|
||||||
return {prepare_insert(hashval), true};
|
return (size_t)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class K>
|
||||||
|
std::pair<size_t, bool> find_or_prepare_insert(const K& key, size_t hashval) {
|
||||||
|
size_t offset = _find_key(key, hashval);
|
||||||
|
if (offset == (size_t)-1)
|
||||||
|
return {prepare_insert(hashval), true};
|
||||||
|
return {offset, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t prepare_insert(size_t hashval) PHMAP_ATTRIBUTE_NOINLINE {
|
size_t prepare_insert(size_t hashval) PHMAP_ATTRIBUTE_NOINLINE {
|
||||||
@@ -2479,26 +2492,30 @@ private:
|
|||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
|
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
|
||||||
size_t hashval = this->hash(k);
|
size_t hashval = this->hash(k);
|
||||||
auto res = this->find_or_prepare_insert(k, hashval);
|
size_t offset = this->_find_key(k, hashval);
|
||||||
if (res.second) {
|
if (offset == (size_t)-1) {
|
||||||
this->emplace_at(res.first, std::forward<K>(k), std::forward<V>(v));
|
offset = this->prepare_insert(hashval);
|
||||||
this->set_ctrl(res.first, H2(hashval));
|
this->emplace_at(offset, std::forward<K>(k), std::forward<V>(v));
|
||||||
} else
|
this->set_ctrl(offset, H2(hashval));
|
||||||
Policy::value(&*this->iterator_at(res.first)) = std::forward<V>(v);
|
return {this->iterator_at(offset), true};
|
||||||
return {this->iterator_at(res.first), res.second};
|
}
|
||||||
|
Policy::value(&*this->iterator_at(offset)) = std::forward<V>(v);
|
||||||
|
return {this->iterator_at(offset), false};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class K = key_type, class... Args>
|
template <class K = key_type, class... Args>
|
||||||
std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
|
std::pair<iterator, bool> try_emplace_impl(K&& k, Args&&... args) {
|
||||||
size_t hashval = this->hash(k);
|
size_t hashval = this->hash(k);
|
||||||
auto res = this->find_or_prepare_insert(k, hashval);
|
size_t offset = this->_find_key(k, hashval);
|
||||||
if (res.second) {
|
if (offset == (size_t)-1) {
|
||||||
this->emplace_at(res.first, std::piecewise_construct,
|
offset = this->prepare_insert(hashval);
|
||||||
|
this->emplace_at(offset, std::piecewise_construct,
|
||||||
std::forward_as_tuple(std::forward<K>(k)),
|
std::forward_as_tuple(std::forward<K>(k)),
|
||||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||||
this->set_ctrl(res.first, H2(hashval));
|
this->set_ctrl(offset, H2(hashval));
|
||||||
|
return {this->iterator_at(offset), true};
|
||||||
}
|
}
|
||||||
return {this->iterator_at(res.first), res.second};
|
return {this->iterator_at(offset), false};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2565,6 +2582,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
using Lockable = phmap::LockableImpl<Mtx_>;
|
using Lockable = phmap::LockableImpl<Mtx_>;
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
struct Inner : public Lockable
|
struct Inner : public Lockable
|
||||||
@@ -3324,7 +3342,7 @@ public:
|
|||||||
template <class K = key_type, class FExists, class FEmplace>
|
template <class K = key_type, class FExists, class FEmplace>
|
||||||
bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
|
bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
|
||||||
size_t hashval = this->hash(key);
|
size_t hashval = this->hash(key);
|
||||||
typename Lockable::UniqueLock m;
|
typename Lockable::ReadWriteLock m;
|
||||||
auto res = this->find_or_prepare_insert_with_hash(hashval, key, m);
|
auto res = this->find_or_prepare_insert_with_hash(hashval, key, m);
|
||||||
Inner* inner = std::get<0>(res);
|
Inner* inner = std::get<0>(res);
|
||||||
if (std::get<2>(res)) {
|
if (std::get<2>(res)) {
|
||||||
@@ -3781,17 +3799,22 @@ protected:
|
|||||||
|
|
||||||
template <class K>
|
template <class K>
|
||||||
std::tuple<Inner*, size_t, bool>
|
std::tuple<Inner*, size_t, bool>
|
||||||
find_or_prepare_insert_with_hash(size_t hashval, const K& key, typename Lockable::UniqueLock &mutexlock) {
|
find_or_prepare_insert_with_hash(size_t hashval, const K& key, typename Lockable::ReadWriteLock &mutexlock) {
|
||||||
Inner& inner = sets_[subidx(hashval)];
|
Inner& inner = sets_[subidx(hashval)];
|
||||||
auto& set = inner.set_;
|
auto& set = inner.set_;
|
||||||
mutexlock = std::move(typename Lockable::UniqueLock(inner));
|
mutexlock = std::move(typename Lockable::ReadWriteLock(inner));
|
||||||
auto p = set.find_or_prepare_insert(key, hashval); // std::pair<size_t, bool>
|
size_t offset = set._find_key(key, hashval);
|
||||||
return std::make_tuple(&inner, p.first, p.second);
|
if (offset == (size_t)-1) {
|
||||||
|
mutexlock.switch_to_unique();
|
||||||
|
offset = set.prepare_insert(hashval);
|
||||||
|
return std::make_tuple(&inner, offset, true);
|
||||||
|
}
|
||||||
|
return std::make_tuple(&inner, offset, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class K>
|
template <class K>
|
||||||
std::tuple<Inner*, size_t, bool>
|
std::tuple<Inner*, size_t, bool>
|
||||||
find_or_prepare_insert(const K& key, typename Lockable::UniqueLock &mutexlock) {
|
find_or_prepare_insert(const K& key, typename Lockable::ReadWriteLock &mutexlock) {
|
||||||
return find_or_prepare_insert_with_hash<K>(this->hash(key), key, mutexlock);
|
return find_or_prepare_insert_with_hash<K>(this->hash(key), key, mutexlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4060,7 +4083,7 @@ private:
|
|||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
|
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
|
||||||
size_t hashval = this->hash(k);
|
size_t hashval = this->hash(k);
|
||||||
typename Lockable::UniqueLock m;
|
typename Lockable::ReadWriteLock m;
|
||||||
auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
|
auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
|
||||||
typename Base::Inner *inner = std::get<0>(res);
|
typename Base::Inner *inner = std::get<0>(res);
|
||||||
if (std::get<2>(res)) {
|
if (std::get<2>(res)) {
|
||||||
@@ -4080,7 +4103,7 @@ private:
|
|||||||
|
|
||||||
template <class K = key_type, class... Args>
|
template <class K = key_type, class... Args>
|
||||||
std::pair<iterator, bool> try_emplace_impl_with_hash(size_t hashval, K&& k, Args&&... args) {
|
std::pair<iterator, bool> try_emplace_impl_with_hash(size_t hashval, K&& k, Args&&... args) {
|
||||||
typename Lockable::UniqueLock m;
|
typename Lockable::ReadWriteLock m;
|
||||||
auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
|
auto res = this->find_or_prepare_insert_with_hash(hashval, k, m);
|
||||||
typename Base::Inner *inner = std::get<0>(res);
|
typename Base::Inner *inner = std::get<0>(res);
|
||||||
if (std::get<2>(res)) {
|
if (std::get<2>(res)) {
|
||||||
|
|||||||
Vendored
+102
@@ -4631,6 +4631,11 @@ public:
|
|||||||
DoNothing& operator=(DoNothing&&) noexcept { return *this; }
|
DoNothing& operator=(DoNothing&&) noexcept { return *this; }
|
||||||
void swap(DoNothing &) {}
|
void swap(DoNothing &) {}
|
||||||
bool owns_lock() const noexcept { return true; }
|
bool owns_lock() const noexcept { return true; }
|
||||||
|
void lock() {}
|
||||||
|
void unlock() {}
|
||||||
|
void lock_shared() {}
|
||||||
|
void unlock_shared() {}
|
||||||
|
void switch_to_unique() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
@@ -4705,6 +4710,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutex_type *mutex() const noexcept { return m_; }
|
mutex_type *mutex() const noexcept { return m_; }
|
||||||
|
|
||||||
|
void switch_to_unique() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutex_type *m_;
|
mutex_type *m_;
|
||||||
@@ -4784,11 +4791,101 @@ public:
|
|||||||
|
|
||||||
mutex_type *mutex() const noexcept { return m_; }
|
mutex_type *mutex() const noexcept { return m_; }
|
||||||
|
|
||||||
|
void switch_to_unique() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutex_type *m_;
|
mutex_type *m_;
|
||||||
bool locked_;
|
bool locked_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
class ReadWriteLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using mutex_type = MutexType;
|
||||||
|
|
||||||
|
ReadWriteLock() : m_(nullptr), locked_(false), locked_shared_(false) {}
|
||||||
|
|
||||||
|
explicit ReadWriteLock(mutex_type &m) : m_(&m), locked_(false), locked_shared_(true) {
|
||||||
|
m_->lock_shared();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadWriteLock(mutex_type& m, defer_lock_t) noexcept :
|
||||||
|
m_(&m), locked_(false), locked_shared_(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ReadWriteLock(ReadWriteLock &&o) noexcept :
|
||||||
|
m_(std::move(o.m_)), locked_(o.locked_), locked_shared_(o.locked_shared_) {
|
||||||
|
o.locked_ = false;
|
||||||
|
o.locked_shared_ = false;
|
||||||
|
o.m_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadWriteLock& operator=(ReadWriteLock&& other) noexcept {
|
||||||
|
ReadWriteLock temp(std::move(other));
|
||||||
|
swap(temp);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ReadWriteLock() {
|
||||||
|
if (locked_shared_)
|
||||||
|
m_->unlock_shared();
|
||||||
|
else if (locked_)
|
||||||
|
m_->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock_shared() {
|
||||||
|
if (!locked_shared_) {
|
||||||
|
m_->lock_shared();
|
||||||
|
locked_shared_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock_shared() {
|
||||||
|
if (locked_shared_) {
|
||||||
|
m_->unlock_shared();
|
||||||
|
locked_shared_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock() {
|
||||||
|
if (!locked_) {
|
||||||
|
m_->lock();
|
||||||
|
locked_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock() {
|
||||||
|
if (locked_) {
|
||||||
|
m_->unlock();
|
||||||
|
locked_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool owns_lock() const noexcept { return locked_; }
|
||||||
|
bool owns_shared_lock() const noexcept { return locked_shared_; }
|
||||||
|
|
||||||
|
void swap(ReadWriteLock &o) noexcept {
|
||||||
|
std::swap(m_, o.m_);
|
||||||
|
std::swap(locked_, o.locked_);
|
||||||
|
std::swap(locked_shared_, o.locked_shared_);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_type *mutex() const noexcept { return m_; }
|
||||||
|
|
||||||
|
void switch_to_unique() {
|
||||||
|
assert(locked_shared_);
|
||||||
|
unlock_shared();
|
||||||
|
lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutex_type *m_;
|
||||||
|
bool locked_shared_;
|
||||||
|
bool locked_;
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
class WriteLocks
|
class WriteLocks
|
||||||
{
|
{
|
||||||
@@ -4877,6 +4974,7 @@ public:
|
|||||||
using SharedLock = typename Base::WriteLock;
|
using SharedLock = typename Base::WriteLock;
|
||||||
using UpgradeLock = typename Base::WriteLock;
|
using UpgradeLock = typename Base::WriteLock;
|
||||||
using UniqueLock = typename Base::WriteLock;
|
using UniqueLock = typename Base::WriteLock;
|
||||||
|
using ReadWriteLock = typename Base::WriteLock;
|
||||||
using SharedLocks = typename Base::WriteLocks;
|
using SharedLocks = typename Base::WriteLocks;
|
||||||
using UniqueLocks = typename Base::WriteLocks;
|
using UniqueLocks = typename Base::WriteLocks;
|
||||||
using UpgradeToUnique = typename Base::DoNothing; // we already have unique ownership
|
using UpgradeToUnique = typename Base::DoNothing; // we already have unique ownership
|
||||||
@@ -4893,6 +4991,7 @@ public:
|
|||||||
using Base = LockableBaseImpl<phmap::NullMutex>;
|
using Base = LockableBaseImpl<phmap::NullMutex>;
|
||||||
using SharedLock = typename Base::DoNothing;
|
using SharedLock = typename Base::DoNothing;
|
||||||
using UpgradeLock = typename Base::DoNothing;
|
using UpgradeLock = typename Base::DoNothing;
|
||||||
|
using ReadWriteLock = typename Base::DoNothing;
|
||||||
using UniqueLock = typename Base::DoNothing;
|
using UniqueLock = typename Base::DoNothing;
|
||||||
using UpgradeToUnique = typename Base::DoNothing;
|
using UpgradeToUnique = typename Base::DoNothing;
|
||||||
using SharedLocks = typename Base::DoNothing;
|
using SharedLocks = typename Base::DoNothing;
|
||||||
@@ -4921,6 +5020,7 @@ public:
|
|||||||
using mutex_type = phmap::AbslMutex;
|
using mutex_type = phmap::AbslMutex;
|
||||||
using Base = LockableBaseImpl<phmap::AbslMutex>;
|
using Base = LockableBaseImpl<phmap::AbslMutex>;
|
||||||
using SharedLock = typename Base::ReadLock;
|
using SharedLock = typename Base::ReadLock;
|
||||||
|
using ReadWriteLock = typename Base::ReadWriteLock;
|
||||||
using UpgradeLock = typename Base::WriteLock;
|
using UpgradeLock = typename Base::WriteLock;
|
||||||
using UniqueLock = typename Base::WriteLock;
|
using UniqueLock = typename Base::WriteLock;
|
||||||
using SharedLocks = typename Base::ReadLocks;
|
using SharedLocks = typename Base::ReadLocks;
|
||||||
@@ -4943,6 +5043,7 @@ public:
|
|||||||
using mutex_type = boost::shared_mutex;
|
using mutex_type = boost::shared_mutex;
|
||||||
using Base = LockableBaseImpl<boost::shared_mutex>;
|
using Base = LockableBaseImpl<boost::shared_mutex>;
|
||||||
using SharedLock = boost::shared_lock<mutex_type>;
|
using SharedLock = boost::shared_lock<mutex_type>;
|
||||||
|
using ReadWriteLock = typename Base::ReadWriteLock;
|
||||||
using UpgradeLock = boost::unique_lock<mutex_type>; // assume can't upgrade
|
using UpgradeLock = boost::unique_lock<mutex_type>; // assume can't upgrade
|
||||||
using UniqueLock = boost::unique_lock<mutex_type>;
|
using UniqueLock = boost::unique_lock<mutex_type>;
|
||||||
using SharedLocks = typename Base::ReadLocks;
|
using SharedLocks = typename Base::ReadLocks;
|
||||||
@@ -4965,6 +5066,7 @@ public:
|
|||||||
using mutex_type = std::shared_mutex;
|
using mutex_type = std::shared_mutex;
|
||||||
using Base = LockableBaseImpl<std::shared_mutex>;
|
using Base = LockableBaseImpl<std::shared_mutex>;
|
||||||
using SharedLock = std::shared_lock<mutex_type>;
|
using SharedLock = std::shared_lock<mutex_type>;
|
||||||
|
using ReadWriteLock = typename Base::ReadWriteLock;
|
||||||
using UpgradeLock = std::unique_lock<mutex_type>; // assume can't upgrade
|
using UpgradeLock = std::unique_lock<mutex_type>; // assume can't upgrade
|
||||||
using UniqueLock = std::unique_lock<mutex_type>;
|
using UniqueLock = std::unique_lock<mutex_type>;
|
||||||
using SharedLocks = typename Base::ReadLocks;
|
using SharedLocks = typename Base::ReadLocks;
|
||||||
|
|||||||
Reference in New Issue
Block a user