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_