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