summaryrefslogtreecommitdiff
path: root/Kernel/Library/NonnullLockRefPtr.h
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2022-08-19 20:53:40 +0200
committerAndreas Kling <kling@serenityos.org>2022-08-20 17:20:43 +0200
commit11eee67b8510767d76fb4793e3b62ac1793dd723 (patch)
tree8ce47a3813ce74bba56c60f62b29bdd6cdf287da /Kernel/Library/NonnullLockRefPtr.h
parente475263113387404e63cdc3666391934604eb6e7 (diff)
downloadserenity-11eee67b8510767d76fb4793e3b62ac1793dd723.zip
Kernel: Make self-contained locking smart pointers their own classes
Until now, our kernel has reimplemented a number of AK classes to provide automatic internal locking: - RefPtr - NonnullRefPtr - WeakPtr - Weakable This patch renames the Kernel classes so that they can coexist with the original AK classes: - RefPtr => LockRefPtr - NonnullRefPtr => NonnullLockRefPtr - WeakPtr => LockWeakPtr - Weakable => LockWeakable The goal here is to eventually get rid of the Lock* classes in favor of using external locking.
Diffstat (limited to 'Kernel/Library/NonnullLockRefPtr.h')
-rw-r--r--Kernel/Library/NonnullLockRefPtr.h337
1 files changed, 337 insertions, 0 deletions
diff --git a/Kernel/Library/NonnullLockRefPtr.h b/Kernel/Library/NonnullLockRefPtr.h
new file mode 100644
index 0000000000..44289a3b55
--- /dev/null
+++ b/Kernel/Library/NonnullLockRefPtr.h
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Assertions.h>
+#include <AK/Atomic.h>
+#include <AK/Format.h>
+#include <AK/NonnullRefPtr.h>
+#include <AK/Traits.h>
+#include <AK/Types.h>
+#ifdef KERNEL
+# include <Kernel/Arch/Processor.h>
+# include <Kernel/Arch/ScopedCritical.h>
+#endif
+
+#define NONNULLLOCKREFPTR_SCRUB_BYTE 0xa1
+
+namespace AK {
+
+template<typename T>
+class OwnPtr;
+template<typename T, typename PtrTraits>
+class LockRefPtr;
+
+template<typename T>
+class [[nodiscard]] NonnullLockRefPtr {
+ template<typename U, typename P>
+ friend class LockRefPtr;
+ template<typename U>
+ friend class NonnullLockRefPtr;
+ template<typename U>
+ friend class LockWeakPtr;
+
+public:
+ using ElementType = T;
+
+ enum AdoptTag { Adopt };
+
+ ALWAYS_INLINE NonnullLockRefPtr(T const& object)
+ : m_bits((FlatPtr)&object)
+ {
+ VERIFY(!(m_bits & 1));
+ const_cast<T&>(object).ref();
+ }
+ template<typename U>
+ ALWAYS_INLINE NonnullLockRefPtr(U const& object) requires(IsConvertible<U*, T*>)
+ : m_bits((FlatPtr) static_cast<T const*>(&object))
+ {
+ VERIFY(!(m_bits & 1));
+ const_cast<T&>(static_cast<T const&>(object)).ref();
+ }
+ ALWAYS_INLINE NonnullLockRefPtr(AdoptTag, T& object)
+ : m_bits((FlatPtr)&object)
+ {
+ VERIFY(!(m_bits & 1));
+ }
+ ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr&& other)
+ : m_bits((FlatPtr)&other.leak_ref())
+ {
+ VERIFY(!(m_bits & 1));
+ }
+ template<typename U>
+ ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr<U>&& other) requires(IsConvertible<U*, T*>)
+ : m_bits((FlatPtr)&other.leak_ref())
+ {
+ VERIFY(!(m_bits & 1));
+ }
+ ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr const& other)
+ : m_bits((FlatPtr)other.add_ref())
+ {
+ VERIFY(!(m_bits & 1));
+ }
+ template<typename U>
+ ALWAYS_INLINE NonnullLockRefPtr(NonnullLockRefPtr<U> const& other) requires(IsConvertible<U*, T*>)
+ : m_bits((FlatPtr)other.add_ref())
+ {
+ VERIFY(!(m_bits & 1));
+ }
+ ALWAYS_INLINE ~NonnullLockRefPtr()
+ {
+ assign(nullptr);
+#ifdef SANITIZE_PTRS
+ m_bits.store(explode_byte(NONNULLLOCKREFPTR_SCRUB_BYTE), AK::MemoryOrder::memory_order_relaxed);
+#endif
+ }
+
+ template<typename U>
+ NonnullLockRefPtr(OwnPtr<U> const&) = delete;
+ template<typename U>
+ NonnullLockRefPtr& operator=(OwnPtr<U> const&) = delete;
+
+ template<typename U>
+ NonnullLockRefPtr(LockRefPtr<U> const&) = delete;
+ template<typename U>
+ NonnullLockRefPtr& operator=(LockRefPtr<U> const&) = delete;
+ NonnullLockRefPtr(LockRefPtr<T> const&) = delete;
+ NonnullLockRefPtr& operator=(LockRefPtr<T> const&) = delete;
+
+ NonnullLockRefPtr& operator=(NonnullLockRefPtr const& other)
+ {
+ if (this != &other)
+ assign(other.add_ref());
+ return *this;
+ }
+
+ template<typename U>
+ NonnullLockRefPtr& operator=(NonnullLockRefPtr<U> const& other) requires(IsConvertible<U*, T*>)
+ {
+ assign(other.add_ref());
+ return *this;
+ }
+
+ ALWAYS_INLINE NonnullLockRefPtr& operator=(NonnullLockRefPtr&& other)
+ {
+ if (this != &other)
+ assign(&other.leak_ref());
+ return *this;
+ }
+
+ template<typename U>
+ NonnullLockRefPtr& operator=(NonnullLockRefPtr<U>&& other) requires(IsConvertible<U*, T*>)
+ {
+ assign(&other.leak_ref());
+ return *this;
+ }
+
+ NonnullLockRefPtr& operator=(T const& object)
+ {
+ const_cast<T&>(object).ref();
+ assign(const_cast<T*>(&object));
+ return *this;
+ }
+
+ [[nodiscard]] ALWAYS_INLINE T& leak_ref()
+ {
+ T* ptr = exchange(nullptr);
+ VERIFY(ptr);
+ return *ptr;
+ }
+
+ ALWAYS_INLINE RETURNS_NONNULL T* ptr()
+ {
+ return as_nonnull_ptr();
+ }
+ ALWAYS_INLINE RETURNS_NONNULL T const* ptr() const
+ {
+ return as_nonnull_ptr();
+ }
+
+ ALWAYS_INLINE RETURNS_NONNULL T* operator->()
+ {
+ return as_nonnull_ptr();
+ }
+ ALWAYS_INLINE RETURNS_NONNULL T const* operator->() const
+ {
+ return as_nonnull_ptr();
+ }
+
+ ALWAYS_INLINE T& operator*()
+ {
+ return *as_nonnull_ptr();
+ }
+ ALWAYS_INLINE T const& operator*() const
+ {
+ return *as_nonnull_ptr();
+ }
+
+ ALWAYS_INLINE RETURNS_NONNULL operator T*()
+ {
+ return as_nonnull_ptr();
+ }
+ ALWAYS_INLINE RETURNS_NONNULL operator T const*() const
+ {
+ return as_nonnull_ptr();
+ }
+
+ ALWAYS_INLINE operator T&()
+ {
+ return *as_nonnull_ptr();
+ }
+ ALWAYS_INLINE operator T const&() const
+ {
+ return *as_nonnull_ptr();
+ }
+
+ operator bool() const = delete;
+ bool operator!() const = delete;
+
+ void swap(NonnullLockRefPtr& other)
+ {
+ if (this == &other)
+ return;
+
+ // NOTE: swap is not atomic!
+ T* other_ptr = other.exchange(nullptr);
+ T* ptr = exchange(other_ptr);
+ other.exchange(ptr);
+ }
+
+ template<typename U>
+ void swap(NonnullLockRefPtr<U>& other) requires(IsConvertible<U*, T*>)
+ {
+ // NOTE: swap is not atomic!
+ U* other_ptr = other.exchange(nullptr);
+ T* ptr = exchange(other_ptr);
+ other.exchange(ptr);
+ }
+
+ // clang-format off
+private:
+ NonnullLockRefPtr() = delete;
+ // clang-format on
+
+ ALWAYS_INLINE T* as_ptr() const
+ {
+ return (T*)(m_bits.load(AK::MemoryOrder::memory_order_relaxed) & ~(FlatPtr)1);
+ }
+
+ ALWAYS_INLINE RETURNS_NONNULL T* as_nonnull_ptr() const
+ {
+ T* ptr = (T*)(m_bits.load(AK::MemoryOrder::memory_order_relaxed) & ~(FlatPtr)1);
+ VERIFY(ptr);
+ return ptr;
+ }
+
+ template<typename F>
+ void do_while_locked(F f) const
+ {
+#ifdef KERNEL
+ // We don't want to be pre-empted while we have the lock bit set
+ Kernel::ScopedCritical critical;
+#endif
+ FlatPtr bits;
+ for (;;) {
+ bits = m_bits.fetch_or(1, AK::MemoryOrder::memory_order_acq_rel);
+ if (!(bits & 1))
+ break;
+#ifdef KERNEL
+ Kernel::Processor::wait_check();
+#endif
+ }
+ VERIFY(!(bits & 1));
+ f((T*)bits);
+ m_bits.store(bits, AK::MemoryOrder::memory_order_release);
+ }
+
+ ALWAYS_INLINE void assign(T* new_ptr)
+ {
+ T* prev_ptr = exchange(new_ptr);
+ unref_if_not_null(prev_ptr);
+ }
+
+ ALWAYS_INLINE T* exchange(T* new_ptr)
+ {
+ VERIFY(!((FlatPtr)new_ptr & 1));
+#ifdef KERNEL
+ // We don't want to be pre-empted while we have the lock bit set
+ Kernel::ScopedCritical critical;
+#endif
+ // Only exchange while not locked
+ FlatPtr expected = m_bits.load(AK::MemoryOrder::memory_order_relaxed);
+ for (;;) {
+ expected &= ~(FlatPtr)1; // only if lock bit is not set
+ if (m_bits.compare_exchange_strong(expected, (FlatPtr)new_ptr, AK::MemoryOrder::memory_order_acq_rel))
+ break;
+#ifdef KERNEL
+ Kernel::Processor::wait_check();
+#endif
+ }
+ VERIFY(!(expected & 1));
+ return (T*)expected;
+ }
+
+ T* add_ref() const
+ {
+#ifdef KERNEL
+ // We don't want to be pre-empted while we have the lock bit set
+ Kernel::ScopedCritical critical;
+#endif
+ // Lock the pointer
+ FlatPtr expected = m_bits.load(AK::MemoryOrder::memory_order_relaxed);
+ for (;;) {
+ expected &= ~(FlatPtr)1; // only if lock bit is not set
+ if (m_bits.compare_exchange_strong(expected, expected | 1, AK::MemoryOrder::memory_order_acq_rel))
+ break;
+#ifdef KERNEL
+ Kernel::Processor::wait_check();
+#endif
+ }
+
+ // Add a reference now that we locked the pointer
+ ref_if_not_null((T*)expected);
+
+ // Unlock the pointer again
+ m_bits.store(expected, AK::MemoryOrder::memory_order_release);
+ return (T*)expected;
+ }
+
+ mutable Atomic<FlatPtr> m_bits { 0 };
+};
+
+template<typename T>
+inline NonnullLockRefPtr<T> adopt_lock_ref(T& object)
+{
+ return NonnullLockRefPtr<T>(NonnullLockRefPtr<T>::Adopt, object);
+}
+
+template<typename T>
+struct Formatter<NonnullLockRefPtr<T>> : Formatter<T const*> {
+ ErrorOr<void> format(FormatBuilder& builder, NonnullLockRefPtr<T> const& value)
+ {
+ return Formatter<T const*>::format(builder, value.ptr());
+ }
+};
+
+template<typename T, typename U>
+inline void swap(NonnullLockRefPtr<T>& a, NonnullLockRefPtr<U>& b) requires(IsConvertible<U*, T*>)
+{
+ a.swap(b);
+}
+
+}
+
+template<typename T>
+struct Traits<NonnullLockRefPtr<T>> : public GenericTraits<NonnullLockRefPtr<T>> {
+ using PeekType = T*;
+ using ConstPeekType = T const*;
+ static unsigned hash(NonnullLockRefPtr<T> const& p) { return ptr_hash(p.ptr()); }
+ static bool equals(NonnullLockRefPtr<T> const& a, NonnullLockRefPtr<T> const& b) { return a.ptr() == b.ptr(); }
+};
+
+using AK::adopt_lock_ref;
+using AK::NonnullLockRefPtr;