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