// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TASK_THREAD_POOL_TRACKED_REF_H_ #define BASE_TASK_THREAD_POOL_TRACKED_REF_H_ #include <optional> #include "base/atomic_ref_count.h" #include "base/check.h" #include "base/gtest_prod_util.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.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). // // Why would we ever need such a thing? In thread_pool 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 // ThreadPool/TaskTracker/ThreadGroup/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, thread_pool // 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 thread_pool 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 // ThreadPool 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 { … }; // TrackedRefFactory<T> should be the last member of T. template <class T> class TrackedRefFactory { … }; } // namespace internal } // namespace base #endif // BASE_TASK_THREAD_POOL_TRACKED_REF_H_