1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_async_fuchsia/fake_dispatcher.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include "pw_async_fuchsia/util.h"
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker namespace pw::async::test::backend {
20*61c4878aSAndroid Build Coastguard Worker
NativeFakeDispatcher(Dispatcher & test_dispatcher)21*61c4878aSAndroid Build Coastguard Worker NativeFakeDispatcher::NativeFakeDispatcher(Dispatcher& test_dispatcher)
22*61c4878aSAndroid Build Coastguard Worker : dispatcher_(test_dispatcher) {}
23*61c4878aSAndroid Build Coastguard Worker
DestroyLoop()24*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::DestroyLoop() { return fake_loop_.Shutdown(); }
25*61c4878aSAndroid Build Coastguard Worker
now()26*61c4878aSAndroid Build Coastguard Worker chrono::SystemClock::time_point NativeFakeDispatcher::now() {
27*61c4878aSAndroid Build Coastguard Worker return fake_loop_.Now();
28*61c4878aSAndroid Build Coastguard Worker }
29*61c4878aSAndroid Build Coastguard Worker
Post(Task & task)30*61c4878aSAndroid Build Coastguard Worker void NativeFakeDispatcher::Post(Task& task) { PostAt(task, now()); }
31*61c4878aSAndroid Build Coastguard Worker
PostAfter(Task & task,chrono::SystemClock::duration delay)32*61c4878aSAndroid Build Coastguard Worker void NativeFakeDispatcher::PostAfter(Task& task,
33*61c4878aSAndroid Build Coastguard Worker chrono::SystemClock::duration delay) {
34*61c4878aSAndroid Build Coastguard Worker PostAt(task, now() + delay);
35*61c4878aSAndroid Build Coastguard Worker }
36*61c4878aSAndroid Build Coastguard Worker
PostAt(Task & task,chrono::SystemClock::time_point time)37*61c4878aSAndroid Build Coastguard Worker void NativeFakeDispatcher::PostAt(Task& task,
38*61c4878aSAndroid Build Coastguard Worker chrono::SystemClock::time_point time) {
39*61c4878aSAndroid Build Coastguard Worker // TODO: https://fxbug.dev/42075952 - Return errors once these methods return
40*61c4878aSAndroid Build Coastguard Worker // a Status.
41*61c4878aSAndroid Build Coastguard Worker if (!fake_loop_.Runnable()) {
42*61c4878aSAndroid Build Coastguard Worker Context ctx{.dispatcher = &dispatcher_, .task = &task};
43*61c4878aSAndroid Build Coastguard Worker task(ctx, Status::Cancelled());
44*61c4878aSAndroid Build Coastguard Worker return;
45*61c4878aSAndroid Build Coastguard Worker }
46*61c4878aSAndroid Build Coastguard Worker ::pw::async::backend::NativeTask& native_task = task.native_type();
47*61c4878aSAndroid Build Coastguard Worker native_task.set_due_time(time);
48*61c4878aSAndroid Build Coastguard Worker native_task.dispatcher_ = &dispatcher_;
49*61c4878aSAndroid Build Coastguard Worker fake_loop_.PostTask(&native_task);
50*61c4878aSAndroid Build Coastguard Worker }
51*61c4878aSAndroid Build Coastguard Worker
Cancel(Task & task)52*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::Cancel(Task& task) {
53*61c4878aSAndroid Build Coastguard Worker return fake_loop_.Runnable() &&
54*61c4878aSAndroid Build Coastguard Worker fake_loop_.CancelTask(&task.native_type()) == ZX_OK;
55*61c4878aSAndroid Build Coastguard Worker }
56*61c4878aSAndroid Build Coastguard Worker
RunUntilIdle()57*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::RunUntilIdle() {
58*61c4878aSAndroid Build Coastguard Worker if (stop_requested_) {
59*61c4878aSAndroid Build Coastguard Worker return DestroyLoop();
60*61c4878aSAndroid Build Coastguard Worker }
61*61c4878aSAndroid Build Coastguard Worker return fake_loop_.RunUntilIdle();
62*61c4878aSAndroid Build Coastguard Worker }
63*61c4878aSAndroid Build Coastguard Worker
RunUntil(chrono::SystemClock::time_point end_time)64*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::RunUntil(chrono::SystemClock::time_point end_time) {
65*61c4878aSAndroid Build Coastguard Worker if (stop_requested_) {
66*61c4878aSAndroid Build Coastguard Worker return DestroyLoop();
67*61c4878aSAndroid Build Coastguard Worker }
68*61c4878aSAndroid Build Coastguard Worker return fake_loop_.Run(pw::async_fuchsia::TimepointToZxTime(end_time).get(),
69*61c4878aSAndroid Build Coastguard Worker false);
70*61c4878aSAndroid Build Coastguard Worker }
71*61c4878aSAndroid Build Coastguard Worker
RunFor(chrono::SystemClock::duration duration)72*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::RunFor(chrono::SystemClock::duration duration) {
73*61c4878aSAndroid Build Coastguard Worker return RunUntil(now() + duration);
74*61c4878aSAndroid Build Coastguard Worker }
75*61c4878aSAndroid Build Coastguard Worker
FakeAsyncLoop()76*61c4878aSAndroid Build Coastguard Worker NativeFakeDispatcher::FakeAsyncLoop::FakeAsyncLoop() {
77*61c4878aSAndroid Build Coastguard Worker list_initialize(&task_list_);
78*61c4878aSAndroid Build Coastguard Worker list_initialize(&due_list_);
79*61c4878aSAndroid Build Coastguard Worker }
80*61c4878aSAndroid Build Coastguard Worker
Now() const81*61c4878aSAndroid Build Coastguard Worker chrono::SystemClock::time_point NativeFakeDispatcher::FakeAsyncLoop::Now()
82*61c4878aSAndroid Build Coastguard Worker const {
83*61c4878aSAndroid Build Coastguard Worker return pw::async_fuchsia::ZxTimeToTimepoint(zx::time{now_});
84*61c4878aSAndroid Build Coastguard Worker }
85*61c4878aSAndroid Build Coastguard Worker
PostTask(async_task_t * task)86*61c4878aSAndroid Build Coastguard Worker zx_status_t NativeFakeDispatcher::FakeAsyncLoop::PostTask(async_task_t* task) {
87*61c4878aSAndroid Build Coastguard Worker if (state_ == ASYNC_LOOP_SHUTDOWN) {
88*61c4878aSAndroid Build Coastguard Worker return ZX_ERR_BAD_STATE;
89*61c4878aSAndroid Build Coastguard Worker }
90*61c4878aSAndroid Build Coastguard Worker
91*61c4878aSAndroid Build Coastguard Worker InsertTask(task);
92*61c4878aSAndroid Build Coastguard Worker if (!dispatching_tasks_ && TaskToNode(task)->prev == &task_list_) {
93*61c4878aSAndroid Build Coastguard Worker // Task inserted at head. Earliest deadline changed.
94*61c4878aSAndroid Build Coastguard Worker RestartTimer();
95*61c4878aSAndroid Build Coastguard Worker }
96*61c4878aSAndroid Build Coastguard Worker
97*61c4878aSAndroid Build Coastguard Worker return ZX_OK;
98*61c4878aSAndroid Build Coastguard Worker }
99*61c4878aSAndroid Build Coastguard Worker
CancelTask(async_task_t * task)100*61c4878aSAndroid Build Coastguard Worker zx_status_t NativeFakeDispatcher::FakeAsyncLoop::CancelTask(
101*61c4878aSAndroid Build Coastguard Worker async_task_t* task) {
102*61c4878aSAndroid Build Coastguard Worker // Note: We need to process cancellations even while the loop is being
103*61c4878aSAndroid Build Coastguard Worker // destroyed in case the client is counting on the handler not being
104*61c4878aSAndroid Build Coastguard Worker // invoked again past this point. Also, the task we're removing here
105*61c4878aSAndroid Build Coastguard Worker // might be present in the dispatcher's |due_list| if it is pending
106*61c4878aSAndroid Build Coastguard Worker // dispatch instead of in the loop's |task_list| as usual. The same
107*61c4878aSAndroid Build Coastguard Worker // logic works in both cases.
108*61c4878aSAndroid Build Coastguard Worker
109*61c4878aSAndroid Build Coastguard Worker list_node_t* node = TaskToNode(task);
110*61c4878aSAndroid Build Coastguard Worker if (!list_in_list(node)) {
111*61c4878aSAndroid Build Coastguard Worker return ZX_ERR_NOT_FOUND;
112*61c4878aSAndroid Build Coastguard Worker }
113*61c4878aSAndroid Build Coastguard Worker
114*61c4878aSAndroid Build Coastguard Worker // Determine whether the head task was canceled and following task has
115*61c4878aSAndroid Build Coastguard Worker // a later deadline. If so, we will bump the timer along to that deadline.
116*61c4878aSAndroid Build Coastguard Worker bool must_restart = !dispatching_tasks_ && node->prev == &task_list_ &&
117*61c4878aSAndroid Build Coastguard Worker (node->next == &task_list_ ||
118*61c4878aSAndroid Build Coastguard Worker NodeToTask(node->next)->deadline > task->deadline);
119*61c4878aSAndroid Build Coastguard Worker list_delete(node);
120*61c4878aSAndroid Build Coastguard Worker if (must_restart) {
121*61c4878aSAndroid Build Coastguard Worker RestartTimer();
122*61c4878aSAndroid Build Coastguard Worker }
123*61c4878aSAndroid Build Coastguard Worker
124*61c4878aSAndroid Build Coastguard Worker return ZX_OK;
125*61c4878aSAndroid Build Coastguard Worker }
126*61c4878aSAndroid Build Coastguard Worker
RunUntilIdle()127*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::FakeAsyncLoop::RunUntilIdle() {
128*61c4878aSAndroid Build Coastguard Worker return Run(now_, false);
129*61c4878aSAndroid Build Coastguard Worker }
130*61c4878aSAndroid Build Coastguard Worker
Run(zx_time_t deadline,bool once)131*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::FakeAsyncLoop::Run(zx_time_t deadline, bool once) {
132*61c4878aSAndroid Build Coastguard Worker zx_status_t status;
133*61c4878aSAndroid Build Coastguard Worker bool task_invoked = false;
134*61c4878aSAndroid Build Coastguard Worker do {
135*61c4878aSAndroid Build Coastguard Worker status = RunOnce(deadline, &task_invoked);
136*61c4878aSAndroid Build Coastguard Worker } while (status == ZX_OK && !once);
137*61c4878aSAndroid Build Coastguard Worker return task_invoked;
138*61c4878aSAndroid Build Coastguard Worker }
139*61c4878aSAndroid Build Coastguard Worker
InsertTask(async_task_t * task)140*61c4878aSAndroid Build Coastguard Worker void NativeFakeDispatcher::FakeAsyncLoop::InsertTask(async_task_t* task) {
141*61c4878aSAndroid Build Coastguard Worker list_node_t* node;
142*61c4878aSAndroid Build Coastguard Worker for (node = task_list_.prev; node != &task_list_; node = node->prev) {
143*61c4878aSAndroid Build Coastguard Worker if (task->deadline >= NodeToTask(node)->deadline) {
144*61c4878aSAndroid Build Coastguard Worker break;
145*61c4878aSAndroid Build Coastguard Worker }
146*61c4878aSAndroid Build Coastguard Worker }
147*61c4878aSAndroid Build Coastguard Worker list_add_after(node, TaskToNode(task));
148*61c4878aSAndroid Build Coastguard Worker }
149*61c4878aSAndroid Build Coastguard Worker
RestartTimer()150*61c4878aSAndroid Build Coastguard Worker void NativeFakeDispatcher::FakeAsyncLoop::RestartTimer() {
151*61c4878aSAndroid Build Coastguard Worker zx_time_t deadline = NextDeadline();
152*61c4878aSAndroid Build Coastguard Worker
153*61c4878aSAndroid Build Coastguard Worker if (deadline == ZX_TIME_INFINITE) {
154*61c4878aSAndroid Build Coastguard Worker // Nothing is left on the queue to fire.
155*61c4878aSAndroid Build Coastguard Worker if (timer_armed_) {
156*61c4878aSAndroid Build Coastguard Worker next_timer_expiration_ =
157*61c4878aSAndroid Build Coastguard Worker ZX_TIME_INFINITE; // Simulate timer cancellation.
158*61c4878aSAndroid Build Coastguard Worker timer_armed_ = false;
159*61c4878aSAndroid Build Coastguard Worker }
160*61c4878aSAndroid Build Coastguard Worker return;
161*61c4878aSAndroid Build Coastguard Worker }
162*61c4878aSAndroid Build Coastguard Worker
163*61c4878aSAndroid Build Coastguard Worker next_timer_expiration_ = deadline;
164*61c4878aSAndroid Build Coastguard Worker
165*61c4878aSAndroid Build Coastguard Worker if (!timer_armed_) {
166*61c4878aSAndroid Build Coastguard Worker timer_armed_ = true;
167*61c4878aSAndroid Build Coastguard Worker }
168*61c4878aSAndroid Build Coastguard Worker }
169*61c4878aSAndroid Build Coastguard Worker
NextDeadline()170*61c4878aSAndroid Build Coastguard Worker zx_time_t NativeFakeDispatcher::FakeAsyncLoop::NextDeadline() {
171*61c4878aSAndroid Build Coastguard Worker if (list_is_empty(&due_list_)) {
172*61c4878aSAndroid Build Coastguard Worker list_node_t* head = list_peek_head(&task_list_);
173*61c4878aSAndroid Build Coastguard Worker if (!head) {
174*61c4878aSAndroid Build Coastguard Worker return ZX_TIME_INFINITE;
175*61c4878aSAndroid Build Coastguard Worker }
176*61c4878aSAndroid Build Coastguard Worker async_task_t* task = NodeToTask(head);
177*61c4878aSAndroid Build Coastguard Worker return task->deadline;
178*61c4878aSAndroid Build Coastguard Worker }
179*61c4878aSAndroid Build Coastguard Worker // Fire now.
180*61c4878aSAndroid Build Coastguard Worker return 0ULL;
181*61c4878aSAndroid Build Coastguard Worker }
182*61c4878aSAndroid Build Coastguard Worker
RunOnce(zx_time_t deadline,bool * task_invoked)183*61c4878aSAndroid Build Coastguard Worker zx_status_t NativeFakeDispatcher::FakeAsyncLoop::RunOnce(zx_time_t deadline,
184*61c4878aSAndroid Build Coastguard Worker bool* task_invoked) {
185*61c4878aSAndroid Build Coastguard Worker if (state_ == ASYNC_LOOP_SHUTDOWN) {
186*61c4878aSAndroid Build Coastguard Worker return ZX_ERR_BAD_STATE;
187*61c4878aSAndroid Build Coastguard Worker }
188*61c4878aSAndroid Build Coastguard Worker if (state_ != ASYNC_LOOP_RUNNABLE) {
189*61c4878aSAndroid Build Coastguard Worker return ZX_ERR_CANCELED;
190*61c4878aSAndroid Build Coastguard Worker }
191*61c4878aSAndroid Build Coastguard Worker
192*61c4878aSAndroid Build Coastguard Worker // Simulate timeout of zx_port_wait() syscall.
193*61c4878aSAndroid Build Coastguard Worker if (deadline < next_timer_expiration_) {
194*61c4878aSAndroid Build Coastguard Worker now_ = deadline;
195*61c4878aSAndroid Build Coastguard Worker return ZX_ERR_TIMED_OUT;
196*61c4878aSAndroid Build Coastguard Worker }
197*61c4878aSAndroid Build Coastguard Worker // Otherwise, a timer would have expired at or before `deadline`.
198*61c4878aSAndroid Build Coastguard Worker now_ = next_timer_expiration_;
199*61c4878aSAndroid Build Coastguard Worker next_timer_expiration_ = ZX_TIME_INFINITE;
200*61c4878aSAndroid Build Coastguard Worker *task_invoked |= DispatchTasks();
201*61c4878aSAndroid Build Coastguard Worker return ZX_OK;
202*61c4878aSAndroid Build Coastguard Worker }
203*61c4878aSAndroid Build Coastguard Worker
DispatchTasks()204*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::FakeAsyncLoop::DispatchTasks() {
205*61c4878aSAndroid Build Coastguard Worker bool task_invoked = false;
206*61c4878aSAndroid Build Coastguard Worker // Dequeue and dispatch one task at a time in case an earlier task wants
207*61c4878aSAndroid Build Coastguard Worker // to cancel a later task which has also come due. Timer restarts are
208*61c4878aSAndroid Build Coastguard Worker // suppressed until we run out of tasks to dispatch.
209*61c4878aSAndroid Build Coastguard Worker if (!dispatching_tasks_) {
210*61c4878aSAndroid Build Coastguard Worker dispatching_tasks_ = true;
211*61c4878aSAndroid Build Coastguard Worker
212*61c4878aSAndroid Build Coastguard Worker // Extract all of the tasks that are due into |due_list| for dispatch
213*61c4878aSAndroid Build Coastguard Worker // unless we already have some waiting from a previous iteration which
214*61c4878aSAndroid Build Coastguard Worker // we would like to process in order.
215*61c4878aSAndroid Build Coastguard Worker list_node_t* node;
216*61c4878aSAndroid Build Coastguard Worker if (list_is_empty(&due_list_)) {
217*61c4878aSAndroid Build Coastguard Worker zx_time_t due_time = now_;
218*61c4878aSAndroid Build Coastguard Worker list_node_t* tail = nullptr;
219*61c4878aSAndroid Build Coastguard Worker list_for_every(&task_list_, node) {
220*61c4878aSAndroid Build Coastguard Worker if (NodeToTask(node)->deadline > due_time) {
221*61c4878aSAndroid Build Coastguard Worker break;
222*61c4878aSAndroid Build Coastguard Worker }
223*61c4878aSAndroid Build Coastguard Worker tail = node;
224*61c4878aSAndroid Build Coastguard Worker }
225*61c4878aSAndroid Build Coastguard Worker if (tail) {
226*61c4878aSAndroid Build Coastguard Worker list_node_t* head = task_list_.next;
227*61c4878aSAndroid Build Coastguard Worker task_list_.next = tail->next;
228*61c4878aSAndroid Build Coastguard Worker tail->next->prev = &task_list_;
229*61c4878aSAndroid Build Coastguard Worker due_list_.next = head;
230*61c4878aSAndroid Build Coastguard Worker head->prev = &due_list_;
231*61c4878aSAndroid Build Coastguard Worker due_list_.prev = tail;
232*61c4878aSAndroid Build Coastguard Worker tail->next = &due_list_;
233*61c4878aSAndroid Build Coastguard Worker }
234*61c4878aSAndroid Build Coastguard Worker }
235*61c4878aSAndroid Build Coastguard Worker
236*61c4878aSAndroid Build Coastguard Worker // Dispatch all due tasks.
237*61c4878aSAndroid Build Coastguard Worker while ((node = list_remove_head(&due_list_))) {
238*61c4878aSAndroid Build Coastguard Worker // Invoke the handler. Note that it might destroy itself.
239*61c4878aSAndroid Build Coastguard Worker async_task_t* task = NodeToTask(node);
240*61c4878aSAndroid Build Coastguard Worker
241*61c4878aSAndroid Build Coastguard Worker task->handler(nullptr, task, ZX_OK);
242*61c4878aSAndroid Build Coastguard Worker task_invoked = true;
243*61c4878aSAndroid Build Coastguard Worker
244*61c4878aSAndroid Build Coastguard Worker if (state_ != ASYNC_LOOP_RUNNABLE) {
245*61c4878aSAndroid Build Coastguard Worker break;
246*61c4878aSAndroid Build Coastguard Worker }
247*61c4878aSAndroid Build Coastguard Worker }
248*61c4878aSAndroid Build Coastguard Worker
249*61c4878aSAndroid Build Coastguard Worker dispatching_tasks_ = false;
250*61c4878aSAndroid Build Coastguard Worker timer_armed_ = false;
251*61c4878aSAndroid Build Coastguard Worker RestartTimer();
252*61c4878aSAndroid Build Coastguard Worker }
253*61c4878aSAndroid Build Coastguard Worker
254*61c4878aSAndroid Build Coastguard Worker return task_invoked;
255*61c4878aSAndroid Build Coastguard Worker }
256*61c4878aSAndroid Build Coastguard Worker
Shutdown()257*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::FakeAsyncLoop::Shutdown() {
258*61c4878aSAndroid Build Coastguard Worker if (state_ == ASYNC_LOOP_SHUTDOWN) {
259*61c4878aSAndroid Build Coastguard Worker return false;
260*61c4878aSAndroid Build Coastguard Worker }
261*61c4878aSAndroid Build Coastguard Worker state_ = ASYNC_LOOP_SHUTDOWN;
262*61c4878aSAndroid Build Coastguard Worker
263*61c4878aSAndroid Build Coastguard Worker // Cancel any remaining pending tasks on our queues.
264*61c4878aSAndroid Build Coastguard Worker return CancelAll();
265*61c4878aSAndroid Build Coastguard Worker }
266*61c4878aSAndroid Build Coastguard Worker
CancelAll()267*61c4878aSAndroid Build Coastguard Worker bool NativeFakeDispatcher::FakeAsyncLoop::CancelAll() {
268*61c4878aSAndroid Build Coastguard Worker ZX_DEBUG_ASSERT(state_ == ASYNC_LOOP_SHUTDOWN);
269*61c4878aSAndroid Build Coastguard Worker bool task_invoked = false;
270*61c4878aSAndroid Build Coastguard Worker
271*61c4878aSAndroid Build Coastguard Worker list_node_t* node;
272*61c4878aSAndroid Build Coastguard Worker
273*61c4878aSAndroid Build Coastguard Worker while ((node = list_remove_head(&due_list_))) {
274*61c4878aSAndroid Build Coastguard Worker async_task_t* task = NodeToTask(node);
275*61c4878aSAndroid Build Coastguard Worker task->handler(nullptr, task, ZX_ERR_CANCELED);
276*61c4878aSAndroid Build Coastguard Worker task_invoked = true;
277*61c4878aSAndroid Build Coastguard Worker }
278*61c4878aSAndroid Build Coastguard Worker while ((node = list_remove_head(&task_list_))) {
279*61c4878aSAndroid Build Coastguard Worker async_task_t* task = NodeToTask(node);
280*61c4878aSAndroid Build Coastguard Worker task->handler(nullptr, task, ZX_ERR_CANCELED);
281*61c4878aSAndroid Build Coastguard Worker task_invoked = true;
282*61c4878aSAndroid Build Coastguard Worker }
283*61c4878aSAndroid Build Coastguard Worker return task_invoked;
284*61c4878aSAndroid Build Coastguard Worker }
285*61c4878aSAndroid Build Coastguard Worker
286*61c4878aSAndroid Build Coastguard Worker } // namespace pw::async::test::backend
287