summaryrefslogtreecommitdiff
path: root/src/mongo/util/intrusive_counter.h
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2019-01-02 17:46:54 -0500
committerMathias Stearn <mathias@10gen.com>2019-01-23 18:50:54 -0500
commit3e402ffbff291c582c1e2eec80dfd809f18730e4 (patch)
tree0fa6a97b1481943e7ad8490585b8957da84f2f45 /src/mongo/util/intrusive_counter.h
parent33c750267b6481c0d0320d75c94ef755f3e467fe (diff)
downloadmongo-3e402ffbff291c582c1e2eec80dfd809f18730e4.tar.gz
SERVER-38812 Merge RefCountable improvements for Futures back to common implementation
Diffstat (limited to 'src/mongo/util/intrusive_counter.h')
-rw-r--r--src/mongo/util/intrusive_counter.h36
1 files changed, 30 insertions, 6 deletions
diff --git a/src/mongo/util/intrusive_counter.h b/src/mongo/util/intrusive_counter.h
index b78f83809df..515e5554591 100644
--- a/src/mongo/util/intrusive_counter.h
+++ b/src/mongo/util/intrusive_counter.h
@@ -47,17 +47,32 @@ class RefCountable {
public:
/// If false you have exclusive access to this object. This is useful for implementing COW.
bool isShared() const {
- // TODO: switch to unfenced read method after SERVER-6973
- return reinterpret_cast<unsigned&>(_count) > 1;
+ // This needs to be at least acquire to ensure that it is sequenced-after any
+ // intrusive_ptr_release calls. Otherwise there is a subtle race where the releaser's memory
+ // accesses could see writes done by a thread that thinks it has exclusive access to this
+ // object. Note that acquire reads are free on x86 and cheap on most other platforms.
+ return _count.load(std::memory_order_acquire) > 1;
+ }
+
+ /**
+ * Sets the refcount to count, assuming it is currently one less. This should only be used
+ * during logical initialization before another thread could possibly have access to this
+ * object.
+ */
+ void threadUnsafeIncRefCountTo(uint32_t count) const {
+ dassert(_count.load(std::memory_order_relaxed) == (count - 1));
+ _count.store(count, std::memory_order_relaxed);
}
friend void intrusive_ptr_add_ref(const RefCountable* ptr) {
- ptr->_count.addAndFetch(1);
+ // See this for a description of why relaxed is OK here. It is also used in libc++.
+ // http://www.boost.org/doc/libs/1_66_0/doc/html/atomic/usage_examples.html#boost_atomic.usage_examples.example_reference_counters.discussion
+ ptr->_count.fetch_add(1, std::memory_order_relaxed);
};
friend void intrusive_ptr_release(const RefCountable* ptr) {
- if (ptr->_count.subtractAndFetch(1) == 0) {
- delete ptr; // uses subclass destructor and operator delete
+ if (ptr->_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
+ delete ptr;
}
};
@@ -66,9 +81,18 @@ protected:
virtual ~RefCountable() {}
private:
- mutable AtomicWord<unsigned> _count; // default initialized to 0
+ mutable std::atomic<uint32_t> _count{0}; // NOLINT
};
+template <typename T,
+ typename... Args,
+ typename = std::enable_if_t<std::is_base_of<RefCountable, T>::value>>
+boost::intrusive_ptr<T> make_intrusive(Args&&... args) {
+ auto ptr = new T(std::forward<Args>(args)...);
+ ptr->threadUnsafeIncRefCountTo(1);
+ return boost::intrusive_ptr<T>(ptr, /*add ref*/ false);
+}
+
/// This is an immutable reference-counted string
class RCString : public RefCountable {
public: