1 // Copyright 2018 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 <fbl/ref_counted_upgradeable.h>
8 #include <zircon/assert.h>
9 #include <zircon/compiler.h>
10 #include <zircon/types.h>
11 
12 #include <atomic>
13 
14 namespace fs {
15 
16 // VnodeRefCounted implements a customized RefCounted object.
17 //
18 // It adds an additional method, "ResurrectRef", which allows Vnodes to be
19 // re-used after a reference count of zero has been reached.
20 template <typename T,
21           bool EnableAdoptionValidator = ZX_DEBUG_ASSERT_IMPLEMENTED>
22 class VnodeRefCounted :
23     private ::fbl::internal::RefCountedUpgradeableBase<EnableAdoptionValidator> {
24 public:
VnodeRefCounted()25     constexpr VnodeRefCounted() {}
~VnodeRefCounted()26     ~VnodeRefCounted() {}
27 
28     using ::fbl::internal::RefCountedBase<EnableAdoptionValidator>::AddRef;
29     using ::fbl::internal::RefCountedBase<EnableAdoptionValidator>::Release;
30     using ::fbl::internal::RefCountedBase<EnableAdoptionValidator>::Adopt;
31     using ::fbl::internal::RefCountedBase<EnableAdoptionValidator>::ref_count_debug;
32 
33     // Don't use this method. See the relevant RefPtr implementation for details.
34     using ::fbl::internal::RefCountedUpgradeableBase<EnableAdoptionValidator>::
35         AddRefMaybeInDestructor;
36 
37     // VnodeRefCounted<> instances may not be copied, assigned or moved.
38     DISALLOW_COPY_ASSIGN_AND_MOVE(VnodeRefCounted);
39 
40     // This method should only be called if the refcount was "zero", implying the
41     // object is currently executing fbl_recycle. In this case, the refcount
42     // is increased by one.
43     //
44     // This method may be called to prevent fbl_recycle from following the
45     // typical path of object deletion: instead of destroying the object,
46     // this function can be called to "reset" the lifecycle of the RefCounted
47     // object to the initialized state of "ref_count_ = 1", so it can
48     // continue to be utilized after there are no strong references.
49     //
50     // This function should be used EXCLUSIVELY from within fbl_recycle.
51     // If other clients (outside fbl_recycle) attempt to resurrect the Vnode
52     // concurrently with a call to Vnode::fbl_recycle, they risk going through
53     // the entire Vnode lifecycle and destroying it (with another call to
54     // Vnode::fbl_recycle) before the initial recycle execution terminates.
ResurrectRef()55     void ResurrectRef() const {
56         if (EnableAdoptionValidator) {
57             int32_t old = this->ref_count_.load(std::memory_order_relaxed);
58             ZX_DEBUG_ASSERT_MSG(old == 0, "count %d(0x%08x) != 0\n", old, old);
59         }
60         this->ref_count_.store(1, std::memory_order_relaxed);
61     }
62 };
63 
64 } // namespace fs
65