| // 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 "base/message_loop/message_loop.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/macros.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/message_loop/message_loop_current.h" | |
| #include "base/message_loop/message_pump_for_io.h" | |
| #include "base/posix/eintr_wrapper.h" | |
| #include "base/run_loop.h" | |
| #include "base/test/gtest_util.h" | |
| #include "build/build_config.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| namespace base { | |
| #if !defined(OS_NACL) | |
| namespace { | |
| class MessageLoopForIoPosixTest : public testing::Test { | |
| public: | |
| MessageLoopForIoPosixTest() = default; | |
| // 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_; | |
| DISALLOW_COPY_AND_ASSIGN(MessageLoopForIoPosixTest); | |
| }; | |
| 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:MessagePumpFuchsia::Watcher interface. | |
| 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(MessageLoopForIoPosixTest, 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; | |
| { | |
| MessageLoopForIO message_loop; | |
| MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, FileDescriptorWatcherDoubleStop) { | |
| // Verify that it's ok to call StopWatchingFileDescriptor(). | |
| // Arrange for message loop to live longer than watcher. | |
| MessageLoopForIO message_loop; | |
| { | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| TestHandler handler; | |
| MessageLoopCurrentForIO::Get()->WatchFileDescriptor( | |
| write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE, &watcher, | |
| &handler); | |
| ASSERT_TRUE(watcher.StopWatchingFileDescriptor()); | |
| ASSERT_TRUE(watcher.StopWatchingFileDescriptor()); | |
| } | |
| } | |
| TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDeleteInCallback) { | |
| // Verify that it is OK to delete the FileDescriptorWatcher from within a | |
| // callback. | |
| MessageLoopForIO message_loop; | |
| TestHandler handler; | |
| handler.watcher_to_delete_ = | |
| std::make_unique<MessagePumpForIO::FdWatchController>(FROM_HERE); | |
| MessageLoopCurrentForIO::Get()->WatchFileDescriptor( | |
| write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE, | |
| handler.watcher_to_delete_.get(), &handler); | |
| RunLoop().Run(); | |
| } | |
| // Verify that basic readable notification works. | |
| TEST_F(MessageLoopForIoPosixTest, WatchReadable) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| TestHandler handler; | |
| // Watch the pipe for readability. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, WatchWritable) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| TestHandler handler; | |
| // Watch the pipe for writability. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, RunUntilIdle) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| TestHandler handler; | |
| // Watch the pipe for readability. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, StopFromHandler) { | |
| MessageLoopForIO message_loop; | |
| RunLoop run_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| CallClosureHandler handler(BindOnce(&StopWatching, &watcher, &run_loop), | |
| OnceClosure()); | |
| // Create persistent watcher. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, NonPersistentWatcher) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| RunLoop run_loop; | |
| CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure()); | |
| // Create a non-persistent watcher. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, PersistentWatcher) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| RunLoop run_loop1; | |
| CallClosureHandler handler(run_loop1.QuitClosure(), OnceClosure()); | |
| // Create persistent watcher. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, StopAndRestartFromHandler) { | |
| MessageLoopForIO message_loop; | |
| 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(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, IoEventThenTimer) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| RunLoop timer_run_loop; | |
| message_loop.task_runner()->PostDelayedTask( | |
| FROM_HERE, timer_run_loop.QuitClosure(), | |
| base::TimeDelta::FromMilliseconds(10)); | |
| RunLoop watcher_run_loop; | |
| CallClosureHandler handler(watcher_run_loop.QuitClosure(), OnceClosure()); | |
| // Create a non-persistent watcher. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::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(MessageLoopForIoPosixTest, TimerThenIoEvent) { | |
| MessageLoopForIO message_loop; | |
| MessagePumpForIO::FdWatchController watcher(FROM_HERE); | |
| // Trigger read event from a delayed task. | |
| message_loop.task_runner()->PostDelayedTask( | |
| FROM_HERE, | |
| BindOnce(&MessageLoopForIoPosixTest::TriggerReadEvent, Unretained(this)), | |
| TimeDelta::FromMilliseconds(1)); | |
| RunLoop run_loop; | |
| CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure()); | |
| // Create a non-persistent watcher. | |
| ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( | |
| read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ, | |
| &watcher, &handler)); | |
| run_loop.Run(); | |
| } | |
| } // namespace | |
| #endif // !defined(OS_NACL) | |
| } // namespace base |