/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include namespace AK { template class [[nodiscard]] LockWeakPtr { template friend class LockWeakable; public: LockWeakPtr() = default; template LockWeakPtr(WeakPtr const& other) requires(IsBaseOf) : m_link(other.m_link) { } template LockWeakPtr(WeakPtr&& other) requires(IsBaseOf) : m_link(other.take_link()) { } template LockWeakPtr& operator=(WeakPtr&& other) requires(IsBaseOf) { m_link = other.take_link(); return *this; } template LockWeakPtr& operator=(WeakPtr const& other) requires(IsBaseOf) { if ((void const*)this != (void const*)&other) m_link = other.m_link; return *this; } LockWeakPtr& operator=(nullptr_t) { clear(); return *this; } template LockWeakPtr(U const& object) requires(IsBaseOf) : m_link(object.template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link()) { } template LockWeakPtr(U const* object) requires(IsBaseOf) { if (object) m_link = object->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); } template LockWeakPtr(LockRefPtr const& object) requires(IsBaseOf) { object.do_while_locked([&](U* obj) { if (obj) m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); }); } template LockWeakPtr(NonnullLockRefPtr const& object) requires(IsBaseOf) { object.do_while_locked([&](U* obj) { if (obj) m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); }); } template LockWeakPtr& operator=(U const& object) requires(IsBaseOf) { m_link = object.template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); return *this; } template LockWeakPtr& operator=(U const* object) requires(IsBaseOf) { if (object) m_link = object->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); else m_link = nullptr; return *this; } template LockWeakPtr& operator=(LockRefPtr const& object) requires(IsBaseOf) { object.do_while_locked([&](U* obj) { if (obj) m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); else m_link = nullptr; }); return *this; } template LockWeakPtr& operator=(NonnullLockRefPtr const& object) requires(IsBaseOf) { object.do_while_locked([&](U* obj) { if (obj) m_link = obj->template try_make_weak_ptr().release_value_but_fixme_should_propagate_errors().take_link(); else m_link = nullptr; }); return *this; } [[nodiscard]] LockRefPtr strong_ref() const { // This only works with RefCounted objects, but it is the only // safe way to get a strong reference from a LockWeakPtr. Any code // that uses objects not derived from RefCounted will have to // use unsafe_ptr(), but as the name suggests, it is not safe... LockRefPtr ref; // Using do_while_locked protects against a race with clear()! m_link.do_while_locked([&](LockWeakLink* link) { if (link) ref = link->template strong_ref(); }); return ref; } [[nodiscard]] T* unsafe_ptr() const { T* ptr = nullptr; m_link.do_while_locked([&](LockWeakLink* link) { if (link) ptr = link->unsafe_ptr(); }); return ptr; } operator bool() const { return m_link ? !m_link->is_null() : false; } [[nodiscard]] bool is_null() const { return !m_link || m_link->is_null(); } void clear() { m_link = nullptr; } [[nodiscard]] LockRefPtr take_link() { return move(m_link); } private: LockWeakPtr(LockRefPtr const& link) : m_link(link) { } LockRefPtr m_link; }; template template inline ErrorOr> LockWeakable::try_make_weak_ptr() const { if constexpr (IsBaseOf) { // Checking m_being_destroyed isn't sufficient when dealing with // a RefCounted type.The reference count will drop to 0 before the // destructor is invoked and revoke_weak_ptrs is called. So, try // to add a ref (which should fail if the ref count is at 0) so // that we prevent the destructor and revoke_weak_ptrs from being // triggered until we're done. if (!static_cast(this)->try_ref()) return LockWeakPtr {}; } else { // For non-RefCounted types this means a weak reference can be // obtained until the ~LockWeakable destructor is invoked! if (m_being_destroyed.load(AK::MemoryOrder::memory_order_acquire)) return LockWeakPtr {}; } if (!m_link) { // There is a small chance that we create a new LockWeakLink and throw // it away because another thread beat us to it. But the window is // pretty small and the overhead isn't terrible. m_link.assign_if_null(TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) LockWeakLink(const_cast(static_cast(*this)))))); } LockWeakPtr weak_ptr(m_link); if constexpr (IsBaseOf) { // Now drop the reference we temporarily added if (static_cast(this)->unref()) { // We just dropped the last reference, which should have called // revoke_weak_ptrs, which should have invalidated our weak_ptr VERIFY(!weak_ptr.strong_ref()); return LockWeakPtr {}; } } return weak_ptr; } template struct Formatter> : Formatter { ErrorOr format(FormatBuilder& builder, LockWeakPtr const& value) { auto ref = value.strong_ref(); return Formatter::format(builder, ref.ptr()); } }; template ErrorOr> try_make_weak_ptr_if_nonnull(T const* ptr) { if (ptr) { return ptr->template try_make_weak_ptr(); } return LockWeakPtr {}; } } using AK::LockWeakPtr;