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