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