// 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. #include "base/logging.h" #include "base/message_loop/message_pump.h" #include "base/run_loop.h" #include "base/task/sequence_manager/sequence_manager.h" #include "base/task/sequence_manager/task_queue.h" #include "base/task/single_thread_task_runner.h" // See the documentation below. enum class TaskType : unsigned char { kMain = 1, }; void RunTaskAndQuit(base::OnceClosure quit_closure) { LOG(INFO) << "Whoah, we're doing a task! Now let's quit"; std::move(quit_closure).Run(); } int main() { base::PlatformThread::SetName("SchedulingDemoMain"); // Create MessagePump that drives the SequenceManager that gets bound to this // thread further down. This pump's type is "default", which is a really // simple kind of platform-agtnostic message pump that only has the ability to // listen for tasks (possibly with delays) and run them; it can't, for // example: // 1.) Watch file descriptors or native sockets // 2.) Listen to platform-specific UI events, such as WM_PAINT on Windows or // `NSEvent*`s on macOS. std::unique_ptr pump = base::MessagePump::Create(base::MessagePumpType::DEFAULT); // Create the thread's SequenceManager. // // What this really does is: // 1.) Creates an unbound SequenceManager (the default) // 2.) Immediately "binds" it to the current thread // What (2) really means is that we invoke // `SequenceManagerImpl::CompleteInitializationOnBoundThread()`, which sets // the thread-local storage SequenceManager to `sequence_manager` below. // That's important so that `RunLoop` objects that have no direct access to // `sequence_manager` know which manager to tell to "run" later on. std::unique_ptr sequence_manager = base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump( std::move(pump), base::sequence_manager::SequenceManager::Settings::Builder().Build()); // Create a default TaskQueue that feeds into the SequenceManager. Inside a // SequenceManager, TaskQueue is the basic unit of scheduling... base::sequence_manager::TaskQueue::Handle default_task_queue = sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec( base::sequence_manager::QueueName::DEFAULT_TQ)); // ... and from a TaskQueue you can get an arbitrary number of TaskRunners, // which actually let you post tasks to the underlying/internal queue. // // Why do we even have this distinction? Why even have multiple TaskRunners // associated with the same underlying queue? Answer: this is for task // attribution purposes, so you can query metrics to see which TaskRunners // (used by different places in your code) are posting what kind of tasks // and get other interesting information about them. scoped_refptr task_runner = default_task_queue->CreateTaskRunner(static_cast(TaskType::kMain)); sequence_manager->SetDefaultTaskRunner(task_runner); // Now that this thread has a bound sequence manager set up, we can: // 1.) Start posting tasks to its queues which are not yet being processed // 2.) Start processing tasks from the queues by "running" the loop, which // runs the sequence manager's underlying message pump. base::RunLoop run_loop; task_runner->PostTask( FROM_HERE, base::BindOnce(&RunTaskAndQuit, run_loop.QuitClosure())); // This synchronously blocks (by running this thread's loop) until the quit // closure is called. run_loop.Run(); return 0; }