// 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 <string_view>

#include "base/logging.h"
#include "base/message_loop/message_pump.h"
#include "base/rand_util.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"

enum class TaskType : unsigned char {
  kSource1 = 1,
};

void Task(std::string_view tq, int task_number) {
  if (tq == "A") {
    LOG(INFO) << "TaskQueue(" << tq << "): " << task_number;
  } else {
    LOG(INFO) << "    TaskQueue(" << tq << "): " << task_number;
  }
}

void QuitTask(base::OnceClosure quit_closure) {
  LOG(INFO) << "Quittin' time!";
  std::move(quit_closure).Run();
}

int main() {
  base::PlatformThread::SetName("SchedulingDemoMain");

  std::unique_ptr<base::MessagePump> pump =
      base::MessagePump::Create(base::MessagePumpType::DEFAULT);

  // Create the main thread's SequenceManager; it will choose at random which of
  // the two below TaskQueues will be served at each spin of the loop, despite
  // them being the same priority.
  std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager =
      base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(
          std::move(pump),
          base::sequence_manager::SequenceManager::Settings::Builder()
              .SetRandomTaskSelectionSeed(base::RandUint64())
              .Build());

  // Create two same-priority TaskQueues that feed into the main thread's
  // SequenceManager.
  base::sequence_manager::TaskQueue::Handle tq_a =
      sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec(
          base::sequence_manager::QueueName::TEST_TQ));
  base::sequence_manager::TaskQueue::Handle tq_b =
      sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec(
          base::sequence_manager::QueueName::TEST2_TQ));

  // Get TaskRunners for both TaskQueues.
  scoped_refptr<base::SingleThreadTaskRunner> a_runner_1 =
      tq_a->CreateTaskRunner(static_cast<int>(TaskType::kSource1));
  sequence_manager->SetDefaultTaskRunner(a_runner_1, tq_a->GetQueuePriority());
  scoped_refptr<base::SingleThreadTaskRunner> b_runner =
      tq_b->CreateTaskRunner(static_cast<int>(TaskType::kSource1));

  base::RunLoop run_loop;

  // Like the last example, this shows that TaskQueue is the principal unit of
  // ordering. All non-delayed tasks posted to the same queue run in FIFO order,
  // but tasks *across* queues in this case run in random order, as directed by
  // the SequenceManager that multiplexes them.
  for (int i = 1; i <= 20; ++i) {
    a_runner_1->PostTask(FROM_HERE, base::BindOnce(&Task, "A", i));
  }
  for (int i = 0; i < 20; ++i) {
    b_runner->PostTask(FROM_HERE, base::BindOnce(&Task, "B", i));
  }

  a_runner_1->PostDelayedTask(FROM_HERE,
                              base::BindOnce(&QuitTask, run_loop.QuitClosure()),
                              base::Seconds(1));
  run_loop.Run();
  return 0;
}
