ThreadSafeRefCounted.h 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Assertions.h>
  8. #include <AK/Atomic.h>
  9. #include <AK/Checked.h>
  10. #include <AK/Noncopyable.h>
  11. #include <AK/Platform.h>
  12. #include <AK/StdLibExtras.h>
  13. namespace AK {
  14. template<class T>
  15. constexpr auto call_will_be_destroyed_if_present(const T* object) -> decltype(const_cast<T*>(object)->will_be_destroyed(), TrueType {})
  16. {
  17. const_cast<T*>(object)->will_be_destroyed();
  18. return {};
  19. }
  20. // NOLINTNEXTLINE(cert-dcl50-cpp) variadic argument used to implement "is detected" pattern
  21. constexpr auto call_will_be_destroyed_if_present(...) -> FalseType
  22. {
  23. return {};
  24. }
  25. template<class T>
  26. constexpr auto call_one_ref_left_if_present(const T* object) -> decltype(const_cast<T*>(object)->one_ref_left(), TrueType {})
  27. {
  28. const_cast<T*>(object)->one_ref_left();
  29. return {};
  30. }
  31. // NOLINTNEXTLINE(cert-dcl50-cpp) variadic argument used to implement "is detected" pattern
  32. constexpr auto call_one_ref_left_if_present(...) -> FalseType
  33. {
  34. return {};
  35. }
  36. class RefCountedBase {
  37. AK_MAKE_NONCOPYABLE(RefCountedBase);
  38. AK_MAKE_NONMOVABLE(RefCountedBase);
  39. public:
  40. using RefCountType = unsigned int;
  41. using AllowOwnPtr = FalseType;
  42. void ref() const
  43. {
  44. auto old_ref_count = m_ref_count.fetch_add(1, AK::MemoryOrder::memory_order_relaxed);
  45. VERIFY(old_ref_count > 0);
  46. VERIFY(!Checked<RefCountType>::addition_would_overflow(old_ref_count, 1));
  47. }
  48. [[nodiscard]] bool try_ref() const
  49. {
  50. RefCountType expected = m_ref_count.load(AK::MemoryOrder::memory_order_relaxed);
  51. for (;;) {
  52. if (expected == 0)
  53. return false;
  54. VERIFY(!Checked<RefCountType>::addition_would_overflow(expected, 1));
  55. if (m_ref_count.compare_exchange_strong(expected, expected + 1, AK::MemoryOrder::memory_order_acquire))
  56. return true;
  57. }
  58. }
  59. [[nodiscard]] RefCountType ref_count() const
  60. {
  61. return m_ref_count.load(AK::MemoryOrder::memory_order_relaxed);
  62. }
  63. protected:
  64. RefCountedBase() = default;
  65. ~RefCountedBase()
  66. {
  67. VERIFY(m_ref_count.load(AK::MemoryOrder::memory_order_relaxed) == 0);
  68. }
  69. RefCountType deref_base() const
  70. {
  71. auto old_ref_count = m_ref_count.fetch_sub(1, AK::MemoryOrder::memory_order_acq_rel);
  72. VERIFY(old_ref_count > 0);
  73. return old_ref_count - 1;
  74. }
  75. mutable Atomic<RefCountType> m_ref_count { 1 };
  76. };
  77. template<typename T>
  78. class RefCounted : public RefCountedBase {
  79. public:
  80. bool unref() const
  81. {
  82. auto new_ref_count = deref_base();
  83. if (new_ref_count == 0) {
  84. call_will_be_destroyed_if_present(static_cast<const T*>(this));
  85. delete static_cast<const T*>(this);
  86. return true;
  87. }
  88. if (new_ref_count == 1)
  89. call_one_ref_left_if_present(static_cast<const T*>(this));
  90. return false;
  91. }
  92. };
  93. }
  94. using AK::RefCounted;
  95. using AK::RefCountedBase;