/* -*- 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--; } // Try to acquire the write lock, but only if there are no readers. // If successful, sets the counter to a negative value. bool tryWriteLock() { return mCounter.compareExchange(0, std::numeric_limits::min()); } // 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) { if (mLock.tryWriteLock()) { 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(IsBaseOf, 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() {} ThreadSafeWeakPtr& operator=(const ThreadSafeWeakPtr& aOther) { mRef = aOther.mRef; return *this; } ThreadSafeWeakPtr(const ThreadSafeWeakPtr& aOther) : mRef(aOther.mRef) { } ThreadSafeWeakPtr& operator=(ThreadSafeWeakPtr&& aOther) { mRef = aOther.mRef.forget(); return *this; } ThreadSafeWeakPtr(ThreadSafeWeakPtr&& aOther) : mRef(aOther.mRef.forget()) { } 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(IsBaseOf::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(IsBaseOf::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 */