/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* A thread-safe weak pointer */ /** * Derive from SupportsThreadSafeWeakPtr to allow thread-safe weak pointers to * an atomically refcounted derived class. These thread-safe weak pointers may * be safely accessed and converted to strong pointers on multiple threads. * * Note that SupportsThreadSafeWeakPtr necessarily already inherits from * AtomicRefCounted, so you should not separately inherit from AtomicRefCounted. * * ThreadSafeWeakPtr and its implementation is distinct from the normal WeakPtr * which is not thread-safe. The interface discipline and implementation details * are different enough that these two implementations are separated for now for * efficiency reasons. If you don't actually need to use weak pointers on * multiple threads, you can just use WeakPtr instead. * * When deriving from SupportsThreadSafeWeakPtr, you should add * MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(ClassName) and * MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public section of your * class, where ClassName is the name of your class. * * Example usage: * * class C : public SupportsThreadSafeWeakPtr * { * public: * MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(C) * MOZ_DECLARE_REFCOUNTED_TYPENAME(C) * void doStuff(); * }; * * ThreadSafeWeakPtr weak; * { * RefPtr strong = new C; * if (strong) { * strong->doStuff(); * } * // Make a new weak reference to the object from the strong reference. * weak = strong; * } * MOZ_ASSERT(!bool(weak), "Weak pointers are cleared after all " * "strong references are released."); * * // Convert the weak reference to a strong reference for usage. * RefPtr other(weak); * if (other) { * other->doStuff(); * } */ #ifndef mozilla_ThreadSafeWeakPtr_h #define mozilla_ThreadSafeWeakPtr_h #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/RefCounted.h" #include "mozilla/RefPtr.h" #include "mozilla/TypeTraits.h" #include "mozilla/Unused.h" #include namespace mozilla { template class ThreadSafeWeakPtr; template class SupportsThreadSafeWeakPtr; #ifdef MOZ_REFCOUNTED_LEAK_CHECKING # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) \ static const char* threadSafeWeakReferenceTypeName() { \ return "ThreadSafeWeakReference<" #T ">"; \ } #else # define MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(T) #endif namespace detail { // A multiple reader, single writer spin-lock. // This lock maintains an atomic counter which is incremented every time the // lock is acquired reading. So long as the counter remains positive, it may be // incremented for reading multiple times. When acquiring the lock for writing, // we must ensure the counter is 0 (no readers), and if so, set it to a negative // value to indicate that no new readers may take the lock. class ReadWriteSpinLock { // Only need a type large enough to represent the number of simultaneously // accessing threads. typedef int32_t CounterType; public: // Try to increment the counter for reading, so long as it is positive. void readLock() { for (;;) { CounterType oldCounter = mCounter & std::numeric_limits::max(); CounterType newCounter = oldCounter + 1; if (mCounter.compareExchange(oldCounter, newCounter)) { break; } } } // Decrement the counter to remove a read lock. void readUnlock() { mCounter--; } // Spins until a write lock is acquired. This can only occur if there are no // readers or writers. Once it is acquired, the counter is set to a negative // value. void writeLock() { while (true) { if (mCounter.compareExchange(0, std::numeric_limits::min())) { return; } } } // Reset the counter to 0. void writeUnlock() { mCounter = 0; } private: Atomic mCounter; }; // A shared weak reference that is used to track a SupportsThreadSafeWeakPtr // object. It guards access to that object via a read-write spinlock. template class ThreadSafeWeakReference : public external::AtomicRefCounted> { public: typedef T ElementType; explicit ThreadSafeWeakReference(T* aPtr) { mPtr = aPtr; } #ifdef MOZ_REFCOUNTED_LEAK_CHECKING const char* typeName() const { // The first time this is called mPtr is null, so don't // invoke any methods on mPtr. return T::threadSafeWeakReferenceTypeName(); } size_t typeSize() const { return sizeof(*this); } #endif private: friend class mozilla::SupportsThreadSafeWeakPtr; template friend class mozilla::ThreadSafeWeakPtr; // Does an unsafe read of the raw weak pointer. T* get() const { return mPtr; } // Creates a new RefPtr to the tracked object. // We need to acquire the read lock while we do this, as we need to atomically // both read the pointer and then increment the refcount on it within the // scope of the lock. This guards against the object being destroyed while in // the middle of creating the new RefPtr. already_AddRefed getRefPtr() { mLock.readLock(); RefPtr result(get()); mLock.readUnlock(); return result.forget(); } // Try to detach the weak reference from the tracked object. // We need to acquire the write lock while we do this, to ensure that no // RefPtr is created to this while detaching. Once acquired, it is safe // to check the refcount and verify that this is the last reference to // the tracked object, so the weak reference can be safely detached. void tryDetach(const SupportsThreadSafeWeakPtr* aOwner) { mLock.writeLock(); if (aOwner->hasOneRef()) { mPtr = nullptr; } mLock.writeUnlock(); } ReadWriteSpinLock mLock; Atomic mPtr; }; } // namespace detail template class SupportsThreadSafeWeakPtr : public external::AtomicRefCounted { protected: typedef external::AtomicRefCounted AtomicRefCounted; typedef detail::ThreadSafeWeakReference ThreadSafeWeakReference; public: ~SupportsThreadSafeWeakPtr() { // Clean up the shared weak reference if one exists. if (ThreadSafeWeakReference* ptr = mRef) { ptr->Release(); } } void Release() const { // If there is only one remaining reference to the object when trying to // release, then attempt to detach it from its weak reference. New // references could possibly be created to the object while this happens, so // take care to do this atomically inside tryDetach. if (AtomicRefCounted::hasOneRef()) { if (ThreadSafeWeakReference* ptr = mRef) { ptr->tryDetach(this); } } // Once possibly detached, it is now safe to continue to decrement the // refcount. AtomicRefCounted::Release(); } private: template friend class ThreadSafeWeakPtr; // Creates a shared weak reference for the object if one does not exist. Note // that the object may be of an actual derived type U, but the weak reference // is created for the supplied type T of SupportsThreadSafeWeakPtr. already_AddRefed getThreadSafeWeakReference() { static_assert(std::is_base_of, T>::value, "T must derive from SupportsThreadSafeWeakPtr"); if (!mRef) { RefPtr ptr( new ThreadSafeWeakReference(static_cast(this))); // Only set the new weak reference if one does not exist (== nullptr). // If there is already a weak reference, just let this superflous weak // reference get destroyed when it goes out of scope. if (mRef.compareExchange(nullptr, ptr)) { // If successful, forget the refcount so that the weak reference stays // alive. Unused << ptr.forget(); } } // Create a new RefPtr to weak reference. RefPtr ptr(mRef); return ptr.forget(); } Atomic mRef; }; // A thread-safe variant of a weak pointer template class ThreadSafeWeakPtr { // Be careful to use the weak reference type T in the // SupportsThreadSafeWeakPtr definition. typedef typename T::ThreadSafeWeakReference ThreadSafeWeakReference; public: ThreadSafeWeakPtr() = default; ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) = default; ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) = default; ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) = default; ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) = default; ThreadSafeWeakPtr& operator=(const RefPtr& aOther) { if (aOther) { // Get the underlying shared weak reference to the object, creating one if // necessary. mRef = aOther->getThreadSafeWeakReference(); } else { mRef = nullptr; } return *this; } explicit ThreadSafeWeakPtr(const RefPtr& aOther) { *this = aOther; } ThreadSafeWeakPtr& operator=(decltype(nullptr)) { mRef = nullptr; return *this; } explicit ThreadSafeWeakPtr(decltype(nullptr)) {} explicit operator bool() const { return !!get(); } bool operator==(const ThreadSafeWeakPtr& aOther) const { return get() == aOther.get(); } bool operator==(const RefPtr& aOther) const { return get() == aOther.get(); } bool operator==(const T* aOther) const { return get() == aOther; } template bool operator!=(const U& aOther) const { return !(*this == aOther); } // Convert the weak pointer to a strong RefPtr. explicit operator RefPtr() const { return getRefPtr(); } private: // Gets a new strong reference of the proper type T to the tracked object. already_AddRefed getRefPtr() const { static_assert(std::is_base_of::value, "T must derive from ThreadSafeWeakReference::ElementType"); return mRef ? mRef->getRefPtr().template downcast() : nullptr; } // Get a pointer to the tracked object, downcasting to the proper type T. // Note that this operation is unsafe as it may cause races if downwind // code depends on the value not to change after reading. T* get() const { static_assert(std::is_base_of::value, "T must derive from ThreadSafeWeakReference::ElementType"); return mRef ? static_cast(mRef->get()) : nullptr; } // A shared weak reference to an object. Note that this may be null so as to // save memory (at the slight cost of an extra null check) if no object is // being tracked. RefPtr mRef; }; } // namespace mozilla template inline already_AddRefed do_AddRef( const mozilla::ThreadSafeWeakPtr& aObj) { RefPtr ref(aObj); return ref.forget(); } #endif /* mozilla_ThreadSafeWeakPtr_h */