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 <zircon/assert.h> 8#include <zircon/compiler.h> 9#include <fbl/atomic.h> 10#include <fbl/canary.h> 11 12namespace fbl { 13namespace 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 22template <bool EnableAdoptionValidator> 23class RefCountedBase { 24protected: 25 constexpr RefCountedBase() 26 : ref_count_(kPreAdoptSentinel) {} 27 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, memory_order_release); 35 } 36 } 37 38 void AddRef() const { 39 const int32_t rc = ref_count_.fetch_add(1, 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 // This method should not be used. See MakeRefPtrUpgradeFromRaw() 63 // for details in the proper use of this method. The actual job of 64 // this function is to atomically increment the refcount if the 65 // refcount is greater than zero. 66 // 67 // This method returns false if the object was found with an invalid 68 // refcount (refcount was <= 0), and true if the refcount was not zero and 69 // it was incremented. 70 // 71 // The procedure used is the while-CAS loop with the advantage that 72 // compare_exchange on failure updates |old| on failure (to exchange) 73 // so the loop does not have to do a separate load. 74 // 75 bool AddRefMaybeInDestructor() const __WARN_UNUSED_RESULT { 76 int32_t old = ref_count_.load(memory_order_acquire); 77 do { 78 if (old <= 0) { 79 return false; 80 } 81 } while (!ref_count_.compare_exchange_weak(&old, 82 old + 1, 83 memory_order_acq_rel, 84 memory_order_acquire)); 85 return true; 86 } 87 88 // Returns true if the object should self-delete. 89 bool Release() const __WARN_UNUSED_RESULT { 90 const int32_t rc = ref_count_.fetch_sub(1, memory_order_release); 91 92 // This assertion will fire if someone manually calls Release() 93 // on a ref-counted object too many times, or if Release is called 94 // before an object has been Adopted. 95 // 96 // Note: leave the ASSERT on in all builds. The constant 97 // EnableAdoptionValidator check above should cause this code path to be 98 // pruned in release builds, but leaving this as an always on ASSERT 99 // will mean that the tests continue to function even when built as 100 // release. 101 if (EnableAdoptionValidator) { 102 ZX_ASSERT_MSG(rc >= 1, "count %d(0x%08x) < 1\n", rc, static_cast<uint32_t>(rc)); 103 } 104 105 if (rc == 1) { 106 atomic_thread_fence(memory_order_acquire); 107 return true; 108 } 109 110 return false; 111 } 112 113 void Adopt() const { 114 // TODO(johngro): turn this into an if-constexpr when we have moved up 115 // to C++17 116 if (EnableAdoptionValidator) { 117 int32_t expected = kPreAdoptSentinel; 118 bool res = ref_count_.compare_exchange_strong(&expected, 1, 119 memory_order_acq_rel, 120 memory_order_acquire); 121 // Note: leave the ASSERT on in all builds. The constant 122 // EnableAdoptionValidator check above should cause this code path 123 // to be pruned in release builds, but leaving this as an always on 124 // ASSERT will mean that the tests continue to function even when 125 // built as release. 126 ZX_ASSERT_MSG(res, 127 "count(0x%08x) != sentinel(0x%08x)\n", 128 static_cast<uint32_t>(expected), 129 static_cast<uint32_t>(kPreAdoptSentinel)); 130 } else { 131 ref_count_.store(1, memory_order_release); 132 } 133 } 134 135 // Current ref count. Only to be used for debugging purposes. 136 int ref_count_debug() const { 137 return ref_count_.load(memory_order_relaxed); 138 } 139 140 // Note: 141 // 142 // The PreAdoptSentinel value is chosen specifically to be negative when 143 // stored as an int32_t, and as far away from becoming positiive (via either 144 // addition or subtraction) as possible. These properties allow us to 145 // combine the debug-build adopt sanity checks and the lifecycle sanity 146 // checks into a single debug assert. 147 // 148 // If a user creates an object, but never adopts it, they would need to 149 // perform 0x4000000 (about 1 billion) unchecked AddRef or Release 150 // operations before making the internal ref_count become positive again. 151 // At this point, even a checked AddRef or Release operation would fail to 152 // detect the bad state of the system fails to detect the problem. 153 // 154 static constexpr int32_t kPreAdoptSentinel = static_cast<int32_t>(0xC0000000); 155 mutable fbl::atomic_int32_t ref_count_; 156}; 157 158} // namespace internal 159} // namespace fbl 160