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