|  | // Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef BASE_TASK_TASK_SCHEDULER_TRACKED_REF_H_ | 
|  | #define BASE_TASK_TASK_SCHEDULER_TRACKED_REF_H_ | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/atomic_ref_count.h" | 
|  | #include "base/gtest_prod_util.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace internal { | 
|  |  | 
|  | // TrackedRefs are effectively a ref-counting scheme for objects that have a | 
|  | // single owner. | 
|  | // | 
|  | // Deletion is still controlled by the single owner but ~T() itself will block | 
|  | // until all the TrackedRefs handed by its TrackedRefFactory have been released | 
|  | // (by ~TrackedRef<T>()). | 
|  | // | 
|  | // Just like WeakPtrFactory: TrackedRefFactory<T> should be the last member of T | 
|  | // to ensure ~TrackedRefFactory<T>() runs first in ~T(). | 
|  | // | 
|  | // The owner of a T should hence be certain that the last TrackedRefs to T are | 
|  | // already gone or on their way out before destroying it or ~T() will hang | 
|  | // (indicating a bug in the tear down logic -- proper refcounting on the other | 
|  | // hand would result in a leak). | 
|  | // | 
|  | // TrackedRefFactory only makes sense to use on types that are always leaked in | 
|  | // production but need to be torn down in tests (blocking destruction is | 
|  | // impractical in production -- ref. ScopedAllowBaseSyncPrimitivesForTesting | 
|  | // below). | 
|  | // | 
|  | // Why would we ever need such a thing? In task_scheduler there is a clear | 
|  | // ownership hierarchy with mostly single owners and little refcounting. In | 
|  | // production nothing is ever torn down so this isn't a problem. In tests | 
|  | // however we must JoinForTesting(). At that point, all the raw back T* refs | 
|  | // used by the worker threads are problematic because they can result in use- | 
|  | // after-frees if a worker outlives the deletion of its corresponding | 
|  | // TaskScheduler/TaskTracker/SchedulerWorkerPool/etc. | 
|  | // | 
|  | // JoinForTesting() isn't so hard when all workers are managed. But with cleanup | 
|  | // semantics (reclaiming a worker who's been idle for too long) it becomes | 
|  | // tricky because workers can go unaccounted for before they exit their main | 
|  | // (https://crbug.com/827615). | 
|  | // | 
|  | // For that reason and to clearly document the ownership model, task_scheduler | 
|  | // uses TrackedRefs. | 
|  | // | 
|  | // On top of being a clearer ownership model than proper refcounting, a hang in | 
|  | // tear down in a test with out-of-order tear down logic is much preferred to | 
|  | // letting its worker thread and associated constructs outlive the test | 
|  | // (potentially resulting in flakes in unrelated tests running later in the same | 
|  | // process). | 
|  | // | 
|  | // Note: While there's nothing task_scheduler specific about TrackedRefs it | 
|  | // requires an ownership model where all the TrackedRefs are released on other | 
|  | // threads in sync with ~T(). This isn't a typical use case beyond shutting down | 
|  | // TaskScheduler in tests and as such this is kept internal here for now. | 
|  |  | 
|  | template <class T> | 
|  | class TrackedRefFactory; | 
|  |  | 
|  | // TrackedRef<T> can be used like a T*. | 
|  | template <class T> | 
|  | class TrackedRef { | 
|  | public: | 
|  | // Moveable and copyable. | 
|  | TrackedRef(TrackedRef<T>&& other) | 
|  | : ptr_(other.ptr_), factory_(other.factory_) { | 
|  | // Null out |other_|'s factory so its destructor doesn't decrement | 
|  | // |live_tracked_refs_|. | 
|  | other.factory_ = nullptr; | 
|  | } | 
|  | TrackedRef(const TrackedRef<T>& other) | 
|  | : ptr_(other.ptr_), factory_(other.factory_) { | 
|  | factory_->live_tracked_refs_.Increment(); | 
|  | } | 
|  |  | 
|  | // Intentionally not assignable for now because it makes the logic slightly | 
|  | // convoluted and it's not a use case that makes sense for the types using | 
|  | // this at the moment. | 
|  | TrackedRef& operator=(TrackedRef<T>&& other) = delete; | 
|  | TrackedRef& operator=(const TrackedRef<T>& other) = delete; | 
|  |  | 
|  | ~TrackedRef() { | 
|  | if (factory_ && !factory_->live_tracked_refs_.Decrement()) { | 
|  | DCHECK(factory_->ready_to_destroy_); | 
|  | DCHECK(!factory_->ready_to_destroy_->IsSignaled()); | 
|  | factory_->ready_to_destroy_->Signal(); | 
|  | } | 
|  | } | 
|  |  | 
|  | T& operator*() const { return *ptr_; } | 
|  |  | 
|  | T* operator->() const { return ptr_; } | 
|  |  | 
|  | explicit operator bool() const { return ptr_ != nullptr; } | 
|  |  | 
|  | private: | 
|  | friend class TrackedRefFactory<T>; | 
|  |  | 
|  | TrackedRef(T* ptr, TrackedRefFactory<T>* factory) | 
|  | : ptr_(ptr), factory_(factory) { | 
|  | factory_->live_tracked_refs_.Increment(); | 
|  | } | 
|  |  | 
|  | T* ptr_; | 
|  | TrackedRefFactory<T>* factory_; | 
|  | }; | 
|  |  | 
|  | // TrackedRefFactory<T> should be the last member of T. | 
|  | template <class T> | 
|  | class TrackedRefFactory { | 
|  | public: | 
|  | TrackedRefFactory(T* ptr) | 
|  | : ptr_(ptr), self_ref_(WrapUnique(new TrackedRef<T>(ptr_, this))) { | 
|  | DCHECK(ptr_); | 
|  | } | 
|  |  | 
|  | ~TrackedRefFactory() { | 
|  | // Enter the destruction phase. | 
|  | ready_to_destroy_ = std::make_unique<WaitableEvent>(); | 
|  |  | 
|  | // Release self-ref (if this was the last one it will signal the event right | 
|  | // away). | 
|  | self_ref_.reset(); | 
|  |  | 
|  | ready_to_destroy_->Wait(); | 
|  | } | 
|  |  | 
|  | TrackedRef<T> GetTrackedRef() { | 
|  | // TrackedRefs cannot be obtained after |live_tracked_refs_| has already | 
|  | // reached zero. In other words, the owner of a TrackedRefFactory shouldn't | 
|  | // vend new TrackedRefs while it's being destroyed (owners of TrackedRefs | 
|  | // may still copy/move their refs around during the destruction phase). | 
|  | DCHECK(!live_tracked_refs_.IsZero()); | 
|  | return TrackedRef<T>(ptr_, this); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class TrackedRef<T>; | 
|  | FRIEND_TEST_ALL_PREFIXES(TrackedRefTest, CopyAndMoveSemantics); | 
|  |  | 
|  | T* const ptr_; | 
|  |  | 
|  | // The number of live TrackedRefs vended by this factory. | 
|  | AtomicRefCount live_tracked_refs_{0}; | 
|  |  | 
|  | // Non-null during the destruction phase. Signaled once |live_tracked_refs_| | 
|  | // reaches 0. Note: while this could a direct member, only initializing it in | 
|  | // the destruction phase avoids keeping a handle open for the entire session. | 
|  | std::unique_ptr<WaitableEvent> ready_to_destroy_; | 
|  |  | 
|  | // TrackedRefFactory holds a TrackedRef as well to prevent | 
|  | // |live_tracked_refs_| from ever reaching zero before ~TrackedRefFactory(). | 
|  | std::unique_ptr<TrackedRef<T>> self_ref_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(TrackedRefFactory); | 
|  | }; | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base | 
|  |  | 
|  | #endif  // BASE_TASK_TASK_SCHEDULER_TRACKED_REF_H_ |