1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
// 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_
|