0

Update the Chromium Codelab

- New top-level //codelabs directory
- Update exercises 0-4 to compile
- Add mojo exercise

Previous codelab is here:
https://www.chromium.org/developers/cpp-in-chromium-101-codelab

Bug: N/A
Change-Id: I2218160fa9efc8017dce809db8546604ade7e5d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2414488
Reviewed-by: Victor Costan <pwnall@chromium.org>
Reviewed-by: Dirk Pranke <dpranke@google.com>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Reviewed-by: enne <enne@chromium.org>
Commit-Queue: Austin Sullivan <asully@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814678}
This commit is contained in:
Austin Sullivan
2020-10-07 15:39:41 +00:00
committed by Commit Bot
parent 4b418527ec
commit 33da70ab00
18 changed files with 739 additions and 0 deletions

@ -73,6 +73,7 @@ group("gn_all") {
"//base/util:base_util_unittests",
"//chrome/installer",
"//chrome/updater",
"//codelabs",
"//components:components_unittests",
"//components/gwp_asan:gwp_asan_unittests",
"//net:net_unittests",

@ -68,6 +68,8 @@ _TEST_CODE_EXCLUDED_PATHS = (
r'ios[\\/].*_app_interface\.mm$',
# Views Examples code
r'ui[\\/]views[\\/]examples[\\/].*',
# Chromium Codelab
r'codelabs[\\/]*'
)
_THIRD_PARTY_EXCEPT_BLINK = 'third_party/(?!blink/)'

54
codelabs/BUILD.gn Normal file

@ -0,0 +1,54 @@
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
group("codelabs") {
testonly = true
deps = [
":codelab_factor",
":codelab_fibonacci",
":codelab_hello_world",
":codelab_mojo",
":codelab_sleep",
]
}
executable("codelab_hello_world") {
sources = [ "cpp101/hello_world.cc" ]
deps = [ "//base" ]
}
executable("codelab_fibonacci") {
sources = [ "cpp101/fibonacci.cc" ]
deps = [ "//base" ]
}
executable("codelab_sleep") {
testonly = true
sources = [ "cpp101/sleep.cc" ]
deps = [
"//base",
"//base/test:test_support",
]
}
executable("codelab_factor") {
testonly = true
sources = [ "cpp101/factor.cc" ]
deps = [
"//base",
"//base/test:test_support",
]
}
executable("codelab_mojo") {
testonly = true
sources = [ "cpp101/mojo.cc" ]
deps = [
"//base",
"//base/test:test_support",
"//codelabs/cpp101/services/math/",
"//mojo/core/embedder",
"//mojo/public/cpp/base",
]
}

4
codelabs/DEPS Normal file

@ -0,0 +1,4 @@
include_rules = [
"+mojo/core/embedder",
"+mojo/public/cpp/bindings",
]

1
codelabs/OWNERS Normal file

@ -0,0 +1 @@
*

13
codelabs/README.md Normal file

@ -0,0 +1,13 @@
# Chromium Codelab
See the `cpp101/` directory for the Chromium C++ codelab,
including example solutions.
## Motivation
The goal of this codelab is to introduce new Chromium developers to both the
important design patterns and the style of code they can expect to become
familiar with.
The code in this directory is for documentation purposes only. It is compiled
via the top level BUILD.gn's `gn_all` target to make sure it is kept up to date.

282
codelabs/cpp101/codelab.md Normal file

@ -0,0 +1,282 @@
# C++ in Chromium 101 - Codelab
This tutorial will guide you through the creation of various example C++
applications, highlighting important Chromium C++ concepts.
This tutorial assumes robust knowledge of C++ (the language) but does not
assume you know how to write an application specific to Chromium's style and
architecture. This tutorial does assume that you know how to check files out
of Chromium's repository.
As always, consider the following resources as of primary importance:
- [Coding Style](https://chromium.googlesource.com/chromium/src/+/master/styleguide/styleguide.md)
- [Callback<> and Bind()](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md)
- [Threading and Tasks in Chrome](https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md)
- [Intro to Mojo & Services](https://chromium.googlesource.com/chromium/src.git/+/master/docs/mojo_and_services.md)
- [Important Abstractions and Data Structures](https://sites.google.com/a/chromium.org/dev/developers/coding-style/important-abstractions-and-data-structures) (badly needs updating)
This tutorial does not assume you have read any of the above,
though you should feel free to peruse them when necessary.
This tutorial will cover information across all of those guides.
Exercise solutions are available in the `codelabs/cpp101/` directory of the
Chromium source code. Build all of the example solutions with
`autoninja -C out/Default codelabs`. You are encouraged to create a new
`base/cpp101/` directory locally if you want to try implementing these
exercises yourself.
### Exercise 0: "Hello World!"
This exercise demonstrates the use of the [ninja](https://ninja-build.org/)
build system to build a simple C++ binary and demonstrates how typical C++
builds are organized within Chromium.
Create a new target in `base/BUILD.gn` for a new executable
named `codelab_hello_world`. Then write the classic "Hello, world!" program in
C++. You should be able to build it with
`autoninja -C out/Default codelab_hello_world` and execute it directly by
finding the binary within `out/Default`.
Sample execution:
```shell
$ cd /path/to/chromium/src
$ gclient runhooks
$ autoninja -C out/Default codelab_hello_world
$ out/Default/codelab_hello_world
Hello, world!
[0923/185218.645640:INFO:hello_world.cc(27)] Hello, world!
```
### More information
[Git Tips](https://chromium.googlesource.com/chromium/src.git/+/master/docs/git_tips.md)
and [Git Cookbook](https://chromium.googlesource.com/chromium/src.git/+/master/docs/git_cookbook.md)
[Life of a Chromium Developer](https://docs.google.com/a/google.com/presentation/d/1abnqM9j6zFodPHA38JG1061rG2iGj_GABxEDgZsdbJg/)
## Part 1: Using command-line arguments
We will augment our `codelab_hello_world` binary to parse command-line flags and
use those values to print messages to the user.
Command-line arguments within Chromium are processed by the
`CommandLine::Init()` function, which takes command line flags from the
[argc and argv](https://crasseux.com/books/ctutorial/argc-and-argv.html)
(argument count & vector) variables of the main() method. A typical invocation
of `CommandLine::Init()` looks like the following:
```cpp
int main(int argc, char** argv) {
  CommandLine::Init(argc, argv);
  // Main program execution ...
  return 0;
}
```
Flags are not explicitly defined in Chromium. Instead, we
use `GetSwitchValueASCII()` and friends to retrieve values passed in.
### Important include files
```cpp
#include "base/command_line.h"
#include "base/logging.h"
```
### Exercise 1: Using command-line arguments
Change `codelab_hello_world` to take a `--greeting` and a `--name` switch.
The greeting, if not specified, should default to "Hello",
and the name, if not specified, should default to "World".
## Part 2: Callbacks and Bind
C++, unlike other languages such as Python, Javascript, or Lisp, has only
rudimentary support for [callbacks](https://en.wikipedia.org/wiki/Callbacks)
and no support for
[partial application](https://en.wikipedia.org/wiki/Partial_application).
However, Chromium has the `base::OnceCallback<Sig>` and
 `base::RepeatingCallback<Sig>`class, whose instances can be freely passed
around, returned, and generally be treated as first-class values.
base::OnceCallback<Sig> is the move-only, single-call variant,
and base::RepeatingCallback<Sig> is the copyable, multiple-call variant.
The `Sig` template parameter is a function signature type:
```cpp
// The type of a callback that:
//  - Can run only once.
//  - Is move-only and non-copyable.
//  - Takes no arguments and does not return anything.
// base::OnceClosure is an alias of this type.
base::OnceCallback<void()>
// The type of a callback that:
//  - Can run more than once.
//  - Is copyable.
//  - Takes no arguments and does not return anything.
// base::RepeatingClosure is an alias of this type.
base::RepeatingCallback<void()>
// The types of a callback that takes two arguments (a string and a double)
// and returns an int.
base::OnceCallback<int(std::string, double)>
base::RepeatingCallback<int(std::string, double)>
```
Callbacks are executed by invoking the `Run()` member function.
base::OnceCallback<Sig> needs to be rvalue to run.
```cpp
void MyFunction1(base::OnceCallback<int(std::string, double)> my_callback) {
  // OnceCallback
  int result1 = std::move(my_callback).Run("my string 1", 1.0);
  // After running a OnceCallback, it's consumed and nulled out.
  DCHECK(!my_callback);
  ...
}
void MyFunction2(base::RepeatingCallback<int(std::string, double)> my_callback) {
  int result1 = my_callback.Run("my string 1", 1.0);
  // Run() can be called as many times as you wish for RepeatingCallback.
  int result2 = my_callback.Run("my string 2", 2);
  ...
```
Callbacks are constructed using the `base::BindOnce()` or `base::BindRepeating()` function,
which handles partial application:
```cpp
// Declare a function.
void MyFunction(int32 a, double b);
base::OnceCallback<void(double)> my_callback1 = base::BindOnce(&MyFunction, 10);
base::RepeatingCallback<void(double)> my_callback2 = base::BindRepeating(&MyFunction, 10);
// Equivalent to:
//
// MyFunction(10, 3.5);
//
std::move(my_callback1).Run(3.5);
my_callback2.Run(3.5);
```
`base::BindOnce()` and `base::BindRepeating()` can do a lot more, including
binding class member functions and binding additional arguments to an
existing `base::OnceCallback` or `base::RepeatingCallback`. See
[docs/callback.md](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md)
for details.
### Important Include Files
```cpp
#include "base/bind.h"
#include "base/callback.h"
```
### More Information
[Callback<> and Bind()](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/callback.md)
### Exercise 2: Fibonacci closures
Implement a function that returns a callback that takes no arguments and returns
successive Fibonacci numbers. That is, a function that can be used like this:
```cpp
base::RepeatingCallback<int()> fibonacci_closure = MakeFibonacciClosure();
LOG(INFO) << fibonacci_closure.Run(); // Prints "1"
LOG(INFO) << fibonacci_closure.Run(); // Prints "1"
LOG(INFO) << fibonacci_closure.Run(); // Prints "2"
...
```
Each returned Fibonacci callback should be independent;
running one callback shouldn't affect the result of running another callback.
Write a `fibonacci` executable that takes an integer argument `n`
and uses your function to print out the first `n` Fibonacci numbers.
(This exercise was inspired by
[this Go exercise: Function closures](https://tour.golang.org/moretypes/25).)
## Part 3: Threads and task runners
Chromium has a number of abstractions for sequencing and threading.
[Threading and Tasks in Chrome](https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md)
is a must-read and go-to reference for anything related to tasks, thread pools,
task runners, and more.
Sequenced execution (on virtual threads) is strongly preferred to
single-threaded execution (on physical threads). Chromium's abstraction for
asynchronously running posted tasks is `base::TaskRunner`. Task runners allow
you to write code that posts tasks without depending on what exactly will run
those tasks.
`base::SequencedTaskRunner` (which extends `base::TaskRunner`) is a commonly
used abstraction which handles running tasks (which are instances
of `base::Closure`) in sequential order. These tasks are not guaranteed to run
on the same thread. The preferred way of posting to the current (virtual) thread
is `base::SequencedTaskRunnerHandle::Get()`.
A task that can run on any thread and doesnt have ordering or mutual exclusion
requirements with other tasks should be posted using one of the
`base::ThreadPool::PostTask()` functions.
There are a number of ways to post tasks to a thread pool or task runner.
- `PostTask()`
- `PostDelayedTask()` if you want to add a delay.
- `PostTaskAndReply()` lets you post a task which will post a task back to your
current thread when its done.
- `PostTaskAndReplyWithResult()` to automatically pass the return value of the
first call as argument to the second call.
Normally you wouldn't have to worry about setting up a threading environment and
keeping it running, since that is automatically done by Chromium's thread
classes. However, since the main thread doesn't automatically start off with
`TaskEnvironment`, there's a bit of extra setup involved. This setup code is
available in the exercise solution files.
### Important header files
```cpp
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
```
### Exercise 3a: Sleep
Implement the Unix command-line utility `sleep` using only
a `base::SequencedTaskRunnerHandle` (i.e., without using the `sleep` function
or `base::PlatformThread::Sleep`).
### Exercise 3b: Integer factorization
Take the given (slow) function to find a non-trivial factor of a given integer:
```cpp
bool FindNonTrivialFactor(int n, int* factor) {
  // Really naive algorithm.
  for (int i = n-1; i >= 2; --i) {
   if (n % i == 0) {
     *factor = i;
      return true;
    }
  }
  return false;
}
```
Write a command-line utility `factor` that takes a number, posts a task to the
background using `FindNonTrivialFactor`, and prints a status update every second
as long as the factoring task is executing.
### More information
[Threading and Tasks in Chrome](https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md)
## Part 4: Mojo
Mojo is Chromium's abstraction of IPC. Mojo allows for developers to easily
connect interface clients and implementations across arbitrary intra- and
inter-process boundaries. See the
[Intro to Mojo and Services](https://chromium.googlesource.com/chromium/src.git/+/master/docs/mojo_and_services.md)
guide to get started.
### Exercise 4: Building a simple out-of-process service
See the [building a simple out-of-process service](https://chromium.googlesource.com/chromium/src.git/+/master/docs/mojo_and_services.md#example_building-a-simple-out_of_process-service)
tutorial on using Mojo to define, hook up, and launch an out-of-process service.
### More Information
[Mojo C++ Bindings API Docs](https://chromium.googlesource.com/chromium/src.git/+/master/mojo/public/cpp/bindings/README.md)
[Mojo Docs](https://chromium.googlesource.com/chromium/src.git/+/master/mojo/README.md)

94
codelabs/cpp101/factor.cc Normal file

@ -0,0 +1,94 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
namespace {
base::Optional<int> FindNonTrivialFactor(int n) {
// Really naive algorithm.
for (int i = 2; i < n; ++i) {
if (n % i == 0) {
return i;
}
}
return base::nullopt;
}
void FindNonTrivialFactorHelper(int n, base::Optional<int>* result) {
*result = FindNonTrivialFactor(n);
}
void PrintStatusUpdate(base::TimeTicks start_time) {
double num_seconds = (base::TimeTicks::Now() - start_time).InSecondsF();
LOG(INFO) << "Waited for " << num_seconds << " seconds...\n";
}
void PrintStatusUpdateRepeatedly(base::TimeTicks start_time,
base::TimeDelta print_interval) {
PrintStatusUpdate(start_time);
base::ThreadPool::PostDelayedTask(
FROM_HERE,
base::BindOnce(&PrintStatusUpdateRepeatedly, start_time, print_interval),
print_interval);
}
} // namespace
int main(int argc, char* argv[]) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
TestTimeouts::Initialize();
base::test::TaskEnvironment task_environment{
base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
if (argc <= 1) {
LOG(INFO) << argv[0] << ": missing operand\n";
return -1;
}
int n = 0;
if (!base::StringToInt(argv[1], &n) || n < 2) {
LOG(INFO) << argv[0] << ": invalid n '" << argv[1] << "'";
return -1;
}
base::RunLoop run_loop;
base::Optional<int> result;
// Notice that we're posting the long-running factoring operation to
// |base::ThreadPool| to avoid blocking the main thread
//
// |run_loop.QuitClosure()| will be called once the factoring task completes.
base::ThreadPool::PostTaskAndReply(
FROM_HERE, base::BindOnce(&FindNonTrivialFactorHelper, n, &result),
run_loop.QuitClosure());
base::ThreadPool::PostTask(
FROM_HERE,
base::BindOnce(&PrintStatusUpdateRepeatedly, base::TimeTicks::Now(),
base::TimeDelta::FromSeconds(1)));
run_loop.Run();
if (result.has_value()) {
LOG(INFO) << "found non-trivial factor " << result.value() << " for " << n;
DCHECK_EQ(n % result.value(), 0);
} else {
LOG(INFO) << n << " is prime";
}
return 0;
}

@ -0,0 +1,60 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
namespace {
struct FibonacciState {
FibonacciState() = default;
int i{0}, j{1};
};
int ComputeNextFibonacciNumber(FibonacciState* state) {
int next = state->i + state->j;
state->i = state->j;
state->j = next;
return state->i;
}
base::RepeatingCallback<int()> MakeFibonacciClosure() {
auto state = std::make_unique<FibonacciState>();
return base::BindRepeating(&ComputeNextFibonacciNumber,
base::Owned(std::move(state)));
}
} // namespace
int main(int argc, char* argv[]) {
if (argc <= 1) {
LOG(INFO) << argv[0] << ": missing operand";
return -1;
}
int n = 0;
if (!base::StringToInt(argv[1], &n) || n < 0) {
LOG(INFO) << argv[0] << ": invalid n '" << argv[1] << "'";
return -1;
}
// |fibonacci_closure1| and |fibonacci_closure2| are independent. Though they
// are bound to the same method, they each have their own |FibonacciState|.
// Running one closure does not affect the other.
base::RepeatingCallback<int()> fibonacci_closure1 = MakeFibonacciClosure();
base::RepeatingCallback<int()> fibonacci_closure2 = MakeFibonacciClosure();
for (int i = 0; i < n; ++i) {
// Run both closures and confirm the values match.
int fibonacci_i = fibonacci_closure1.Run();
int fibonacci_i_backup = fibonacci_closure2.Run();
DCHECK_EQ(fibonacci_i, fibonacci_i_backup);
LOG(INFO) << "F_" << i << " = " << fibonacci_i;
}
return 0;
}

@ -0,0 +1,28 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <string>
#include "base/command_line.h"
#include "base/logging.h"
int main(int argc, char* argv[]) {
CHECK(base::CommandLine::Init(argc, argv));
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
std::string greeting = command_line.GetSwitchValueASCII("greeting");
if (greeting.empty())
greeting = "Hello";
std::string name = command_line.GetSwitchValueASCII("name");
if (name.empty())
name = "world";
CHECK_GT(printf("%s, %s!\n", greeting.c_str(), name.c_str()), 0);
LOG(INFO) << greeting << ", " << name << "!";
}

62
codelabs/cpp101/mojo.cc Normal file

@ -0,0 +1,62 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "codelabs/cpp101/services/math/math_service.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/public/cpp/bindings/remote.h"
int main(int argc, char* argv[]) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
TestTimeouts::Initialize();
base::test::TaskEnvironment task_environment{
base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
mojo::core::Init();
if (argc <= 2) {
LOG(INFO) << argv[0] << ": missing operand";
return -1;
}
int divisor = 0;
if (!base::StringToInt(argv[1], &divisor)) {
LOG(INFO) << argv[0] << ": invalid divisor '" << argv[1] << "'";
return -1;
}
int dividend = 0;
if (!base::StringToInt(argv[2], &dividend) || dividend == 0) {
LOG(INFO) << argv[0] << ": invalid divisor '" << argv[2] << "'";
return -1;
}
base::RunLoop run_loop;
mojo::Remote<math::mojom::MathService> math_service;
math::MathService math_service_impl(
math_service.BindNewPipeAndPassReceiver());
math_service->Divide(divisor, dividend,
base::BindOnce(
[](base::OnceClosure quit, int32_t quotient) {
LOG(INFO) << "Quotient: " << quotient;
std::move(quit).Run();
},
run_loop.QuitClosure()));
run_loop.Run();
return 0;
}

@ -0,0 +1,15 @@
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
source_set("math") {
sources = [
"math_service.cc",
"math_service.h",
]
deps = [
"public/mojom",
"//base",
]
}

@ -0,0 +1,21 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "math_service.h"
namespace math {
MathService::MathService(mojo::PendingReceiver<mojom::MathService> receiver)
: receiver_(this, std::move(receiver)) {}
MathService::~MathService() = default;
void MathService::Divide(int32_t dividend,
int32_t divisor,
DivideCallback callback) {
// Respond with the quotient!
std::move(callback).Run(dividend / divisor);
}
} // namespace math

@ -0,0 +1,32 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CODELABS_CPP101_SERVICES_MATH_MATH_SERVICE_H_
#define CODELABS_CPP101_SERVICES_MATH_MATH_SERVICE_H_
#include "base/macros.h"
#include "codelabs/cpp101/services/math/public/mojom/math_service.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
namespace math {
class MathService : public mojom::MathService {
public:
explicit MathService(mojo::PendingReceiver<mojom::MathService> receiver);
~MathService() override;
MathService(const MathService&) = delete;
MathService& operator=(const MathService&) = delete;
private:
// mojom::MathService:
void Divide(int32_t dividend,
int32_t divisor,
DivideCallback callback) override;
mojo::Receiver<mojom::MathService> receiver_;
};
} // namespace math
#endif // CODELABS_CPP101_SERVICES_MATH_MATH_SERVICE_H_

@ -0,0 +1,9 @@
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [ "math_service.mojom" ]
}

@ -0,0 +1,2 @@
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS

@ -0,0 +1,9 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module math.mojom;
interface MathService {
Divide(int32 dividend, int32 divisor) => (int32 quotient);
};

50
codelabs/cpp101/sleep.cc Normal file

@ -0,0 +1,50 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
int main(int argc, char* argv[]) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
TestTimeouts::Initialize();
base::test::TaskEnvironment task_environment{
base::test::TaskEnvironment::TimeSource::SYSTEM_TIME};
if (argc <= 1) {
LOG(INFO) << argv[0] << ": missing operand";
return -1;
}
int duration_seconds = 0;
if (!base::StringToInt(argv[1], &duration_seconds) || duration_seconds < 0) {
LOG(INFO) << ": invalid time interval '" << argv[1] << "'";
return -1;
}
base::RunLoop run_loop;
base::TimeDelta duration = base::TimeDelta::FromSeconds(duration_seconds);
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(run_loop.QuitClosure()), duration);
// Tasks are run asynchronously, so this will print before the task runs.
LOG(INFO) << "Going to sleep for " << duration_seconds << " seconds...";
// Runs all the tasks that have been posted to the |SequencedTaskRunner|.
run_loop.Run();
// This will NOT complete until |run_loop.QuitClosure()| runs.
LOG(INFO) << "I'm awake!";
return 0;
}