// 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
|