diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-20 10:33:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-22 11:45:12 +0000 |
commit | be59a35641616a4cf23c4a13fa0632624b021c1b (patch) | |
tree | 9da183258bdf9cc413f7562079d25ace6955467f /chromium/base/message_loop | |
parent | d702e4b6a64574e97fc7df8fe3238cde70242080 (diff) | |
download | qtwebengine-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')
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(); } }; |