summaryrefslogtreecommitdiff
path: root/chromium/base/message_loop
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-11-20 10:33:36 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-11-22 11:45:12 +0000
commitbe59a35641616a4cf23c4a13fa0632624b021c1b (patch)
tree9da183258bdf9cc413f7562079d25ace6955467f /chromium/base/message_loop
parentd702e4b6a64574e97fc7df8fe3238cde70242080 (diff)
downloadqtwebengine-chromium-be59a35641616a4cf23c4a13fa0632624b021c1b.tar.gz
BASELINE: Update Chromium to 62.0.3202.101
Change-Id: I2d5eca8117600df6d331f6166ab24d943d9814ac Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/base/message_loop')
-rw-r--r--chromium/base/message_loop/incoming_task_queue.cc5
-rw-r--r--chromium/base/message_loop/incoming_task_queue.h4
-rw-r--r--chromium/base/message_loop/message_loop.cc83
-rw-r--r--chromium/base/message_loop/message_loop.h68
-rw-r--r--chromium/base/message_loop/message_loop_io_posix_unittest.cc265
-rw-r--r--chromium/base/message_loop/message_loop_task_runner_unittest.cc2
-rw-r--r--chromium/base/message_loop/message_loop_test.cc995
-rw-r--r--chromium/base/message_loop/message_loop_test.h129
-rw-r--r--chromium/base/message_loop/message_loop_unittest.cc1293
-rw-r--r--chromium/base/message_loop/message_pump_android.cc52
-rw-r--r--chromium/base/message_loop/message_pump_android.h5
-rw-r--r--chromium/base/message_loop/message_pump_default.cc8
-rw-r--r--chromium/base/message_loop/message_pump_fuchsia.cc237
-rw-r--r--chromium/base/message_loop/message_pump_fuchsia.h122
-rw-r--r--chromium/base/message_loop/message_pump_glib_unittest.cc8
-rw-r--r--chromium/base/message_loop/message_pump_perftest.cc3
16 files changed, 1710 insertions, 1569 deletions
diff --git a/chromium/base/message_loop/incoming_task_queue.cc b/chromium/base/message_loop/incoming_task_queue.cc
index 67796ffa377..a945abbb475 100644
--- a/chromium/base/message_loop/incoming_task_queue.cc
+++ b/chromium/base/message_loop/incoming_task_queue.cc
@@ -86,11 +86,6 @@ bool IncomingTaskQueue::AddToIncomingQueue(
return PostPendingTask(&pending_task);
}
-bool IncomingTaskQueue::HasHighResolutionTasks() {
- AutoLock lock(incoming_queue_lock_);
- return high_res_task_count_ > 0;
-}
-
bool IncomingTaskQueue::IsIdleForTesting() {
AutoLock lock(incoming_queue_lock_);
return incoming_queue_.empty();
diff --git a/chromium/base/message_loop/incoming_task_queue.h b/chromium/base/message_loop/incoming_task_queue.h
index 17bea07674c..d6028cb914e 100644
--- a/chromium/base/message_loop/incoming_task_queue.h
+++ b/chromium/base/message_loop/incoming_task_queue.h
@@ -40,10 +40,6 @@ class BASE_EXPORT IncomingTaskQueue
TimeDelta delay,
bool nestable);
- // Returns true if the queue contains tasks that require higher than default
- // timer resolution. Currently only needed for Windows.
- bool HasHighResolutionTasks();
-
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc
index a5c74e40a5f..67b9e225617 100644
--- a/chromium/base/message_loop/message_loop.cc
+++ b/chromium/base/message_loop/message_loop.cc
@@ -48,13 +48,13 @@ base::ThreadLocalPointer<MessageLoop>* GetTLSMessageLoop() {
MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL;
#if defined(OS_IOS)
-typedef MessagePumpIOSForIO MessagePumpForIO;
+using MessagePumpForIO = MessagePumpIOSForIO;
#elif defined(OS_NACL_SFI)
-typedef MessagePumpDefault MessagePumpForIO;
+using MessagePumpForIO = MessagePumpDefault;
#elif defined(OS_FUCHSIA)
-typedef MessagePumpFuchsia MessagePumpForIO;
+using MessagePumpForIO = MessagePumpFuchsia;
#elif defined(OS_POSIX)
-typedef MessagePumpLibevent MessagePumpForIO;
+using MessagePumpForIO = MessagePumpLibevent;
#endif
#if !defined(OS_NACL_SFI)
@@ -105,8 +105,7 @@ MessageLoop::~MessageLoop() {
// There should be no active RunLoops on this thread, unless this MessageLoop
// isn't bound to the current thread (see other condition at the top of this
// method).
- DCHECK((!pump_ && current() != this) ||
- !run_loop_client_->GetTopMostRunLoop());
+ DCHECK((!pump_ && current() != this) || !RunLoop::IsRunningOnCurrentThread());
#endif
#if defined(OS_WIN)
@@ -168,11 +167,11 @@ bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) {
std::unique_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) {
// TODO(rvargas): Get rid of the OS guards.
#if defined(USE_GLIB) && !defined(OS_NACL)
- typedef MessagePumpGlib MessagePumpForUI;
+ using MessagePumpForUI = MessagePumpGlib;
#elif (defined(OS_LINUX) && !defined(OS_NACL)) || defined(OS_BSD)
- typedef MessagePumpLibevent MessagePumpForUI;
+ using MessagePumpForUI = MessagePumpLibevent;
#elif defined(OS_FUCHSIA)
- typedef MessagePumpFuchsia MessagePumpForUI;
+ using MessagePumpForUI = MessagePumpFuchsia;
#endif
#if defined(OS_IOS) || defined(OS_MACOSX)
@@ -223,31 +222,13 @@ void MessageLoop::RemoveDestructionObserver(
destruction_observers_.RemoveObserver(destruction_observer);
}
-void MessageLoop::QuitWhenIdle() {
- DCHECK_EQ(this, current());
- DCHECK(run_loop_client_->GetTopMostRunLoop())
- << "Must be inside Run to call QuitWhenIdle";
- run_loop_client_->GetTopMostRunLoop()->QuitWhenIdle();
-}
-
-void MessageLoop::QuitNow() {
- DCHECK_EQ(this, current());
- DCHECK(run_loop_client_->GetTopMostRunLoop())
- << "Must be inside Run to call Quit";
- pump_->Quit();
-}
-
bool MessageLoop::IsType(Type type) const {
return type_ == type;
}
-static void QuitCurrentWhenIdle() {
- MessageLoop::current()->QuitWhenIdle();
-}
-
// static
Closure MessageLoop::QuitWhenIdleClosure() {
- return Bind(&QuitCurrentWhenIdle);
+ return Bind(&RunLoop::QuitCurrentWhenIdleDeprecated);
}
void MessageLoop::SetNestableTasksAllowed(bool allowed) {
@@ -255,14 +236,14 @@ void MessageLoop::SetNestableTasksAllowed(bool allowed) {
CHECK(RunLoop::IsNestingAllowedOnCurrentThread());
// Kick the native pump just in case we enter a OS-driven nested message
- // loop.
+ // loop that does not go through RunLoop::Run().
pump_->ScheduleWork();
}
nestable_tasks_allowed_ = allowed;
}
bool MessageLoop::NestableTasksAllowed() const {
- return nestable_tasks_allowed_;
+ return nestable_tasks_allowed_ || run_loop_client_->ProcessingTasksAllowed();
}
// TODO(gab): Migrate TaskObservers to RunLoop as part of separating concerns
@@ -280,10 +261,6 @@ void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
task_observers_.RemoveObserver(task_observer);
}
-bool MessageLoop::HasHighResolutionTasks() {
- return incoming_task_queue_->HasHighResolutionTasks();
-}
-
bool MessageLoop::IsIdleForTesting() {
// We only check the incoming queue, since we don't want to lock the work
// queue.
@@ -333,9 +310,9 @@ void MessageLoop::BindToCurrentThread() {
SetThreadTaskRunnerHandle();
thread_id_ = PlatformThread::CurrentId();
- scoped_set_sequence_local_storage_map_for_current_thread_ =
- MakeUnique<internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
- &sequence_local_storage_map_);
+ scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique<
+ internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
+ &sequence_local_storage_map_);
run_loop_client_ = RunLoop::RegisterDelegateForCurrentThread(this);
}
@@ -371,7 +348,14 @@ void MessageLoop::Run() {
void MessageLoop::Quit() {
DCHECK_EQ(this, current());
- QuitNow();
+ pump_->Quit();
+}
+
+void MessageLoop::EnsureWorkScheduled() {
+ DCHECK_EQ(this, current());
+ ReloadWorkQueue();
+ if (!work_queue_.empty())
+ pump_->ScheduleWork();
}
void MessageLoop::SetThreadTaskRunnerHandle() {
@@ -405,7 +389,7 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
}
void MessageLoop::RunTask(PendingTask* pending_task) {
- DCHECK(nestable_tasks_allowed_);
+ DCHECK(NestableTasksAllowed());
current_pending_task_ = pending_task;
#if defined(OS_WIN)
@@ -510,7 +494,7 @@ void MessageLoop::ScheduleWork() {
}
bool MessageLoop::DoWork() {
- if (!nestable_tasks_allowed_) {
+ if (!NestableTasksAllowed()) {
// Task can't be executed right now.
return false;
}
@@ -548,7 +532,7 @@ bool MessageLoop::DoWork() {
}
bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
- if (!nestable_tasks_allowed_ ||
+ if (!NestableTasksAllowed() ||
!SweepDelayedWorkQueueAndReturnTrueIfStillHasWork()) {
recent_time_ = *next_delayed_work_time = TimeTicks();
return false;
@@ -584,7 +568,7 @@ bool MessageLoop::DoIdleWork() {
if (ProcessNextDelayedNonNestableTask())
return true;
- if (run_loop_client_->GetTopMostRunLoop()->quit_when_idle_received_)
+ if (run_loop_client_->ShouldQuitWhenIdle())
pump_->Quit();
// When we return we will do a kernel wait for more tasks.
@@ -644,7 +628,8 @@ void MessageLoopForUI::Attach() {
}
#endif
-#if defined(USE_OZONE) || (defined(USE_X11) && !defined(USE_GLIB))
+#if (defined(USE_OZONE) && !defined(OS_FUCHSIA)) || \
+ (defined(USE_X11) && !defined(USE_GLIB))
bool MessageLoopForUI::WatchFileDescriptor(
int fd,
bool persistent,
@@ -696,4 +681,16 @@ bool MessageLoopForIO::WatchFileDescriptor(int fd,
#endif // !defined(OS_NACL_SFI)
+#if defined(OS_FUCHSIA)
+// Additional watch API for native platform resources.
+bool MessageLoopForIO::WatchMxHandle(mx_handle_t handle,
+ bool persistent,
+ mx_signals_t signals,
+ MxHandleWatchController* controller,
+ MxHandleWatcher* delegate) {
+ return ToPumpIO(pump_.get())
+ ->WatchMxHandle(handle, persistent, signals, controller, delegate);
+}
+#endif
+
} // namespace base
diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h
index 83c3191b47b..a118917ea1c 100644
--- a/chromium/base/message_loop/message_loop.h
+++ b/chromium/base/message_loop/message_loop.h
@@ -132,7 +132,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// Returns the MessageLoop object for the current thread, or null if none.
static MessageLoop* current();
- typedef std::unique_ptr<MessagePump>(MessagePumpFactory)();
+ using MessagePumpFactory = std::unique_ptr<MessagePump>();
// Uses the given base::MessagePumpForUIFactory to override the default
// MessagePump implementation for 'TYPE_UI'. Returns true if the factory
// was successfully registered.
@@ -167,29 +167,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
void RemoveDestructionObserver(DestructionObserver* destruction_observer);
// Deprecated: use RunLoop instead.
- //
- // Signals the Run method to return when it becomes idle. It will continue to
- // process pending messages and future messages as long as they are enqueued.
- // Warning: if the MessageLoop remains busy, it may never quit. Only use this
- // Quit method when looping procedures (such as web pages) have been shut
- // down.
- //
- // This method may only be called on the same thread that called Run, and Run
- // must still be on the call stack.
- //
- // Use QuitClosure variants if you need to Quit another thread's MessageLoop,
- // but note that doing so is fairly dangerous if the target thread makes
- // nested calls to MessageLoop::Run. The problem being that you won't know
- // which nested run loop you are quitting, so be careful!
- void QuitWhenIdle();
-
- // Deprecated: use RunLoop instead.
- //
- // This method is a variant of Quit, that does not wait for pending messages
- // to be processed before returning from Run.
- void QuitNow();
-
- // Deprecated: use RunLoop instead.
// Construct a Closure that will call QuitWhenIdle(). Useful to schedule an
// arbitrary MessageLoop to QuitWhenIdle.
static Closure QuitWhenIdleClosure();
@@ -248,10 +225,20 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// - With NestableTasksAllowed set to true, the task #2 will run right away.
// Otherwise, it will get executed right after task #1 completes at "thread
// message loop level".
+ //
+ // DEPRECATED: Use RunLoop::Type on the relevant RunLoop instead of these
+ // methods.
+ // TODO(gab): Migrate usage and delete these methods.
void SetNestableTasksAllowed(bool allowed);
bool NestableTasksAllowed() const;
// Enables nestable tasks on |loop| while in scope.
+ // DEPRECATED: This should not be used when the nested loop is driven by
+ // RunLoop (use RunLoop::Type::KNestableTasksAllowed instead). It can however
+ // still be useful in a few scenarios where re-entrancy is caused by a native
+ // message loop.
+ // TODO(gab): Remove usage of this class alongside RunLoop and rename it to
+ // ScopedApplicationTasksAllowedInNativeNestedLoop(?).
class ScopedNestableTaskAllower {
public:
explicit ScopedNestableTaskAllower(MessageLoop* loop)
@@ -291,10 +278,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
void AddTaskObserver(TaskObserver* task_observer);
void RemoveTaskObserver(TaskObserver* task_observer);
- // Returns true if the message loop has high resolution timers enabled.
- // Provided for testing.
- bool HasHighResolutionTasks();
-
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
@@ -357,6 +340,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
// RunLoop::Delegate:
void Run() override;
void Quit() override;
+ void EnsureWorkScheduled() override;
// Called to process any delayed non-nestable tasks.
bool ProcessNextDelayedNonNestableTask();
@@ -424,7 +408,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
ObserverList<DestructionObserver> destruction_observers_;
// A recursion block that prevents accidentally running additional tasks when
- // insider a (accidentally induced?) nested message pump.
+ // insider a (accidentally induced?) nested message pump. Deprecated in favor
+ // of run_loop_client_->ProcessingTasksAllowed(), equivalent until then (both
+ // need to be checked in conditionals).
bool nestable_tasks_allowed_;
// pump_factory_.Run() is called to create a message pump for this loop
@@ -520,7 +506,8 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop {
void Abort();
#endif
-#if defined(USE_OZONE) || (defined(USE_X11) && !defined(USE_GLIB))
+#if (defined(USE_OZONE) && !defined(OS_FUCHSIA)) || \
+ (defined(USE_X11) && !defined(USE_GLIB))
// Please see MessagePumpLibevent for definition.
bool WatchFileDescriptor(
int fd,
@@ -570,12 +557,15 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
typedef MessagePumpForIO::IOHandler IOHandler;
typedef MessagePumpForIO::IOContext IOContext;
#elif defined(OS_FUCHSIA)
- typedef MessagePumpFuchsia::Watcher Watcher;
- typedef MessagePumpFuchsia::FileDescriptorWatcher FileDescriptorWatcher;
+ typedef MessagePumpFuchsia::FdWatcher Watcher;
+ typedef MessagePumpFuchsia::FdWatchController FileDescriptorWatcher;
enum Mode{WATCH_READ = MessagePumpFuchsia::WATCH_READ,
WATCH_WRITE = MessagePumpFuchsia::WATCH_WRITE,
WATCH_READ_WRITE = MessagePumpFuchsia::WATCH_READ_WRITE};
+
+ typedef MessagePumpFuchsia::MxHandleWatchController MxHandleWatchController;
+ typedef MessagePumpFuchsia::MxHandleWatcher MxHandleWatcher;
#elif defined(OS_IOS)
typedef MessagePumpIOSForIO::Watcher Watcher;
typedef MessagePumpIOSForIO::FileDescriptorWatcher
@@ -587,9 +577,8 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
WATCH_READ_WRITE = MessagePumpIOSForIO::WATCH_READ_WRITE
};
#elif defined(OS_POSIX)
- typedef MessagePumpLibevent::Watcher Watcher;
- typedef MessagePumpLibevent::FileDescriptorWatcher
- FileDescriptorWatcher;
+ using Watcher = MessagePumpLibevent::Watcher;
+ using FileDescriptorWatcher = MessagePumpLibevent::FileDescriptorWatcher;
enum Mode {
WATCH_READ = MessagePumpLibevent::WATCH_READ,
@@ -612,6 +601,15 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
Watcher* delegate);
#endif // defined(OS_IOS) || defined(OS_POSIX)
#endif // !defined(OS_NACL_SFI)
+
+#if defined(OS_FUCHSIA)
+ // Additional watch API for native platform resources.
+ bool WatchMxHandle(mx_handle_t handle,
+ bool persistent,
+ mx_signals_t signals,
+ MxHandleWatchController* controller,
+ MxHandleWatcher* delegate);
+#endif
};
// Do not add any member variables to MessageLoopForIO! This is important b/c
diff --git a/chromium/base/message_loop/message_loop_io_posix_unittest.cc b/chromium/base/message_loop/message_loop_io_posix_unittest.cc
index 78a09bc297e..6e6a92a6bef 100644
--- a/chromium/base/message_loop/message_loop_io_posix_unittest.cc
+++ b/chromium/base/message_loop/message_loop_io_posix_unittest.cc
@@ -2,15 +2,18 @@
// 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.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.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"
@@ -32,12 +35,19 @@ class MessageLoopForIoPosixTest : public testing::Test {
int pipefds[2];
int err = pipe(pipefds);
ASSERT_EQ(0, err);
- read_fd_ = base::File(pipefds[0]);
- write_fd_ = base::File(pipefds[1]);
+ read_fd_ = ScopedFD(pipefds[0]);
+ write_fd_ = ScopedFD(pipefds[1]);
}
- base::File read_fd_;
- base::File write_fd_;
+ 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);
};
@@ -47,12 +57,12 @@ class TestHandler : public MessageLoopForIO::Watcher {
void OnFileCanReadWithoutBlocking(int fd) override {
watcher_to_delete_ = nullptr;
is_readable_ = true;
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
void OnFileCanWriteWithoutBlocking(int fd) override {
watcher_to_delete_ = nullptr;
is_writable_ = true;
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
bool is_readable_ = false;
@@ -62,6 +72,57 @@ class TestHandler : public MessageLoopForIO::Watcher {
std::unique_ptr<MessageLoopForIO::FileDescriptorWatcher> 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 MessageLoopForIO::Watcher {
+ 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.
@@ -72,7 +133,7 @@ TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherOutlivesMessageLoop) {
{
MessageLoopForIO message_loop;
- message_loop.WatchFileDescriptor(write_fd_.GetPlatformFile(), true,
+ message_loop.WatchFileDescriptor(write_fd_.get(), true,
MessageLoopForIO::WATCH_WRITE, &watcher,
&handler);
// Don't run the message loop, just destroy it.
@@ -92,7 +153,7 @@ TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDoubleStop) {
MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
TestHandler handler;
- message_loop.WatchFileDescriptor(write_fd_.GetPlatformFile(), true,
+ message_loop.WatchFileDescriptor(write_fd_.get(), true,
MessageLoopForIO::WATCH_WRITE, &watcher,
&handler);
ASSERT_TRUE(watcher.StopWatchingFileDescriptor());
@@ -107,9 +168,9 @@ TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDeleteInCallback) {
TestHandler handler;
handler.watcher_to_delete_ =
- base::MakeUnique<MessageLoopForIO::FileDescriptorWatcher>(FROM_HERE);
+ std::make_unique<MessageLoopForIO::FileDescriptorWatcher>(FROM_HERE);
- message_loop.WatchFileDescriptor(write_fd_.GetPlatformFile(), true,
+ message_loop.WatchFileDescriptor(write_fd_.get(), true,
MessageLoopForIO::WATCH_WRITE,
handler.watcher_to_delete_.get(), &handler);
RunLoop().Run();
@@ -123,18 +184,15 @@ TEST_F(MessageLoopForIoPosixTest, WatchReadable) {
// Watch the pipe for readability.
ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
- read_fd_.GetPlatformFile(), /* persistent= */ false,
- MessageLoopForIO::WATCH_READ, &watcher, &handler));
+ read_fd_.get(), /*persistent=*/false, MessageLoopForIO::WATCH_READ,
+ &watcher, &handler));
// The pipe should not be readable when first created.
- base::RunLoop().RunUntilIdle();
+ RunLoop().RunUntilIdle();
ASSERT_FALSE(handler.is_readable_);
ASSERT_FALSE(handler.is_writable_);
- // Write a byte to the other end, making it readable.
- const char buf = 0;
- ASSERT_TRUE(
- WriteFileDescriptor(write_fd_.GetPlatformFile(), &buf, sizeof(buf)));
+ 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.
@@ -152,8 +210,8 @@ TEST_F(MessageLoopForIoPosixTest, WatchWritable) {
// Watch the pipe for writability.
ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
- write_fd_.GetPlatformFile(), /* persistent= */ false,
- MessageLoopForIO::WATCH_WRITE, &watcher, &handler));
+ write_fd_.get(), /*persistent=*/false, MessageLoopForIO::WATCH_WRITE,
+ &watcher, &handler));
// We should not receive a writable notification until we process events.
ASSERT_FALSE(handler.is_readable_);
@@ -167,6 +225,171 @@ TEST_F(MessageLoopForIoPosixTest, WatchWritable) {
ASSERT_TRUE(handler.is_writable_);
}
+void StopWatching(MessageLoopForIO::FileDescriptorWatcher* 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;
+ MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+ CallClosureHandler handler(BindOnce(&StopWatching, &watcher, &run_loop),
+ OnceClosure());
+
+ // Create persistent watcher.
+ ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/true, MessageLoopForIO::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;
+ MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+
+ RunLoop run_loop;
+ CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure());
+
+ // Create a non-persistent watcher.
+ ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/false, MessageLoopForIO::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;
+ MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
+
+ RunLoop run_loop1;
+ CallClosureHandler handler(run_loop1.QuitClosure(), OnceClosure());
+
+ // Create persistent watcher.
+ ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/true, MessageLoopForIO::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(
+ MessageLoopForIO::FileDescriptorWatcher* controller,
+ int fd,
+ MessageLoopForIO::Watcher* new_handler,
+ RunLoop* run_loop) {
+ controller->StopWatchingFileDescriptor();
+
+ ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
+ fd, /*persistent=*/true, MessageLoopForIO::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;
+ MessageLoopForIO::FileDescriptorWatcher 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(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/true, MessageLoopForIO::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;
+ MessageLoopForIO::FileDescriptorWatcher 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(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/false, MessageLoopForIO::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;
+ MessageLoopForIO::FileDescriptorWatcher 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(MessageLoopForIO::current()->WatchFileDescriptor(
+ read_fd_.get(), /*persistent=*/false, MessageLoopForIO::WATCH_READ,
+ &watcher, &handler));
+
+ run_loop.Run();
+}
+
} // namespace
#endif // !defined(OS_NACL)
diff --git a/chromium/base/message_loop/message_loop_task_runner_unittest.cc b/chromium/base/message_loop/message_loop_task_runner_unittest.cc
index 32017f1b02e..323a3e88b1a 100644
--- a/chromium/base/message_loop/message_loop_task_runner_unittest.cc
+++ b/chromium/base/message_loop/message_loop_task_runner_unittest.cc
@@ -82,7 +82,7 @@ class MessageLoopTaskRunnerTest : public testing::Test {
static void RecordLoopAndQuit(scoped_refptr<LoopRecorder> recorder) {
recorder->RecordRun();
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
void UnblockTaskThread() { thread_sync_.Signal(); }
diff --git a/chromium/base/message_loop/message_loop_test.cc b/chromium/base/message_loop/message_loop_test.cc
deleted file mode 100644
index 9ed2434248f..00000000000
--- a/chromium/base/message_loop/message_loop_test.cc
+++ /dev/null
@@ -1,995 +0,0 @@
-// Copyright 2013 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_test.h"
-
-#include <stddef.h>
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-
-namespace base {
-namespace test {
-
-namespace {
-
-class Foo : public RefCounted<Foo> {
- public:
- Foo() : test_count_(0) {
- }
-
- void Test0() {
- ++test_count_;
- }
-
- void Test1ConstRef(const std::string& a) {
- ++test_count_;
- result_.append(a);
- }
-
- void Test1Ptr(std::string* a) {
- ++test_count_;
- result_.append(*a);
- }
-
- void Test1Int(int a) {
- test_count_ += a;
- }
-
- void Test2Ptr(std::string* a, std::string* b) {
- ++test_count_;
- result_.append(*a);
- result_.append(*b);
- }
-
- void Test2Mixed(const std::string& a, std::string* b) {
- ++test_count_;
- result_.append(a);
- result_.append(*b);
- }
-
- int test_count() const { return test_count_; }
- const std::string& result() const { return result_; }
-
- private:
- friend class RefCounted<Foo>;
-
- ~Foo() {}
-
- int test_count_;
- std::string result_;
-
- DISALLOW_COPY_AND_ASSIGN(Foo);
-};
-
-// This function runs slowly to simulate a large amount of work being done.
-void SlowFunc(TimeDelta pause, int* quit_counter) {
- PlatformThread::Sleep(pause);
- if (--(*quit_counter) == 0)
- MessageLoop::current()->QuitWhenIdle();
-}
-
-// This function records the time when Run was called in a Time object, which is
-// useful for building a variety of MessageLoop tests.
-// TODO(sky): remove?
-void RecordRunTimeFunc(TimeTicks* run_time, int* quit_counter) {
- *run_time = TimeTicks::Now();
-
- // Cause our Run function to take some time to execute. As a result we can
- // count on subsequent RecordRunTimeFunc()s running at a future time,
- // without worry about the resolution of our system clock being an issue.
- SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
-}
-
-} // namespace
-
-void RunTest_PostTask(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
- // Add tests to message loop
- scoped_refptr<Foo> foo(new Foo());
- std::string a("a"), b("b"), c("c"), d("d");
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&Foo::Test0, foo));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&Foo::Test1Ptr, foo, &b));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&Foo::Test1Int, foo, 100));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d));
- // After all tests, post a message that will shut down the message loop
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
-
- // Now kick things off
- RunLoop().Run();
-
- EXPECT_EQ(foo->test_count(), 105);
- EXPECT_EQ(foo->result(), "abacad");
-}
-
-void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- // Test that PostDelayedTask results in a delayed task.
-
- const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
-
- int num_tasks = 1;
- TimeTicks run_time;
-
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), kDelay);
-
- TimeTicks time_before_run = TimeTicks::Now();
- RunLoop().Run();
- TimeTicks time_after_run = TimeTicks::Now();
-
- EXPECT_EQ(0, num_tasks);
- EXPECT_LT(kDelay, time_after_run - time_before_run);
-}
-
-void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- // Test that two tasks with different delays run in the right order.
- int num_tasks = 2;
- TimeTicks run_time1, run_time2;
-
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks),
- TimeDelta::FromMilliseconds(200));
- // If we get a large pause in execution (due to a context switch) here, this
- // test could fail.
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
- TimeDelta::FromMilliseconds(10));
-
- RunLoop().Run();
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_TRUE(run_time2 < run_time1);
-}
-
-void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- // Test that two tasks with the same delay run in the order in which they
- // were posted.
- //
- // NOTE: This is actually an approximate test since the API only takes a
- // "delay" parameter, so we are not exactly simulating two tasks that get
- // posted at the exact same time. It would be nice if the API allowed us to
- // specify the desired run time.
-
- const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
-
- int num_tasks = 2;
- TimeTicks run_time1, run_time2;
-
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
-
- RunLoop().Run();
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_TRUE(run_time1 < run_time2);
-}
-
-void RunTest_PostDelayedTask_InPostOrder_2(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- // Test that a delayed task still runs after a normal tasks even if the
- // normal tasks take a long time to run.
-
- const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
-
- int num_tasks = 2;
- TimeTicks run_time;
-
- loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&SlowFunc, kPause, &num_tasks));
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks),
- TimeDelta::FromMilliseconds(10));
-
- TimeTicks time_before_run = TimeTicks::Now();
- RunLoop().Run();
- TimeTicks time_after_run = TimeTicks::Now();
-
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_LT(kPause, time_after_run - time_before_run);
-}
-
-void RunTest_PostDelayedTask_InPostOrder_3(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- // Test that a delayed task still runs after a pile of normal tasks. The key
- // difference between this test and the previous one is that here we return
- // the MessageLoop a lot so we give the MessageLoop plenty of opportunities
- // to maybe run the delayed task. It should know not to do so until the
- // delayed task's delay has passed.
-
- int num_tasks = 11;
- TimeTicks run_time1, run_time2;
-
- // Clutter the ML with tasks.
- for (int i = 1; i < num_tasks; ++i)
- loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks));
-
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
- TimeDelta::FromMilliseconds(1));
-
- RunLoop().Run();
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_TRUE(run_time2 > run_time1);
-}
-
-void RunTest_PostDelayedTask_SharedTimer(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- // Test that the interval of the timer, used to run the next delayed task, is
- // set to a value corresponding to when the next delayed task should run.
-
- // By setting num_tasks to 1, we ensure that the first task to run causes the
- // run loop to exit.
- int num_tasks = 1;
- TimeTicks run_time1, run_time2;
-
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks),
- TimeDelta::FromSeconds(1000));
- loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
- TimeDelta::FromMilliseconds(10));
-
- TimeTicks start_time = TimeTicks::Now();
-
- RunLoop().Run();
- EXPECT_EQ(0, num_tasks);
-
- // Ensure that we ran in far less time than the slower timer.
- TimeDelta total_time = TimeTicks::Now() - start_time;
- EXPECT_GT(5000, total_time.InMilliseconds());
-
- // In case both timers somehow run at nearly the same time, sleep a little
- // and then run all pending to force them both to have run. This is just
- // encouraging flakiness if there is any.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
- RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(run_time1.is_null());
- EXPECT_FALSE(run_time2.is_null());
-}
-
-// This is used to inject a test point for recording the destructor calls for
-// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we
-// are trying to hook the actual destruction, which is not a common operation.
-class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> {
- public:
- RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted)
- : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {
- }
- void Run() {}
-
- private:
- friend class RefCounted<RecordDeletionProbe>;
-
- ~RecordDeletionProbe() {
- *was_deleted_ = true;
- if (post_on_delete_.get())
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_));
- }
-
- scoped_refptr<RecordDeletionProbe> post_on_delete_;
- bool* was_deleted_;
-};
-
-void RunTest_EnsureDeletion(MessagePumpFactory factory) {
- bool a_was_deleted = false;
- bool b_was_deleted = false;
- {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
- loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&RecordDeletionProbe::Run,
- new RecordDeletionProbe(NULL, &a_was_deleted)));
- // TODO(ajwong): Do we really need 1000ms here?
- loop.task_runner()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&RecordDeletionProbe::Run,
- new RecordDeletionProbe(NULL, &b_was_deleted)),
- TimeDelta::FromMilliseconds(1000));
- }
- EXPECT_TRUE(a_was_deleted);
- EXPECT_TRUE(b_was_deleted);
-}
-
-void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory) {
- bool a_was_deleted = false;
- bool b_was_deleted = false;
- bool c_was_deleted = false;
- {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
- // The scoped_refptr for each of the below is held either by the chained
- // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback.
- RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted);
- RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
- RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
- loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&RecordDeletionProbe::Run, c));
- }
- EXPECT_TRUE(a_was_deleted);
- EXPECT_TRUE(b_was_deleted);
- EXPECT_TRUE(c_was_deleted);
-}
-
-void NestingFunc(int* depth) {
- if (*depth > 0) {
- *depth -= 1;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&NestingFunc, depth));
-
- MessageLoop::current()->SetNestableTasksAllowed(true);
- RunLoop().Run();
- }
- MessageLoop::current()->QuitWhenIdle();
-}
-
-void RunTest_Nesting(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- int depth = 50;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&NestingFunc, &depth));
- RunLoop().Run();
- EXPECT_EQ(depth, 0);
-}
-
-enum TaskType {
- MESSAGEBOX,
- ENDDIALOG,
- RECURSIVE,
- TIMEDMESSAGELOOP,
- QUITMESSAGELOOP,
- ORDERED,
- PUMPS,
- SLEEP,
- RUNS,
-};
-
-struct TaskItem {
- TaskItem(TaskType t, int c, bool s)
- : type(t),
- cookie(c),
- start(s) {
- }
-
- TaskType type;
- int cookie;
- bool start;
-
- bool operator == (const TaskItem& other) const {
- return type == other.type && cookie == other.cookie && start == other.start;
- }
-};
-
-std::ostream& operator <<(std::ostream& os, TaskType type) {
- switch (type) {
- case MESSAGEBOX: os << "MESSAGEBOX"; break;
- case ENDDIALOG: os << "ENDDIALOG"; break;
- case RECURSIVE: os << "RECURSIVE"; break;
- case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break;
- case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break;
- case ORDERED: os << "ORDERED"; break;
- case PUMPS: os << "PUMPS"; break;
- case SLEEP: os << "SLEEP"; break;
- default:
- NOTREACHED();
- os << "Unknown TaskType";
- break;
- }
- return os;
-}
-
-std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
- if (item.start)
- return os << item.type << " " << item.cookie << " starts";
- else
- return os << item.type << " " << item.cookie << " ends";
-}
-
-class TaskList {
- public:
- void RecordStart(TaskType type, int cookie) {
- TaskItem item(type, cookie, true);
- DVLOG(1) << item;
- task_list_.push_back(item);
- }
-
- void RecordEnd(TaskType type, int cookie) {
- TaskItem item(type, cookie, false);
- DVLOG(1) << item;
- task_list_.push_back(item);
- }
-
- size_t Size() {
- return task_list_.size();
- }
-
- TaskItem Get(int n) {
- return task_list_[n];
- }
-
- private:
- std::vector<TaskItem> task_list_;
-};
-
-void RecursiveFunc(TaskList* order, int cookie, int depth,
- bool is_reentrant) {
- order->RecordStart(RECURSIVE, cookie);
- if (depth > 0) {
- if (is_reentrant)
- MessageLoop::current()->SetNestableTasksAllowed(true);
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
- }
- order->RecordEnd(RECURSIVE, cookie);
-}
-
-void QuitFunc(TaskList* order, int cookie) {
- order->RecordStart(QUITMESSAGELOOP, cookie);
- MessageLoop::current()->QuitWhenIdle();
- order->RecordEnd(QUITMESSAGELOOP, cookie);
-}
-void RunTest_RecursiveDenial1(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
- TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, false));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, false));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&QuitFunc, &order, 3));
-
- RunLoop().Run();
-
- // FIFO order.
- ASSERT_EQ(14U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
- EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
-}
-
-void RecursiveSlowFunc(TaskList* order, int cookie, int depth,
- bool is_reentrant) {
- RecursiveFunc(order, cookie, depth, is_reentrant);
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
-}
-
-void OrderedFunc(TaskList* order, int cookie) {
- order->RecordStart(ORDERED, cookie);
- order->RecordEnd(ORDERED, cookie);
-}
-
-void RunTest_RecursiveDenial3(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
- TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 1, 2, false));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 2, 2, false));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, BindOnce(&OrderedFunc, &order, 3),
- TimeDelta::FromMilliseconds(5));
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, BindOnce(&QuitFunc, &order, 4),
- TimeDelta::FromMilliseconds(5));
-
- RunLoop().Run();
-
- // FIFO order.
- ASSERT_EQ(16U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true));
- EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false));
-}
-
-void RunTest_RecursiveSupport1(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, true));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, true));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&QuitFunc, &order, 3));
-
- RunLoop().Run();
-
- // FIFO order.
- ASSERT_EQ(14U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
- EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
-}
-
-// Tests that non nestable tasks run in FIFO if there are no nested loops.
-void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
- FROM_HERE, BindOnce(&OrderedFunc, &order, 1));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&QuitFunc, &order, 3));
- RunLoop().Run();
-
- // FIFO order.
- ASSERT_EQ(6U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
-}
-
-void FuncThatPumps(TaskList* order, int cookie) {
- order->RecordStart(PUMPS, cookie);
- {
- MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
- RunLoop().RunUntilIdle();
- }
- order->RecordEnd(PUMPS, cookie);
-}
-
-void SleepFunc(TaskList* order, int cookie, TimeDelta delay) {
- order->RecordStart(SLEEP, cookie);
- PlatformThread::Sleep(delay);
- order->RecordEnd(SLEEP, cookie);
-}
-
-// Tests that non nestable tasks don't run when there's code in the call stack.
-void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&FuncThatPumps, &order, 1));
- ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
- FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 3));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 5));
- ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
- FROM_HERE, BindOnce(&QuitFunc, &order, 6));
-
- RunLoop().Run();
-
- // FIFO order.
- ASSERT_EQ(12U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true));
- EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false));
- EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true));
- EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false));
- EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false));
- EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true));
- EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false));
-}
-
-void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) {
- order->RecordStart(RUNS, cookie);
- {
- MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
- run_loop->Run();
- }
- order->RecordEnd(RUNS, cookie);
-}
-
-void FuncThatQuitsNow() {
- MessageLoop::current()->QuitNow();
-}
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_QuitNow(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop run_loop;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&FuncThatQuitsNow));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 3));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&FuncThatQuitsNow));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&OrderedFunc, &order, 4)); // never runs
-
- RunLoop().Run();
-
- ASSERT_EQ(6U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitTop(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_run_loop;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_run_loop.QuitClosure());
-
- outer_run_loop.Run();
-
- ASSERT_EQ(4U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitNested(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_run_loop;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
-
- outer_run_loop.Run();
-
- ASSERT_EQ(4U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitBogus(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_run_loop;
- RunLoop bogus_run_loop;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- bogus_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_run_loop.QuitClosure());
-
- outer_run_loop.Run();
-
- ASSERT_EQ(4U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitDeep(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_loop1;
- RunLoop nested_loop2;
- RunLoop nested_loop3;
- RunLoop nested_loop4;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 5));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- outer_run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 6));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop1.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 7));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop2.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 8));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop3.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 9));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- nested_loop4.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 10));
-
- outer_run_loop.Run();
-
- ASSERT_EQ(18U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit works before RunWithID.
-void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop run_loop;
-
- run_loop.Quit();
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); // never runs
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs
-
- run_loop.Run();
-
- ASSERT_EQ(0U, order.Size());
-}
-
-// Tests RunLoopQuit works during RunWithID.
-void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop run_loop;
-
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 1));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); // never runs
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs
-
- run_loop.Run();
-
- ASSERT_EQ(2U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit works after RunWithID.
-void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory) {
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
-
- TaskList order;
-
- RunLoop run_loop;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 2));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&FuncThatQuitsNow));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 3));
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, run_loop.QuitClosure()); // has no affect
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&OrderedFunc, &order, 4));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&FuncThatQuitsNow));
-
- RunLoop outer_run_loop;
- outer_run_loop.Run();
-
- ASSERT_EQ(8U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-void PostNTasksThenQuit(int posts_remaining) {
- if (posts_remaining > 1) {
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1));
- } else {
- MessageLoop::current()->QuitWhenIdle();
- }
-}
-
-// There was a bug in the MessagePumpGLib where posting tasks recursively
-// caused the message loop to hang, due to the buffer of the internal pipe
-// becoming full. Test all MessageLoop types to ensure this issue does not
-// exist in other MessagePumps.
-//
-// On Linux, the pipe buffer size is 64KiB by default. The bug caused one
-// byte accumulated in the pipe per two posts, so we should repeat 128K
-// times to reproduce the bug.
-void RunTest_RecursivePosts(MessagePumpFactory factory) {
- const int kNumTimes = 1 << 17;
- std::unique_ptr<MessagePump> pump(factory());
- MessageLoop loop(std::move(pump));
- loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&PostNTasksThenQuit, kNumTimes));
- RunLoop().Run();
-}
-
-} // namespace test
-} // namespace base
diff --git a/chromium/base/message_loop/message_loop_test.h b/chromium/base/message_loop/message_loop_test.h
deleted file mode 100644
index dbdbfbfc1da..00000000000
--- a/chromium/base/message_loop/message_loop_test.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_
-#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_
-
-#include "base/message_loop/message_loop.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// This file consists of tests meant to exercise the combination of MessageLoop
-// and MessagePump. To use these define the macro RUN_MESSAGE_LOOP_TESTS using
-// an ID appropriate for your MessagePump, eg
-// RUN_MESSAGE_LOOP_TESTS(UI, factory). Factory is a function called to create
-// the MessagePump.
-namespace base {
-namespace test {
-
-typedef MessageLoop::MessagePumpFactory MessagePumpFactory;
-
-void RunTest_PostTask(MessagePumpFactory factory);
-void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory);
-void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory);
-void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory);
-void RunTest_PostDelayedTask_InPostOrder_2(MessagePumpFactory factory);
-void RunTest_PostDelayedTask_InPostOrder_3(MessagePumpFactory factory);
-void RunTest_PostDelayedTask_SharedTimer(MessagePumpFactory factory);
-void RunTest_EnsureDeletion(MessagePumpFactory factory);
-void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory);
-void RunTest_Nesting(MessagePumpFactory factory);
-void RunTest_RecursiveDenial1(MessagePumpFactory factory);
-void RunTest_RecursiveDenial3(MessagePumpFactory factory);
-void RunTest_RecursiveSupport1(MessagePumpFactory factory);
-void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory);
-void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory);
-void RunTest_QuitNow(MessagePumpFactory factory);
-void RunTest_RunLoopQuitTop(MessagePumpFactory factory);
-void RunTest_RunLoopQuitNested(MessagePumpFactory factory);
-void RunTest_RunLoopQuitBogus(MessagePumpFactory factory);
-void RunTest_RunLoopQuitDeep(MessagePumpFactory factory);
-void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory);
-void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory);
-void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory);
-void RunTest_RecursivePosts(MessagePumpFactory factory);
-
-} // namespace test
-} // namespace base
-
-#define RUN_MESSAGE_LOOP_TESTS(id, factory) \
- TEST(MessageLoopTestType##id, PostTask) { \
- base::test::RunTest_PostTask(factory); \
- } \
- TEST(MessageLoopTestType##id, PostDelayedTask_Basic) { \
- base::test::RunTest_PostDelayedTask_Basic(factory); \
- } \
- TEST(MessageLoopTestType##id, PostDelayedTask_InDelayOrder) { \
- base::test::RunTest_PostDelayedTask_InDelayOrder(factory); \
- } \
- TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder) { \
- base::test::RunTest_PostDelayedTask_InPostOrder(factory); \
- } \
- TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder_2) { \
- base::test::RunTest_PostDelayedTask_InPostOrder_2(factory); \
- } \
- TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder_3) { \
- base::test::RunTest_PostDelayedTask_InPostOrder_3(factory); \
- } \
- TEST(MessageLoopTestType##id, PostDelayedTask_SharedTimer) { \
- base::test::RunTest_PostDelayedTask_SharedTimer(factory); \
- } \
- /* TODO(darin): MessageLoop does not support deleting all tasks in the */ \
- /* destructor. */ \
- /* Fails, http://crbug.com/50272. */ \
- TEST(MessageLoopTestType##id, DISABLED_EnsureDeletion) { \
- base::test::RunTest_EnsureDeletion(factory); \
- } \
- /* TODO(darin): MessageLoop does not support deleting all tasks in the */ \
- /* destructor. */ \
- /* Fails, http://crbug.com/50272. */ \
- TEST(MessageLoopTestType##id, DISABLED_EnsureDeletion_Chain) { \
- base::test::RunTest_EnsureDeletion_Chain(factory); \
- } \
- TEST(MessageLoopTestType##id, Nesting) { \
- base::test::RunTest_Nesting(factory); \
- } \
- TEST(MessageLoopTestType##id, RecursiveDenial1) { \
- base::test::RunTest_RecursiveDenial1(factory); \
- } \
- TEST(MessageLoopTestType##id, RecursiveDenial3) { \
- base::test::RunTest_RecursiveDenial3(factory); \
- } \
- TEST(MessageLoopTestType##id, RecursiveSupport1) { \
- base::test::RunTest_RecursiveSupport1(factory); \
- } \
- TEST(MessageLoopTestType##id, NonNestableWithNoNesting) { \
- base::test::RunTest_NonNestableWithNoNesting(factory); \
- } \
- TEST(MessageLoopTestType##id, NonNestableDelayedInNestedLoop) { \
- base::test::RunTest_NonNestableInNestedLoop(factory); \
- } \
- TEST(MessageLoopTestType##id, QuitNow) { \
- base::test::RunTest_QuitNow(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitTop) { \
- base::test::RunTest_RunLoopQuitTop(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitNested) { \
- base::test::RunTest_RunLoopQuitNested(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitBogus) { \
- base::test::RunTest_RunLoopQuitBogus(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitDeep) { \
- base::test::RunTest_RunLoopQuitDeep(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitOrderBefore) { \
- base::test::RunTest_RunLoopQuitOrderBefore(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitOrderDuring) { \
- base::test::RunTest_RunLoopQuitOrderDuring(factory); \
- } \
- TEST(MessageLoopTestType##id, RunLoopQuitOrderAfter) { \
- base::test::RunTest_RunLoopQuitOrderAfter(factory); \
- } \
- TEST(MessageLoopTestType##id, RecursivePosts) { \
- base::test::RunTest_RecursivePosts(factory); \
- } \
-
-#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_
diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc
index b9fce13da6e..c01f7e2b800 100644
--- a/chromium/base/message_loop/message_loop_unittest.cc
+++ b/chromium/base/message_loop/message_loop_unittest.cc
@@ -15,7 +15,6 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_test.h"
#include "base/pending_task.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
@@ -49,28 +48,37 @@ namespace base {
namespace {
-std::unique_ptr<MessagePump> TypeDefaultMessagePumpFactory() {
- return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT);
-}
-
-std::unique_ptr<MessagePump> TypeIOMessagePumpFactory() {
- return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_IO);
-}
-
-std::unique_ptr<MessagePump> TypeUIMessagePumpFactory() {
- return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_UI);
-}
-
class Foo : public RefCounted<Foo> {
public:
Foo() : test_count_(0) {
}
+ void Test0() { ++test_count_; }
+
void Test1ConstRef(const std::string& a) {
++test_count_;
result_.append(a);
}
+ void Test1Ptr(std::string* a) {
+ ++test_count_;
+ result_.append(*a);
+ }
+
+ void Test1Int(int a) { test_count_ += a; }
+
+ void Test2Ptr(std::string* a, std::string* b) {
+ ++test_count_;
+ result_.append(*a);
+ result_.append(*b);
+ }
+
+ void Test2Mixed(const std::string& a, std::string* b) {
+ ++test_count_;
+ result_.append(a);
+ result_.append(*b);
+ }
+
int test_count() const { return test_count_; }
const std::string& result() const { return result_; }
@@ -81,8 +89,169 @@ class Foo : public RefCounted<Foo> {
int test_count_;
std::string result_;
+
+ DISALLOW_COPY_AND_ASSIGN(Foo);
};
+// This function runs slowly to simulate a large amount of work being done.
+static void SlowFunc(TimeDelta pause, int* quit_counter) {
+ PlatformThread::Sleep(pause);
+ if (--(*quit_counter) == 0)
+ RunLoop::QuitCurrentWhenIdleDeprecated();
+}
+
+// This function records the time when Run was called in a Time object, which is
+// useful for building a variety of MessageLoop tests.
+static void RecordRunTimeFunc(TimeTicks* run_time, int* quit_counter) {
+ *run_time = TimeTicks::Now();
+
+ // Cause our Run function to take some time to execute. As a result we can
+ // count on subsequent RecordRunTimeFunc()s running at a future time,
+ // without worry about the resolution of our system clock being an issue.
+ SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
+}
+
+enum TaskType {
+ MESSAGEBOX,
+ ENDDIALOG,
+ RECURSIVE,
+ TIMEDMESSAGELOOP,
+ QUITMESSAGELOOP,
+ ORDERED,
+ PUMPS,
+ SLEEP,
+ RUNS,
+};
+
+// Saves the order in which the tasks executed.
+struct TaskItem {
+ TaskItem(TaskType t, int c, bool s)
+ : type(t),
+ cookie(c),
+ start(s) {
+ }
+
+ TaskType type;
+ int cookie;
+ bool start;
+
+ bool operator == (const TaskItem& other) const {
+ return type == other.type && cookie == other.cookie && start == other.start;
+ }
+};
+
+std::ostream& operator <<(std::ostream& os, TaskType type) {
+ switch (type) {
+ case MESSAGEBOX: os << "MESSAGEBOX"; break;
+ case ENDDIALOG: os << "ENDDIALOG"; break;
+ case RECURSIVE: os << "RECURSIVE"; break;
+ case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break;
+ case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break;
+ case ORDERED: os << "ORDERED"; break;
+ case PUMPS: os << "PUMPS"; break;
+ case SLEEP: os << "SLEEP"; break;
+ default:
+ NOTREACHED();
+ os << "Unknown TaskType";
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
+ if (item.start)
+ return os << item.type << " " << item.cookie << " starts";
+ else
+ return os << item.type << " " << item.cookie << " ends";
+}
+
+class TaskList {
+ public:
+ void RecordStart(TaskType type, int cookie) {
+ TaskItem item(type, cookie, true);
+ DVLOG(1) << item;
+ task_list_.push_back(item);
+ }
+
+ void RecordEnd(TaskType type, int cookie) {
+ TaskItem item(type, cookie, false);
+ DVLOG(1) << item;
+ task_list_.push_back(item);
+ }
+
+ size_t Size() {
+ return task_list_.size();
+ }
+
+ TaskItem Get(int n) {
+ return task_list_[n];
+ }
+
+ private:
+ std::vector<TaskItem> task_list_;
+};
+
+class DummyTaskObserver : public MessageLoop::TaskObserver {
+ public:
+ explicit DummyTaskObserver(int num_tasks)
+ : num_tasks_started_(0), num_tasks_processed_(0), num_tasks_(num_tasks) {}
+
+ DummyTaskObserver(int num_tasks, int num_tasks_started)
+ : num_tasks_started_(num_tasks_started),
+ num_tasks_processed_(0),
+ num_tasks_(num_tasks) {}
+
+ ~DummyTaskObserver() override {}
+
+ void WillProcessTask(const PendingTask& pending_task) override {
+ num_tasks_started_++;
+ EXPECT_LE(num_tasks_started_, num_tasks_);
+ EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1);
+ }
+
+ void DidProcessTask(const PendingTask& pending_task) override {
+ num_tasks_processed_++;
+ EXPECT_LE(num_tasks_started_, num_tasks_);
+ EXPECT_EQ(num_tasks_started_, num_tasks_processed_);
+ }
+
+ int num_tasks_started() const { return num_tasks_started_; }
+ int num_tasks_processed() const { return num_tasks_processed_; }
+
+ private:
+ int num_tasks_started_;
+ int num_tasks_processed_;
+ const int num_tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyTaskObserver);
+};
+
+void RecursiveFunc(TaskList* order, int cookie, int depth,
+ bool is_reentrant) {
+ order->RecordStart(RECURSIVE, cookie);
+ if (depth > 0) {
+ if (is_reentrant)
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ BindOnce(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
+ }
+ order->RecordEnd(RECURSIVE, cookie);
+}
+
+void QuitFunc(TaskList* order, int cookie) {
+ order->RecordStart(QUITMESSAGELOOP, cookie);
+ RunLoop::QuitCurrentWhenIdleDeprecated();
+ order->RecordEnd(QUITMESSAGELOOP, cookie);
+}
+
+void PostNTasks(int posts_remaining) {
+ if (posts_remaining > 1) {
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&PostNTasks, posts_remaining - 1));
+ }
+}
+
#if defined(OS_ANDROID)
void AbortMessagePump() {
JNIEnv* env = base::android::AttachCurrentThread();
@@ -141,27 +310,37 @@ TEST(MessageLoopTest, JavaExceptionAbortInitJavaFirst) {
constexpr bool init_java_first = true;
RunTest_AbortDontRunMoreTasks(delayed, init_java_first);
}
-#endif // defined(OS_ANDROID)
-#if defined(OS_WIN)
+TEST(MessageLoopTest, RunTasksWhileShuttingDownJavaThread) {
+ const int kNumPosts = 6;
+ DummyTaskObserver observer(kNumPosts, 1);
-// This function runs slowly to simulate a large amount of work being done.
-static void SlowFunc(TimeDelta pause, int* quit_counter) {
- PlatformThread::Sleep(pause);
- if (--(*quit_counter) == 0)
- MessageLoop::current()->QuitWhenIdle();
-}
+ auto java_thread = MakeUnique<android::JavaHandlerThread>("test");
+ java_thread->Start();
-// This function records the time when Run was called in a Time object, which is
-// useful for building a variety of MessageLoop tests.
-static void RecordRunTimeFunc(Time* run_time, int* quit_counter) {
- *run_time = Time::Now();
+ java_thread->message_loop()->task_runner()->PostTask(
+ FROM_HERE,
+ BindOnce(
+ [](android::JavaHandlerThread* java_thread,
+ DummyTaskObserver* observer, int num_posts) {
+ java_thread->message_loop()->AddTaskObserver(observer);
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, BindOnce([]() { ADD_FAILURE(); }),
+ TimeDelta::FromDays(1));
+ java_thread->StopMessageLoopForTesting();
+ PostNTasks(num_posts);
+ },
+ Unretained(java_thread.get()), Unretained(&observer), kNumPosts));
+
+ java_thread->JoinForTesting();
+ java_thread.reset();
- // Cause our Run function to take some time to execute. As a result we can
- // count on subsequent RecordRunTimeFunc()s running at a future time,
- // without worry about the resolution of our system clock being an issue.
- SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
+ EXPECT_EQ(kNumPosts, observer.num_tasks_started());
+ EXPECT_EQ(kNumPosts, observer.num_tasks_processed());
}
+#endif // defined(OS_ANDROID)
+
+#if defined(OS_WIN)
void SubPumpFunc() {
MessageLoop::current()->SetNestableTasksAllowed(true);
@@ -170,7 +349,7 @@ void SubPumpFunc() {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
void RunTest_PostDelayedTask_SharedTimer_SubPump() {
@@ -182,7 +361,7 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() {
// By setting num_tasks to 1, we ensure that the first task to run causes the
// run loop to exit.
int num_tasks = 1;
- Time run_time;
+ TimeTicks run_time;
message_loop.task_runner()->PostTask(FROM_HERE, BindOnce(&SubPumpFunc));
@@ -216,86 +395,6 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() {
const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test";
-enum TaskType {
- MESSAGEBOX,
- ENDDIALOG,
- RECURSIVE,
- TIMEDMESSAGELOOP,
- QUITMESSAGELOOP,
- ORDERED,
- PUMPS,
- SLEEP,
- RUNS,
-};
-
-// Saves the order in which the tasks executed.
-struct TaskItem {
- TaskItem(TaskType t, int c, bool s)
- : type(t),
- cookie(c),
- start(s) {
- }
-
- TaskType type;
- int cookie;
- bool start;
-
- bool operator == (const TaskItem& other) const {
- return type == other.type && cookie == other.cookie && start == other.start;
- }
-};
-
-std::ostream& operator <<(std::ostream& os, TaskType type) {
- switch (type) {
- case MESSAGEBOX: os << "MESSAGEBOX"; break;
- case ENDDIALOG: os << "ENDDIALOG"; break;
- case RECURSIVE: os << "RECURSIVE"; break;
- case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break;
- case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break;
- case ORDERED: os << "ORDERED"; break;
- case PUMPS: os << "PUMPS"; break;
- case SLEEP: os << "SLEEP"; break;
- default:
- NOTREACHED();
- os << "Unknown TaskType";
- break;
- }
- return os;
-}
-
-std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
- if (item.start)
- return os << item.type << " " << item.cookie << " starts";
- else
- return os << item.type << " " << item.cookie << " ends";
-}
-
-class TaskList {
- public:
- void RecordStart(TaskType type, int cookie) {
- TaskItem item(type, cookie, true);
- DVLOG(1) << item;
- task_list_.push_back(item);
- }
-
- void RecordEnd(TaskType type, int cookie) {
- TaskItem item(type, cookie, false);
- DVLOG(1) << item;
- task_list_.push_back(item);
- }
-
- size_t Size() {
- return task_list_.size();
- }
-
- TaskItem Get(int n) {
- return task_list_[n];
- }
-
- private:
- std::vector<TaskItem> task_list_;
-};
-
// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
// common controls (like OpenFile) and StartDoc printing function can cause
// implicit message loops.
@@ -319,25 +418,6 @@ void EndDialogFunc(TaskList* order, int cookie) {
}
}
-void RecursiveFunc(TaskList* order, int cookie, int depth,
- bool is_reentrant) {
- order->RecordStart(RECURSIVE, cookie);
- if (depth > 0) {
- if (is_reentrant)
- MessageLoop::current()->SetNestableTasksAllowed(true);
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
- }
- order->RecordEnd(RECURSIVE, cookie);
-}
-
-void QuitFunc(TaskList* order, int cookie) {
- order->RecordStart(QUITMESSAGELOOP, cookie);
- MessageLoop::current()->QuitWhenIdle();
- order->RecordEnd(QUITMESSAGELOOP, cookie);
-}
-
void RecursiveFuncWin(scoped_refptr<SingleThreadTaskRunner> task_runner,
HANDLE event,
bool expect_window,
@@ -475,7 +555,7 @@ void PostNTasksThenQuit(int posts_remaining) {
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1));
} else {
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
}
@@ -625,9 +705,871 @@ void RunTest_WaitForIO() {
// that message loops work properly in all configurations. Of course, in some
// cases, a unit test may only be for a particular type of loop.
-RUN_MESSAGE_LOOP_TESTS(Default, &TypeDefaultMessagePumpFactory);
-RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory);
-RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory);
+namespace {
+
+class MessageLoopTypedTest
+ : public ::testing::TestWithParam<MessageLoop::Type> {
+ public:
+ MessageLoopTypedTest() = default;
+ ~MessageLoopTypedTest() = default;
+
+ static std::string ParamInfoToString(
+ ::testing::TestParamInfo<MessageLoop::Type> param_info) {
+ switch (param_info.param) {
+ case MessageLoop::TYPE_DEFAULT:
+ return "Default";
+ case MessageLoop::TYPE_IO:
+ return "IO";
+ case MessageLoop::TYPE_UI:
+ return "UI";
+ default:
+ NOTREACHED();
+ return "Unknown";
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopTypedTest);
+};
+
+} // namespace
+
+TEST_P(MessageLoopTypedTest, PostTask) {
+ MessageLoop loop(GetParam());
+ // Add tests to message loop
+ scoped_refptr<Foo> foo(new Foo());
+ std::string a("a"), b("b"), c("c"), d("d");
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&Foo::Test0, foo));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&Foo::Test1Ptr, foo, &b));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&Foo::Test1Int, foo, 100));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d));
+ // After all tests, post a message that will shut down the message loop
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated));
+
+ // Now kick things off
+ RunLoop().Run();
+
+ EXPECT_EQ(foo->test_count(), 105);
+ EXPECT_EQ(foo->result(), "abacad");
+}
+
+TEST_P(MessageLoopTypedTest, PostDelayedTask_Basic) {
+ MessageLoop loop(GetParam());
+
+ // Test that PostDelayedTask results in a delayed task.
+
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ int num_tasks = 1;
+ TimeTicks run_time;
+
+ TimeTicks time_before_run = TimeTicks::Now();
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), kDelay);
+ RunLoop().Run();
+ TimeTicks time_after_run = TimeTicks::Now();
+
+ EXPECT_EQ(0, num_tasks);
+ EXPECT_LT(kDelay, time_after_run - time_before_run);
+}
+
+TEST_P(MessageLoopTypedTest, PostDelayedTask_InDelayOrder) {
+ MessageLoop loop(GetParam());
+
+ // Test that two tasks with different delays run in the right order.
+ int num_tasks = 2;
+ TimeTicks run_time1, run_time2;
+
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks),
+ TimeDelta::FromMilliseconds(200));
+ // If we get a large pause in execution (due to a context switch) here, this
+ // test could fail.
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ RunLoop().Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time2 < run_time1);
+}
+
+TEST_P(MessageLoopTypedTest, PostDelayedTask_InPostOrder) {
+ MessageLoop loop(GetParam());
+
+ // Test that two tasks with the same delay run in the order in which they
+ // were posted.
+ //
+ // NOTE: This is actually an approximate test since the API only takes a
+ // "delay" parameter, so we are not exactly simulating two tasks that get
+ // posted at the exact same time. It would be nice if the API allowed us to
+ // specify the desired run time.
+
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ int num_tasks = 2;
+ TimeTicks run_time1, run_time2;
+
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
+
+ RunLoop().Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time1 < run_time2);
+}
+
+TEST_P(MessageLoopTypedTest, PostDelayedTask_InPostOrder_2) {
+ MessageLoop loop(GetParam());
+
+ // Test that a delayed task still runs after a normal tasks even if the
+ // normal tasks take a long time to run.
+
+ const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
+
+ int num_tasks = 2;
+ TimeTicks run_time;
+
+ loop.task_runner()->PostTask(FROM_HERE,
+ BindOnce(&SlowFunc, kPause, &num_tasks));
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ TimeTicks time_before_run = TimeTicks::Now();
+ RunLoop().Run();
+ TimeTicks time_after_run = TimeTicks::Now();
+
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_LT(kPause, time_after_run - time_before_run);
+}
+
+TEST_P(MessageLoopTypedTest, PostDelayedTask_InPostOrder_3) {
+ MessageLoop loop(GetParam());
+
+ // Test that a delayed task still runs after a pile of normal tasks. The key
+ // difference between this test and the previous one is that here we return
+ // the MessageLoop a lot so we give the MessageLoop plenty of opportunities
+ // to maybe run the delayed task. It should know not to do so until the
+ // delayed task's delay has passed.
+
+ int num_tasks = 11;
+ TimeTicks run_time1, run_time2;
+
+ // Clutter the ML with tasks.
+ for (int i = 1; i < num_tasks; ++i)
+ loop.task_runner()->PostTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks));
+
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(1));
+
+ RunLoop().Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time2 > run_time1);
+}
+
+TEST_P(MessageLoopTypedTest, PostDelayedTask_SharedTimer) {
+ MessageLoop loop(GetParam());
+
+ // Test that the interval of the timer, used to run the next delayed task, is
+ // set to a value corresponding to when the next delayed task should run.
+
+ // By setting num_tasks to 1, we ensure that the first task to run causes the
+ // run loop to exit.
+ int num_tasks = 1;
+ TimeTicks run_time1, run_time2;
+
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks),
+ TimeDelta::FromSeconds(1000));
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ TimeTicks start_time = TimeTicks::Now();
+
+ RunLoop().Run();
+ EXPECT_EQ(0, num_tasks);
+
+ // Ensure that we ran in far less time than the slower timer.
+ TimeDelta total_time = TimeTicks::Now() - start_time;
+ EXPECT_GT(5000, total_time.InMilliseconds());
+
+ // In case both timers somehow run at nearly the same time, sleep a little
+ // and then run all pending to force them both to have run. This is just
+ // encouraging flakiness if there is any.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(run_time1.is_null());
+ EXPECT_FALSE(run_time2.is_null());
+}
+
+namespace {
+
+// This is used to inject a test point for recording the destructor calls for
+// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we
+// are trying to hook the actual destruction, which is not a common operation.
+class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> {
+ public:
+ RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted)
+ : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {}
+ void Run() {}
+
+ private:
+ friend class RefCounted<RecordDeletionProbe>;
+
+ ~RecordDeletionProbe() {
+ *was_deleted_ = true;
+ if (post_on_delete_.get())
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_));
+ }
+
+ scoped_refptr<RecordDeletionProbe> post_on_delete_;
+ bool* was_deleted_;
+};
+
+} // namespace
+
+/* TODO(darin): MessageLoop does not support deleting all tasks in the */
+/* destructor. */
+/* Fails, http://crbug.com/50272. */
+TEST_P(MessageLoopTypedTest, DISABLED_EnsureDeletion) {
+ bool a_was_deleted = false;
+ bool b_was_deleted = false;
+ {
+ MessageLoop loop(GetParam());
+ loop.task_runner()->PostTask(
+ FROM_HERE, BindOnce(&RecordDeletionProbe::Run,
+ new RecordDeletionProbe(NULL, &a_was_deleted)));
+ // TODO(ajwong): Do we really need 1000ms here?
+ loop.task_runner()->PostDelayedTask(
+ FROM_HERE,
+ BindOnce(&RecordDeletionProbe::Run,
+ new RecordDeletionProbe(NULL, &b_was_deleted)),
+ TimeDelta::FromMilliseconds(1000));
+ }
+ EXPECT_TRUE(a_was_deleted);
+ EXPECT_TRUE(b_was_deleted);
+}
+
+/* TODO(darin): MessageLoop does not support deleting all tasks in the */
+/* destructor. */
+/* Fails, http://crbug.com/50272. */
+TEST_P(MessageLoopTypedTest, DISABLED_EnsureDeletion_Chain) {
+ bool a_was_deleted = false;
+ bool b_was_deleted = false;
+ bool c_was_deleted = false;
+ {
+ MessageLoop loop(GetParam());
+ // The scoped_refptr for each of the below is held either by the chained
+ // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback.
+ RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted);
+ RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
+ RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
+ loop.task_runner()->PostTask(FROM_HERE,
+ BindOnce(&RecordDeletionProbe::Run, c));
+ }
+ EXPECT_TRUE(a_was_deleted);
+ EXPECT_TRUE(b_was_deleted);
+ EXPECT_TRUE(c_was_deleted);
+}
+
+namespace {
+
+void NestingFunc(int* depth) {
+ if (*depth > 0) {
+ *depth -= 1;
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&NestingFunc, depth));
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ RunLoop().Run();
+ }
+ base::RunLoop::QuitCurrentWhenIdleDeprecated();
+}
+
+} // namespace
+
+TEST_P(MessageLoopTypedTest, Nesting) {
+ MessageLoop loop(GetParam());
+
+ int depth = 50;
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&NestingFunc, &depth));
+ RunLoop().Run();
+ EXPECT_EQ(depth, 0);
+}
+
+TEST_P(MessageLoopTypedTest, RecursiveDenial1) {
+ MessageLoop loop(GetParam());
+
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, false));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, false));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&QuitFunc, &order, 3));
+
+ RunLoop().Run();
+
+ // FIFO order.
+ ASSERT_EQ(14U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
+}
+
+namespace {
+
+void RecursiveSlowFunc(TaskList* order,
+ int cookie,
+ int depth,
+ bool is_reentrant) {
+ RecursiveFunc(order, cookie, depth, is_reentrant);
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
+}
+
+void OrderedFunc(TaskList* order, int cookie) {
+ order->RecordStart(ORDERED, cookie);
+ order->RecordEnd(ORDERED, cookie);
+}
+
+} // namespace
+
+TEST_P(MessageLoopTypedTest, RecursiveDenial3) {
+ MessageLoop loop(GetParam());
+
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 1, 2, false));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 2, 2, false));
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, BindOnce(&OrderedFunc, &order, 3),
+ TimeDelta::FromMilliseconds(5));
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, BindOnce(&QuitFunc, &order, 4),
+ TimeDelta::FromMilliseconds(5));
+
+ RunLoop().Run();
+
+ // FIFO order.
+ ASSERT_EQ(16U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true));
+ EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false));
+}
+
+TEST_P(MessageLoopTypedTest, RecursiveSupport1) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, true));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, true));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&QuitFunc, &order, 3));
+
+ RunLoop().Run();
+
+ // FIFO order.
+ ASSERT_EQ(14U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
+}
+
+// Tests that non nestable tasks run in FIFO if there are no nested loops.
+TEST_P(MessageLoopTypedTest, NonNestableWithNoNesting) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
+ FROM_HERE, BindOnce(&OrderedFunc, &order, 1));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&QuitFunc, &order, 3));
+ RunLoop().Run();
+
+ // FIFO order.
+ ASSERT_EQ(6U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+}
+
+namespace {
+
+void FuncThatPumps(TaskList* order, int cookie) {
+ order->RecordStart(PUMPS, cookie);
+ {
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ RunLoop().RunUntilIdle();
+ }
+ order->RecordEnd(PUMPS, cookie);
+}
+
+void SleepFunc(TaskList* order, int cookie, TimeDelta delay) {
+ order->RecordStart(SLEEP, cookie);
+ PlatformThread::Sleep(delay);
+ order->RecordEnd(SLEEP, cookie);
+}
+
+} // namespace
+
+// Tests that non nestable tasks don't run when there's code in the call stack.
+TEST_P(MessageLoopTypedTest, NonNestableDelayedInNestedLoop) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&FuncThatPumps, &order, 1));
+ ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
+ FROM_HERE, BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 3));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ BindOnce(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 5));
+ ThreadTaskRunnerHandle::Get()->PostNonNestableTask(
+ FROM_HERE, BindOnce(&QuitFunc, &order, 6));
+
+ RunLoop().Run();
+
+ // FIFO order.
+ ASSERT_EQ(12U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true));
+ EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false));
+ EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true));
+ EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false));
+ EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true));
+ EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false));
+}
+
+namespace {
+
+void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) {
+ order->RecordStart(RUNS, cookie);
+ {
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ run_loop->Run();
+ }
+ order->RecordEnd(RUNS, cookie);
+}
+
+void FuncThatQuitsNow() {
+ base::RunLoop::QuitCurrentDeprecated();
+}
+
+} // namespace
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+TEST_P(MessageLoopTypedTest, QuitNow) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&FuncThatQuitsNow));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 3));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&FuncThatQuitsNow));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&OrderedFunc, &order, 4)); // never runs
+
+ RunLoop().Run();
+
+ ASSERT_EQ(6U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+TEST_P(MessageLoopTypedTest, RunLoopQuitTop) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+TEST_P(MessageLoopTypedTest, RunLoopQuitNested) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Quits current loop and immediately runs a nested loop.
+void QuitAndRunNestedLoop(TaskList* order,
+ int cookie,
+ RunLoop* outer_run_loop,
+ RunLoop* nested_run_loop) {
+ order->RecordStart(RUNS, cookie);
+ outer_run_loop->Quit();
+ nested_run_loop->Run();
+ order->RecordEnd(RUNS, cookie);
+}
+
+// Test that we can run nested loop after quitting the current one.
+TEST_P(MessageLoopTypedTest, RunLoopNestedAfterQuit) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&QuitAndRunNestedLoop, &order, 1, &outer_run_loop,
+ &nested_run_loop));
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(2U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+TEST_P(MessageLoopTypedTest, RunLoopQuitBogus) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+ RunLoop bogus_run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ bogus_run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+TEST_P(MessageLoopTypedTest, RunLoopQuitDeep) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_loop1;
+ RunLoop nested_loop2;
+ RunLoop nested_loop3;
+ RunLoop nested_loop4;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 5));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ outer_run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 6));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_loop1.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 7));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_loop2.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 8));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_loop3.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 9));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ nested_loop4.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 10));
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(18U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit works before RunWithID.
+TEST_P(MessageLoopTypedTest, RunLoopQuitOrderBefore) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ run_loop.Quit();
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); // never runs
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs
+
+ run_loop.Run();
+
+ ASSERT_EQ(0U, order.Size());
+}
+
+// Tests RunLoopQuit works during RunWithID.
+TEST_P(MessageLoopTypedTest, RunLoopQuitOrderDuring) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 1));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); // never runs
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs
+
+ run_loop.Run();
+
+ ASSERT_EQ(2U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit works after RunWithID.
+TEST_P(MessageLoopTypedTest, RunLoopQuitOrderAfter) {
+ MessageLoop loop(GetParam());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 2));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&FuncThatQuitsNow));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 3));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, run_loop.QuitClosure()); // has no affect
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&OrderedFunc, &order, 4));
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ BindOnce(&FuncThatQuitsNow));
+
+ RunLoop outer_run_loop;
+ outer_run_loop.Run();
+
+ ASSERT_EQ(8U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// There was a bug in the MessagePumpGLib where posting tasks recursively
+// caused the message loop to hang, due to the buffer of the internal pipe
+// becoming full. Test all MessageLoop types to ensure this issue does not
+// exist in other MessagePumps.
+//
+// On Linux, the pipe buffer size is 64KiB by default. The bug caused one
+// byte accumulated in the pipe per two posts, so we should repeat 128K
+// times to reproduce the bug.
+TEST_P(MessageLoopTypedTest, RecursivePosts) {
+ const int kNumTimes = 1 << 17;
+ MessageLoop loop(GetParam());
+ loop.task_runner()->PostTask(FROM_HERE,
+ BindOnce(&PostNTasksThenQuit, kNumTimes));
+ RunLoop().Run();
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ MessageLoopTypedTest,
+ ::testing::Values(MessageLoop::TYPE_DEFAULT,
+ MessageLoop::TYPE_IO,
+ MessageLoop::TYPE_UI),
+ MessageLoopTypedTest::ParamInfoToString);
#if defined(OS_WIN)
TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
@@ -647,38 +1589,6 @@ TEST(MessageLoopTest, RecursiveSupport2) {
}
#endif // defined(OS_WIN)
-class DummyTaskObserver : public MessageLoop::TaskObserver {
- public:
- explicit DummyTaskObserver(int num_tasks)
- : num_tasks_started_(0),
- num_tasks_processed_(0),
- num_tasks_(num_tasks) {}
-
- ~DummyTaskObserver() override {}
-
- void WillProcessTask(const PendingTask& pending_task) override {
- num_tasks_started_++;
- EXPECT_LE(num_tasks_started_, num_tasks_);
- EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1);
- }
-
- void DidProcessTask(const PendingTask& pending_task) override {
- num_tasks_processed_++;
- EXPECT_LE(num_tasks_started_, num_tasks_);
- EXPECT_EQ(num_tasks_started_, num_tasks_processed_);
- }
-
- int num_tasks_started() const { return num_tasks_started_; }
- int num_tasks_processed() const { return num_tasks_processed_; }
-
- private:
- int num_tasks_started_;
- int num_tasks_processed_;
- const int num_tasks_;
-
- DISALLOW_COPY_AND_ASSIGN(DummyTaskObserver);
-};
-
TEST(MessageLoopTest, TaskObserver) {
const int kNumPosts = 6;
DummyTaskObserver observer(kNumPosts);
@@ -707,24 +1617,40 @@ TEST(MessageLoopTest, HighResolutionTimer) {
MessageLoop message_loop;
Time::EnableHighResolutionTimer(true);
- const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
- const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);
+ constexpr TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
+ constexpr TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
- // Post a fast task to enable the high resolution timers.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&PostNTasksThenQuit, 1), kFastTimer);
- EXPECT_TRUE(message_loop.HasHighResolutionTasks());
- RunLoop().Run();
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
+ {
+ // Post a fast task to enable the high resolution timers.
+ RunLoop run_loop;
+ message_loop.task_runner()->PostDelayedTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop) {
+ EXPECT_TRUE(Time::IsHighResolutionTimerInUse());
+ run_loop->QuitWhenIdle();
+ },
+ &run_loop),
+ kFastTimer);
+ run_loop.Run();
+ }
EXPECT_FALSE(Time::IsHighResolutionTimerInUse());
- // Check that a slow task does not trigger the high resolution logic.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&PostNTasksThenQuit, 1), kSlowTimer);
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
- RunLoop().Run();
- EXPECT_FALSE(message_loop.HasHighResolutionTasks());
+ {
+ // Check that a slow task does not trigger the high resolution logic.
+ RunLoop run_loop;
+ message_loop.task_runner()->PostDelayedTask(
+ FROM_HERE,
+ BindOnce(
+ [](RunLoop* run_loop) {
+ EXPECT_FALSE(Time::IsHighResolutionTimerInUse());
+ run_loop->QuitWhenIdle();
+ },
+ &run_loop),
+ kSlowTimer);
+ run_loop.Run();
+ }
Time::EnableHighResolutionTimer(false);
+ Time::ResetHighResolutionTimerUsage();
}
#endif // defined(OS_WIN)
@@ -816,8 +1742,7 @@ TEST(MessageLoopTest, ThreadMainTaskRunner) {
// Post quit task;
ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(&MessageLoop::QuitWhenIdle, Unretained(MessageLoop::current())));
+ FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated));
// Now kick things off
RunLoop().Run();
@@ -895,7 +1820,7 @@ LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message,
break;
}
EXPECT_TRUE(did_run);
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
break;
}
return 0;
diff --git a/chromium/base/message_loop/message_pump_android.cc b/chromium/base/message_loop/message_pump_android.cc
index 4dfe3447020..532665886e1 100644
--- a/chromium/base/message_loop/message_pump_android.cc
+++ b/chromium/base/message_loop/message_pump_android.cc
@@ -38,10 +38,18 @@ static void DoRunLoopOnce(JNIEnv* env,
// This is based on MessagePumpForUI::DoRunLoop() from desktop.
// Note however that our system queue is handled in the java side.
// In desktop we inspect and process a single system message and then
- // we call DoWork() / DoDelayedWork().
+ // we call DoWork() / DoDelayedWork(). This is then wrapped in a for loop and
+ // repeated until no work is left to do, at which point DoIdleWork is called.
// On Android, the java message queue may contain messages for other handlers
// that will be processed before calling here again.
- bool did_work = delegate->DoWork();
+ // This means that unlike Desktop, we can't wrap a for loop around this
+ // function and keep processing tasks until we have no work left to do - we
+ // have to return control back to the Android Looper after each message. This
+ // also means we have to perform idle detection differently, which is why we
+ // add an IdleHandler to the message queue in SystemMessageHandler.java, which
+ // calls DoIdleWork whenever control returns back to the looper and there are
+ // no tasks queued up to run immediately.
+ delegate->DoWork();
if (pump->ShouldAbort()) {
// There is a pending JNI exception, return to Java so that the exception is
// thrown correctly.
@@ -71,7 +79,7 @@ static void DoRunLoopOnce(JNIEnv* env,
// This roundtrip allows comparing TimeTicks directly (cheap) and
// avoid comparisons with TimeDelta / Now() (expensive).
base::TimeTicks next_delayed_work_time;
- did_work |= delegate->DoDelayedWork(&next_delayed_work_time);
+ delegate->DoDelayedWork(&next_delayed_work_time);
if (pump->ShouldAbort()) {
// There is a pending JNI exception, return to Java so that the exception is
// thrown correctly
@@ -90,27 +98,27 @@ static void DoRunLoopOnce(JNIEnv* env,
base::TimeTicks::Now()).InMillisecondsRoundedUp());
}
}
+}
- // This is a major difference between android and other platforms: since we
- // can't inspect it and process just one single message, instead we'll yeld
- // the callstack.
- if (did_work)
- return;
-
+// This is called by the java SystemMessageHandler whenever the message queue
+// detects an idle state (as in, control returns to the looper and there are no
+// tasks available to be run immediately).
+// See the comments in DoRunLoopOnce for how this differs from the
+// implementation on other platforms.
+static void DoIdleWork(JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ jlong native_delegate,
+ jlong native_message_pump) {
+ base::MessagePump::Delegate* delegate =
+ reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
+ DCHECK(delegate);
delegate->DoIdleWork();
- // Note that we do not check whether we should abort here since we are
- // returning to the JVM anyway. If, in the future, we add any more code after
- // the call to DoIdleWork() here, we should add an abort-check and return
- // immediately if the check passes.
-}
+};
namespace base {
-MessagePumpForUI::MessagePumpForUI()
- : run_loop_(nullptr), should_abort_(false) {}
-
-MessagePumpForUI::~MessagePumpForUI() {
-}
+MessagePumpForUI::MessagePumpForUI() = default;
+MessagePumpForUI::~MessagePumpForUI() = default;
void MessagePumpForUI::Run(Delegate* delegate) {
NOTREACHED() << "UnitTests should rely on MessagePumpForUIStub in"
@@ -118,6 +126,7 @@ void MessagePumpForUI::Run(Delegate* delegate) {
}
JNIEnv* MessagePumpForUI::StartInternal() {
+ DCHECK(!quit_);
run_loop_ = new RunLoop();
// Since the RunLoop was just created above, BeforeRun should be guaranteed to
// return true (it only returns false if the RunLoop has been Quit already).
@@ -148,6 +157,7 @@ void MessagePumpForUI::StartForUnitTest(
}
void MessagePumpForUI::Quit() {
+ quit_ = true;
if (!system_message_handler_obj_.is_null()) {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
@@ -165,6 +175,8 @@ void MessagePumpForUI::Quit() {
}
void MessagePumpForUI::ScheduleWork() {
+ if (quit_)
+ return;
DCHECK(!system_message_handler_obj_.is_null());
JNIEnv* env = base::android::AttachCurrentThread();
@@ -174,6 +186,8 @@ void MessagePumpForUI::ScheduleWork() {
}
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
+ if (quit_)
+ return;
DCHECK(!system_message_handler_obj_.is_null());
JNIEnv* env = base::android::AttachCurrentThread();
diff --git a/chromium/base/message_loop/message_pump_android.h b/chromium/base/message_loop/message_pump_android.h
index 895d21323da..3c8ac654c46 100644
--- a/chromium/base/message_loop/message_pump_android.h
+++ b/chromium/base/message_loop/message_pump_android.h
@@ -50,9 +50,10 @@ class BASE_EXPORT MessagePumpForUI : public MessagePump {
private:
JNIEnv* StartInternal();
- RunLoop* run_loop_;
+ RunLoop* run_loop_ = nullptr;
base::android::ScopedJavaGlobalRef<jobject> system_message_handler_obj_;
- bool should_abort_;
+ bool should_abort_ = false;
+ bool quit_ = false;
DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
};
diff --git a/chromium/base/message_loop/message_pump_default.cc b/chromium/base/message_loop/message_pump_default.cc
index cf68270c56d..651ebdc8ead 100644
--- a/chromium/base/message_loop/message_pump_default.cc
+++ b/chromium/base/message_loop/message_pump_default.cc
@@ -4,6 +4,7 @@
#include "base/message_loop/message_pump_default.h"
+#include "base/auto_reset.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
@@ -19,11 +20,10 @@ MessagePumpDefault::MessagePumpDefault()
event_(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED) {}
-MessagePumpDefault::~MessagePumpDefault() {
-}
+MessagePumpDefault::~MessagePumpDefault() {}
void MessagePumpDefault::Run(Delegate* delegate) {
- DCHECK(keep_running_) << "Quit must have been called outside of Run!";
+ AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
for (;;) {
#if defined(OS_MACOSX)
@@ -61,8 +61,6 @@ void MessagePumpDefault::Run(Delegate* delegate) {
// Since event_ is auto-reset, we don't need to do anything special here
// other than service each delegate method.
}
-
- keep_running_ = true;
}
void MessagePumpDefault::Quit() {
diff --git a/chromium/base/message_loop/message_pump_fuchsia.cc b/chromium/base/message_loop/message_pump_fuchsia.cc
index 20948bae5b5..598bff28dcf 100644
--- a/chromium/base/message_loop/message_pump_fuchsia.cc
+++ b/chromium/base/message_loop/message_pump_fuchsia.cc
@@ -7,131 +7,207 @@
#include <magenta/status.h>
#include <magenta/syscalls.h>
+#include "base/auto_reset.h"
#include "base/logging.h"
namespace base {
-MessagePumpFuchsia::FileDescriptorWatcher::FileDescriptorWatcher(
+MessagePumpFuchsia::MxHandleWatchController::MxHandleWatchController(
const tracked_objects::Location& from_here)
- : created_from_location_(from_here) {
-}
+ : created_from_location_(from_here) {}
-MessagePumpFuchsia::FileDescriptorWatcher::~FileDescriptorWatcher() {
- if (!StopWatchingFileDescriptor())
+MessagePumpFuchsia::MxHandleWatchController::~MxHandleWatchController() {
+ if (!StopWatchingMxHandle())
NOTREACHED();
- if (io_)
- __mxio_release(io_);
- if (was_destroyed_) {
- DCHECK(!*was_destroyed_);
- *was_destroyed_ = true;
- }
}
-bool MessagePumpFuchsia::FileDescriptorWatcher::StopWatchingFileDescriptor() {
- // If our pump is gone, or we do not have a wait operation active, then there
- // is nothing to do here.
- if (!weak_pump_ || handle_ == MX_HANDLE_INVALID)
+bool MessagePumpFuchsia::MxHandleWatchController::StopWatchingMxHandle() {
+ if (was_stopped_) {
+ DCHECK(!*was_stopped_);
+ *was_stopped_ = true;
+
+ // |was_stopped_| points at a value stored on the stack, which will go out
+ // of scope. MessagePumpFuchsia::Run() will reset it only if the value is
+ // false. So we need to reset this pointer here as well, to make sure it's
+ // not used again.
+ was_stopped_ = nullptr;
+ }
+
+ if (!has_begun_)
return true;
- int result = mx_port_cancel(weak_pump_->port_, handle_, wait_key());
+ has_begun_ = false;
+
+ // If the pump is gone then there is nothing to cancel.
+ if (!weak_pump_)
+ return true;
+
+ int result = mx_port_cancel(weak_pump_->port_.get(), handle_, wait_key());
DLOG_IF(ERROR, result != MX_OK)
<< "mx_port_cancel(handle=" << handle_
<< ") failed: " << mx_status_get_string(result);
- handle_ = MX_HANDLE_INVALID;
return result == MX_OK;
}
-MessagePumpFuchsia::MessagePumpFuchsia()
- : keep_running_(true), weak_factory_(this) {
- CHECK_EQ(MX_OK, mx_port_create(0, &port_));
+void MessagePumpFuchsia::FdWatchController::OnMxHandleSignalled(
+ mx_handle_t handle,
+ mx_signals_t signals) {
+ uint32_t events;
+ __mxio_wait_end(io_, signals, &events);
+
+ // Each |watcher_| callback we invoke may stop or delete |this|. The pump has
+ // set |was_stopped_| to point to a safe location on the calling stack, so we
+ // can use that to detect being stopped mid-callback and avoid doing further
+ // work that would touch |this|.
+ bool* was_stopped = was_stopped_;
+ if (events & MXIO_EVT_WRITABLE)
+ watcher_->OnFileCanWriteWithoutBlocking(fd_);
+ if (!*was_stopped && (events & MXIO_EVT_READABLE))
+ watcher_->OnFileCanReadWithoutBlocking(fd_);
+
+ // Don't add additional work here without checking |*was_stopped_| again.
}
-MessagePumpFuchsia::~MessagePumpFuchsia() {
- mx_status_t status = mx_handle_close(port_);
- if (status != MX_OK) {
- DLOG(ERROR) << "mx_handle_close failed: " << mx_status_get_string(status);
+MessagePumpFuchsia::FdWatchController::FdWatchController(
+ const tracked_objects::Location& from_here)
+ : MxHandleWatchController(from_here) {}
+
+MessagePumpFuchsia::FdWatchController::~FdWatchController() {
+ if (!StopWatchingFileDescriptor())
+ NOTREACHED();
+}
+
+bool MessagePumpFuchsia::FdWatchController::StopWatchingFileDescriptor() {
+ bool success = StopWatchingMxHandle();
+ if (io_) {
+ __mxio_release(io_);
+ io_ = nullptr;
}
+ return success;
+}
+
+MessagePumpFuchsia::MessagePumpFuchsia() : weak_factory_(this) {
+ CHECK_EQ(MX_OK, mx_port_create(0, port_.receive()));
}
bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
bool persistent,
int mode,
- FileDescriptorWatcher* controller,
- Watcher* delegate) {
+ FdWatchController* controller,
+ FdWatcher* delegate) {
DCHECK_GE(fd, 0);
DCHECK(controller);
DCHECK(delegate);
+ if (!controller->StopWatchingFileDescriptor())
+ NOTREACHED();
+
controller->fd_ = fd;
- controller->persistent_ = persistent;
controller->watcher_ = delegate;
- uint32_t events = 0;
+ DCHECK(!controller->io_);
+ controller->io_ = __mxio_fd_to_io(fd);
+ if (!controller->io_) {
+ DLOG(ERROR) << "Failed to get IO for FD";
+ return false;
+ }
+
switch (mode) {
case WATCH_READ:
- events = MXIO_EVT_READABLE;
+ controller->desired_events_ = MXIO_EVT_READABLE;
break;
case WATCH_WRITE:
- events = MXIO_EVT_WRITABLE;
+ controller->desired_events_ = MXIO_EVT_WRITABLE;
break;
case WATCH_READ_WRITE:
- events = MXIO_EVT_READABLE | MXIO_EVT_WRITABLE;
+ controller->desired_events_ = MXIO_EVT_READABLE | MXIO_EVT_WRITABLE;
break;
default:
NOTREACHED() << "unexpected mode: " << mode;
return false;
}
- controller->desired_events_ = events;
- controller->io_ = __mxio_fd_to_io(fd);
- if (!controller->io_) {
- DLOG(ERROR) << "Failed to get IO for FD";
+ // Pass dummy |handle| and |signals| values to WatchMxHandle(). The real
+ // values will be populated by FdWatchController::WaitBegin(), before actually
+ // starting the wait operation.
+ return WatchMxHandle(MX_HANDLE_INVALID, persistent, 1, controller,
+ controller);
+}
+
+bool MessagePumpFuchsia::FdWatchController::WaitBegin() {
+ // Refresh the |handle_| and |desired_signals_| from the mxio for the fd.
+ // Some types of mxio map read/write events to different signals depending on
+ // their current state, so we must do this every time we begin to wait.
+ __mxio_wait_begin(io_, desired_events_, &handle_, &desired_signals_);
+ if (handle_ == MX_HANDLE_INVALID) {
+ DLOG(ERROR) << "mxio_wait_begin failed";
return false;
}
+ return MessagePumpFuchsia::MxHandleWatchController::WaitBegin();
+}
+
+bool MessagePumpFuchsia::WatchMxHandle(mx_handle_t handle,
+ bool persistent,
+ mx_signals_t signals,
+ MxHandleWatchController* controller,
+ MxHandleWatcher* delegate) {
+ DCHECK_NE(0u, signals);
+ DCHECK(controller);
+ DCHECK(delegate);
+ DCHECK(handle == MX_HANDLE_INVALID ||
+ controller->handle_ == MX_HANDLE_INVALID ||
+ handle == controller->handle_);
+
+ if (!controller->StopWatchingMxHandle())
+ NOTREACHED();
+
+ controller->handle_ = handle;
+ controller->persistent_ = persistent;
+ controller->desired_signals_ = signals;
+ controller->watcher_ = delegate;
+
controller->weak_pump_ = weak_factory_.GetWeakPtr();
return controller->WaitBegin();
}
-bool MessagePumpFuchsia::FileDescriptorWatcher::WaitBegin() {
- uint32_t signals = 0u;
- __mxio_wait_begin(io_, desired_events_, &handle_, &signals);
- if (handle_ == MX_HANDLE_INVALID) {
- DLOG(ERROR) << "mxio_wait_begin failed";
- return false;
- }
+bool MessagePumpFuchsia::MxHandleWatchController::WaitBegin() {
+ DCHECK(!has_begun_);
- mx_status_t status = mx_object_wait_async(
- handle_, weak_pump_->port_, wait_key(), signals, MX_WAIT_ASYNC_ONCE);
+ mx_status_t status =
+ mx_object_wait_async(handle_, weak_pump_->port_.get(), wait_key(),
+ desired_signals_, MX_WAIT_ASYNC_ONCE);
if (status != MX_OK) {
DLOG(ERROR) << "mx_object_wait_async failed: "
<< mx_status_get_string(status)
- << " (port=" << weak_pump_->port_ << ")";
+ << " (port=" << weak_pump_->port_.get() << ")";
return false;
}
+
+ has_begun_ = true;
+
return true;
}
-uint32_t MessagePumpFuchsia::FileDescriptorWatcher::WaitEnd(uint32_t observed) {
- // Clear |handle_| so that StopWatchingFileDescriptor() is correctly treated
- // as a no-op. WaitBegin() will refresh |handle_| if the wait is persistent.
- handle_ = MX_HANDLE_INVALID;
+uint32_t MessagePumpFuchsia::MxHandleWatchController::WaitEnd(
+ mx_signals_t signals) {
+ DCHECK(has_begun_);
- uint32_t events;
- __mxio_wait_end(io_, observed, &events);
- // |observed| can include other spurious things, in particular, that the fd
+ has_begun_ = false;
+
+ // |signals| can include other spurious things, in particular, that an fd
// is writable, when we only asked to know when it was readable. In that
// case, we don't want to call both the CanWrite and CanRead callback,
// when the caller asked for only, for example, readable callbacks. So,
// mask with the events that we actually wanted to know about.
- events &= desired_events_;
- return events;
+ signals &= desired_signals_;
+ return signals;
}
void MessagePumpFuchsia::Run(Delegate* delegate) {
- DCHECK(keep_running_);
+ AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
for (;;) {
bool did_work = delegate->DoWork();
@@ -157,38 +233,39 @@ void MessagePumpFuchsia::Run(Delegate* delegate) {
: delayed_work_time_.ToMXTime();
mx_port_packet_t packet;
- const mx_status_t wait_status = mx_port_wait(port_, deadline, &packet, 0);
- if (wait_status != MX_OK && wait_status != MX_ERR_TIMED_OUT) {
- NOTREACHED() << "unexpected wait status: "
- << mx_status_get_string(wait_status);
+ const mx_status_t wait_status =
+ mx_port_wait(port_.get(), deadline, &packet, 0);
+ if (wait_status != MX_OK) {
+ if (wait_status != MX_ERR_TIMED_OUT) {
+ NOTREACHED() << "unexpected wait status: "
+ << mx_status_get_string(wait_status);
+ }
continue;
}
if (packet.type == MX_PKT_TYPE_SIGNAL_ONE) {
// A watched fd caused the wakeup via mx_object_wait_async().
DCHECK_EQ(MX_OK, packet.status);
- FileDescriptorWatcher* controller =
- reinterpret_cast<FileDescriptorWatcher*>(
+ MxHandleWatchController* controller =
+ reinterpret_cast<MxHandleWatchController*>(
static_cast<uintptr_t>(packet.key));
DCHECK_NE(0u, packet.signal.trigger & packet.signal.observed);
- uint32_t events = controller->WaitEnd(packet.signal.observed);
-
- // Multiple callbacks may be called, must check controller destruction
- // after the first callback is run, which is done by letting the
- // destructor set a bool here (which is located on the stack). If it's set
- // during the first callback, then the controller was destroyed during the
- // first callback so we do not call the second one, as the controller
- // pointer is now invalid.
- bool controller_was_destroyed = false;
- controller->was_destroyed_ = &controller_was_destroyed;
- if (events & MXIO_EVT_WRITABLE)
- controller->watcher_->OnFileCanWriteWithoutBlocking(controller->fd_);
- if (!controller_was_destroyed && (events & MXIO_EVT_READABLE))
- controller->watcher_->OnFileCanReadWithoutBlocking(controller->fd_);
- if (!controller_was_destroyed) {
- controller->was_destroyed_ = nullptr;
+ mx_signals_t signals = controller->WaitEnd(packet.signal.observed);
+
+ // In the case of a persistent Watch, the Watch may be stopped and
+ // potentially deleted by the caller within the callback, in which case
+ // |controller| should not be accessed again, and we mustn't continue the
+ // watch. We check for this with a bool on the stack, which the Watch
+ // receives a pointer to.
+ bool controller_was_stopped = false;
+ controller->was_stopped_ = &controller_was_stopped;
+
+ controller->watcher_->OnMxHandleSignalled(controller->handle_, signals);
+
+ if (!controller_was_stopped) {
+ controller->was_stopped_ = nullptr;
if (controller->persistent_)
controller->WaitBegin();
}
@@ -197,8 +274,6 @@ void MessagePumpFuchsia::Run(Delegate* delegate) {
DCHECK_EQ(MX_PKT_TYPE_USER, packet.type);
}
}
-
- keep_running_ = true;
}
void MessagePumpFuchsia::Quit() {
@@ -210,9 +285,9 @@ void MessagePumpFuchsia::ScheduleWork() {
// wakes up.
mx_port_packet_t packet = {};
packet.type = MX_PKT_TYPE_USER;
- mx_status_t status = mx_port_queue(port_, &packet, 0);
+ mx_status_t status = mx_port_queue(port_.get(), &packet, 0);
DLOG_IF(ERROR, status != MX_OK)
- << "mx_port_queue failed: " << status << " (port=" << port_ << ")";
+ << "mx_port_queue failed: " << status << " (port=" << port_.get() << ")";
}
void MessagePumpFuchsia::ScheduleDelayedWork(
diff --git a/chromium/base/message_loop/message_pump_fuchsia.h b/chromium/base/message_loop/message_pump_fuchsia.h
index 001de3481bd..7a221e15607 100644
--- a/chromium/base/message_loop/message_pump_fuchsia.h
+++ b/chromium/base/message_loop/message_pump_fuchsia.h
@@ -6,6 +6,7 @@
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
#include "base/base_export.h"
+#include "base/fuchsia/scoped_mx_handle.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
@@ -19,40 +20,59 @@ namespace base {
class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
public:
- class Watcher {
+ // Implemented by callers to receive notifications of handle & fd events.
+ class MxHandleWatcher {
+ public:
+ virtual void OnMxHandleSignalled(mx_handle_t handle,
+ mx_signals_t signals) = 0;
+
+ protected:
+ virtual ~MxHandleWatcher() {}
+ };
+
+ class FdWatcher {
public:
- // Called from MessageLoop::Run when an FD can be read from/written to
- // without blocking
virtual void OnFileCanReadWithoutBlocking(int fd) = 0;
virtual void OnFileCanWriteWithoutBlocking(int fd) = 0;
-
protected:
- virtual ~Watcher() {}
+ virtual ~FdWatcher() {}
};
- // Object returned by WatchFileDescriptor to manage further watching.
- class FileDescriptorWatcher {
+ // Manages an active watch on an mx_handle_t.
+ class MxHandleWatchController {
public:
- explicit FileDescriptorWatcher(const tracked_objects::Location& from_here);
- ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
+ explicit MxHandleWatchController(
+ const tracked_objects::Location& from_here);
+ // Deleting the Controller implicitly calls StopWatchingMxHandle.
+ virtual ~MxHandleWatchController();
- // Stop watching the FD, always safe to call. No-op if there's nothing
+ // Stop watching the handle, always safe to call. No-op if there's nothing
// to do.
- bool StopWatchingFileDescriptor();
+ bool StopWatchingMxHandle();
const tracked_objects::Location& created_from_location() {
return created_from_location_;
}
- private:
+ protected:
+ // This bool is used by the pump when invoking the MxHandleWatcher callback,
+ // and by the FdHandleWatchController when invoking read & write callbacks,
+ // to cope with the possibility of the caller deleting the *Watcher within
+ // the callback. The pump sets |was_stopped_| to a location on the stack,
+ // and the Watcher writes to it, if set, when deleted, allowing the pump
+ // to check the value on the stack to short-cut any post-callback work.
+ bool* was_stopped_ = nullptr;
+
+ protected:
friend class MessagePumpFuchsia;
- // Start watching the FD.
- bool WaitBegin();
+ // Start watching the handle.
+ virtual bool WaitBegin();
- // Stop watching the FD. Returns the set of events the watcher is interested
- // in based on the observed bits from the underlying packet.
- uint32_t WaitEnd(uint32_t observed);
+ // Called by MessagePumpFuchsia when the handle is signalled. Accepts the
+ // set of signals that fired, and returns the intersection with those the
+ // caller is interested in.
+ mx_signals_t WaitEnd(mx_signals_t observed);
// Returns the key to use to uniquely identify this object's wait operation.
uint64_t wait_key() const {
@@ -62,33 +82,51 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
const tracked_objects::Location created_from_location_;
// Set directly from the inputs to WatchFileDescriptor.
- Watcher* watcher_ = nullptr;
- int fd_ = -1;
- uint32_t desired_events_ = 0;
-
- // Set by WatchFileDescriptor to hold a reference to the descriptor's mxio.
- mxio_t* io_ = nullptr;
-
- // Set to the mxio's waitable handle, while a wait is pending (i.e. between
- // WaitBegin and WaitEnd calls), and MX_HANDLE_INVALID otherwise.
+ MxHandleWatcher* watcher_ = nullptr;
mx_handle_t handle_ = MX_HANDLE_INVALID;
+ mx_signals_t desired_signals_ = 0;
// Used to safely access resources owned by the associated message pump.
WeakPtr<MessagePumpFuchsia> weak_pump_;
- // This bool is used during calling |Watcher| callbacks. This object's
- // lifetime is owned by the user of this class. If the message loop is woken
- // up in the case where it needs to call both the readable and writable
- // callbacks, we need to take care not to call the second one if this object
- // is destroyed by the first one. The bool points to the stack, and is set
- // to true in ~FileDescriptorWatcher() to handle this case.
- bool* was_destroyed_ = nullptr;
-
// A watch may be marked as persistent, which means it remains active even
// after triggering.
bool persistent_ = false;
- DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
+ // Used to determine whether an asynchronous wait operation is active on
+ // this controller.
+ bool has_begun_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(MxHandleWatchController);
+ };
+
+ // Object returned by WatchFileDescriptor to manage further watching.
+ class FdWatchController : public MxHandleWatchController,
+ public MxHandleWatcher {
+ public:
+ explicit FdWatchController(const tracked_objects::Location& from_here);
+ ~FdWatchController() override;
+
+ bool StopWatchingFileDescriptor();
+
+ private:
+ friend class MessagePumpFuchsia;
+
+ // Determines the desires signals, and begins waiting on the handle.
+ bool WaitBegin() override;
+
+ // MxHandleWatcher interface.
+ void OnMxHandleSignalled(mx_handle_t handle, mx_signals_t signals) override;
+
+ // Set directly from the inputs to WatchFileDescriptor.
+ FdWatcher* watcher_ = nullptr;
+ int fd_ = -1;
+ uint32_t desired_events_ = 0;
+
+ // Set by WatchFileDescriptor to hold a reference to the descriptor's mxio.
+ mxio_t* io_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(FdWatchController);
};
enum Mode {
@@ -98,13 +136,17 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
};
MessagePumpFuchsia();
- ~MessagePumpFuchsia() override;
+ bool WatchMxHandle(mx_handle_t handle,
+ bool persistent,
+ mx_signals_t signals,
+ MxHandleWatchController* controller,
+ MxHandleWatcher* delegate);
bool WatchFileDescriptor(int fd,
bool persistent,
int mode,
- FileDescriptorWatcher* controller,
- Watcher* delegate);
+ FdWatchController* controller,
+ FdWatcher* delegate);
// MessagePump implementation:
void Run(Delegate* delegate) override;
@@ -114,9 +156,9 @@ class BASE_EXPORT MessagePumpFuchsia : public MessagePump {
private:
// This flag is set to false when Run should return.
- bool keep_running_;
+ bool keep_running_ = true;
- mx_handle_t port_;
+ ScopedMxHandle port_;
// The time at which we should call DoDelayedWork.
TimeTicks delayed_work_time_;
diff --git a/chromium/base/message_loop/message_pump_glib_unittest.cc b/chromium/base/message_loop/message_pump_glib_unittest.cc
index 564de7d83f5..1bfa70e9be3 100644
--- a/chromium/base/message_loop/message_pump_glib_unittest.cc
+++ b/chromium/base/message_loop/message_pump_glib_unittest.cc
@@ -309,7 +309,7 @@ class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
--task_count_;
}
if (task_count_ == 0 && event_count_ == 0) {
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
} else {
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this));
@@ -321,7 +321,7 @@ class ConcurrentHelper : public RefCounted<ConcurrentHelper> {
--event_count_;
}
if (task_count_ == 0 && event_count_ == 0) {
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
} else {
injector_->AddEventAsTask(0,
BindOnce(&ConcurrentHelper::FromEvent, this));
@@ -465,7 +465,7 @@ void TestGLibLoopInternal(EventInjector* injector) {
ASSERT_EQ(3, task_count);
EXPECT_EQ(4, injector->processed_events());
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
void TestGtkLoopInternal(EventInjector* injector) {
@@ -498,7 +498,7 @@ void TestGtkLoopInternal(EventInjector* injector) {
ASSERT_EQ(3, task_count);
EXPECT_EQ(4, injector->processed_events());
- MessageLoop::current()->QuitWhenIdle();
+ RunLoop::QuitCurrentWhenIdleDeprecated();
}
} // namespace
diff --git a/chromium/base/message_loop/message_pump_perftest.cc b/chromium/base/message_loop/message_pump_perftest.cc
index f5bd0dda05b..6fafa41bfd5 100644
--- a/chromium/base/message_loop/message_pump_perftest.cc
+++ b/chromium/base/message_loop/message_pump_perftest.cc
@@ -94,7 +94,7 @@ class ScheduleWorkTest : public testing::Test {
max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
for (int i = 0; i < num_scheduling_threads; ++i) {
- scheduling_threads.push_back(MakeUnique<Thread>("posting thread"));
+ scheduling_threads.push_back(std::make_unique<Thread>("posting thread"));
scheduling_threads[i]->Start();
}
@@ -285,6 +285,7 @@ class PostTaskTest : public testing::Test {
(now - start).InMicroseconds() / static_cast<double>(num_posted),
"us/task",
true);
+ queue->WillDestroyCurrentMessageLoop();
}
};