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
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354

base / task / sequence_manager / sequence_manager.h [blame]

// Copyright 2018 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_SEQUENCE_MANAGER_H_
#define BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_

#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/task/sequence_manager/task_time_observer.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/default_tick_clock.h"

namespace base {

class MessagePump;
class TaskObserver;

namespace sequence_manager {
class TimeDomain;

// SequenceManager manages TaskQueues which have different properties
// (e.g. priority, common task type) multiplexing all posted tasks into
// a single backing sequence (currently bound to a single thread, which is
// refererred as *main thread* in the comments below). SequenceManager
// implementation can be used in a various ways to apply scheduling logic.
class BASE_EXPORT SequenceManager {
 public:
  class Observer {
   public:
    virtual ~Observer() = default;
    // Called back on the main thread.
    virtual void OnBeginNestedRunLoop() = 0;
    virtual void OnExitNestedRunLoop() = 0;
  };

  class BASE_EXPORT PrioritySettings {
   public:
    // This limit is based on an implementation detail of `TaskQueueSelector`'s
    // `ActivePriorityTracker`, which can be refactored if more priorities are
    // needed.
    static constexpr size_t kMaxPriorities = sizeof(size_t) * 8 - 1;

    static PrioritySettings CreateDefault();

    template <typename T,
              typename = typename std::enable_if_t<std::is_enum_v<T>>>
    PrioritySettings(T priority_count, T default_priority)
        : PrioritySettings(
              static_cast<TaskQueue::QueuePriority>(priority_count),
              static_cast<TaskQueue::QueuePriority>(default_priority)) {
      static_assert(
          std::is_same_v<std::underlying_type_t<T>, TaskQueue::QueuePriority>,
          "Enumerated priorites must have the same underlying type as "
          "TaskQueue::QueuePriority");
    }

    PrioritySettings(TaskQueue::QueuePriority priority_count,
                     TaskQueue::QueuePriority default_priority);

    ~PrioritySettings();

    PrioritySettings(PrioritySettings&&) noexcept;
    PrioritySettings& operator=(PrioritySettings&&);

    TaskQueue::QueuePriority priority_count() const { return priority_count_; }

    TaskQueue::QueuePriority default_priority() const {
      return default_priority_;
    }

#if BUILDFLAG(ENABLE_BASE_TRACING)
    void SetProtoPriorityConverter(
        perfetto::protos::pbzero::SequenceManagerTask::Priority (
            *proto_priority_converter)(TaskQueue::QueuePriority)) {
      proto_priority_converter_ = proto_priority_converter;
    }

    perfetto::protos::pbzero::SequenceManagerTask::Priority TaskPriorityToProto(
        TaskQueue::QueuePriority priority) const;
#endif

   private:
    TaskQueue::QueuePriority priority_count_;
    TaskQueue::QueuePriority default_priority_;

#if BUILDFLAG(ENABLE_BASE_TRACING)
    perfetto::protos::pbzero::SequenceManagerTask::Priority (
        *proto_priority_converter_)(TaskQueue::QueuePriority) = nullptr;
#endif

#if DCHECK_IS_ON()
   public:
    PrioritySettings(
        TaskQueue::QueuePriority priority_count,
        TaskQueue::QueuePriority default_priority,
        std::vector<TimeDelta> per_priority_cross_thread_task_delay,
        std::vector<TimeDelta> per_priority_same_thread_task_delay);

    const std::vector<TimeDelta>& per_priority_cross_thread_task_delay() const
        LIFETIME_BOUND {
      return per_priority_cross_thread_task_delay_;
    }

    const std::vector<TimeDelta>& per_priority_same_thread_task_delay() const
        LIFETIME_BOUND {
      return per_priority_same_thread_task_delay_;
    }

   private:
    // Scheduler policy induced raciness is an area of concern. This lets us
    // apply an extra delay per priority for cross thread posting.
    std::vector<TimeDelta> per_priority_cross_thread_task_delay_;

    // Like the above but for same thread posting.
    std::vector<TimeDelta> per_priority_same_thread_task_delay_;
#endif
  };

  // Settings defining the desired SequenceManager behaviour.
  struct BASE_EXPORT Settings {
    class Builder;

    Settings();
    Settings(const Settings&) = delete;
    Settings& operator=(const Settings&) = delete;
    // In the future MessagePump (which is move-only) will also be a setting,
    // so we are making Settings move-only in preparation.
    Settings(Settings&& move_from) noexcept;

    ~Settings();

    MessagePumpType message_loop_type = MessagePumpType::DEFAULT;
    raw_ptr<const TickClock, DanglingUntriaged> clock =
        DefaultTickClock::GetInstance();

    // Whether or not queueing timestamp will be added to tasks.
    bool add_queue_time_to_tasks = false;

    // Whether many tasks may run between each check for native work.
    bool can_run_tasks_by_batches = false;

    PrioritySettings priority_settings = PrioritySettings::CreateDefault();

#if DCHECK_IS_ON()
    // TODO(alexclarke): Consider adding command line flags to control these.
    enum class TaskLogging {
      kNone,
      kEnabled,
      kEnabledWithBacktrace,

      // Logs high priority tasks and the lower priority tasks they skipped
      // past.  Useful for debugging test failures caused by scheduler policy
      // changes.
      kReorderedOnly,
    };
    TaskLogging task_execution_logging = TaskLogging::kNone;

    // If true PostTask will emit a debug log.
    bool log_post_task = false;

    // If true debug logs will be emitted when a delayed task becomes eligible
    // to run.
    bool log_task_delay_expiry = false;

    // If not zero this seeds a PRNG used by the task selection logic to choose
    // a random TaskQueue for a given priority rather than the TaskQueue with
    // the oldest EnqueueOrder.
    uint64_t random_task_selection_seed = 0;
#endif  // DCHECK_IS_ON()
  };

  virtual ~SequenceManager() = default;

  // Binds the SequenceManager and its TaskQueues to the current thread. Should
  // only be called once. Note that CreateSequenceManagerOnCurrentThread()
  // performs this initialization automatically.
  virtual void BindToCurrentThread() = 0;

  // Returns the task runner the current task was posted on. Returns null if no
  // task is currently running. Must be called on the bound thread.
  virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0;

  // Finishes the initialization for a SequenceManager created via
  // CreateUnboundSequenceManager(). Must not be called in any other
  // circumstances. The ownership of the pump is transferred to SequenceManager.
  virtual void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) = 0;

  // Must be called on the main thread.
  // Can be called only once, before creating TaskQueues.
  // Observer must outlive the SequenceManager.
  virtual void SetObserver(Observer* observer) = 0;

  // Must be called on the main thread.
  virtual void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;
  virtual void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0;

  // Sets `time_domain` to be used by this scheduler and associated task queues.
  // Only one time domain can be set at a time. `time_domain` must outlive this
  // SequenceManager, even if ResetTimeDomain() is called. This has no effect on
  // previously scheduled tasks and it is recommended that `time_domain` be set
  // before posting any task to avoid inconsistencies in time. Otherwise,
  // replacing `time_domain` is very subtle and should be reserved for developer
  // only use cases (e.g. virtual time in devtools) where any flakiness caused
  // by a racy time update isn't surprising.
  virtual void SetTimeDomain(TimeDomain* time_domain) = 0;
  // Disassociates the current `time_domain` and reverts to using
  // RealTimeDomain.
  virtual void ResetTimeDomain() = 0;

  virtual const TickClock* GetTickClock() const = 0;
  virtual TimeTicks NowTicks() const = 0;

  // Returns a wake-up for the next delayed task which is not ripe for
  // execution. If there are no such tasks (immediate tasks don't count),
  // returns nullopt.
  virtual std::optional<WakeUp> GetNextDelayedWakeUp() const = 0;

  // Sets the SingleThreadTaskRunner that will be returned by
  // SingleThreadTaskRunner::GetCurrentDefault on the main thread.
  virtual void SetDefaultTaskRunner(
      scoped_refptr<SingleThreadTaskRunner> task_runner) = 0;

  // Removes all canceled delayed tasks, and considers resizing to fit all
  // internal queues.
  virtual void ReclaimMemory() = 0;

  // Returns true if no tasks were executed in TaskQueues that monitor
  // quiescence since the last call to this method.
  virtual bool GetAndClearSystemIsQuiescentBit() = 0;

  // Set the number of tasks executed in a single SequenceManager invocation.
  // Increasing this number reduces the overhead of the tasks dispatching
  // logic at the cost of a potentially worse latency. 1 by default.
  virtual void SetWorkBatchSize(int work_batch_size) = 0;

  // Enables crash keys that can be set in the scope of a task which help
  // to identify the culprit if upcoming work results in a crash.
  // Key names must be thread-specific to avoid races and corrupted crash dumps.
  virtual void EnableCrashKeys(const char* async_stack_crash_key) = 0;

  virtual TaskQueue::QueuePriority GetPriorityCount() const = 0;

  // Creates a `TaskQueue` and returns a `TaskQueue::Handle`for it. The queue is
  // owned by the handle and shut down when the handle is destroyed. Must be
  // called on the main thread.
  virtual TaskQueue::Handle CreateTaskQueue(const TaskQueue::Spec& spec) = 0;

  // Returns true iff this SequenceManager has no immediate work to do. I.e.
  // there are no pending non-delayed tasks or delayed tasks that are due to
  // run. This method ignores any pending delayed tasks that might have become
  // eligible to run since the last task was executed. This is important because
  // if it did tests would become flaky depending on the exact timing of this
  // call. This is moderately expensive.
  virtual bool IsIdleForTesting() = 0;

  // The total number of posted tasks that haven't executed yet.
  virtual size_t GetPendingTaskCountForTesting() const = 0;

  // Returns a JSON string which describes all pending tasks.
  virtual std::string DescribeAllPendingTasks() const = 0;

  // While Now() is less than `prioritize_until` we will alternate between a
  // SequenceManager task and a yielding to the underlying sequence (e.g., the
  // message pump).
  virtual void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) = 0;

  // Adds an observer which reports task execution. Can only be called on the
  // same thread that `this` is running on.
  virtual void AddTaskObserver(TaskObserver* task_observer) = 0;

  // Removes an observer which reports task execution. Can only be called on the
  // same thread that `this` is running on.
  virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0;
};

class BASE_EXPORT SequenceManager::Settings::Builder {
 public:
  Builder();
  ~Builder();

  // Sets the MessagePumpType which is used to create a MessagePump.
  Builder& SetMessagePumpType(MessagePumpType message_loop_type);

  // Sets the TickClock the SequenceManager uses to obtain Now.
  Builder& SetTickClock(const TickClock* clock);

  // Whether or not queueing timestamp will be added to tasks.
  Builder& SetAddQueueTimeToTasks(bool add_queue_time_to_tasks);

  // Whether many tasks may run between each check for native work.
  Builder& SetCanRunTasksByBatches(bool can_run_tasks_by_batches);

  Builder& SetPrioritySettings(PrioritySettings settings);

#if DCHECK_IS_ON()
  // Controls task execution logging.
  Builder& SetTaskLogging(TaskLogging task_execution_logging);

  // Whether or not PostTask will emit a debug log.
  Builder& SetLogPostTask(bool log_post_task);

  // Whether or not debug logs will be emitted when a delayed task becomes
  // eligible to run.
  Builder& SetLogTaskDelayExpiry(bool log_task_delay_expiry);

  // If not zero this seeds a PRNG used by the task selection logic to choose a
  // random TaskQueue for a given priority rather than the TaskQueue with the
  // oldest EnqueueOrder.
  Builder& SetRandomTaskSelectionSeed(uint64_t random_task_selection_seed);
#endif  // DCHECK_IS_ON()

  Settings Build();

 private:
  Settings settings_;
};

// Create SequenceManager using MessageLoop on the current thread.
// Implementation is located in sequence_manager_impl.cc.
// TODO(scheduler-dev): Remove after every thread has a SequenceManager.
BASE_EXPORT std::unique_ptr<SequenceManager>
CreateSequenceManagerOnCurrentThread(SequenceManager::Settings settings);

// Create a SequenceManager using the given MessagePump on the current thread.
// MessagePump instances can be created with
// MessagePump::CreateMessagePumpForType().
BASE_EXPORT std::unique_ptr<SequenceManager>
CreateSequenceManagerOnCurrentThreadWithPump(
    std::unique_ptr<MessagePump> message_pump,
    SequenceManager::Settings settings = SequenceManager::Settings());

// Create an unbound SequenceManager (typically for a future thread or because
// additional setup is required before binding). The SequenceManager can be
// initialized on the current thread and then needs to be bound and initialized
// on the target thread by calling one of the Bind*() methods.
BASE_EXPORT std::unique_ptr<SequenceManager> CreateUnboundSequenceManager(
    SequenceManager::Settings settings = SequenceManager::Settings());

}  // namespace sequence_manager
}  // namespace base

#endif  // BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_