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
base / task / sequence_manager / work_tracker.h [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_