// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TASK_SEQUENCE_MANAGER_WORK_TRACKER_H_ #define BASE_TASK_SEQUENCE_MANAGER_WORK_TRACKER_H_ #include #include #include "base/base_export.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/synchronization/condition_variable.h" #include "base/task/common/checked_lock.h" #include "base/threading/thread_checker.h" namespace base::sequence_manager::internal { class WorkTracker; // When `IsValid()`, this represents an authorization to execute work // synchronously inside `RunOrPostTask`. class BASE_EXPORT SyncWorkAuthorization { public: SyncWorkAuthorization(SyncWorkAuthorization&&); SyncWorkAuthorization& operator=(SyncWorkAuthorization&&); ~SyncWorkAuthorization(); bool IsValid() const { return !!tracker_; } private: friend class WorkTracker; explicit SyncWorkAuthorization(WorkTracker* state); // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3). RAW_PTR_EXCLUSION WorkTracker* tracker_ = nullptr; }; // Tracks queued and running work to support `RunOrPostTask`. class BASE_EXPORT WorkTracker { public: WorkTracker(); ~WorkTracker(); // Controls whether `RunOrPostTask()` can run its callback synchronously when // no work is tracked by this. Don't allow this when work that is sequenced // with `RunOrPostTask()` may run without being tracked by methods below. void SetRunTaskSynchronouslyAllowed(bool can_run_tasks_synchronously); // Invoked before requesting to reload an empty immediate work queue. After // this, `RunOrPostTask()` can't run tasks synchronously until // `WillReloadImmediateWorkQueues()` and `OnIdle()` have been called in // sequence. void WillRequestReloadImmediateWorkQueue(); // Invoked before reloading empty immediate work queues. void WillReloadImmediateWorkQueues(); // Invoked before doing work. After this `RunOrPostTask()` can't run tasks // until `OnIdle()` is called. Work may begin even if immediate work queues // haven't be reloaded since the last `OnIdle()`, e.g. when a task queue is // enabled, when tasks are moved from the delayed incoming queue to the // delayed work queue or when the pump performs internal work. void OnBeginWork(); // Invoked when the thread is out of work. void OnIdle(); // Returns a valid `SyncWorkAuthorization` iff all these conditions are true: // - Explicitly allowed by `SetRunTaskSynchronouslyAllowed()` // - `WillReloadImmediateWorkQueues()` and `OnIdle()` were called in // sequence after the last call to `WillRequestReloadImmediateWorkQueue()` // - `OnIdle()` was called after the last call to `OnBeginWork()` SyncWorkAuthorization TryAcquireSyncWorkAuthorization(); // Asserts that there is work tracked by this, i.e. // `TryAcquireSyncWorkAuthorization()` would not grant a sync work // authorization even if allowed by `SetRunTaskSynchronouslyAllowed()`. void AssertHasWork(); private: friend class SyncWorkAuthorization; void WaitNoSyncWork(); // An atomic variable to track: // - Whether there is an unfulfilled request to reload immediate work queues. static constexpr uint32_t kImmediateWorkQueueNeedsReload = 1 << 0; // - Whether all work queues are empty and no work is running. static constexpr uint32_t kWorkQueuesEmptyAndNoWorkRunning = 1 << 1; // - Whether a valid `SyncWorkAuthorization` exists. static constexpr uint32_t kActiveSyncWork = 1 << 2; // - Whether a valid `SyncWorkAuthorization` can be granted when no work is // tracked by `this`. static constexpr uint32_t kSyncWorkSupported = 1 << 3; std::atomic_uint32_t state_{kWorkQueuesEmptyAndNoWorkRunning}; // Memory order for `state_`: // // Sync work must see all memory written before it was allowed. Similarly, // non-sync work must see all memory written by sync work. As a result: // // Operations that may allow sync work are std::memory_order_release: // - Set `kWorkQueuesEmptyAndNoWorkRunning` // - Set `kSyncWorkSupported` // // Operations that may allow non-sync work are `std::memory_order_release`: // - Clear `kActiveSyncWork` // // Operations that precede sync work are `std::memory_order_acquire`: // - Set `kActiveSyncWork` // // Operations that precede non-sync work are `std::memory_order_acquire`: // - Check that `kActiveSyncWork` is not set. static constexpr std::memory_order kMemoryReleaseAllowWork = std::memory_order_release; static constexpr std::memory_order kMemoryAcquireBeforeWork = std::memory_order_acquire; static constexpr std::memory_order kMemoryRelaxedNotAllowOrBeforeWork = std::memory_order_relaxed; // Allows `OnBeginWork()` to wait until there is no more valid // `SyncWorkAuthorization`. base::internal::CheckedLock active_sync_work_lock_; ConditionVariable active_sync_work_cv_ = active_sync_work_lock_.CreateConditionVariable(); THREAD_CHECKER(thread_checker_); }; } // namespace base::sequence_manager::internal #endif // BASE_TASK_SEQUENCE_MANAGER_WORK_TRACKER_H_