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 #ifndef BASE_TASK_SEQUENCE_MANAGER_WORK_TRACKER_H_ 6 #define BASE_TASK_SEQUENCE_MANAGER_WORK_TRACKER_H_ 7 8 #include <atomic> 9 #include <cstdint> 10 11 #include "base/base_export.h" 12 #include "base/memory/raw_ptr_exclusion.h" 13 #include "base/synchronization/condition_variable.h" 14 #include "base/task/common/checked_lock.h" 15 #include "base/threading/thread_checker.h" 16 17 namespace base::sequence_manager::internal { 18 19 class WorkTracker; 20 21 // When `IsValid()`, this represents an authorization to execute work 22 // synchronously inside `RunOrPostTask`. 23 class BASE_EXPORT SyncWorkAuthorization { 24 public: 25 SyncWorkAuthorization(SyncWorkAuthorization&&); 26 SyncWorkAuthorization& operator=(SyncWorkAuthorization&&); 27 ~SyncWorkAuthorization(); 28 IsValid()29 bool IsValid() const { return !!tracker_; } 30 31 private: 32 friend class WorkTracker; 33 34 explicit SyncWorkAuthorization(WorkTracker* state); 35 36 // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3). 37 RAW_PTR_EXCLUSION WorkTracker* tracker_ = nullptr; 38 }; 39 40 // Tracks queued and running work to support `RunOrPostTask`. 41 class BASE_EXPORT WorkTracker { 42 public: 43 WorkTracker(); 44 ~WorkTracker(); 45 46 // Controls whether `RunOrPostTask()` can run its callback synchronously when 47 // no work is tracked by this. Don't allow this when work that is sequenced 48 // with `RunOrPostTask()` may run without being tracked by methods below. 49 void SetRunTaskSynchronouslyAllowed(bool can_run_tasks_synchronously); 50 51 // Invoked before requesting to reload an empty immediate work queue. After 52 // this, `RunOrPostTask()` can't run tasks synchronously until 53 // `WillReloadImmediateWorkQueues()` and `OnIdle()` have been called in 54 // sequence. 55 void WillRequestReloadImmediateWorkQueue(); 56 57 // Invoked before reloading empty immediate work queues. 58 void WillReloadImmediateWorkQueues(); 59 60 // Invoked before doing work. After this `RunOrPostTask()` can't run tasks 61 // until `OnIdle()` is called. Work may begin even if immediate work queues 62 // haven't be reloaded since the last `OnIdle()`, e.g. when a task queue is 63 // enabled, when tasks are moved from the delayed incoming queue to the 64 // delayed work queue or when the pump performs internal work. 65 void OnBeginWork(); 66 67 // Invoked when the thread is out of work. 68 void OnIdle(); 69 70 // Returns a valid `SyncWorkAuthorization` iff all these conditions are true: 71 // - Explicitly allowed by `SetRunTaskSynchronouslyAllowed()` 72 // - `WillReloadImmediateWorkQueues()` and `OnIdle()` were called in 73 // sequence after the last call to `WillRequestReloadImmediateWorkQueue()` 74 // - `OnIdle()` was called after the last call to `OnBeginWork()` 75 SyncWorkAuthorization TryAcquireSyncWorkAuthorization(); 76 77 // Asserts that there is work tracked by this, i.e. 78 // `TryAcquireSyncWorkAuthorization()` would not grant a sync work 79 // authorization even if allowed by `SetRunTaskSynchronouslyAllowed()`. 80 void AssertHasWork(); 81 82 private: 83 friend class SyncWorkAuthorization; 84 85 void WaitNoSyncWork(); 86 87 // An atomic variable to track: 88 // - Whether there is an unfulfilled request to reload immediate work queues. 89 static constexpr uint32_t kImmediateWorkQueueNeedsReload = 1 << 0; 90 // - Whether all work queues are empty and no work is running. 91 static constexpr uint32_t kWorkQueuesEmptyAndNoWorkRunning = 1 << 1; 92 // - Whether a valid `SyncWorkAuthorization` exists. 93 static constexpr uint32_t kActiveSyncWork = 1 << 2; 94 // - Whether a valid `SyncWorkAuthorization` can be granted when no work is 95 // tracked by `this`. 96 static constexpr uint32_t kSyncWorkSupported = 1 << 3; 97 std::atomic_uint32_t state_{kWorkQueuesEmptyAndNoWorkRunning}; 98 99 // Memory order for `state_`: 100 // 101 // Sync work must see all memory written before it was allowed. Similarly, 102 // non-sync work must see all memory written by sync work. As a result: 103 // 104 // Operations that may allow sync work are std::memory_order_release: 105 // - Set `kWorkQueuesEmptyAndNoWorkRunning` 106 // - Set `kSyncWorkSupported` 107 // 108 // Operations that may allow non-sync work are `std::memory_order_release`: 109 // - Clear `kActiveSyncWork` 110 // 111 // Operations that precede sync work are `std::memory_order_acquire`: 112 // - Set `kActiveSyncWork` 113 // 114 // Operations that precede non-sync work are `std::memory_order_acquire`: 115 // - Check that `kActiveSyncWork` is not set. 116 static constexpr std::memory_order kMemoryReleaseAllowWork = 117 std::memory_order_release; 118 static constexpr std::memory_order kMemoryAcquireBeforeWork = 119 std::memory_order_acquire; 120 static constexpr std::memory_order kMemoryRelaxedNotAllowOrBeforeWork = 121 std::memory_order_relaxed; 122 123 // Allows `OnBeginWork()` to wait until there is no more valid 124 // `SyncWorkAuthorization`. 125 base::internal::CheckedLock active_sync_work_lock_; 126 ConditionVariable active_sync_work_cv_ = 127 active_sync_work_lock_.CreateConditionVariable(); 128 129 THREAD_CHECKER(thread_checker_); 130 }; 131 132 } // namespace base::sequence_manager::internal 133 134 #endif // BASE_TASK_SEQUENCE_MANAGER_WORK_TRACKER_H_ 135