From 9e994da2acdec9807a82b3e3356876a2e1dd59b0 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 7 May 2022 11:59:46 +0200 Subject: [PATCH] Kernel+AK: Split Weakable.h into userspace and kernel variants Only the kernel expects AK::Weakable to lock its refcount manipulation, so let's not force userspace to pay for that as well. --- AK/Weakable.h | 36 +++----- Kernel/Library/ThreadSafeWeakable.h | 124 ++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 26 deletions(-) create mode 100644 Kernel/Library/ThreadSafeWeakable.h diff --git a/AK/Weakable.h b/AK/Weakable.h index 122fb6ddfa..ee784aa090 100644 --- a/AK/Weakable.h +++ b/AK/Weakable.h @@ -1,22 +1,20 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include "Assertions.h" -#include "Atomic.h" -#include "RefCounted.h" -#include "RefPtr.h" -#include "StdLibExtras.h" #ifdef KERNEL -# include -# include +# include #else +# include +# include +# include +# include +# include # include -#endif namespace AK { @@ -39,11 +37,6 @@ public: RefPtr ref; { -#ifdef KERNEL - // We don't want to be pre-empted while we are trying to obtain - // a strong reference - Kernel::ScopedCritical critical; -#endif if (!(m_consumers.fetch_add(1u << 1, AK::MemoryOrder::memory_order_acquire) & 1u)) { T* ptr = (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire); if (ptr && ptr->try_ref()) @@ -79,11 +72,7 @@ public: // We flagged revocation, now wait until everyone trying to obtain // a strong reference is done while (current_consumers > 0) { -#ifdef KERNEL - Kernel::Processor::wait_check(); -#else sched_yield(); -#endif current_consumers = m_consumers.load(AK::MemoryOrder::memory_order_acquire) & ~1u; } // No one is trying to use it (anymore) @@ -106,13 +95,12 @@ private: class Link; public: -#ifndef KERNEL template WeakPtr make_weak_ptr() const { return MUST(try_make_weak_ptr()); } -#endif + template ErrorOr> try_make_weak_ptr() const; @@ -121,9 +109,6 @@ protected: ~Weakable() { -#ifdef KERNEL - m_being_destroyed.store(true, AK::MemoryOrder::memory_order_release); -#endif revoke_weak_ptrs(); } @@ -135,11 +120,10 @@ protected: private: mutable RefPtr m_link; -#ifdef KERNEL - Atomic m_being_destroyed { false }; -#endif }; } using AK::Weakable; + +#endif diff --git a/Kernel/Library/ThreadSafeWeakable.h b/Kernel/Library/ThreadSafeWeakable.h new file mode 100644 index 0000000000..3abbcea2d1 --- /dev/null +++ b/Kernel/Library/ThreadSafeWeakable.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2018-2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace AK { + +template +class Weakable; +template +class WeakPtr; + +class WeakLink : public RefCounted { + template + friend class Weakable; + template + friend class WeakPtr; + +public: + template> + RefPtr strong_ref() const + requires(IsBaseOf) + { + RefPtr ref; + + { + // We don't want to be preempted while we are trying to obtain + // a strong reference + Kernel::ScopedCritical critical; + if (!(m_consumers.fetch_add(1u << 1, AK::MemoryOrder::memory_order_acquire) & 1u)) { + T* ptr = (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire); + if (ptr && ptr->try_ref()) + ref = adopt_ref(*ptr); + } + m_consumers.fetch_sub(1u << 1, AK::MemoryOrder::memory_order_release); + } + + return ref; + } + + template + T* unsafe_ptr() const + { + if (m_consumers.load(AK::MemoryOrder::memory_order_relaxed) & 1u) + return nullptr; + // NOTE: This may return a non-null pointer even if revocation + // has been triggered as there is a possible race! But it's "unsafe" + // anyway because we return a raw pointer without ensuring a + // reference... + return (T*)m_ptr.load(AK::MemoryOrder::memory_order_acquire); + } + + bool is_null() const + { + return unsafe_ptr() == nullptr; + } + + void revoke() + { + auto current_consumers = m_consumers.fetch_or(1u, AK::MemoryOrder::memory_order_relaxed); + VERIFY(!(current_consumers & 1u)); + // We flagged revocation, now wait until everyone trying to obtain + // a strong reference is done + while (current_consumers > 0) { + Kernel::Processor::wait_check(); + current_consumers = m_consumers.load(AK::MemoryOrder::memory_order_acquire) & ~1u; + } + // No one is trying to use it (anymore) + m_ptr.store(nullptr, AK::MemoryOrder::memory_order_release); + } + +private: + template + explicit WeakLink(T& weakable) + : m_ptr(&weakable) + { + } + mutable Atomic m_ptr; + mutable Atomic m_consumers; // LSB indicates revocation in progress +}; + +template +class Weakable { +private: + class Link; + +public: + template + ErrorOr> try_make_weak_ptr() const; + +protected: + Weakable() = default; + + ~Weakable() + { + m_being_destroyed.store(true, AK::MemoryOrder::memory_order_release); + revoke_weak_ptrs(); + } + + void revoke_weak_ptrs() + { + if (auto link = move(m_link)) + link->revoke(); + } + +private: + mutable RefPtr m_link; + Atomic m_being_destroyed { false }; +}; + +} + +using AK::Weakable;