blob: 24bf7b86598c0e9c527b2f040b98e73ef4fc8d09 [file] [log] [blame]
// 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