1 // Copyright 2016 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #pragma once 6 7 #include <atomic> 8 #include <fbl/canary.h> 9 #include <zircon/assert.h> 10 #include <zircon/compiler.h> 11 12 namespace fbl { 13 namespace internal { 14 15 // Adoption validation will help to catch: 16 // - Double-adoptions 17 // - AddRef/Release without adopting first 18 // - Re-wrapping raw pointers to destroyed objects 19 // 20 // It also provides some limited defense against 21 // - Wrapping bad pointers 22 template <bool EnableAdoptionValidator> 23 class RefCountedBase { 24 protected: RefCountedBase()25 constexpr RefCountedBase() 26 : ref_count_(kPreAdoptSentinel) {} 27 ~RefCountedBase()28 ~RefCountedBase() { 29 if (EnableAdoptionValidator) { 30 // Reset the ref-count back to the pre-adopt sentinel value so that we 31 // have the best chance of catching a use-after-free situation, even if 32 // we have a messed up mix of debug/release translation units being 33 // linked together. 34 ref_count_.store(kPreAdoptSentinel, std::memory_order_release); 35 } 36 } 37 AddRef()38 void AddRef() const { 39 const int32_t rc = ref_count_.fetch_add(1, std::memory_order_relaxed); 40 41 // This assertion will fire if either of the following occur. 42 // 43 // 1) someone calls AddRef() before the object has been properly 44 // Adopted. 45 // 46 // 2) someone calls AddRef() on a ref-counted object that has 47 // reached ref_count_ == 0 but has not been destroyed yet. This 48 // could happen by manually calling AddRef(), or re-wrapping such a 49 // pointer with WrapRefPtr() or RefPtr<T>(T*) (both of which call 50 // AddRef()). 51 // 52 // Note: leave the ASSERT on in all builds. The constant 53 // EnableAdoptionValidator check above should cause this code path to be 54 // pruned in release builds, but leaving this as an always on ASSERT 55 // will mean that the tests continue to function even when built as 56 // release. 57 if (EnableAdoptionValidator) { 58 ZX_ASSERT_MSG(rc >= 1, "count %d(0x%08x) < 1\n", rc, static_cast<uint32_t>(rc)); 59 } 60 } 61 62 // Returns true if the object should self-delete. Release()63 bool Release() const __WARN_UNUSED_RESULT { 64 const int32_t rc = ref_count_.fetch_sub(1, std::memory_order_release); 65 66 // This assertion will fire if someone manually calls Release() 67 // on a ref-counted object too many times, or if Release is called 68 // before an object has been Adopted. 69 // 70 // Note: leave the ASSERT on in all builds. The constant 71 // EnableAdoptionValidator check above should cause this code path to be 72 // pruned in release builds, but leaving this as an always on ASSERT 73 // will mean that the tests continue to function even when built as 74 // release. 75 if (EnableAdoptionValidator) { 76 ZX_ASSERT_MSG(rc >= 1, "count %d(0x%08x) < 1\n", rc, static_cast<uint32_t>(rc)); 77 } 78 79 if (rc == 1) { 80 atomic_thread_fence(std::memory_order_acquire); 81 return true; 82 } 83 84 return false; 85 } 86 Adopt()87 void Adopt() const { 88 // TODO(johngro): turn this into an if-constexpr when we have moved up 89 // to C++17 90 if (EnableAdoptionValidator) { 91 int32_t expected = kPreAdoptSentinel; 92 bool res = ref_count_.compare_exchange_strong(expected, 1, 93 std::memory_order_acq_rel, 94 std::memory_order_acquire); 95 // Note: leave the ASSERT on in all builds. The constant 96 // EnableAdoptionValidator check above should cause this code path 97 // to be pruned in release builds, but leaving this as an always on 98 // ASSERT will mean that the tests continue to function even when 99 // built as release. 100 ZX_ASSERT_MSG(res, 101 "count(0x%08x) != sentinel(0x%08x)\n", 102 static_cast<uint32_t>(expected), 103 static_cast<uint32_t>(kPreAdoptSentinel)); 104 } else { 105 ref_count_.store(1, std::memory_order_release); 106 } 107 } 108 109 // Current ref count. Only to be used for debugging purposes. ref_count_debug()110 int ref_count_debug() const { 111 return ref_count_.load(std::memory_order_relaxed); 112 } 113 114 // Note: 115 // 116 // The PreAdoptSentinel value is chosen specifically to be negative when 117 // stored as an int32_t, and as far away from becoming positive (via either 118 // addition or subtraction) as possible. These properties allow us to 119 // combine the debug-build adopt sanity checks and the lifecycle sanity 120 // checks into a single debug assert. 121 // 122 // If a user creates an object, but never adopts it, they would need to 123 // perform 0x4000000 (about 1 billion) unchecked AddRef or Release 124 // operations before making the internal ref_count become positive again. 125 // At this point, even a checked AddRef or Release operation would fail to 126 // detect the bad state of the system fails to detect the problem. 127 // 128 static constexpr int32_t kPreAdoptSentinel = static_cast<int32_t>(0xC0000000); 129 mutable std::atomic_int32_t ref_count_; 130 }; 131 132 } // namespace internal 133 } // namespace fbl 134