| // Copyright 2017 The Fuchsia 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 "real_loop.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/zx/clock.h> |
| |
| namespace loop_fixture { |
| |
| namespace { |
| |
| bool RunGivenLoopWithTimeout(async::Loop* loop, zx::duration timeout) { |
| // This cannot be a local variable because the delayed task below can execute |
| // after this function returns. |
| auto canceled = std::make_shared<bool>(false); |
| bool timed_out = false; |
| async::PostDelayedTask( |
| loop->dispatcher(), |
| [loop, canceled, &timed_out] { |
| if (*canceled) { |
| return; |
| } |
| timed_out = true; |
| loop->Quit(); |
| }, |
| timeout); |
| loop->Run(); |
| loop->ResetQuit(); |
| // Another task can call Quit() on the message loop, which exits the |
| // message loop before the delayed task executes, in which case |timed_out| is |
| // still false here because the delayed task hasn't run yet. |
| // Since the message loop isn't destroyed then (as it usually would after |
| // Quit()), and presumably can be reused after this function returns we |
| // still need to prevent the delayed task to quit it again at some later time |
| // using the canceled pointer. |
| if (!timed_out) { |
| *canceled = true; |
| } |
| return timed_out; |
| } |
| |
| } // namespace |
| |
| RealLoop::RealLoop() : loop_(&kAsyncLoopConfigAttachToCurrentThread) {} |
| |
| RealLoop::~RealLoop() = default; |
| |
| async_dispatcher_t* RealLoop::dispatcher() { return loop_.dispatcher(); } |
| |
| void RealLoop::RunLoop() { |
| loop_.Run(); |
| loop_.ResetQuit(); |
| } |
| |
| bool RealLoop::RunLoopWithTimeout(zx::duration timeout) { |
| return RunGivenLoopWithTimeout(&loop_, timeout); |
| } |
| |
| bool RealLoop::RunLoopWithTimeoutOrUntil(fit::function<bool()> condition, zx::duration timeout, |
| zx::duration step) { |
| const zx::time timeout_deadline = zx::deadline_after(timeout); |
| |
| while (zx::clock::get_monotonic() < timeout_deadline && loop_.GetState() == ASYNC_LOOP_RUNNABLE) { |
| if (condition()) { |
| loop_.ResetQuit(); |
| return true; |
| } |
| |
| if (step == zx::duration::infinite()) { |
| // Performs a single unit of work, possibly blocking until there is work |
| // to do or the timeout deadline arrives. |
| loop_.Run(timeout_deadline, true); |
| } else { |
| // Performs work until the step deadline arrives. |
| RunGivenLoopWithTimeout(&loop_, step); |
| } |
| } |
| |
| loop_.ResetQuit(); |
| return condition(); |
| } |
| |
| void RealLoop::RunLoopUntil(fit::function<bool()> condition, zx::duration step) { |
| RunLoopWithTimeoutOrUntil(std::move(condition), zx::duration::infinite(), step); |
| } |
| |
| void RealLoop::RunLoopUntilIdle() { |
| loop_.RunUntilIdle(); |
| loop_.ResetQuit(); |
| } |
| |
| void RealLoop::QuitLoop() { loop_.Quit(); } |
| |
| fit::closure RealLoop::QuitLoopClosure() { |
| return [this] { loop_.Quit(); }; |
| } |
| |
| } // namespace loop_fixture |