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