serenity/AK/RefCounted.h
Andreas Kling 5b1f697460 AK+Kernel: Make automatically locking RefPtr & co a kernel-only thing
Some time ago, automatic locking was added to the AK smart pointers to
paper over various race conditions in the kernel. Until we've actually
solved the issues in the kernel, we're stuck with the locking.

However, we don't need to punish single-threaded userspace programs with
the high cost of locking. This patch moves the thread-safe variants of
RefPtr, NonnullRefPtr, WeakPtr and RefCounted into Kernel/Library/.
2021-10-07 19:27:30 +02:00

106 lines
2.3 KiB
C++

/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#ifdef KERNEL
# include <Kernel/Library/ThreadSafeRefCounted.h>
#else
# include <AK/Assertions.h>
# include <AK/Checked.h>
# include <AK/Noncopyable.h>
# include <AK/Platform.h>
# include <AK/StdLibExtras.h>
namespace AK {
template<class T>
constexpr auto call_will_be_destroyed_if_present(const T* object) -> decltype(const_cast<T*>(object)->will_be_destroyed(), TrueType {})
{
const_cast<T*>(object)->will_be_destroyed();
return {};
}
constexpr auto call_will_be_destroyed_if_present(...) -> FalseType
{
return {};
}
template<class T>
constexpr auto call_one_ref_left_if_present(const T* object) -> decltype(const_cast<T*>(object)->one_ref_left(), TrueType {})
{
const_cast<T*>(object)->one_ref_left();
return {};
}
constexpr auto call_one_ref_left_if_present(...) -> FalseType
{
return {};
}
class RefCountedBase {
AK_MAKE_NONCOPYABLE(RefCountedBase);
AK_MAKE_NONMOVABLE(RefCountedBase);
public:
using RefCountType = unsigned int;
using AllowOwnPtr = FalseType;
void ref() const
{
VERIFY(m_ref_count > 0);
VERIFY(!Checked<RefCountType>::addition_would_overflow(m_ref_count, 1));
++m_ref_count;
}
[[nodiscard]] bool try_ref() const
{
if (m_ref_count == 0)
return false;
ref();
return true;
}
[[nodiscard]] RefCountType ref_count() const { return m_ref_count; }
protected:
RefCountedBase() = default;
~RefCountedBase() { VERIFY(!m_ref_count); }
RefCountType deref_base() const
{
VERIFY(m_ref_count);
return --m_ref_count;
}
RefCountType mutable m_ref_count { 1 };
};
template<typename T>
class RefCounted : public RefCountedBase {
public:
bool unref() const
{
auto new_ref_count = deref_base();
if (new_ref_count == 0) {
call_will_be_destroyed_if_present(static_cast<const T*>(this));
delete static_cast<const T*>(this);
return true;
} else if (new_ref_count == 1) {
call_one_ref_left_if_present(static_cast<const T*>(this));
}
return false;
}
};
}
using AK::RefCounted;
using AK::RefCountedBase;
#endif