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