mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 14:42:44 +00:00 
			
		
		
		
	 b18a7297c5
			
		
	
	
		b18a7297c5
		
	
	
	
	
		
			
			This file does not contain any architecture specific implementations, so we can move it to the Kernel base directory. Also update the relevant include paths.
		
			
				
	
	
		
			340 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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/ScopedCritical.h>
 | |
| #endif
 | |
| 
 | |
| #define NONNULLLOCKREFPTR_SCRUB_BYTE 0xa1
 | |
| 
 | |
| namespace AK {
 | |
| 
 | |
| 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);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     NonnullLockRefPtr() = delete;
 | |
| 
 | |
|     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;
 |