xref: /aosp_15_r20/external/cronet/base/task/sequence_manager/work_tracker.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/task/sequence_manager/work_tracker.h"
6 
7 #include "base/check.h"
8 #include "base/task/common/scoped_defer_task_posting.h"
9 #include "base/threading/thread_restrictions.h"
10 
11 namespace base::sequence_manager::internal {
12 
SyncWorkAuthorization(SyncWorkAuthorization && other)13 SyncWorkAuthorization::SyncWorkAuthorization(SyncWorkAuthorization&& other)
14     : tracker_(other.tracker_) {
15   other.tracker_ = nullptr;
16 }
17 
operator =(SyncWorkAuthorization && other)18 SyncWorkAuthorization& SyncWorkAuthorization::operator=(
19     SyncWorkAuthorization&& other) {
20   tracker_ = other.tracker_;
21   other.tracker_ = nullptr;
22   return *this;
23 }
24 
~SyncWorkAuthorization()25 SyncWorkAuthorization::~SyncWorkAuthorization() {
26   if (!tracker_) {
27     return;
28   }
29 
30   {
31     base::internal::CheckedAutoLock auto_lock(tracker_->active_sync_work_lock_);
32     uint32_t prev = tracker_->state_.fetch_and(
33         ~WorkTracker::kActiveSyncWork, WorkTracker::kMemoryReleaseAllowWork);
34     DCHECK(prev & WorkTracker::kActiveSyncWork);
35   }
36 
37   tracker_->active_sync_work_cv_.Signal();
38 }
39 
SyncWorkAuthorization(WorkTracker * state)40 SyncWorkAuthorization::SyncWorkAuthorization(WorkTracker* state)
41     : tracker_(state) {}
42 
WorkTracker()43 WorkTracker::WorkTracker() {
44   DETACH_FROM_THREAD(thread_checker_);
45 }
46 
47 WorkTracker::~WorkTracker() = default;
48 
SetRunTaskSynchronouslyAllowed(bool can_run_tasks_synchronously)49 void WorkTracker::SetRunTaskSynchronouslyAllowed(
50     bool can_run_tasks_synchronously) {
51   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
52 
53   if (can_run_tasks_synchronously) {
54     state_.fetch_or(kSyncWorkSupported, kMemoryReleaseAllowWork);
55   } else {
56     // After this returns, non-sync work may run without being tracked by
57     // `this`. Ensures that such work is correctly sequenced with sync work by:
58     //  - Waiting until sync work is complete.
59     //  - Acquiring memory written by sync work (`kMemoryAcquireBeforeWork` here
60     //    is paired with `kMemoryReleaseAllowWork` in `~SyncWorkAuthorization`).
61     uint32_t prev =
62         state_.fetch_and(~kSyncWorkSupported, kMemoryAcquireBeforeWork);
63     if (prev & kActiveSyncWork) {
64       WaitNoSyncWork();
65     }
66   }
67 }
68 
WaitNoSyncWork()69 void WorkTracker::WaitNoSyncWork() {
70   // Do not process new PostTasks, defer them. Tracing can call PostTask, but
71   // it will try to grab locks that are not allowed here.
72   ScopedDeferTaskPosting disallow_task_posting;
73   ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow;
74   // `std::memory_order_relaxed` instead of `kMemoryAcquireBeforeWork` because
75   // the lock implicitly acquires memory released by `~SyncWorkAuthorization`.
76   base::internal::CheckedAutoLock auto_lock(active_sync_work_lock_);
77   uint32_t prev = state_.load(std::memory_order_relaxed);
78   while (prev & kActiveSyncWork) {
79     active_sync_work_cv_.Wait();
80     prev = state_.load(std::memory_order_relaxed);
81   }
82 }
83 
WillRequestReloadImmediateWorkQueue()84 void WorkTracker::WillRequestReloadImmediateWorkQueue() {
85   // May be called from any thread.
86 
87   // Sync work is disallowed until `WillReloadImmediateWorkQueues()` and
88   // `OnIdle()` are called.
89   state_.fetch_or(kImmediateWorkQueueNeedsReload,
90                   kMemoryRelaxedNotAllowOrBeforeWork);
91 }
92 
WillReloadImmediateWorkQueues()93 void WorkTracker::WillReloadImmediateWorkQueues() {
94   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
95 
96   // Sync work is disallowed until `OnIdle()` is called.
97   state_.fetch_and(
98       ~(kImmediateWorkQueueNeedsReload | kWorkQueuesEmptyAndNoWorkRunning),
99       kMemoryRelaxedNotAllowOrBeforeWork);
100 }
101 
OnBeginWork()102 void WorkTracker::OnBeginWork() {
103   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
104 
105   uint32_t prev = state_.fetch_and(~kWorkQueuesEmptyAndNoWorkRunning,
106                                    kMemoryAcquireBeforeWork);
107   if (prev & kActiveSyncWork) {
108     DCHECK(prev & kSyncWorkSupported);
109     WaitNoSyncWork();
110   }
111 }
112 
OnIdle()113 void WorkTracker::OnIdle() {
114   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
115 
116   // This may allow sync work. "release" so that sync work that runs after this
117   // sees all writes issued by previous sequenced work.
118   state_.fetch_or(kWorkQueuesEmptyAndNoWorkRunning, std::memory_order_release);
119 }
120 
TryAcquireSyncWorkAuthorization()121 SyncWorkAuthorization WorkTracker::TryAcquireSyncWorkAuthorization() {
122   // May be called from any thread.
123 
124   uint32_t state = state_.load(std::memory_order_relaxed);
125   // "acquire" so that sync work sees writes issued by sequenced work that
126   // precedes it.
127   if (state == (kSyncWorkSupported | kWorkQueuesEmptyAndNoWorkRunning) &&
128       state_.compare_exchange_strong(state, state | kActiveSyncWork,
129                                      std::memory_order_acquire,
130                                      std::memory_order_relaxed)) {
131     return SyncWorkAuthorization(this);
132   }
133 
134   return SyncWorkAuthorization(nullptr);
135 }
136 
AssertHasWork()137 void WorkTracker::AssertHasWork() {
138   CHECK(!(state_.load(std::memory_order_relaxed) &
139           kWorkQueuesEmptyAndNoWorkRunning));
140 }
141 
142 }  // namespace base::sequence_manager::internal
143