# The Synchronous RunLoop Pattern
The synchronous RunLoop pattern involves creating a new RunLoop, setting up a
specified quit condition for it, then calling Run() on it to block the current
thread until that quit condition is reached.
## Use this pattern when:
You need to **block the current thread** until an event happens, and you have a
way to get notified of that event, via a callback or observer interface or
similar. A couple of common scenarios might be:
* Waiting for an asynchronous event (like a network request) to complete
* Waiting for an animation to finish
* Waiting for a page to have loaded
* Waiting for some call that requires a thread hop to complete
The fact that this blocks a thread means it is **almost never appropriate
outside test code**.
## Don't use this pattern when:
* You don't really need the entire thread to wait
* You don't have and can't add a way to get notified when the event happens
* You're waiting for a timer to fire - for that, [TaskEnvironment] is likely a
better fit.
## Alternatives / see also:
* [TaskEnvironment]
* Restructuring your code to not require blocking a thread
## How this pattern works:
This pattern relies on two important facts about [base::RunLoop]:
1. `base::RunLoop::Quit()` is idempotent - once a RunLoop enters the quit
state, quitting it again does nothing
2. Once a RunLoop is in the quit state, calling `base::RunLoop::Run()` on it is
a no-op
That means that if your code does this:
```c++
base::RunLoop loop;
maybe-asynchronously { loop.Quit(); }
loop.Run();
LOG(INFO) << "Hello!";
```
then regardless of whether the maybe-asynchronous `loop.Quit()` is executed
before or after `loop.Run()`, the "Hello!" message will never be printed before
both `loop.Run()` and `loop.Quit()` have happened. If the `Quit` happens
before the `Run`, the `Run` will be a no-op; if the `Quit` happens after the
`Run` has started, the `Run` will exit after the `Quit`.
## How to use this pattern in Chromium:
If the asynchronous thing in question takes a completion callback:
```c++
base::RunLoop run_loop;
Reply reply;
DoThingAndReply(
base::BindLambdaForTesting([&](const Reply& r) {
reply = r;
run_loop.Quit();
}));
run_loop.Run();
```
or perhaps even just:
```c++
base::RunLoop run_loop;
DoThing(run_loop.QuitClosure());
run_loop.Run();
```
If there exists a GizmoObserver interface with an OnThingDone event:
```c++
class TestGizmoObserver : public GizmoObserver {
public:
TestGizmoObserver(base::RunLoop* loop, Gizmo* thing)
: GizmoObserver(thing), loop_(loop) {}
// GizmoObserver:
void OnThingStarted(Gizmo* observed_gizmo) override { ... }
void OnThingProgressed(Gizmo* observed_gizmo) override { ... }
void OnThingDone(Gizmo* observed_gizmo) override {
loop_->Quit();
}
};
base::RunLoop run_loop;
TestGizmoObserver observer(&run_loop, gizmo);
gizmo->StartDoingThing();
run_loop.Run();
```
This is sometimes wrapped up into a helper class that internally constructs the
RunLoop like so, if all you need to do is wait for the event but don't care
about observing any intermediate states too:
```c++
class ThingDoneWaiter : public GizmoObserver {
public:
ThingDoneWaiter(Gizmo* thing) : GizmoObserver(thing) {}
void Wait() {
run_loop_.Run();
}
// GizmoObserver:
void OnThingDone(Gizmo* observed_gizmo) {
run_loop_.Quit();
}
private:
RunLoop run_loop_;
};
ThingDoneWaiter waiter(gizmo);
gizmo->StartDoingThing();
waiter.Wait();
```
## Events vs States
It's important to differentiate between waiting on an *event* (such as a
notification or callback being fired) vs waiting for a *state* (such as a
property on a given object).
When waiting for events, it is crucial that the observer is constructed in time
to see the event (see also [waiting too late](#starting-to-wait-for-an-event-too-late)).
States, on the other hand, can be queried beforehand in the body of a
Wait()-style function.
The following is an example of a Waiter helper class that waits for a state, as
opposed to an event:
```c++
class GizmoReadyWaiter : public GizmoObserver {
public:
GizmoReadyObserver(Gizmo* gizmo)
: gizmo_(gizmo) {}
~GizmoReadyObserver() override = default;
void WaitForGizmoReady() {
if (!gizmo_->ready()) {
gizmo_observation_.Observe(gizmo_);
run_loop_.Run();
}
}
// GizmoObserver:
void OnGizmoReady(Gizmo* observed_gizmo) {
run_loop_.Quit();
}
private:
RunLoop run_loop_;
Gizmo* gizmo_;
base::ScopedObservation<Gizmo, GizmoObserver> gizmo_observation_{this};
};
```
## Sharp edges
### Starting to wait for an event too late
A common mis-use of this pattern is like so:
```c++
gizmo->StartDoingThing();
base::RunLoop run_loop;
TestGizmoObserver observer(&run_loop, gizmo);
run_loop.Run();
```
This looks tempting because it seems that you can write a helper function:
```c++
void TerribleHorribleNoGoodVeryBadWaitForThing(Gizmo* gizmo) {
base::RunLoop run_loop;
TestGizmoObserver observer(&run_loop, gizmo);
run_loop.Run();
}
```
and then your test code can simply read:
```c++
gizmo->StartDoingThing();
TerribleHorribleNoGoodVeryBadWaitForThing(gizmo);
```
However, this is a recipe for a flaky test: if `gizmo->StartDoingThing()`
*completes* and would deliver the `OnThingDone` callback before your
`TestGizmoObserver` is ever constructed, the `TestGizmoObserver` will never
receive `OnThingDone`, and then your `run_loop.Run()` will run forever,
frustrating a future tree sheriff (and then probably you, shortly afterward).
This is especially dangerous when `gizmo->StartDoingThing()` involves a thread
hop or network request, because these can unpredictably complete before or after
your observer gets constructed. To be safe, always begin observing the event
*before* running the code that will eventually cause the event!
If you still really want a helper function, perhaps you just want to inline the
start:
```c++
void NiceFriendlyDoThingAndWait(Gizmo* gizmo) {
base::RunLoop run_loop;
TestGizmoObserver observer(&run_loop, gizmo);
gizmo->StartDoingThing();
run_loop.Run();
}
```
with the test code being:
```c++
NiceFriendlyDoThingAndWait(gizmo);
```
Note that this is not an issue when waiting on a *state*, since the observer can
query to see if that state is already the current state.
### Guessing RunLoop cycles
Sometimes, there's no easy way to observe completion of an event. In that case,
if the code under test looks like this:
```c++
void StartDoingThing() { PostTask(&StepOne); }
void StepOne() { PostTask(&StepTwo); }
void StepTwo() { /* done! */ }
```
it can be tempting to do:
```c++
gizmo->StartDoingThing();
base::RunLoop().RunUntilIdle();
/* now it's done! */
```
However, doing this is adding dependencies to your test code on the exact async
behavior of the production code - for example, the production code may depend on
work happening on another TaskRunner, which this won't successfully wait for.
This will make your test brittle and flaky.
Instead of doing this, it's vastly better to add a way (even if it's just via a
[test API]) to observe the event you're interested in.
### Not managing lifetimes
As with most patterns, lifetimes can be an issue with this pattern when using
observers. If you are waiting on a given event to happen, and the object that's
being observed instead goes out of scope, the test may hang.
Similar badness can happen if the Waiter isn't properly removed as an observer,
which could lead to Use-After-Frees.
There are two good mitigation practices here.
#### Keep Waiter-style helper classes as narrowly scoped as possible.
Consider something like
```c++
TEST_F(GizmoTest, WaitForGizmo) {
GizmoWaiter waiter;
Gizmo gizmo;
gizmo.Initialize();
waiter.WaitForGizmoReady();
ASSERT_TRUE(gizmo.ready());
}
```
This looks safe, but may not be. If GizmoObserver removes itself as an observer
from Gizmo in its destructor, this will result in a Use-After-Free during the
test tear down. Instead, scope the GizmoWaiter more narrowly:
```c++
TEST_F(GizmoTest, WaitForGizmo) {
Gizmo gizmo;
{
GizmoWaiter waiter;
gizmo.Initialize();
waiter.WaitForGizmoReady();
}
ASSERT_TRUE(gizmo.ready());
}
```
Since the GizmoWaiter is now narrowly-scoped, it will be destroyed when it is
no longer needed, and avoid Use-After-Free concerns.
#### If in doubt, handle the destruction case appropriately
If you need to potentially handle the case where the object being observed is
destroyed while a waiter is still active, you can handle the destruction case
gracefully.
```c++
class GizmoReadyWaiter : public GizmoObserver {
public:
GizmoReadyObserver(Gizmo* gizmo)
: gizmo_(gizmo) {}
~GizmoReadyObserver() override = default;
void WaitForGizmoReady() {
ASSERT_TRUE(gizmo_)
<< "Trying to call Wait() after the Gizmo was destroyed!";
if (!gizmo_->ready()) {
gizmo_observation_.Observe(gizmo_);
run_loop_.Run();
}
}
// GizmoObserver:
void OnGizmoReady(Gizmo* observed_gizmo) {
gizmo_observation_.Reset();
run_loop_.Quit();
}
void OnGizmoDestroying(Gizmo* observed_gizmo) {
DCHECK_EQ(gizmo_, observed_gizmo);
gizmo_ = nullptr;
// Remove the observer now, to avoid a UAF in the destructor.
gizmo_observation_.Reset();
// Bail out so we don't time out in the test waiting for a ready state
// that will never come.
run_loop_.Quit();
// Was this a possible expected outcome? If not, consider:
// ADD_FAILURE() << "The Gizmo was destroyed before it was ready!";
}
private:
RunLoop run_loop_;
Gizmo* gizmo_;
base::ScopedObservation<Gizmo, GizmoObserver> gizmo_observation_{this};
};
```
[base::RunLoop]: ../../base/run_loop.h
[TaskEnvironment]: ../threading_and_tasks_testing.md
[test API]: testapi.md