xref: /aosp_15_r20/external/libchrome/base/task_scheduler/tracked_ref.h (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker // Copyright 2018 The Chromium Authors. All rights reserved.
2*635a8641SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*635a8641SAndroid Build Coastguard Worker // found in the LICENSE file.
4*635a8641SAndroid Build Coastguard Worker 
5*635a8641SAndroid Build Coastguard Worker #ifndef BASE_TASK_SCHEDULER_TRACKED_REF_H_
6*635a8641SAndroid Build Coastguard Worker #define BASE_TASK_SCHEDULER_TRACKED_REF_H_
7*635a8641SAndroid Build Coastguard Worker 
8*635a8641SAndroid Build Coastguard Worker #include <memory>
9*635a8641SAndroid Build Coastguard Worker 
10*635a8641SAndroid Build Coastguard Worker #include "base/atomic_ref_count.h"
11*635a8641SAndroid Build Coastguard Worker #include "base/gtest_prod_util.h"
12*635a8641SAndroid Build Coastguard Worker #include "base/logging.h"
13*635a8641SAndroid Build Coastguard Worker #include "base/macros.h"
14*635a8641SAndroid Build Coastguard Worker #include "base/memory/ptr_util.h"
15*635a8641SAndroid Build Coastguard Worker #include "base/synchronization/waitable_event.h"
16*635a8641SAndroid Build Coastguard Worker 
17*635a8641SAndroid Build Coastguard Worker namespace base {
18*635a8641SAndroid Build Coastguard Worker namespace internal {
19*635a8641SAndroid Build Coastguard Worker 
20*635a8641SAndroid Build Coastguard Worker // TrackedRefs are effectively a ref-counting scheme for objects that have a
21*635a8641SAndroid Build Coastguard Worker // single owner.
22*635a8641SAndroid Build Coastguard Worker //
23*635a8641SAndroid Build Coastguard Worker // Deletion is still controlled by the single owner but ~T() itself will block
24*635a8641SAndroid Build Coastguard Worker // until all the TrackedRefs handed by its TrackedRefFactory have been released
25*635a8641SAndroid Build Coastguard Worker // (by ~TrackedRef<T>()).
26*635a8641SAndroid Build Coastguard Worker //
27*635a8641SAndroid Build Coastguard Worker // Just like WeakPtrFactory: TrackedRefFactory<T> should be the last member of T
28*635a8641SAndroid Build Coastguard Worker // to ensure ~TrackedRefFactory<T>() runs first in ~T().
29*635a8641SAndroid Build Coastguard Worker //
30*635a8641SAndroid Build Coastguard Worker // The owner of a T should hence be certain that the last TrackedRefs to T are
31*635a8641SAndroid Build Coastguard Worker // already gone or on their way out before destroying it or ~T() will hang
32*635a8641SAndroid Build Coastguard Worker // (indicating a bug in the tear down logic -- proper refcounting on the other
33*635a8641SAndroid Build Coastguard Worker // hand would result in a leak).
34*635a8641SAndroid Build Coastguard Worker //
35*635a8641SAndroid Build Coastguard Worker // TrackedRefFactory only makes sense to use on types that are always leaked in
36*635a8641SAndroid Build Coastguard Worker // production but need to be torn down in tests (blocking destruction is
37*635a8641SAndroid Build Coastguard Worker // impractical in production -- ref. ScopedAllowBaseSyncPrimitivesForTesting
38*635a8641SAndroid Build Coastguard Worker // below).
39*635a8641SAndroid Build Coastguard Worker //
40*635a8641SAndroid Build Coastguard Worker // Why would we ever need such a thing? In task_scheduler there is a clear
41*635a8641SAndroid Build Coastguard Worker // ownership hierarchy with mostly single owners and little refcounting. In
42*635a8641SAndroid Build Coastguard Worker // production nothing is ever torn down so this isn't a problem. In tests
43*635a8641SAndroid Build Coastguard Worker // however we must JoinForTesting(). At that point, all the raw back T* refs
44*635a8641SAndroid Build Coastguard Worker // used by the worker threads are problematic because they can result in use-
45*635a8641SAndroid Build Coastguard Worker // after-frees if a worker outlives the deletion of its corresponding
46*635a8641SAndroid Build Coastguard Worker // TaskScheduler/TaskTracker/SchedulerWorkerPool/etc.
47*635a8641SAndroid Build Coastguard Worker //
48*635a8641SAndroid Build Coastguard Worker // JoinForTesting() isn't so hard when all workers are managed. But with cleanup
49*635a8641SAndroid Build Coastguard Worker // semantics (reclaiming a worker who's been idle for too long) it becomes
50*635a8641SAndroid Build Coastguard Worker // tricky because workers can go unaccounted for before they exit their main
51*635a8641SAndroid Build Coastguard Worker // (https://crbug.com/827615).
52*635a8641SAndroid Build Coastguard Worker //
53*635a8641SAndroid Build Coastguard Worker // For that reason and to clearly document the ownership model, task_scheduler
54*635a8641SAndroid Build Coastguard Worker // uses TrackedRefs.
55*635a8641SAndroid Build Coastguard Worker //
56*635a8641SAndroid Build Coastguard Worker // On top of being a clearer ownership model than proper refcounting, a hang in
57*635a8641SAndroid Build Coastguard Worker // tear down in a test with out-of-order tear down logic is much preferred to
58*635a8641SAndroid Build Coastguard Worker // letting its worker thread and associated constructs outlive the test
59*635a8641SAndroid Build Coastguard Worker // (potentially resulting in flakes in unrelated tests running later in the same
60*635a8641SAndroid Build Coastguard Worker // process).
61*635a8641SAndroid Build Coastguard Worker //
62*635a8641SAndroid Build Coastguard Worker // Note: While there's nothing task_scheduler specific about TrackedRefs it
63*635a8641SAndroid Build Coastguard Worker // requires an ownership model where all the TrackedRefs are released on other
64*635a8641SAndroid Build Coastguard Worker // threads in sync with ~T(). This isn't a typical use case beyond shutting down
65*635a8641SAndroid Build Coastguard Worker // TaskScheduler in tests and as such this is kept internal here for now.
66*635a8641SAndroid Build Coastguard Worker 
67*635a8641SAndroid Build Coastguard Worker template <class T>
68*635a8641SAndroid Build Coastguard Worker class TrackedRefFactory;
69*635a8641SAndroid Build Coastguard Worker 
70*635a8641SAndroid Build Coastguard Worker // TrackedRef<T> can be used like a T*.
71*635a8641SAndroid Build Coastguard Worker template <class T>
72*635a8641SAndroid Build Coastguard Worker class TrackedRef {
73*635a8641SAndroid Build Coastguard Worker  public:
74*635a8641SAndroid Build Coastguard Worker   // Moveable and copyable.
TrackedRef(TrackedRef<T> && other)75*635a8641SAndroid Build Coastguard Worker   TrackedRef(TrackedRef<T>&& other)
76*635a8641SAndroid Build Coastguard Worker       : ptr_(other.ptr_), factory_(other.factory_) {
77*635a8641SAndroid Build Coastguard Worker     // Null out |other_|'s factory so its destructor doesn't decrement
78*635a8641SAndroid Build Coastguard Worker     // |live_tracked_refs_|.
79*635a8641SAndroid Build Coastguard Worker     other.factory_ = nullptr;
80*635a8641SAndroid Build Coastguard Worker   }
TrackedRef(const TrackedRef<T> & other)81*635a8641SAndroid Build Coastguard Worker   TrackedRef(const TrackedRef<T>& other)
82*635a8641SAndroid Build Coastguard Worker       : ptr_(other.ptr_), factory_(other.factory_) {
83*635a8641SAndroid Build Coastguard Worker     factory_->live_tracked_refs_.Increment();
84*635a8641SAndroid Build Coastguard Worker   }
85*635a8641SAndroid Build Coastguard Worker 
86*635a8641SAndroid Build Coastguard Worker   // Intentionally not assignable for now because it makes the logic slightly
87*635a8641SAndroid Build Coastguard Worker   // convoluted and it's not a use case that makes sense for the types using
88*635a8641SAndroid Build Coastguard Worker   // this at the moment.
89*635a8641SAndroid Build Coastguard Worker   TrackedRef& operator=(TrackedRef<T>&& other) = delete;
90*635a8641SAndroid Build Coastguard Worker   TrackedRef& operator=(const TrackedRef<T>& other) = delete;
91*635a8641SAndroid Build Coastguard Worker 
~TrackedRef()92*635a8641SAndroid Build Coastguard Worker   ~TrackedRef() {
93*635a8641SAndroid Build Coastguard Worker     if (factory_ && !factory_->live_tracked_refs_.Decrement()) {
94*635a8641SAndroid Build Coastguard Worker       DCHECK(factory_->ready_to_destroy_);
95*635a8641SAndroid Build Coastguard Worker       DCHECK(!factory_->ready_to_destroy_->IsSignaled());
96*635a8641SAndroid Build Coastguard Worker       factory_->ready_to_destroy_->Signal();
97*635a8641SAndroid Build Coastguard Worker     }
98*635a8641SAndroid Build Coastguard Worker   }
99*635a8641SAndroid Build Coastguard Worker 
100*635a8641SAndroid Build Coastguard Worker   T& operator*() const { return *ptr_; }
101*635a8641SAndroid Build Coastguard Worker 
102*635a8641SAndroid Build Coastguard Worker   T* operator->() const { return ptr_; }
103*635a8641SAndroid Build Coastguard Worker 
104*635a8641SAndroid Build Coastguard Worker   explicit operator bool() const { return ptr_ != nullptr; }
105*635a8641SAndroid Build Coastguard Worker 
106*635a8641SAndroid Build Coastguard Worker  private:
107*635a8641SAndroid Build Coastguard Worker   friend class TrackedRefFactory<T>;
108*635a8641SAndroid Build Coastguard Worker 
TrackedRef(T * ptr,TrackedRefFactory<T> * factory)109*635a8641SAndroid Build Coastguard Worker   TrackedRef(T* ptr, TrackedRefFactory<T>* factory)
110*635a8641SAndroid Build Coastguard Worker       : ptr_(ptr), factory_(factory) {
111*635a8641SAndroid Build Coastguard Worker     factory_->live_tracked_refs_.Increment();
112*635a8641SAndroid Build Coastguard Worker   }
113*635a8641SAndroid Build Coastguard Worker 
114*635a8641SAndroid Build Coastguard Worker   T* ptr_;
115*635a8641SAndroid Build Coastguard Worker   TrackedRefFactory<T>* factory_;
116*635a8641SAndroid Build Coastguard Worker };
117*635a8641SAndroid Build Coastguard Worker 
118*635a8641SAndroid Build Coastguard Worker // TrackedRefFactory<T> should be the last member of T.
119*635a8641SAndroid Build Coastguard Worker template <class T>
120*635a8641SAndroid Build Coastguard Worker class TrackedRefFactory {
121*635a8641SAndroid Build Coastguard Worker  public:
TrackedRefFactory(T * ptr)122*635a8641SAndroid Build Coastguard Worker   TrackedRefFactory(T* ptr)
123*635a8641SAndroid Build Coastguard Worker       : ptr_(ptr), self_ref_(WrapUnique(new TrackedRef<T>(ptr_, this))) {
124*635a8641SAndroid Build Coastguard Worker     DCHECK(ptr_);
125*635a8641SAndroid Build Coastguard Worker   }
126*635a8641SAndroid Build Coastguard Worker 
~TrackedRefFactory()127*635a8641SAndroid Build Coastguard Worker   ~TrackedRefFactory() {
128*635a8641SAndroid Build Coastguard Worker     // Enter the destruction phase.
129*635a8641SAndroid Build Coastguard Worker     ready_to_destroy_ = std::make_unique<WaitableEvent>();
130*635a8641SAndroid Build Coastguard Worker 
131*635a8641SAndroid Build Coastguard Worker     // Release self-ref (if this was the last one it will signal the event right
132*635a8641SAndroid Build Coastguard Worker     // away).
133*635a8641SAndroid Build Coastguard Worker     self_ref_.reset();
134*635a8641SAndroid Build Coastguard Worker 
135*635a8641SAndroid Build Coastguard Worker     ready_to_destroy_->Wait();
136*635a8641SAndroid Build Coastguard Worker   }
137*635a8641SAndroid Build Coastguard Worker 
GetTrackedRef()138*635a8641SAndroid Build Coastguard Worker   TrackedRef<T> GetTrackedRef() {
139*635a8641SAndroid Build Coastguard Worker     // TrackedRefs cannot be obtained after |live_tracked_refs_| has already
140*635a8641SAndroid Build Coastguard Worker     // reached zero. In other words, the owner of a TrackedRefFactory shouldn't
141*635a8641SAndroid Build Coastguard Worker     // vend new TrackedRefs while it's being destroyed (owners of TrackedRefs
142*635a8641SAndroid Build Coastguard Worker     // may still copy/move their refs around during the destruction phase).
143*635a8641SAndroid Build Coastguard Worker     DCHECK(!live_tracked_refs_.IsZero());
144*635a8641SAndroid Build Coastguard Worker     return TrackedRef<T>(ptr_, this);
145*635a8641SAndroid Build Coastguard Worker   }
146*635a8641SAndroid Build Coastguard Worker 
147*635a8641SAndroid Build Coastguard Worker  private:
148*635a8641SAndroid Build Coastguard Worker   friend class TrackedRef<T>;
149*635a8641SAndroid Build Coastguard Worker   FRIEND_TEST_ALL_PREFIXES(TrackedRefTest, CopyAndMoveSemantics);
150*635a8641SAndroid Build Coastguard Worker 
151*635a8641SAndroid Build Coastguard Worker   T* const ptr_;
152*635a8641SAndroid Build Coastguard Worker 
153*635a8641SAndroid Build Coastguard Worker   // The number of live TrackedRefs vended by this factory.
154*635a8641SAndroid Build Coastguard Worker   AtomicRefCount live_tracked_refs_{0};
155*635a8641SAndroid Build Coastguard Worker 
156*635a8641SAndroid Build Coastguard Worker   // Non-null during the destruction phase. Signaled once |live_tracked_refs_|
157*635a8641SAndroid Build Coastguard Worker   // reaches 0. Note: while this could a direct member, only initializing it in
158*635a8641SAndroid Build Coastguard Worker   // the destruction phase avoids keeping a handle open for the entire session.
159*635a8641SAndroid Build Coastguard Worker   std::unique_ptr<WaitableEvent> ready_to_destroy_;
160*635a8641SAndroid Build Coastguard Worker 
161*635a8641SAndroid Build Coastguard Worker   // TrackedRefFactory holds a TrackedRef as well to prevent
162*635a8641SAndroid Build Coastguard Worker   // |live_tracked_refs_| from ever reaching zero before ~TrackedRefFactory().
163*635a8641SAndroid Build Coastguard Worker   std::unique_ptr<TrackedRef<T>> self_ref_;
164*635a8641SAndroid Build Coastguard Worker 
165*635a8641SAndroid Build Coastguard Worker   DISALLOW_COPY_AND_ASSIGN(TrackedRefFactory);
166*635a8641SAndroid Build Coastguard Worker };
167*635a8641SAndroid Build Coastguard Worker 
168*635a8641SAndroid Build Coastguard Worker }  // namespace internal
169*635a8641SAndroid Build Coastguard Worker }  // namespace base
170*635a8641SAndroid Build Coastguard Worker 
171*635a8641SAndroid Build Coastguard Worker #endif  // BASE_TASK_SCHEDULER_TRACKED_REF_H_
172