diff options
author | Mathias Stearn <mathias@10gen.com> | 2019-01-02 17:46:54 -0500 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2019-01-23 18:50:54 -0500 |
commit | 3e402ffbff291c582c1e2eec80dfd809f18730e4 (patch) | |
tree | 0fa6a97b1481943e7ad8490585b8957da84f2f45 /src/mongo/util/intrusive_counter.h | |
parent | 33c750267b6481c0d0320d75c94ef755f3e467fe (diff) | |
download | mongo-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.h | 36 |
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: |