| // 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 "src/lib/fsl/tasks/fd_waiter.h" |
| |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async-testing/test_loop.h> |
| #include <lib/fit/defer.h> |
| #include <poll.h> |
| |
| #include <gtest/gtest.h> |
| |
| namespace fsl { |
| namespace { |
| |
| // Test disabled because it's hanging. |
| TEST(FDWaiter, DISABLED_WaitStdOut) { |
| async::TestLoop loop; |
| |
| FDWaiter waiter; |
| EXPECT_TRUE(waiter.Wait( |
| [&](zx_status_t status, uint32_t events) { |
| EXPECT_EQ(ZX_OK, status); |
| EXPECT_TRUE(events & POLLOUT); |
| loop.Quit(); |
| }, |
| STDOUT_FILENO, POLLOUT)); |
| |
| loop.RunUntilIdle(); |
| } |
| |
| TEST(FDWaiter, WaitFailed) { |
| async::TestLoop loop; |
| FDWaiter waiter; |
| EXPECT_FALSE(waiter.Wait([](zx_status_t status, uint32_t events) {}, -1, POLLOUT)); |
| } |
| |
| // Verify that FDWaiter can be used with a separate loop thread. |
| TEST(FDWaiter, UseLoopThread) { |
| async::Loop async_loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| async_loop.StartThread("UseLoopThread"); |
| auto cleanup = fit::defer([&async_loop] { async_loop.Shutdown(); }); |
| |
| fsl::FDWaiter fd_waiter(async_loop.dispatcher()); |
| std::atomic<bool> stdout_is_writable{false}; |
| ASSERT_TRUE(fd_waiter.Wait( |
| [&stdout_is_writable](zx_status_t, uint32_t) { stdout_is_writable.store(true); }, 1, |
| POLLOUT)); |
| while (!stdout_is_writable.load()) { |
| zx::nanosleep(zx::deadline_after(zx::usec(100))); |
| } |
| } |
| |
| // Verify that we don't deadlock when destroying an FDWaiter containing callback whose destructor |
| // invokes FDWaiter::Cancel. |
| TEST(FDWaiter, DtorCancelDeadlock) { |
| async::Loop async_loop(&kAsyncLoopConfigAttachToCurrentThread); |
| std::atomic<bool> about_to_call_cancel = false; |
| |
| { |
| fsl::FDWaiter fd_waiter(async_loop.dispatcher()); |
| |
| // Create a callback object that upon destruction calls FDWaiter::Cancel. |
| auto cancel_on_destruction = fit::defer([&about_to_call_cancel, &fd_waiter]() { |
| about_to_call_cancel.store(true); |
| fd_waiter.Cancel(); |
| }); |
| auto callback = [cod = std::move(cancel_on_destruction)](zx_status_t, uint32_t) { |
| // This callback will never be invoked, but when it is destroyed, |cod| will be destroyed, |
| // thereby invoking FDWaiter::Cancel. |
| abort(); |
| }; |
| |
| // The callback will never execute because stdout (1) will never become readable. |
| ASSERT_TRUE(fd_waiter.Wait(std::move(callback), 1, POLLIN)); |
| |
| // See that Cancel hasn't been called. Once |fd_waiter| goes out of scope, the callback will be |
| // destroyed and Cancel will be called in the context of FDWaiter's dtor. |
| ASSERT_FALSE(about_to_call_cancel.load()); |
| } |
| |
| ASSERT_TRUE(about_to_call_cancel.load()); |
| } |
| |
| } // namespace |
| } // namespace fsl |