blob: fa109dcc7f98805e0b8497ccb23f455e936eb7ba [file] [log] [blame]
// 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 <atomic>
#include <cstdint>
#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_