Commit 0bb8ba0c authored by Dave Watson's avatar Dave Watson Committed by Facebook Github Bot

Add domain support to freestanding rcu functions

Summary:
Add a domain argument to frestanding RCU functions.
Perhaps slightly less awkward than using the domain, especially
for retire() and rcu_reader, but nothing that can't already
be done with domain calls directly.

Reviewed By: yfeldblum

Differential Revision: D6878941

fbshipit-source-id: 2397bc8c18a4175793932687e7c3c59f2825caa2
parent 77e7e33f
...@@ -386,74 +386,101 @@ inline rcu_domain<RcuTag>* rcu_default_domain() { ...@@ -386,74 +386,101 @@ inline rcu_domain<RcuTag>* rcu_default_domain() {
} }
// Main reader guard class. // Main reader guard class.
class rcu_reader { template <typename Tag = RcuTag>
class rcu_reader_domain {
public: public:
FOLLY_ALWAYS_INLINE rcu_reader() noexcept FOLLY_ALWAYS_INLINE rcu_reader_domain(
: epoch_(rcu_default_domain()->lock_shared()) {} rcu_domain<Tag>* domain = rcu_default_domain()) noexcept
rcu_reader(std::defer_lock_t) noexcept {} : epoch_(domain->lock_shared()), domain_(domain) {}
rcu_reader(const rcu_reader&) = delete; rcu_reader_domain(
rcu_reader(rcu_reader&& other) noexcept : epoch_(std::move(other.epoch_)) {} std::defer_lock_t,
rcu_reader& operator=(const rcu_reader&) = delete; rcu_domain<Tag>* domain = rcu_default_domain()) noexcept
rcu_reader& operator=(rcu_reader&& other) noexcept { : domain_(domain) {}
rcu_reader_domain(const rcu_reader_domain&) = delete;
rcu_reader_domain(rcu_reader_domain&& other) noexcept
: epoch_(std::move(other.epoch_)), domain_(std::move(other.domain_)) {}
rcu_reader_domain& operator=(const rcu_reader_domain&) = delete;
rcu_reader_domain& operator=(rcu_reader_domain&& other) noexcept {
if (epoch_.has_value()) { if (epoch_.has_value()) {
rcu_default_domain()->unlock_shared(std::move(epoch_.value())); domain_->unlock_shared(std::move(epoch_.value()));
} }
epoch_ = std::move(other.epoch_); epoch_ = std::move(other.epoch_);
domain_ = std::move(other.domain_);
return *this; return *this;
} }
FOLLY_ALWAYS_INLINE ~rcu_reader() noexcept { FOLLY_ALWAYS_INLINE ~rcu_reader_domain() noexcept {
if (epoch_.has_value()) { if (epoch_.has_value()) {
rcu_default_domain()->unlock_shared(std::move(epoch_.value())); domain_->unlock_shared(std::move(epoch_.value()));
} }
} }
void swap(rcu_reader& other) noexcept { void swap(rcu_reader_domain& other) noexcept {
std::swap(epoch_, other.epoch_); std::swap(epoch_, other.epoch_);
std::swap(domain_, other.domain_);
} }
FOLLY_ALWAYS_INLINE void lock() noexcept { FOLLY_ALWAYS_INLINE void lock() noexcept {
DCHECK(!epoch_.has_value()); DCHECK(!epoch_.has_value());
epoch_ = rcu_default_domain()->lock_shared(); epoch_ = domain_->lock_shared();
} }
FOLLY_ALWAYS_INLINE void unlock() noexcept { FOLLY_ALWAYS_INLINE void unlock() noexcept {
DCHECK(epoch_.has_value()); DCHECK(epoch_.has_value());
rcu_default_domain()->unlock_shared(std::move(epoch_.value())); domain_->unlock_shared(std::move(epoch_.value()));
} }
private: private:
Optional<rcu_token> epoch_; Optional<rcu_token> epoch_;
rcu_domain<Tag>* domain_;
}; };
inline void swap(rcu_reader& a, rcu_reader& b) noexcept { using rcu_reader = rcu_reader_domain<RcuTag>;
template <typename Tag = RcuTag>
inline void swap(
rcu_reader_domain<Tag>& a,
rcu_reader_domain<Tag>& b) noexcept {
a.swap(b); a.swap(b);
} }
inline void synchronize_rcu() noexcept { template <typename Tag = RcuTag>
rcu_default_domain()->synchronize(); inline void synchronize_rcu(
rcu_domain<Tag>* domain = rcu_default_domain()) noexcept {
domain->synchronize();
} }
inline void rcu_barrier() noexcept { template <typename Tag = RcuTag>
rcu_default_domain()->synchronize(); inline void rcu_barrier(
rcu_domain<Tag>* domain = rcu_default_domain()) noexcept {
domain->synchronize();
} }
// Free-function retire. Always allocates. // Free-function retire. Always allocates.
template <typename T, typename D = std::default_delete<T>> template <
void rcu_retire(T* p, D d = {}) { typename T,
rcu_default_domain()->call([p, del = std::move(d)]() { del(p); }); typename D = std::default_delete<T>,
typename Tag = RcuTag>
void rcu_retire(
T* p,
D d = {},
rcu_domain<Tag>* domain = rcu_default_domain()) {
domain->call([p, del = std::move(d)]() { del(p); });
} }
// Base class for rcu objects. retire() will use preallocated storage // Base class for rcu objects. retire() will use preallocated storage
// from rcu_obj_base, vs. rcu_retire() which always allocates. // from rcu_obj_base, vs. rcu_retire() which always allocates.
template <typename T, typename D = std::default_delete<T>> template <
typename T,
typename D = std::default_delete<T>,
typename Tag = RcuTag>
class rcu_obj_base : detail::ThreadCachedListsBase::Node { class rcu_obj_base : detail::ThreadCachedListsBase::Node {
public: public:
void retire(D d = {}) { void retire(D d = {}, rcu_domain<Tag>* domain = rcu_default_domain()) {
// This implementation assumes folly::Function has enough // This implementation assumes folly::Function has enough
// inline storage for D, otherwise, it allocates. // inline storage for D, otherwise, it allocates.
this->cb_ = [this, d = std::move(d)]() { d(static_cast<T*>(this)); }; this->cb_ = [this, d = std::move(d)]() { d(static_cast<T*>(this)); };
rcu_default_domain()->retire(this); domain->retire(this);
} }
}; };
......
...@@ -174,6 +174,17 @@ TEST(RcuTest, NewDomainTest) { ...@@ -174,6 +174,17 @@ TEST(RcuTest, NewDomainTest) {
synchronize_rcu(); synchronize_rcu();
} }
TEST(RcuTest, NewDomainGuardTest) {
struct UniqueTag;
rcu_domain<UniqueTag> newdomain(nullptr);
bool del = false;
auto foo = new des(&del);
{ rcu_reader_domain<UniqueTag> g(&newdomain); }
rcu_retire(foo, {}, &newdomain);
synchronize_rcu(&newdomain);
EXPECT_TRUE(del);
}
TEST(RcuTest, MovableReader) { TEST(RcuTest, MovableReader) {
{ {
rcu_reader g; rcu_reader g;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment