123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- // Copyright 2017 The Chromium 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 <sys/socket.h>
- #include "base/bind.h"
- #include "base/compiler_specific.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_file.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/message_loop/message_pump_for_io.h"
- #include "base/posix/eintr_wrapper.h"
- #include "base/run_loop.h"
- #include "base/task/current_thread.h"
- #include "base/test/gtest_util.h"
- #include "base/test/task_environment.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace base {
- #if !BUILDFLAG(IS_NACL)
- namespace {
- class FdWatchControllerPosixTest : public testing::Test {
- public:
- FdWatchControllerPosixTest() = default;
- FdWatchControllerPosixTest(const FdWatchControllerPosixTest&) = delete;
- FdWatchControllerPosixTest& operator=(const FdWatchControllerPosixTest&) =
- delete;
- // testing::Test interface.
- void SetUp() override {
- // Create a file descriptor. Doesn't need to be readable or writable,
- // as we don't need to actually get any notifications.
- // pipe() is just the easiest way to do it.
- int pipefds[2];
- int err = pipe(pipefds);
- ASSERT_EQ(0, err);
- read_fd_ = ScopedFD(pipefds[0]);
- write_fd_ = ScopedFD(pipefds[1]);
- }
- void TriggerReadEvent() {
- // Write from the other end of the pipe to trigger the event.
- char c = '\0';
- EXPECT_EQ(1, HANDLE_EINTR(write(write_fd_.get(), &c, 1)));
- }
- protected:
- ScopedFD read_fd_;
- ScopedFD write_fd_;
- };
- class TestHandler : public MessagePumpForIO::FdWatcher {
- public:
- void OnFileCanReadWithoutBlocking(int fd) override {
- watcher_to_delete_ = nullptr;
- is_readable_ = true;
- RunLoop::QuitCurrentWhenIdleDeprecated();
- }
- void OnFileCanWriteWithoutBlocking(int fd) override {
- watcher_to_delete_ = nullptr;
- is_writable_ = true;
- RunLoop::QuitCurrentWhenIdleDeprecated();
- }
- bool is_readable_ = false;
- bool is_writable_ = false;
- // If set then the contained watcher will be deleted on notification.
- std::unique_ptr<MessagePumpForIO::FdWatchController> watcher_to_delete_;
- };
- // Watcher that calls specified closures when read/write events occur. Verifies
- // that each non-null closure passed to this class is called once and only once.
- // Also resets the read event by reading from the FD.
- class CallClosureHandler : public MessagePumpForIO::FdWatcher {
- public:
- CallClosureHandler(OnceClosure read_closure, OnceClosure write_closure)
- : read_closure_(std::move(read_closure)),
- write_closure_(std::move(write_closure)) {}
- ~CallClosureHandler() override {
- EXPECT_TRUE(read_closure_.is_null());
- EXPECT_TRUE(write_closure_.is_null());
- }
- void SetReadClosure(OnceClosure read_closure) {
- EXPECT_TRUE(read_closure_.is_null());
- read_closure_ = std::move(read_closure);
- }
- void SetWriteClosure(OnceClosure write_closure) {
- EXPECT_TRUE(write_closure_.is_null());
- write_closure_ = std::move(write_closure);
- }
- // base::WatchableIOMessagePumpPosix::FdWatcher:
- void OnFileCanReadWithoutBlocking(int fd) override {
- // Empty the pipe buffer to reset the event. Otherwise libevent
- // implementation of MessageLoop may call the event handler again even if
- // |read_closure_| below quits the RunLoop.
- char c;
- int result = HANDLE_EINTR(read(fd, &c, 1));
- if (result == -1) {
- PLOG(ERROR) << "read";
- FAIL();
- }
- EXPECT_EQ(result, 1);
- ASSERT_FALSE(read_closure_.is_null());
- std::move(read_closure_).Run();
- }
- void OnFileCanWriteWithoutBlocking(int fd) override {
- ASSERT_FALSE(write_closure_.is_null());
- std::move(write_closure_).Run();
- }
- private:
- OnceClosure read_closure_;
- OnceClosure write_closure_;
- };
- TEST_F(FdWatchControllerPosixTest, FileDescriptorWatcherOutlivesMessageLoop) {
- // Simulate a MessageLoop that dies before an FileDescriptorWatcher.
- // This could happen when people use the Singleton pattern or atexit.
- // Arrange for watcher to live longer than message loop.
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- TestHandler handler;
- {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- CurrentIOThread::Get()->WatchFileDescriptor(write_fd_.get(), true,
- MessagePumpForIO::WATCH_WRITE,
- &watcher, &handler);
- // Don't run the message loop, just destroy it.
- }
- ASSERT_FALSE(handler.is_readable_);
- ASSERT_FALSE(handler.is_writable_);
- }
- TEST_F(FdWatchControllerPosixTest, FileDescriptorWatcherDoubleStop) {
- // Verify that it's ok to call StopWatchingFileDescriptor().
- // Arrange for message loop to live longer than watcher.
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- {
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- TestHandler handler;
- CurrentIOThread::Get()->WatchFileDescriptor(write_fd_.get(), true,
- MessagePumpForIO::WATCH_WRITE,
- &watcher, &handler);
- ASSERT_TRUE(watcher.StopWatchingFileDescriptor());
- ASSERT_TRUE(watcher.StopWatchingFileDescriptor());
- }
- }
- TEST_F(FdWatchControllerPosixTest, FileDescriptorWatcherDeleteInCallback) {
- // Verify that it is OK to delete the FileDescriptorWatcher from within a
- // callback.
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- TestHandler handler;
- handler.watcher_to_delete_ =
- std::make_unique<MessagePumpForIO::FdWatchController>(FROM_HERE);
- CurrentIOThread::Get()->WatchFileDescriptor(
- write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE,
- handler.watcher_to_delete_.get(), &handler);
- RunLoop().Run();
- }
- // A watcher that owns its controller and will either delete itself or stop
- // watching the FD after observing the specified event type.
- class ReaderWriterHandler : public MessagePumpForIO::FdWatcher {
- public:
- enum Action {
- // Just call StopWatchingFileDescriptor().
- kStopWatching,
- // Delete |this| and its owned controller.
- kDelete,
- };
- enum ActWhen {
- // Take the Action after observing a read event.
- kOnReadEvent,
- // Take the Action after observing a write event.
- kOnWriteEvent,
- };
- ReaderWriterHandler(Action action,
- ActWhen when,
- OnceClosure idle_quit_closure)
- : action_(action),
- when_(when),
- controller_(FROM_HERE),
- idle_quit_closure_(std::move(idle_quit_closure)) {}
- ReaderWriterHandler(const ReaderWriterHandler&) = delete;
- ReaderWriterHandler& operator=(const ReaderWriterHandler&) = delete;
- // base::WatchableIOMessagePumpPosix::FdWatcher:
- void OnFileCanReadWithoutBlocking(int fd) override {
- if (when_ == kOnReadEvent) {
- DoAction();
- } else {
- char c;
- EXPECT_EQ(1, HANDLE_EINTR(read(fd, &c, 1)));
- }
- }
- void OnFileCanWriteWithoutBlocking(int fd) override {
- if (when_ == kOnWriteEvent) {
- DoAction();
- } else {
- char c = '\0';
- EXPECT_EQ(1, HANDLE_EINTR(write(fd, &c, 1)));
- }
- }
- MessagePumpForIO::FdWatchController* controller() { return &controller_; }
- private:
- void DoAction() {
- OnceClosure idle_quit_closure = std::move(idle_quit_closure_);
- if (action_ == kDelete) {
- delete this;
- } else if (action_ == kStopWatching) {
- controller_.StopWatchingFileDescriptor();
- }
- std::move(idle_quit_closure).Run();
- }
- Action action_;
- ActWhen when_;
- MessagePumpForIO::FdWatchController controller_;
- OnceClosure idle_quit_closure_;
- };
- class MessageLoopForIoPosixReadAndWriteTest
- : public testing::TestWithParam<ReaderWriterHandler::Action> {
- protected:
- bool CreateSocketPair(ScopedFD* one, ScopedFD* two) {
- int fds[2];
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1)
- return false;
- one->reset(fds[0]);
- two->reset(fds[1]);
- return true;
- }
- };
- INSTANTIATE_TEST_SUITE_P(StopWatchingOrDelete,
- MessageLoopForIoPosixReadAndWriteTest,
- testing::Values(ReaderWriterHandler::kStopWatching,
- ReaderWriterHandler::kDelete));
- // Test deleting or stopping watch after a read event for a watcher that is
- // registered for both read and write.
- TEST_P(MessageLoopForIoPosixReadAndWriteTest, AfterRead) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- ScopedFD one, two;
- ASSERT_TRUE(CreateSocketPair(&one, &two));
- RunLoop run_loop;
- ReaderWriterHandler* handler =
- new ReaderWriterHandler(GetParam(), ReaderWriterHandler::kOnReadEvent,
- run_loop.QuitWhenIdleClosure());
- // Trigger a read event on |one| by writing to |two|.
- char c = '\0';
- EXPECT_EQ(1, HANDLE_EINTR(write(two.get(), &c, 1)));
- // The triggered read will cause the watcher action to run. |one| would
- // also be immediately available for writing, so this should not cause a
- // use-after-free on the |handler|.
- CurrentIOThread::Get()->WatchFileDescriptor(
- one.get(), true, MessagePumpForIO::WATCH_READ_WRITE,
- handler->controller(), handler);
- run_loop.Run();
- if (GetParam() == ReaderWriterHandler::kStopWatching) {
- delete handler;
- }
- }
- // Test deleting or stopping watch after a write event for a watcher that is
- // registered for both read and write.
- TEST_P(MessageLoopForIoPosixReadAndWriteTest, AfterWrite) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- ScopedFD one, two;
- ASSERT_TRUE(CreateSocketPair(&one, &two));
- RunLoop run_loop;
- ReaderWriterHandler* handler =
- new ReaderWriterHandler(GetParam(), ReaderWriterHandler::kOnWriteEvent,
- run_loop.QuitWhenIdleClosure());
- // Trigger two read events on |one| by writing to |two|. Because each read
- // event only reads one char, |one| will be available for reading again after
- // the first read event is handled.
- char c = '\0';
- EXPECT_EQ(1, HANDLE_EINTR(write(two.get(), &c, 1)));
- EXPECT_EQ(1, HANDLE_EINTR(write(two.get(), &c, 1)));
- // The triggered read and the immediate availability of |one| for writing
- // should cause both the read and write watchers to be triggered. The
- // |handler| will do its action in response to the write event, which should
- // not trigger a use-after-free for the second read that was queued.
- CurrentIOThread::Get()->WatchFileDescriptor(
- one.get(), true, MessagePumpForIO::WATCH_READ_WRITE,
- handler->controller(), handler);
- run_loop.Run();
- if (GetParam() == ReaderWriterHandler::kStopWatching) {
- delete handler;
- }
- }
- // Verify that basic readable notification works.
- TEST_F(FdWatchControllerPosixTest, WatchReadable) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- TestHandler handler;
- // Watch the pipe for readability.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- // The pipe should not be readable when first created.
- RunLoop().RunUntilIdle();
- ASSERT_FALSE(handler.is_readable_);
- ASSERT_FALSE(handler.is_writable_);
- TriggerReadEvent();
- // We don't want to assume that the read fd becomes readable the
- // instant a bytes is written, so Run until quit by an event.
- RunLoop().Run();
- ASSERT_TRUE(handler.is_readable_);
- ASSERT_FALSE(handler.is_writable_);
- }
- // Verify that watching a file descriptor for writability succeeds.
- TEST_F(FdWatchControllerPosixTest, WatchWritable) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- TestHandler handler;
- // Watch the pipe for writability.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- write_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_WRITE,
- &watcher, &handler));
- // We should not receive a writable notification until we process events.
- ASSERT_FALSE(handler.is_readable_);
- ASSERT_FALSE(handler.is_writable_);
- // The pipe should be writable immediately, but wait for the quit closure
- // anyway, to be sure.
- RunLoop().Run();
- ASSERT_FALSE(handler.is_readable_);
- ASSERT_TRUE(handler.is_writable_);
- }
- // Verify that RunUntilIdle() receives IO notifications.
- TEST_F(FdWatchControllerPosixTest, RunUntilIdle) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- TestHandler handler;
- // Watch the pipe for readability.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- // The pipe should not be readable when first created.
- RunLoop().RunUntilIdle();
- ASSERT_FALSE(handler.is_readable_);
- TriggerReadEvent();
- while (!handler.is_readable_)
- RunLoop().RunUntilIdle();
- }
- void StopWatching(MessagePumpForIO::FdWatchController* controller,
- RunLoop* run_loop) {
- controller->StopWatchingFileDescriptor();
- run_loop->Quit();
- }
- // Verify that StopWatchingFileDescriptor() works from an event handler.
- TEST_F(FdWatchControllerPosixTest, StopFromHandler) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- RunLoop run_loop;
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- CallClosureHandler handler(BindOnce(&StopWatching, &watcher, &run_loop),
- OnceClosure());
- // Create persistent watcher.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- TriggerReadEvent();
- run_loop.Run();
- // Trigger the event again. The event handler should not be called again.
- TriggerReadEvent();
- RunLoop().RunUntilIdle();
- }
- // Verify that non-persistent watcher is called only once.
- TEST_F(FdWatchControllerPosixTest, NonPersistentWatcher) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- RunLoop run_loop;
- CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure());
- // Create a non-persistent watcher.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- TriggerReadEvent();
- run_loop.Run();
- // Trigger the event again. handler should not be called again.
- TriggerReadEvent();
- RunLoop().RunUntilIdle();
- }
- // Verify that persistent watcher is called every time the event is triggered.
- TEST_F(FdWatchControllerPosixTest, PersistentWatcher) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- RunLoop run_loop1;
- CallClosureHandler handler(run_loop1.QuitClosure(), OnceClosure());
- // Create persistent watcher.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- TriggerReadEvent();
- run_loop1.Run();
- RunLoop run_loop2;
- handler.SetReadClosure(run_loop2.QuitClosure());
- // Trigger the event again. handler should be called now, which will quit
- // run_loop2.
- TriggerReadEvent();
- run_loop2.Run();
- }
- void StopWatchingAndWatchAgain(MessagePumpForIO::FdWatchController* controller,
- int fd,
- MessagePumpForIO::FdWatcher* new_handler,
- RunLoop* run_loop) {
- controller->StopWatchingFileDescriptor();
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- fd, /*persistent=*/true, MessagePumpForIO::WATCH_READ, controller,
- new_handler));
- run_loop->Quit();
- }
- // Verify that a watcher can be stopped and reused from an event handler.
- TEST_F(FdWatchControllerPosixTest, StopAndRestartFromHandler) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- RunLoop run_loop1;
- RunLoop run_loop2;
- CallClosureHandler handler2(run_loop2.QuitClosure(), OnceClosure());
- CallClosureHandler handler1(BindOnce(&StopWatchingAndWatchAgain, &watcher,
- read_fd_.get(), &handler2, &run_loop1),
- OnceClosure());
- // Create persistent watcher.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ,
- &watcher, &handler1));
- TriggerReadEvent();
- run_loop1.Run();
- // Trigger the event again. handler2 should be called now, which will quit
- // run_loop2
- TriggerReadEvent();
- run_loop2.Run();
- }
- // Verify that the pump properly handles a delayed task after an IO event.
- TEST_F(FdWatchControllerPosixTest, IoEventThenTimer) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- RunLoop timer_run_loop;
- env.GetMainThreadTaskRunner()->PostDelayedTask(
- FROM_HERE, timer_run_loop.QuitClosure(), base::Milliseconds(10));
- RunLoop watcher_run_loop;
- CallClosureHandler handler(watcher_run_loop.QuitClosure(), OnceClosure());
- // Create a non-persistent watcher.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- TriggerReadEvent();
- // Normally the IO event will be received before the delayed task is
- // executed, so this run loop will first handle the IO event and then quit on
- // the timer.
- timer_run_loop.Run();
- // Run watcher_run_loop in case the IO event wasn't received before the
- // delayed task.
- watcher_run_loop.Run();
- }
- // Verify that the pipe can handle an IO event after a delayed task.
- TEST_F(FdWatchControllerPosixTest, TimerThenIoEvent) {
- test::TaskEnvironment env(test::TaskEnvironment::MainThreadType::IO);
- MessagePumpForIO::FdWatchController watcher(FROM_HERE);
- // Trigger read event from a delayed task.
- env.GetMainThreadTaskRunner()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&FdWatchControllerPosixTest::TriggerReadEvent, Unretained(this)),
- Milliseconds(1));
- RunLoop run_loop;
- CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure());
- // Create a non-persistent watcher.
- ASSERT_TRUE(CurrentIOThread::Get()->WatchFileDescriptor(
- read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
- &watcher, &handler));
- run_loop.Run();
- }
- } // namespace
- #endif // !BUILDFLAG(IS_NACL)
- } // namespace base
|