tracked_ref.h 6.3 KB

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