summaryrefslogtreecommitdiff
path: root/chromium/base/message_loop
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/base/message_loop')
-rw-r--r--chromium/base/message_loop/message_loop.cc164
-rw-r--r--chromium/base/message_loop/message_loop.h201
-rw-r--r--chromium/base/message_loop/message_loop_current.cc25
-rw-r--r--chromium/base/message_loop/message_loop_current.h57
-rw-r--r--chromium/base/message_loop/message_loop_unittest.cc2270
-rw-r--r--chromium/base/message_loop/message_pump.h2
-rw-r--r--chromium/base/message_loop/message_pump_fuchsia.cc2
-rw-r--r--chromium/base/message_loop/message_pump_glib_unittest.cc15
-rw-r--r--chromium/base/message_loop/message_pump_io_ios.cc2
-rw-r--r--chromium/base/message_loop/message_pump_io_ios_unittest.cc1
-rw-r--r--chromium/base/message_loop/message_pump_libevent.cc13
-rw-r--r--chromium/base/message_loop/message_pump_libevent_unittest.cc1
-rw-r--r--chromium/base/message_loop/message_pump_mac.h33
-rw-r--r--chromium/base/message_loop/message_pump_mac.mm147
-rw-r--r--chromium/base/message_loop/message_pump_mac_unittest.mm174
-rw-r--r--chromium/base/message_loop/message_pump_win.cc90
16 files changed, 121 insertions, 3076 deletions
diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc
deleted file mode 100644
index d4b68bed9fb..00000000000
--- a/chromium/base/message_loop/message_loop.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/message_loop/message_loop.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/check_op.h"
-#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_pump_default.h"
-#include "base/message_loop/message_pump_for_io.h"
-#include "base/message_loop/message_pump_for_ui.h"
-#include "base/optional.h"
-#include "base/run_loop.h"
-#include "base/task/sequence_manager/sequence_manager.h"
-#include "base/task/sequence_manager/sequence_manager_impl.h"
-#include "base/task/sequence_manager/task_queue.h"
-#include "build/build_config.h"
-
-#if defined(OS_MACOSX)
-#include "base/message_loop/message_pump_mac.h"
-#endif
-
-namespace base {
-
-MessageLoop::MessageLoop(MessagePumpType type) : MessageLoop(type, nullptr) {
- // For TYPE_CUSTOM you must either use
- // MessageLoop(std::unique_ptr<MessagePump> pump) or
- // MessageLoop::CreateUnbound()
- DCHECK_NE(type_, MessagePumpType::CUSTOM);
- BindToCurrentThread();
-}
-
-MessageLoop::MessageLoop(std::unique_ptr<MessagePump> pump)
- : MessageLoop(MessagePumpType::CUSTOM, std::move(pump)) {
- BindToCurrentThread();
-}
-
-MessageLoop::~MessageLoop() {
- // Clean up any unprocessed tasks, but take care: deleting a task could
- // result in the addition of more tasks (e.g., via DeleteSoon). This is taken
- // care by the queue as it will prevent further tasks from being posted to its
- // associated TaskRunner instances.
- default_task_queue_->ShutdownTaskQueue();
-
- // If |pump_| is non-null, this message loop has been bound and should be the
- // current one on this thread. Otherwise, this loop is being destructed before
- // it was bound to a thread, so a different message loop (or no loop at all)
- // may be current.
- DCHECK((pump_ && IsBoundToCurrentThread()) ||
- (!pump_ && !IsBoundToCurrentThread()));
-
-// iOS just attaches to the loop, it doesn't Run it.
-// TODO(stuartmorgan): Consider wiring up a Detach().
-#if !defined(OS_IOS)
- // 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_ && !IsBoundToCurrentThread()) ||
- !RunLoop::IsRunningOnCurrentThread());
-#endif // !defined(OS_IOS)
-}
-
-bool MessageLoop::IsType(MessagePumpType type) const {
- return type_ == type;
-}
-
-// TODO(gab): Migrate TaskObservers to RunLoop as part of separating concerns
-// between MessageLoop and RunLoop and making MessageLoop a swappable
-// implementation detail. http://crbug.com/703346
-void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
- DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
- sequence_manager_->AddTaskObserver(task_observer);
-}
-
-void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
- DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
- sequence_manager_->RemoveTaskObserver(task_observer);
-}
-
-bool MessageLoop::IsBoundToCurrentThread() const {
- return sequence_manager_->IsBoundToCurrentThread();
-}
-
-bool MessageLoop::IsIdleForTesting() {
- return sequence_manager_->IsIdleForTesting();
-}
-
-//------------------------------------------------------------------------------
-
-// static
-std::unique_ptr<MessageLoop> MessageLoop::CreateUnbound(MessagePumpType type) {
- return WrapUnique(new MessageLoop(type, nullptr));
-}
-
-// static
-std::unique_ptr<MessageLoop> MessageLoop::CreateUnbound(
- std::unique_ptr<MessagePump> custom_pump) {
- return WrapUnique(
- new MessageLoop(MessagePumpType::CUSTOM, std::move(custom_pump)));
-}
-
-MessageLoop::MessageLoop(MessagePumpType type,
- std::unique_ptr<MessagePump> custom_pump)
- : sequence_manager_(
- sequence_manager::internal::SequenceManagerImpl::CreateUnbound(
- sequence_manager::SequenceManager::Settings::Builder()
- .SetMessagePumpType(type)
- .Build())),
- default_task_queue_(CreateDefaultTaskQueue()),
- type_(type),
- custom_pump_(std::move(custom_pump)) {
- // Bound in BindToCurrentThread();
- DETACH_FROM_THREAD(bound_thread_checker_);
-}
-
-scoped_refptr<sequence_manager::TaskQueue>
-MessageLoop::CreateDefaultTaskQueue() {
- auto default_task_queue = sequence_manager_->CreateTaskQueue(
- sequence_manager::TaskQueue::Spec("default_tq"));
- sequence_manager_->SetTaskRunner(default_task_queue->task_runner());
- return default_task_queue;
-}
-
-void MessageLoop::BindToCurrentThread() {
- DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
- thread_id_ = PlatformThread::CurrentId();
-
- DCHECK(!pump_);
-
- std::unique_ptr<MessagePump> pump = CreateMessagePump();
- pump_ = pump.get();
-
- DCHECK(!MessageLoopCurrent::IsSet())
- << "should only have one message loop per thread";
-
- sequence_manager_->BindToCurrentThread(std::move(pump));
-}
-
-std::unique_ptr<MessagePump> MessageLoop::CreateMessagePump() {
- if (custom_pump_) {
- return std::move(custom_pump_);
- } else {
- return MessagePump::Create(type_);
- }
-}
-
-void MessageLoop::SetTimerSlack(TimerSlack timer_slack) {
- sequence_manager_->SetTimerSlack(timer_slack);
-}
-
-scoped_refptr<SingleThreadTaskRunner> MessageLoop::task_runner() const {
- return sequence_manager_->GetTaskRunner();
-}
-
-void MessageLoop::SetTaskRunner(
- scoped_refptr<SingleThreadTaskRunner> task_runner) {
- DCHECK(task_runner);
- sequence_manager_->SetTaskRunner(task_runner);
-}
-
-} // namespace base
diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h
deleted file mode 100644
index 84e4c44500f..00000000000
--- a/chromium/base/message_loop/message_loop.h
+++ /dev/null
@@ -1,201 +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_H_
-#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_
-
-#include <memory>
-#include <string>
-
-#include "base/base_export.h"
-#include "base/callback_forward.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/message_loop/message_pump_type.h"
-#include "base/message_loop/timer_slack.h"
-#include "base/pending_task.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-
-namespace base {
-
-class MessagePump;
-class TaskObserver;
-
-namespace sequence_manager {
-class TaskQueue;
-namespace internal {
-class SequenceManagerImpl;
-} // namespace internal
-} // namespace sequence_manager
-
-// A MessageLoop is used to process events for a particular thread. There is
-// at most one MessageLoop instance per thread.
-//
-// Events include at a minimum Task instances submitted to the MessageLoop's
-// TaskRunner. Depending on the Type of message pump used by the MessageLoop
-// other events such as UI messages may be processed. On Windows APC calls (as
-// time permits) and signals sent to a registered set of HANDLEs may also be
-// processed.
-//
-// The MessageLoop's API should only be used directly by its owner (and users
-// which the owner opts to share a MessageLoop* with). Other ways to access
-// subsets of the MessageLoop API:
-// - base::RunLoop : Drive the MessageLoop from the thread it's bound to.
-// - base::Thread/SequencedTaskRunnerHandle : Post back to the MessageLoop
-// from a task running on it.
-// - SequenceLocalStorageSlot : Bind external state to this MessageLoop.
-// - base::MessageLoopCurrent : Access statically exposed APIs of this
-// MessageLoop.
-// - Embedders may provide their own static accessors to post tasks on
-// specific loops (e.g. content::BrowserThreads).
-//
-// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
-// on the thread where the MessageLoop's Run method executes.
-//
-// NOTE: MessageLoop has task reentrancy protection. This means that if a
-// task is being processed, a second task cannot start until the first task is
-// finished. Reentrancy can happen when processing a task, and an inner
-// message pump is created. That inner pump then processes native messages
-// which could implicitly start an inner task. Inner message pumps are created
-// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions
-// (DoDragDrop), printer functions (StartDoc) and *many* others.
-//
-// Sample workaround when inner task processing is needed:
-// HRESULT hr;
-// {
-// MessageLoopCurrent::ScopedNestableTaskAllower allow;
-// hr = DoDragDrop(...); // Implicitly runs a modal message loop.
-// }
-// // Process |hr| (the result returned by DoDragDrop()).
-//
-// Please be SURE your task is reentrant (nestable) and all global variables
-// are stable and accessible before calling SetNestableTasksAllowed(true).
-//
-// DEPRECATED: Use a SingleThreadTaskExecutor instead or TaskEnvironment
-// for tests. TODO(https://crbug.com/891670/) remove this class.
-class BASE_EXPORT MessageLoop {
- public:
- // Normally, it is not necessary to instantiate a MessageLoop. Instead, it
- // is typical to make use of the current thread's MessageLoop instance.
- explicit MessageLoop(MessagePumpType type = MessagePumpType::DEFAULT);
- // Creates a MessageLoop with the supplied MessagePump, which must be
- // non-null.
- explicit MessageLoop(std::unique_ptr<MessagePump> custom_pump);
-
- virtual ~MessageLoop();
-
- // Set the timer slack for this message loop.
- void SetTimerSlack(TimerSlack timer_slack);
-
- // Returns true if this loop's pump is |type|. This allows subclasses
- // (especially those in tests) to specialize how they are identified.
- virtual bool IsType(MessagePumpType type) const;
-
- // Returns the type passed to the constructor.
- MessagePumpType type() const { return type_; }
-
- // Sets a new TaskRunner for this message loop. If the message loop was
- // already bound, this must be called on the thread to which it is bound.
- void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
-
- // Gets the TaskRunner associated with this message loop.
- scoped_refptr<SingleThreadTaskRunner> task_runner() const;
-
- // These functions can only be called on the same thread that |this| is
- // running on.
- // These functions must not be called from a TaskObserver callback.
- void AddTaskObserver(TaskObserver* task_observer);
- void RemoveTaskObserver(TaskObserver* task_observer);
-
- // Returns true if the message loop is idle (ignoring delayed tasks). This is
- // the same condition which triggers DoWork() to return false: i.e.
- // out of tasks which can be processed at the current run-level -- there might
- // be deferred non-nestable tasks remaining if currently in a nested run
- // level.
- // TODO(alexclarke): Make this const when MessageLoopImpl goes away.
- bool IsIdleForTesting();
-
- //----------------------------------------------------------------------------
- protected:
- // Returns true if this is the active MessageLoop for the current thread.
- bool IsBoundToCurrentThread() const;
-
- using MessagePumpFactoryCallback =
- OnceCallback<std::unique_ptr<MessagePump>()>;
-
- // Common protected constructor. Other constructors delegate the
- // initialization to this constructor.
- // A subclass can invoke this constructor to create a message_loop of a
- // specific type with a custom loop. The implementation does not call
- // BindToCurrentThread. If this constructor is invoked directly by a subclass,
- // then the subclass must subsequently bind the message loop.
- MessageLoop(MessagePumpType type, std::unique_ptr<MessagePump> pump);
-
- // Configure various members and bind this message loop to the current thread.
- void BindToCurrentThread();
-
- // A raw pointer to the MessagePump handed-off to |sequence_manager_|.
- // Valid for the lifetime of |sequence_manager_|.
- MessagePump* pump_ = nullptr;
-
- // TODO(crbug.com/891670): We shouldn't publicly expose all of
- // SequenceManagerImpl.
- const std::unique_ptr<sequence_manager::internal::SequenceManagerImpl>
- sequence_manager_;
- // SequenceManager requires an explicit initialisation of the default task
- // queue.
- const scoped_refptr<sequence_manager::TaskQueue> default_task_queue_;
-
- private:
- friend class MessageLoopTypedTest;
- friend class ScheduleWorkTest;
- friend class Thread;
- friend class sequence_manager::internal::SequenceManagerImpl;
- FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop);
-
- // Creates a MessageLoop without binding to a thread.
- //
- // It is valid to call this to create a new message loop on one thread,
- // and then pass it to the thread where the message loop actually runs.
- // The message loop's BindToCurrentThread() method must be called on the
- // thread the message loop runs on, before calling Run().
- // Before BindToCurrentThread() is called, only Post*Task() functions can
- // be called on the message loop.
- static std::unique_ptr<MessageLoop> CreateUnbound(MessagePumpType type);
- static std::unique_ptr<MessageLoop> CreateUnbound(
- std::unique_ptr<MessagePump> pump);
-
- scoped_refptr<sequence_manager::TaskQueue> CreateDefaultTaskQueue();
-
- std::unique_ptr<MessagePump> CreateMessagePump();
-
- sequence_manager::internal::SequenceManagerImpl* GetSequenceManagerImpl()
- const {
- return sequence_manager_.get();
- }
-
- const MessagePumpType type_;
-
- // If set this will be returned by the next call to CreateMessagePump().
- // This is only set if |type_| is TYPE_CUSTOM and |pump_| is null.
- std::unique_ptr<MessagePump> custom_pump_;
-
- // Id of the thread this message loop is bound to. Initialized once when the
- // MessageLoop is bound to its thread and constant forever after.
- PlatformThreadId thread_id_ = kInvalidThreadId;
-
- // Verifies that calls are made on the thread on which BindToCurrentThread()
- // was invoked.
- THREAD_CHECKER(bound_thread_checker_);
-
- DISALLOW_COPY_AND_ASSIGN(MessageLoop);
-};
-
-} // namespace base
-
-#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_
diff --git a/chromium/base/message_loop/message_loop_current.cc b/chromium/base/message_loop/message_loop_current.cc
index 7688ba3d7b0..14d7b8fbb59 100644
--- a/chromium/base/message_loop/message_loop_current.cc
+++ b/chromium/base/message_loop/message_loop_current.cc
@@ -81,23 +81,22 @@ void MessageLoopCurrent::SetAddQueueTimeToTasks(bool enable) {
current_->SetAddQueueTimeToTasks(enable);
}
-void MessageLoopCurrent::SetNestableTasksAllowed(bool allowed) {
- DCHECK(current_->IsBoundToCurrentThread());
- current_->SetTaskExecutionAllowed(allowed);
-}
-
-bool MessageLoopCurrent::NestableTasksAllowed() const {
- return current_->IsTaskExecutionAllowed();
-}
-
-MessageLoopCurrent::ScopedNestableTaskAllower::ScopedNestableTaskAllower()
+MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop::
+ ScopedAllowApplicationTasksInNativeNestedLoop()
: sequence_manager_(GetCurrentSequenceManagerImpl()),
- old_state_(sequence_manager_->IsTaskExecutionAllowed()) {
+ previous_state_(sequence_manager_->IsTaskExecutionAllowed()) {
+ TRACE_EVENT_BEGIN0("base", "ScopedNestableTaskAllower");
sequence_manager_->SetTaskExecutionAllowed(true);
}
-MessageLoopCurrent::ScopedNestableTaskAllower::~ScopedNestableTaskAllower() {
- sequence_manager_->SetTaskExecutionAllowed(old_state_);
+MessageLoopCurrent::ScopedAllowApplicationTasksInNativeNestedLoop::
+ ~ScopedAllowApplicationTasksInNativeNestedLoop() {
+ sequence_manager_->SetTaskExecutionAllowed(previous_state_);
+ TRACE_EVENT_END0("base", "ScopedNestableTaskAllower");
+}
+
+bool MessageLoopCurrent::NestableTasksAllowed() const {
+ return current_->IsTaskExecutionAllowed();
}
bool MessageLoopCurrent::operator==(const MessageLoopCurrent& other) const {
diff --git a/chromium/base/message_loop/message_loop_current.h b/chromium/base/message_loop/message_loop_current.h
index 61b8c6fcc42..462098e2522 100644
--- a/chromium/base/message_loop/message_loop_current.h
+++ b/chromium/base/message_loop/message_loop_current.h
@@ -8,7 +8,7 @@
#include <ostream>
#include "base/base_export.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/message_loop/message_pump_for_ui.h"
@@ -117,49 +117,46 @@ class BASE_EXPORT MessageLoopCurrent {
// posted tasks.
void SetAddQueueTimeToTasks(bool enable);
- // Enables or disables the recursive task processing. This happens in the case
- // of recursive message loops. Some unwanted message loops may occur when
- // using common controls or printer functions. By default, recursive task
- // processing is disabled.
+ // Enables nested task processing in scope of an upcoming native message loop.
+ // Some unwanted message loops may occur when using common controls or printer
+ // functions. Hence, nested task processing is disabled by default to avoid
+ // unplanned reentrancy. This re-enables it in cases where the stack is
+ // reentrancy safe and processing nestable tasks is explicitly safe.
//
- // Please use |ScopedNestableTaskAllower| instead of calling these methods
- // directly. In general, nestable message loops are to be avoided. They are
- // dangerous and difficult to get right, so please use with extreme caution.
- //
- // The specific case where tasks get queued is:
- // - The thread is running a message loop.
+ // For instance,
+ // - The current thread is running a message loop.
// - It receives a task #1 and executes it.
- // - The task #1 implicitly starts a message loop, like a MessageBox in the
- // unit test. This can also be StartDoc or GetSaveFileName.
+ // - The task #1 implicitly starts a nested message loop, like a MessageBox in
+ // the unit test. This can also be StartDoc or GetSaveFileName.
// - The thread receives a task #2 before or while in this second message
// loop.
// - 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(https://crbug.com/750779): 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 the current MessageLoop while in scope.
- // DEPRECATED(https://crbug.com/750779): 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(?) for remaining use cases.
- class BASE_EXPORT ScopedNestableTaskAllower {
+ // Use RunLoop::Type::kNestableTasksAllowed when nesting is triggered by the
+ // application RunLoop rather than by native code.
+ class BASE_EXPORT ScopedAllowApplicationTasksInNativeNestedLoop {
public:
- ScopedNestableTaskAllower();
- ~ScopedNestableTaskAllower();
+ ScopedAllowApplicationTasksInNativeNestedLoop();
+ ~ScopedAllowApplicationTasksInNativeNestedLoop();
private:
sequence_manager::internal::SequenceManagerImpl* const sequence_manager_;
- const bool old_state_;
+ const bool previous_state_;
};
+ // TODO(https://crbug.com/781352): Remove usage of this old class. Either
+ // renaming it to ScopedAllowApplicationTasksInNativeNestedLoop when truly
+ // native or migrating it to RunLoop::Type::kNestableTasksAllowed otherwise.
+ using ScopedNestableTaskAllower =
+ ScopedAllowApplicationTasksInNativeNestedLoop;
+
+ // Returns true if nestable tasks are allowed on the current loop at this time
+ // (i.e. if a nested loop would start from the callee's point in the stack,
+ // would it be allowed to run application tasks).
+ bool NestableTasksAllowed() const;
+
// Returns true if this is the active MessageLoop for the current thread.
bool IsBoundToCurrentThread() const;
diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc
deleted file mode 100644
index 1d0ed42b239..00000000000
--- a/chromium/base/message_loop/message_loop_unittest.cc
+++ /dev/null
@@ -1,2270 +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.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop_current.h"
-#include "base/message_loop/message_pump_for_io.h"
-#include "base/message_loop/message_pump_type.h"
-#include "base/pending_task.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/task/task_observer.h"
-#include "base/task/thread_pool/thread_pool_instance.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/gtest_util.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/test_simple_task_runner.h"
-#include "base/test/test_timeouts.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/sequence_local_storage_slot.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_ANDROID)
-#include "base/android/java_handler_thread.h"
-#include "base/android/jni_android.h"
-#include "base/test/android/java_handler_thread_helpers.h"
-#endif
-
-#if defined(OS_WIN)
-#include "base/message_loop/message_pump_win.h"
-#include "base/process/memory.h"
-#include "base/strings/string16.h"
-#include "base/win/current_module.h"
-#include "base/win/message_window.h"
-#include "base/win/scoped_handle.h"
-#endif
-
-namespace base {
-
-// TODO(darin): Platform-specific MessageLoop tests should be grouped together
-// to avoid chopping this file up with so many #ifdefs.
-
-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() = default;
-
- 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";
- 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 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 = default;
-
- void WillProcessTask(const PendingTask& pending_task,
- bool /* was_blocked_or_low_priority */) 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)
- MessageLoopCurrent::Get()->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));
- }
-}
-
-class MessageLoopTest : public ::testing::Test {};
-
-#if defined(OS_WIN)
-
-void SubPumpFunc(OnceClosure on_done) {
- MessageLoopCurrent::ScopedNestableTaskAllower allow_nestable_tasks;
- MSG msg;
- while (::GetMessage(&msg, NULL, 0, 0)) {
- ::TranslateMessage(&msg);
- ::DispatchMessage(&msg);
- }
- std::move(on_done).Run();
-}
-
-const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test";
-
-// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
-// common controls (like OpenFile) and StartDoc printing function can cause
-// implicit message loops.
-void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) {
- order->RecordStart(MESSAGEBOX, cookie);
- if (is_reentrant)
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
- MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
- order->RecordEnd(MESSAGEBOX, cookie);
-}
-
-// Will end the MessageBox.
-void EndDialogFunc(TaskList* order, int cookie) {
- order->RecordStart(ENDDIALOG, cookie);
- HWND window = GetActiveWindow();
- if (window != NULL) {
- EXPECT_NE(EndDialog(window, IDCONTINUE), 0);
- // Cheap way to signal that the window wasn't found if RunEnd() isn't
- // called.
- order->RecordEnd(ENDDIALOG, cookie);
- }
-}
-
-void RecursiveFuncWin(scoped_refptr<SingleThreadTaskRunner> task_runner,
- HANDLE event,
- bool expect_window,
- TaskList* order,
- bool is_reentrant) {
- task_runner->PostTask(FROM_HERE,
- BindOnce(&RecursiveFunc, order, 1, 2, is_reentrant));
- task_runner->PostTask(FROM_HERE,
- BindOnce(&MessageBoxFunc, order, 2, is_reentrant));
- task_runner->PostTask(FROM_HERE,
- BindOnce(&RecursiveFunc, order, 3, 2, is_reentrant));
- // The trick here is that for recursive task processing, this task will be
- // ran _inside_ the MessageBox message loop, dismissing the MessageBox
- // without a chance.
- // For non-recursive task processing, this will be executed _after_ the
- // MessageBox will have been dismissed by the code below, where
- // expect_window_ is true.
- task_runner->PostTask(FROM_HERE, BindOnce(&EndDialogFunc, order, 4));
- task_runner->PostTask(FROM_HERE, BindOnce(&QuitFunc, order, 5));
-
- // Enforce that every tasks are sent before starting to run the main thread
- // message loop.
- ASSERT_TRUE(SetEvent(event));
-
- // Poll for the MessageBox. Don't do this at home! At the speed we do it,
- // you will never realize one MessageBox was shown.
- for (; expect_window;) {
- HWND window = FindWindow(L"#32770", kMessageBoxTitle);
- if (window) {
- // Dismiss it.
- for (;;) {
- HWND button = FindWindowEx(window, NULL, L"Button", NULL);
- if (button != NULL) {
- EXPECT_EQ(0, SendMessage(button, WM_LBUTTONDOWN, 0, 0));
- EXPECT_EQ(0, SendMessage(button, WM_LBUTTONUP, 0, 0));
- break;
- }
- }
- break;
- }
- }
-}
-
-#endif // defined(OS_WIN)
-
-void PostNTasksThenQuit(int posts_remaining) {
- if (posts_remaining > 1) {
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1));
- } else {
- RunLoop::QuitCurrentWhenIdleDeprecated();
- }
-}
-
-#if defined(OS_WIN)
-
-class TestIOHandler : public MessagePumpForIO::IOHandler {
- public:
- TestIOHandler(const wchar_t* name, HANDLE signal, bool wait);
-
- void OnIOCompleted(MessagePumpForIO::IOContext* context,
- DWORD bytes_transfered,
- DWORD error) override;
-
- void Init();
- void WaitForIO();
- OVERLAPPED* context() { return &context_.overlapped; }
- DWORD size() { return sizeof(buffer_); }
-
- private:
- char buffer_[48];
- MessagePumpForIO::IOContext context_;
- HANDLE signal_;
- win::ScopedHandle file_;
- bool wait_;
-};
-
-TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait)
- : MessagePumpForIO::IOHandler(FROM_HERE), signal_(signal), wait_(wait) {
- memset(buffer_, 0, sizeof(buffer_));
-
- file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING,
- FILE_FLAG_OVERLAPPED, NULL));
- EXPECT_TRUE(file_.IsValid());
-}
-
-void TestIOHandler::Init() {
- MessageLoopCurrentForIO::Get()->RegisterIOHandler(file_.Get(), this);
-
- DWORD read;
- EXPECT_FALSE(ReadFile(file_.Get(), buffer_, size(), &read, context()));
- EXPECT_EQ(static_cast<DWORD>(ERROR_IO_PENDING), GetLastError());
- if (wait_)
- WaitForIO();
-}
-
-void TestIOHandler::OnIOCompleted(MessagePumpForIO::IOContext* context,
- DWORD bytes_transfered,
- DWORD error) {
- ASSERT_TRUE(context == &context_);
- ASSERT_TRUE(SetEvent(signal_));
-}
-
-void TestIOHandler::WaitForIO() {
- EXPECT_TRUE(MessageLoopCurrentForIO::Get()->WaitForIOCompletion(300, this));
- EXPECT_TRUE(MessageLoopCurrentForIO::Get()->WaitForIOCompletion(400, this));
-}
-
-void RunTest_IOHandler() {
- win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL));
- ASSERT_TRUE(callback_called.IsValid());
-
- const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe";
- win::ScopedHandle server(
- CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
- ASSERT_TRUE(server.IsValid());
-
- Thread thread("IOHandler test");
- Thread::Options options;
- options.message_pump_type = MessagePumpType::IO;
- ASSERT_TRUE(thread.StartWithOptions(options));
-
- TestIOHandler handler(kPipeName, callback_called.Get(), false);
- thread.task_runner()->PostTask(
- FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler)));
- // Make sure the thread runs and sleeps for lack of work.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
-
- const char buffer[] = "Hello there!";
- DWORD written;
- EXPECT_TRUE(WriteFile(server.Get(), buffer, sizeof(buffer), &written, NULL));
-
- DWORD result = WaitForSingleObject(callback_called.Get(), 1000);
- EXPECT_EQ(WAIT_OBJECT_0, result);
-
- thread.Stop();
-}
-
-void RunTest_WaitForIO() {
- win::ScopedHandle callback1_called(CreateEvent(NULL, TRUE, FALSE, NULL));
- win::ScopedHandle callback2_called(CreateEvent(NULL, TRUE, FALSE, NULL));
- ASSERT_TRUE(callback1_called.IsValid());
- ASSERT_TRUE(callback2_called.IsValid());
-
- const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1";
- const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2";
- win::ScopedHandle server1(
- CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
- win::ScopedHandle server2(
- CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL));
- ASSERT_TRUE(server1.IsValid());
- ASSERT_TRUE(server2.IsValid());
-
- Thread thread("IOHandler test");
- Thread::Options options;
- options.message_pump_type = MessagePumpType::IO;
- ASSERT_TRUE(thread.StartWithOptions(options));
-
- TestIOHandler handler1(kPipeName1, callback1_called.Get(), false);
- TestIOHandler handler2(kPipeName2, callback2_called.Get(), true);
- thread.task_runner()->PostTask(
- FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler1)));
- // TODO(ajwong): Do we really need such long Sleeps in this function?
- // Make sure the thread runs and sleeps for lack of work.
- TimeDelta delay = TimeDelta::FromMilliseconds(100);
- PlatformThread::Sleep(delay);
- thread.task_runner()->PostTask(
- FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler2)));
- PlatformThread::Sleep(delay);
-
- // At this time handler1 is waiting to be called, and the thread is waiting
- // on the Init method of handler2, filtering only handler2 callbacks.
-
- const char buffer[] = "Hello there!";
- DWORD written;
- EXPECT_TRUE(WriteFile(server1.Get(), buffer, sizeof(buffer), &written, NULL));
- PlatformThread::Sleep(2 * delay);
- EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
- WaitForSingleObject(callback1_called.Get(), 0))
- << "handler1 has not been called";
-
- EXPECT_TRUE(WriteFile(server2.Get(), buffer, sizeof(buffer), &written, NULL));
-
- HANDLE objects[2] = {callback1_called.Get(), callback2_called.Get()};
- DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000);
- EXPECT_EQ(WAIT_OBJECT_0, result);
-
- thread.Stop();
-}
-
-#endif // defined(OS_WIN)
-
-} // namespace
-
-//-----------------------------------------------------------------------------
-// Each test is run against each type of MessageLoop. That way we are sure
-// 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.
-
-class MessageLoopTypedTest : public ::testing::TestWithParam<MessagePumpType> {
- public:
- MessageLoopTypedTest() = default;
- ~MessageLoopTypedTest() = default;
-
- static std::string ParamInfoToString(
- ::testing::TestParamInfo<MessagePumpType> param_info) {
- switch (param_info.param) {
- case MessagePumpType::DEFAULT:
- return "default_pump";
- case MessagePumpType::IO:
- return "IO_pump";
- case MessagePumpType::UI:
- return "UI_pump";
- case MessagePumpType::CUSTOM:
- break;
-#if defined(OS_ANDROID)
- case MessagePumpType::JAVA:
- break;
-#endif // defined(OS_ANDROID)
-#if defined(OS_MACOSX)
- case MessagePumpType::NS_RUNLOOP:
- break;
-#endif // defined(OS_MACOSX)
-#if defined(OS_WIN)
- case MessagePumpType::UI_WITH_WM_QUIT_SUPPORT:
- break;
-#endif // defined(OS_WIN)
- }
- NOTREACHED();
- return "";
- }
-
- std::unique_ptr<MessageLoop> CreateMessageLoop() {
- auto message_loop = base::WrapUnique(new MessageLoop(GetParam(), nullptr));
- message_loop->BindToCurrentThread();
- return message_loop;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MessageLoopTypedTest);
-};
-
-TEST_P(MessageLoopTypedTest, PostTask) {
- auto loop = CreateMessageLoop();
- // 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) {
- auto loop = CreateMessageLoop();
-
- // 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) {
- auto loop = CreateMessageLoop();
-
- // 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) {
- auto loop = CreateMessageLoop();
-
- // 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) {
- auto loop = CreateMessageLoop();
-
- // 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) {
- auto loop = CreateMessageLoop();
-
- // 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) {
- auto loop = CreateMessageLoop();
-
- // 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;
- {
- auto loop = CreateMessageLoop();
- loop->task_runner()->PostTask(
- FROM_HERE, BindOnce(&RecordDeletionProbe::Run,
- new RecordDeletionProbe(nullptr, &a_was_deleted)));
- // TODO(ajwong): Do we really need 1000ms here?
- loop->task_runner()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&RecordDeletionProbe::Run,
- new RecordDeletionProbe(nullptr, &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;
- {
- auto loop = CreateMessageLoop();
- // 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(nullptr, &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));
-
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
- RunLoop().Run();
- }
- base::RunLoop::QuitCurrentWhenIdleDeprecated();
-}
-
-} // namespace
-
-TEST_P(MessageLoopTypedTest, Nesting) {
- auto loop = CreateMessageLoop();
-
- int depth = 50;
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&NestingFunc, &depth));
- RunLoop().Run();
- EXPECT_EQ(depth, 0);
-}
-
-TEST_P(MessageLoopTypedTest, RecursiveDenial1) {
- auto loop = CreateMessageLoop();
-
- EXPECT_TRUE(MessageLoopCurrent::Get()->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 OrderedFunc(TaskList* order, int cookie) {
- order->RecordStart(ORDERED, cookie);
- order->RecordEnd(ORDERED, cookie);
-}
-
-} // namespace
-
-TEST_P(MessageLoopTypedTest, RecursiveSupport1) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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);
- RunLoop(RunLoop::Type::kNestableTasksAllowed).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) {
- auto loop = CreateMessageLoop();
-
- 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);
- {
- MessageLoopCurrent::ScopedNestableTaskAllower allow;
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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) {
- auto loop = CreateMessageLoop();
-
- 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));
-
- run_loop.allow_quit_current_deprecated_ = true;
-
- 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.
-#if defined(OS_FUCHSIA)
-// TODO(crbug.com/810077): This is flaky on Fuchsia.
-#define MAYBE_RecursivePosts DISABLED_RecursivePosts
-#else
-#define MAYBE_RecursivePosts RecursivePosts
-#endif
-TEST_P(MessageLoopTypedTest, MAYBE_RecursivePosts) {
- const int kNumTimes = 1 << 17;
- auto loop = CreateMessageLoop();
- loop->task_runner()->PostTask(FROM_HERE,
- BindOnce(&PostNTasksThenQuit, kNumTimes));
- RunLoop().Run();
-}
-
-TEST_P(MessageLoopTypedTest, NestableTasksAllowedAtTopLevel) {
- auto loop = CreateMessageLoop();
- EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed());
-}
-
-// Nestable tasks shouldn't be allowed to run reentrantly by default (regression
-// test for https://crbug.com/754112).
-TEST_P(MessageLoopTypedTest, NestableTasksDisallowedByDefault) {
- auto loop = CreateMessageLoop();
- RunLoop run_loop;
- loop->task_runner()->PostTask(
- FROM_HERE,
- BindOnce(
- [](RunLoop* run_loop) {
- EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed());
- run_loop->Quit();
- },
- Unretained(&run_loop)));
- run_loop.Run();
-}
-
-TEST_P(MessageLoopTypedTest, NestableTasksProcessedWhenRunLoopAllows) {
- auto loop = CreateMessageLoop();
- RunLoop run_loop;
- loop->task_runner()->PostTask(
- FROM_HERE,
- BindOnce(
- [](RunLoop* run_loop) {
- // This test would hang if this RunLoop wasn't of type
- // kNestableTasksAllowed (i.e. this is testing that this is
- // processed and doesn't hang).
- RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- BindOnce(
- [](RunLoop* nested_run_loop) {
- // Each additional layer of application task nesting
- // requires its own allowance. The kNestableTasksAllowed
- // RunLoop allowed this task to be processed but further
- // nestable tasks are by default disallowed from this
- // layer.
- EXPECT_FALSE(
- MessageLoopCurrent::Get()->NestableTasksAllowed());
- nested_run_loop->Quit();
- },
- Unretained(&nested_run_loop)));
- nested_run_loop.Run();
-
- run_loop->Quit();
- },
- Unretained(&run_loop)));
- run_loop.Run();
-}
-
-TEST_P(MessageLoopTypedTest, NestableTasksAllowedExplicitlyInScope) {
- auto loop = CreateMessageLoop();
- RunLoop run_loop;
- loop->task_runner()->PostTask(
- FROM_HERE,
- BindOnce(
- [](RunLoop* run_loop) {
- {
- MessageLoopCurrent::ScopedNestableTaskAllower
- allow_nestable_tasks;
- EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed());
- }
- EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed());
- run_loop->Quit();
- },
- Unretained(&run_loop)));
- run_loop.Run();
-}
-
-TEST_P(MessageLoopTypedTest, NestableTasksAllowedManually) {
- auto loop = CreateMessageLoop();
- RunLoop run_loop;
- loop->task_runner()->PostTask(
- FROM_HERE,
- BindOnce(
- [](RunLoop* run_loop) {
- EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed());
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
- EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed());
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(false);
- EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed());
- run_loop->Quit();
- },
- Unretained(&run_loop)));
- run_loop.Run();
-}
-
-TEST_P(MessageLoopTypedTest, IsIdleForTesting) {
- auto loop = CreateMessageLoop();
- EXPECT_TRUE(loop->IsIdleForTesting());
- loop->task_runner()->PostTask(FROM_HERE, BindOnce([]() {}));
- loop->task_runner()->PostDelayedTask(FROM_HERE, BindOnce([]() {}),
- TimeDelta::FromMilliseconds(10));
- EXPECT_FALSE(loop->IsIdleForTesting());
- RunLoop().RunUntilIdle();
- EXPECT_TRUE(loop->IsIdleForTesting());
-
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(20));
- EXPECT_TRUE(loop->IsIdleForTesting());
-}
-
-TEST_P(MessageLoopTypedTest, IsIdleForTestingNonNestableTask) {
- auto loop = CreateMessageLoop();
- RunLoop run_loop;
- EXPECT_TRUE(loop->IsIdleForTesting());
- bool nested_task_run = false;
- loop->task_runner()->PostTask(
- FROM_HERE, BindLambdaForTesting([&]() {
- RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed);
-
- loop->task_runner()->PostNonNestableTask(
- FROM_HERE, BindLambdaForTesting([&]() { nested_task_run = true; }));
-
- loop->task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
- EXPECT_FALSE(nested_task_run);
- EXPECT_TRUE(loop->IsIdleForTesting());
- }));
-
- nested_run_loop.RunUntilIdle();
- EXPECT_FALSE(nested_task_run);
- EXPECT_FALSE(loop->IsIdleForTesting());
- }));
-
- run_loop.RunUntilIdle();
-
- EXPECT_TRUE(nested_task_run);
- EXPECT_TRUE(loop->IsIdleForTesting());
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
- MessageLoopTypedTest,
- ::testing::Values(MessagePumpType::DEFAULT,
- MessagePumpType::UI,
- MessagePumpType::IO),
- MessageLoopTypedTest::ParamInfoToString);
-
-#if defined(OS_WIN)
-
-// Verifies that the MessageLoop ignores WM_QUIT, rather than quitting.
-// Users of MessageLoop typically expect to control when their RunLoops stop
-// Run()ning explicitly, via QuitClosure() etc (see https://crbug.com/720078).
-TEST_F(MessageLoopTest, WmQuitIsIgnored) {
- MessageLoop loop(MessagePumpType::UI);
-
- // Post a WM_QUIT message to the current thread.
- ::PostQuitMessage(0);
-
- // Post a task to the current thread, with a small delay to make it less
- // likely that we process the posted task before looking for WM_* messages.
- bool task_was_run = false;
- RunLoop run_loop;
- loop.task_runner()->PostDelayedTask(
- FROM_HERE,
- BindOnce(
- [](bool* flag, OnceClosure closure) {
- *flag = true;
- std::move(closure).Run();
- },
- &task_was_run, run_loop.QuitClosure()),
- TestTimeouts::tiny_timeout());
-
- // Run the loop, and ensure that the posted task is processed before we quit.
- run_loop.Run();
- EXPECT_TRUE(task_was_run);
-}
-
-TEST_F(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
- MessageLoop message_loop(MessagePumpType::UI);
-
- // 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_time;
-
- RunLoop run_loop;
-
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
-
- // This very delayed task should never run.
- message_loop.task_runner()->PostDelayedTask(
- FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks),
- TimeDelta::FromSeconds(1000));
-
- // This slightly delayed task should run from within SubPumpFunc.
- message_loop.task_runner()->PostDelayedTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0),
- TimeDelta::FromMilliseconds(10));
-
- Time start_time = Time::Now();
-
- run_loop.Run();
- EXPECT_EQ(1, num_tasks);
-
- // Ensure that we ran in far less time than the slower timer.
- TimeDelta total_time = Time::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_time.is_null());
-}
-
-namespace {
-
-// When this fires (per the associated WM_TIMER firing), it posts an
-// application task to quit the native loop.
-bool QuitOnSystemTimer(UINT message,
- WPARAM wparam,
- LPARAM lparam,
- LRESULT* result) {
- if (message == static_cast<UINT>(WM_TIMER)) {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
- }
- *result = 0;
- return true;
-}
-
-// When this fires (per the associated WM_TIMER firing), it posts a delayed
-// application task to quit the native loop.
-bool DelayedQuitOnSystemTimer(UINT message,
- WPARAM wparam,
- LPARAM lparam,
- LRESULT* result) {
- if (message == static_cast<UINT>(WM_TIMER)) {
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, BindOnce(&::PostQuitMessage, 0),
- TimeDelta::FromMilliseconds(10));
- }
- *result = 0;
- return true;
-}
-
-} // namespace
-
-// This is a regression test for
-// https://crrev.com/c/1455266/9/base/message_loop/message_pump_win.cc#125
-// See below for the delayed task version.
-TEST_F(MessageLoopTest, PostImmediateTaskFromSystemPump) {
- MessageLoop message_loop(MessagePumpType::UI);
-
- RunLoop run_loop;
-
- // A native message window to generate a system message which invokes
- // QuitOnSystemTimer() when the native timer fires.
- win::MessageWindow local_message_window;
- local_message_window.Create(BindRepeating(&QuitOnSystemTimer));
- ASSERT_TRUE(::SetTimer(local_message_window.hwnd(), 0, 20, nullptr));
-
- // The first task will enter a native message loop. This test then verifies
- // that the pump is able to run an immediate application task after the native
- // pump went idle.
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
-
- // Test success is determined by not hanging in this Run() call.
- run_loop.Run();
-}
-
-// This is a regression test for
-// https://crrev.com/c/1455266/9/base/message_loop/message_pump_win.cc#125 This
-// is the delayed task equivalent of the above PostImmediateTaskFromSystemPump
-// test.
-//
-// As a reminder of how this works, here's the sequence of events in this test:
-// 1) Test start:
-// work_deduplicator.cc(24): BindToCurrentThread
-// work_deduplicator.cc(34): OnWorkRequested
-// thread_controller_with_message_pump_impl.cc(237) : DoWork
-// work_deduplicator.cc(50): OnWorkStarted
-// 2) SubPumpFunc entered:
-// message_loop_unittest.cc(278): SubPumpFunc
-// 3) ScopedNestableTaskAllower triggers nested ScheduleWork:
-// work_deduplicator.cc(34): OnWorkRequested
-// 4) Nested system loop starts and pumps internal kMsgHaveWork:
-// message_loop_unittest.cc(282): SubPumpFunc : Got Message
-// message_pump_win.cc(302): HandleWorkMessage
-// thread_controller_with_message_pump_impl.cc(237) : DoWork
-// 5) Attempt to DoWork(), there's nothing to do, NextWorkInfo indicates delay.
-// work_deduplicator.cc(50): OnWorkStarted
-// work_deduplicator.cc(58): WillCheckForMoreWork
-// work_deduplicator.cc(67): DidCheckForMoreWork
-// 6) Return control to HandleWorkMessage() which schedules native timer
-// and goes to sleep (no kMsgHaveWork in native queue).
-// message_pump_win.cc(328): HandleWorkMessage ScheduleNativeTimer
-// 7) Native timer fires and posts the delayed application task:
-// message_loop_unittest.cc(282): SubPumpFunc : Got Message
-// message_loop_unittest.cc(1581): DelayedQuitOnSystemTimer
-// !! This is the critical step verified by this test. Since the
-// ThreadController is idle after (6), it won't be invoked again and thus
-// won't get a chance to return a NextWorkInfo that indicates the next
-// delay. A native timer is thus required to have SubPumpFunc handle it.
-// work_deduplicator.cc(42): OnDelayedWorkRequested
-// message_pump_win.cc(129): ScheduleDelayedWork
-// 9) The scheduled native timer fires and runs application task binding
-// ::PostQuitMessage :
-// message_loop_unittest.cc(282) SubPumpFunc : Got Message
-// work_deduplicator.cc(50): OnWorkStarted
-// thread_controller_with_message_pump_impl.cc(237) : DoWork
-// 10) SequenceManager updates delay to none and notifies
-// (TODO(scheduler-dev): Could remove this step but WorkDeduplicator knows
-// to ignore at least):
-// work_deduplicator.cc(42): OnDelayedWorkRequested
-// 11) Nested application task completes and SubPumpFunc unwinds:
-// work_deduplicator.cc(58): WillCheckForMoreWork
-// work_deduplicator.cc(67): DidCheckForMoreWork
-// 12) ~ScopedNestableTaskAllower() makes sure WorkDeduplicator knows we're
-// back in DoWork() (not relevant in this test but important overall).
-// work_deduplicator.cc(50): OnWorkStarted
-// 13) Application task which ran SubPumpFunc completes and test finishes.
-// work_deduplicator.cc(67): DidCheckForMoreWork
-TEST_F(MessageLoopTest, PostDelayedTaskFromSystemPump) {
- MessageLoop message_loop(MessagePumpType::UI);
-
- RunLoop run_loop;
-
- // A native message window to generate a system message which invokes
- // DelayedQuitOnSystemTimer() when the native timer fires.
- win::MessageWindow local_message_window;
- local_message_window.Create(BindRepeating(&DelayedQuitOnSystemTimer));
- ASSERT_TRUE(::SetTimer(local_message_window.hwnd(), 0, 20, nullptr));
-
- // The first task will enter a native message loop. This test then verifies
- // that the pump is able to run a delayed application task after the native
- // pump went idle.
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
-
- // Test success is determined by not hanging in this Run() call.
- run_loop.Run();
-}
-
-TEST_F(MessageLoopTest, WmQuitIsVisibleToSubPump) {
- MessageLoop message_loop(MessagePumpType::UI);
-
- // Regression test for https://crbug.com/888559. When processing a
- // kMsgHaveWork we peek and remove the next message and dispatch that ourself,
- // to minimize impact of these messages on message-queue processing. If we
- // received kMsgHaveWork dispatched by a nested pump (e.g. ::GetMessage()
- // loop) then there is a risk that the next message is that loop's WM_QUIT
- // message, which must be processed directly by ::GetMessage() for the loop to
- // actually quit. This test verifies that WM_QUIT exits works as expected even
- // if it happens to immediately follow a kMsgHaveWork in the queue.
-
- RunLoop run_loop;
-
- // This application task will enter the subpump.
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
-
- // This application task will post a native WM_QUIT.
- message_loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
-
- // The presence of this application task means that the pump will see a
- // non-empty queue after processing the previous application task (which
- // posted the WM_QUIT) and hence will repost a kMsgHaveWork message in the
- // native event queue. Without the fix to https://crbug.com/888559, this would
- // previously result in the subpump processing kMsgHaveWork and it stealing
- // the WM_QUIT message, leaving the test hung in the subpump.
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
-
- // Test success is determined by not hanging in this Run() call.
- run_loop.Run();
-}
-
-TEST_F(MessageLoopTest, RepostingWmQuitDoesntStarveUpcomingNativeLoop) {
- MessageLoop message_loop(MessagePumpType::UI);
-
- // This test ensures that application tasks are being processed by the native
- // subpump despite the kMsgHaveWork event having already been consumed by the
- // time the subpump is entered. This is subtly enforced by
- // MessageLoopCurrent::ScopedNestableTaskAllower which will ScheduleWork()
- // upon construction (and if it's absent, the MessageLoop shouldn't process
- // application tasks so kMsgHaveWork is irrelevant).
- // Note: This test also fails prior to the fix for https://crbug.com/888559
- // (in fact, the last two tasks are sufficient as a regression test), probably
- // because of a dangling kMsgHaveWork recreating the effect from
- // MessageLoopTest.NativeMsgProcessingDoesntStealWmQuit.
-
- RunLoop run_loop;
-
- // This application task will post a native WM_QUIT which will be ignored
- // by the main message pump.
- message_loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
-
- // Make sure the pump does a few extra cycles and processes (ignores) the
- // WM_QUIT.
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
-
- // This application task will enter the subpump.
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
-
- // Post an application task that will post WM_QUIT to the nested loop. The
- // test will hang if the subpump doesn't process application tasks as it
- // should.
- message_loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
-
- // Test success is determined by not hanging in this Run() call.
- run_loop.Run();
-}
-
-// TODO(https://crbug.com/890016): Enable once multiple layers of nested loops
-// works.
-TEST_F(MessageLoopTest,
- DISABLED_UnwindingMultipleSubPumpsDoesntStarveApplicationTasks) {
- MessageLoop message_loop(MessagePumpType::UI);
-
- // Regression test for https://crbug.com/890016.
- // Tests that the subpump is still processing application tasks after
- // unwinding from nested subpumps (i.e. that they didn't consume the last
- // kMsgHaveWork).
-
- RunLoop run_loop;
-
- // Enter multiple levels of nested subpumps.
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, run_loop.QuitClosure()));
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, DoNothing::Once()));
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce(&SubPumpFunc, DoNothing::Once()));
-
- // Quit two layers (with tasks in between to allow each quit to be handled
- // before continuing -- ::PostQuitMessage() sets a bit, it's not a real queued
- // message :
- // https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453).
- message_loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
- message_loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
- message_loop.task_runner()->PostTask(FROM_HERE, DoNothing());
-
- bool last_task_ran = false;
- message_loop.task_runner()->PostTask(
- FROM_HERE, BindOnce([](bool* to_set) { *to_set = true; },
- Unretained(&last_task_ran)));
-
- message_loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&::PostQuitMessage, 0));
-
- run_loop.Run();
-
- EXPECT_TRUE(last_task_ran);
-}
-
-namespace {
-
-// A side effect of this test is the generation a beep. Sorry.
-void RunTest_RecursiveDenial2(MessagePumpType message_pump_type) {
- MessageLoop loop(message_pump_type);
-
- Thread worker("RecursiveDenial2_worker");
- Thread::Options options;
- options.message_pump_type = message_pump_type;
- ASSERT_EQ(true, worker.StartWithOptions(options));
- TaskList order;
- win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
- worker.task_runner()->PostTask(
- FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(),
- event.Get(), true, &order, false));
- // Let the other thread execute.
- WaitForSingleObject(event.Get(), INFINITE);
- RunLoop().Run();
-
- ASSERT_EQ(17u, 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(MESSAGEBOX, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false));
- // When EndDialogFunc is processed, the window is already dismissed, hence no
- // "end" entry.
- EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true));
- EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true));
- EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true));
- EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false));
-}
-
-} // namespace
-
-// This test occasionally hangs. See http://crbug.com/44567.
-TEST_F(MessageLoopTest, DISABLED_RecursiveDenial2) {
- RunTest_RecursiveDenial2(MessagePumpType::DEFAULT);
- RunTest_RecursiveDenial2(MessagePumpType::UI);
- RunTest_RecursiveDenial2(MessagePumpType::IO);
-}
-
-// A side effect of this test is the generation a beep. Sorry. This test also
-// needs to process windows messages on the current thread.
-TEST_F(MessageLoopTest, RecursiveSupport2) {
- MessageLoop loop(MessagePumpType::UI);
-
- Thread worker("RecursiveSupport2_worker");
- Thread::Options options;
- options.message_pump_type = MessagePumpType::UI;
- ASSERT_EQ(true, worker.StartWithOptions(options));
- TaskList order;
- win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
- worker.task_runner()->PostTask(
- FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(),
- event.Get(), false, &order, true));
- // Let the other thread execute.
- WaitForSingleObject(event.Get(), INFINITE);
- RunLoop().Run();
-
- ASSERT_EQ(18u, 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(MESSAGEBOX, 2, true));
- // Note that this executes in the MessageBox modal loop.
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true));
- EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false));
- EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true));
- EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false));
- EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false));
- /* The order can subtly change here. The reason is that when RecursiveFunc(1)
- is called in the main thread, if it is faster than getting to the
- PostTask(FROM_HERE, BindOnce(&QuitFunc) execution, the order of task
- execution can change. We don't care anyway that the order isn't correct.
- EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true));
- EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, 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, 3, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false));
- EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true));
- EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false));
-}
-
-#endif // defined(OS_WIN)
-
-TEST_F(MessageLoopTest, TaskObserver) {
- const int kNumPosts = 6;
- DummyTaskObserver observer(kNumPosts);
-
- MessageLoop loop;
- loop.AddTaskObserver(&observer);
- loop.task_runner()->PostTask(FROM_HERE,
- BindOnce(&PostNTasksThenQuit, kNumPosts));
- RunLoop().Run();
- loop.RemoveTaskObserver(&observer);
-
- EXPECT_EQ(kNumPosts, observer.num_tasks_started());
- EXPECT_EQ(kNumPosts, observer.num_tasks_processed());
-}
-
-#if defined(OS_WIN)
-TEST_F(MessageLoopTest, IOHandler) {
- RunTest_IOHandler();
-}
-
-TEST_F(MessageLoopTest, WaitForIO) {
- RunTest_WaitForIO();
-}
-
-TEST_F(MessageLoopTest, HighResolutionTimer) {
- MessageLoop message_loop;
- Time::EnableHighResolutionTimer(true);
-
- constexpr TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5);
- constexpr TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100);
-
- {
- // 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.
- 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)
-
-namespace {
-// 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 DestructionObserverProbe : public RefCounted<DestructionObserverProbe> {
- public:
- DestructionObserverProbe(bool* task_destroyed,
- bool* destruction_observer_called)
- : task_destroyed_(task_destroyed),
- destruction_observer_called_(destruction_observer_called) {}
- virtual void Run() {
- // This task should never run.
- ADD_FAILURE();
- }
-
- private:
- friend class RefCounted<DestructionObserverProbe>;
-
- virtual ~DestructionObserverProbe() {
- EXPECT_FALSE(*destruction_observer_called_);
- *task_destroyed_ = true;
- }
-
- bool* task_destroyed_;
- bool* destruction_observer_called_;
-};
-
-class MLDestructionObserver : public MessageLoopCurrent::DestructionObserver {
- public:
- MLDestructionObserver(bool* task_destroyed, bool* destruction_observer_called)
- : task_destroyed_(task_destroyed),
- destruction_observer_called_(destruction_observer_called),
- task_destroyed_before_message_loop_(false) {}
- void WillDestroyCurrentMessageLoop() override {
- task_destroyed_before_message_loop_ = *task_destroyed_;
- *destruction_observer_called_ = true;
- }
- bool task_destroyed_before_message_loop() const {
- return task_destroyed_before_message_loop_;
- }
-
- private:
- bool* task_destroyed_;
- bool* destruction_observer_called_;
- bool task_destroyed_before_message_loop_;
-};
-
-} // namespace
-
-TEST_F(MessageLoopTest, DestructionObserverTest) {
- // Verify that the destruction observer gets called at the very end (after
- // all the pending tasks have been destroyed).
- MessageLoop* loop = new MessageLoop;
- const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
-
- bool task_destroyed = false;
- bool destruction_observer_called = false;
-
- MLDestructionObserver observer(&task_destroyed, &destruction_observer_called);
- MessageLoopCurrent::Get()->AddDestructionObserver(&observer);
- loop->task_runner()->PostDelayedTask(
- FROM_HERE,
- BindOnce(&DestructionObserverProbe::Run,
- base::MakeRefCounted<DestructionObserverProbe>(
- &task_destroyed, &destruction_observer_called)),
- kDelay);
- delete loop;
- EXPECT_TRUE(observer.task_destroyed_before_message_loop());
- // The task should have been destroyed when we deleted the loop.
- EXPECT_TRUE(task_destroyed);
- EXPECT_TRUE(destruction_observer_called);
-}
-
-// Verify that MessageLoop sets ThreadMainTaskRunner::current() and it
-// posts tasks on that message loop.
-TEST_F(MessageLoopTest, ThreadMainTaskRunner) {
- MessageLoop loop;
-
- scoped_refptr<Foo> foo(new Foo());
- std::string a("a");
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a));
-
- // Post quit task;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated));
-
- // Now kick things off
- RunLoop().Run();
-
- EXPECT_EQ(foo->test_count(), 1);
- EXPECT_EQ(foo->result(), "a");
-}
-
-TEST_F(MessageLoopTest, IsType) {
- MessageLoop loop(MessagePumpType::UI);
- EXPECT_TRUE(loop.IsType(MessagePumpType::UI));
- EXPECT_FALSE(loop.IsType(MessagePumpType::IO));
- EXPECT_FALSE(loop.IsType(MessagePumpType::DEFAULT));
-}
-
-#if defined(OS_WIN)
-void EmptyFunction() {}
-
-void PostMultipleTasks() {
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::BindOnce(&EmptyFunction));
- ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
- base::BindOnce(&EmptyFunction));
-}
-
-static const int kSignalMsg = WM_USER + 2;
-
-void PostWindowsMessage(HWND message_hwnd) {
- PostMessage(message_hwnd, kSignalMsg, 0, 2);
-}
-
-void EndTest(bool* did_run, HWND hwnd) {
- *did_run = true;
- PostMessage(hwnd, WM_CLOSE, 0, 0);
-}
-
-int kMyMessageFilterCode = 0x5002;
-
-LRESULT CALLBACK TestWndProcThunk(HWND hwnd,
- UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- if (message == WM_CLOSE)
- EXPECT_TRUE(DestroyWindow(hwnd));
- if (message != kSignalMsg)
- return DefWindowProc(hwnd, message, wparam, lparam);
-
- switch (lparam) {
- case 1:
- // First, we post a task that will post multiple no-op tasks to make sure
- // that the pump's incoming task queue does not become empty during the
- // test.
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&PostMultipleTasks));
- // Next, we post a task that posts a windows message to trigger the second
- // stage of the test.
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&PostWindowsMessage, hwnd));
- break;
- case 2:
- // Since we're about to enter a modal loop, tell the message loop that we
- // intend to nest tasks.
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
- bool did_run = false;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&EndTest, &did_run, hwnd));
- // Run a nested windows-style message loop and verify that our task runs.
- // If it doesn't, then we'll loop here until the test times out.
- MSG msg;
- while (GetMessage(&msg, 0, 0, 0)) {
- if (!CallMsgFilter(&msg, kMyMessageFilterCode))
- DispatchMessage(&msg);
- // If this message is a WM_CLOSE, explicitly exit the modal loop.
- // Posting a WM_QUIT should handle this, but unfortunately
- // MessagePumpWin eats WM_QUIT messages even when running inside a modal
- // loop.
- if (msg.message == WM_CLOSE)
- break;
- }
- EXPECT_TRUE(did_run);
- RunLoop::QuitCurrentWhenIdleDeprecated();
- break;
- }
- return 0;
-}
-
-TEST_F(MessageLoopTest, AlwaysHaveUserMessageWhenNesting) {
- MessageLoop loop(MessagePumpType::UI);
- HINSTANCE instance = CURRENT_MODULE();
- WNDCLASSEX wc = {0};
- wc.cbSize = sizeof(wc);
- wc.lpfnWndProc = TestWndProcThunk;
- wc.hInstance = instance;
- wc.lpszClassName = L"MessageLoopTest_HWND";
- ATOM atom = RegisterClassEx(&wc);
- ASSERT_TRUE(atom);
-
- HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0,
- HWND_MESSAGE, 0, instance, 0);
- ASSERT_TRUE(message_hwnd) << GetLastError();
-
- ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1));
-
- RunLoop().Run();
-
- ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance));
-}
-#endif // defined(OS_WIN)
-
-TEST_F(MessageLoopTest, SetTaskRunner) {
- MessageLoop loop;
- scoped_refptr<SingleThreadTaskRunner> new_runner(new TestSimpleTaskRunner());
-
- loop.SetTaskRunner(new_runner);
- EXPECT_EQ(new_runner, loop.task_runner());
- EXPECT_EQ(new_runner, ThreadTaskRunnerHandle::Get());
-}
-
-TEST_F(MessageLoopTest, OriginalRunnerWorks) {
- MessageLoop loop;
- scoped_refptr<SingleThreadTaskRunner> new_runner(new TestSimpleTaskRunner());
- scoped_refptr<SingleThreadTaskRunner> original_runner(loop.task_runner());
- loop.SetTaskRunner(new_runner);
-
- scoped_refptr<Foo> foo(new Foo());
- original_runner->PostTask(FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, "a"));
- RunLoop().RunUntilIdle();
- EXPECT_EQ(1, foo->test_count());
-}
-
-TEST_F(MessageLoopTest, DeleteUnboundLoop) {
- // It should be possible to delete an unbound message loop on a thread which
- // already has another active loop. This happens when thread creation fails.
- MessageLoop loop;
- std::unique_ptr<MessageLoop> unbound_loop(
- MessageLoop::CreateUnbound(MessagePumpType::DEFAULT));
- unbound_loop.reset();
- EXPECT_TRUE(loop.task_runner()->RunsTasksInCurrentSequence());
- EXPECT_EQ(loop.task_runner(), ThreadTaskRunnerHandle::Get());
-}
-
-// Verify that tasks posted to and code running in the scope of the same
-// MessageLoop access the same SequenceLocalStorage values.
-TEST_F(MessageLoopTest, SequenceLocalStorageSetGet) {
- MessageLoop loop;
-
- SequenceLocalStorageSlot<int> slot;
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindLambdaForTesting([&]() { slot.emplace(11); }));
-
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindLambdaForTesting([&]() { EXPECT_EQ(*slot, 11); }));
-
- RunLoop().RunUntilIdle();
- EXPECT_EQ(*slot, 11);
-}
-
-// Verify that tasks posted to and code running in different MessageLoops access
-// different SequenceLocalStorage values.
-TEST_F(MessageLoopTest, SequenceLocalStorageDifferentMessageLoops) {
- SequenceLocalStorageSlot<int> slot;
-
- {
- MessageLoop loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindLambdaForTesting([&]() { slot.emplace(11); }));
-
- RunLoop().RunUntilIdle();
- EXPECT_EQ(*slot, 11);
- }
-
- MessageLoop loop;
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindLambdaForTesting([&]() { EXPECT_FALSE(slot); }));
-
- RunLoop().RunUntilIdle();
- EXPECT_NE(slot.GetOrCreateValue(), 11);
-}
-
-namespace {
-
-class PostTaskOnDestroy {
- public:
- PostTaskOnDestroy(int times) : times_remaining_(times) {}
- ~PostTaskOnDestroy() { PostTaskWithPostingDestructor(times_remaining_); }
-
- // Post a task that will repost itself on destruction |times| times.
- static void PostTaskWithPostingDestructor(int times) {
- if (times > 0) {
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, BindOnce([](std::unique_ptr<PostTaskOnDestroy>) {},
- std::make_unique<PostTaskOnDestroy>(times - 1)));
- }
- }
-
- private:
- const int times_remaining_;
-
- DISALLOW_COPY_AND_ASSIGN(PostTaskOnDestroy);
-};
-
-} // namespace
-
-// Test that MessageLoop destruction handles a task's destructor posting another
-// task.
-TEST(MessageLoopDestructionTest, DestroysFineWithPostTaskOnDestroy) {
- std::unique_ptr<MessageLoop> loop = std::make_unique<MessageLoop>();
-
- PostTaskOnDestroy::PostTaskWithPostingDestructor(10);
- loop.reset();
-}
-
-} // namespace base
diff --git a/chromium/base/message_loop/message_pump.h b/chromium/base/message_loop/message_pump.h
index 2224f40b9a4..3151a5cbfa5 100644
--- a/chromium/base/message_loop/message_pump.h
+++ b/chromium/base/message_loop/message_pump.h
@@ -6,7 +6,7 @@
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
#include "base/base_export.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/message_loop/message_pump_type.h"
#include "base/message_loop/timer_slack.h"
#include "base/sequence_checker.h"
diff --git a/chromium/base/message_loop/message_pump_fuchsia.cc b/chromium/base/message_loop/message_pump_fuchsia.cc
index 1c575681064..d8bda871821 100644
--- a/chromium/base/message_loop/message_pump_fuchsia.cc
+++ b/chromium/base/message_loop/message_pump_fuchsia.cc
@@ -15,7 +15,7 @@
#include "base/auto_reset.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
-#include "base/trace_event/trace_event.h"
+#include "base/trace_event/base_tracing.h"
namespace base {
diff --git a/chromium/base/message_loop/message_pump_glib_unittest.cc b/chromium/base/message_loop/message_pump_glib_unittest.cc
index 54fa300c290..c1da85c0cfe 100644
--- a/chromium/base/message_loop/message_pump_glib_unittest.cc
+++ b/chromium/base/message_loop/message_pump_glib_unittest.cc
@@ -14,6 +14,7 @@
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/files/file_util.h"
+#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
@@ -447,8 +448,6 @@ class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
};
void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) {
- // Allow tasks to be processed from 'native' event loops.
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
int task_count = 0;
@@ -472,7 +471,10 @@ void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) {
TimeDelta::FromMilliseconds(40));
// Run a nested, straight GLib message loop.
- runner->RunGLib();
+ {
+ MessageLoopCurrent::ScopedNestableTaskAllower allow_nestable_tasks;
+ runner->RunGLib();
+ }
ASSERT_EQ(3, task_count);
EXPECT_EQ(4, injector->processed_events());
@@ -480,8 +482,6 @@ void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) {
}
void TestGtkLoopInternal(EventInjector* injector, OnceClosure done) {
- // Allow tasks to be processed from 'native' event loops.
- MessageLoopCurrent::Get()->SetNestableTasksAllowed(true);
scoped_refptr<GLibLoopRunner> runner = new GLibLoopRunner();
int task_count = 0;
@@ -505,7 +505,10 @@ void TestGtkLoopInternal(EventInjector* injector, OnceClosure done) {
TimeDelta::FromMilliseconds(40));
// Run a nested, straight Gtk message loop.
- runner->RunLoop();
+ {
+ MessageLoopCurrent::ScopedNestableTaskAllower allow_nestable_tasks;
+ runner->RunLoop();
+ }
ASSERT_EQ(3, task_count);
EXPECT_EQ(4, injector->processed_events());
diff --git a/chromium/base/message_loop/message_pump_io_ios.cc b/chromium/base/message_loop/message_pump_io_ios.cc
index 9b43e8edb22..6dcc0d7c0bb 100644
--- a/chromium/base/message_loop/message_pump_io_ios.cc
+++ b/chromium/base/message_loop/message_pump_io_ios.cc
@@ -4,6 +4,8 @@
#include "base/message_loop/message_pump_io_ios.h"
+#include "base/notreached.h"
+
namespace base {
MessagePumpIOSForIO::FdWatchController::FdWatchController(
diff --git a/chromium/base/message_loop/message_pump_io_ios_unittest.cc b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
index aec10012a7c..196c6d4aac8 100644
--- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc
+++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
@@ -6,6 +6,7 @@
#include <unistd.h>
+#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/posix/eintr_wrapper.h"
diff --git a/chromium/base/message_loop/message_pump_libevent.cc b/chromium/base/message_loop/message_pump_libevent.cc
index 5a8f5f5249e..17175f76d91 100644
--- a/chromium/base/message_loop/message_pump_libevent.cc
+++ b/chromium/base/message_loop/message_pump_libevent.cc
@@ -16,7 +16,7 @@
#include "base/posix/eintr_wrapper.h"
#include "base/third_party/libevent/event.h"
#include "base/time/time.h"
-#include "base/trace_event/trace_event.h"
+#include "base/trace_event/base_tracing.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
@@ -130,7 +130,7 @@ bool MessagePumpLibevent::WatchFileDescriptor(int fd,
// threadsafe, and your watcher may never be registered.
DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
- TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ TRACE_EVENT_WITH_FLOW1("toplevel.flow",
"MessagePumpLibevent::WatchFileDescriptor",
reinterpret_cast<uintptr_t>(controller) ^ fd,
TRACE_EVENT_FLAG_FLOW_OUT, "fd", fd);
@@ -315,11 +315,10 @@ void MessagePumpLibevent::OnLibeventNotification(int fd,
FdWatchController* controller = static_cast<FdWatchController*>(context);
DCHECK(controller);
TRACE_EVENT0("toplevel", "OnLibevent");
- TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
- "MessagePumpLibevent::OnLibeventNotification",
- reinterpret_cast<uintptr_t>(controller) ^ fd,
- TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
- "fd", fd);
+ TRACE_EVENT_WITH_FLOW1(
+ "toplevel.flow", "MessagePumpLibevent::OnLibeventNotification",
+ reinterpret_cast<uintptr_t>(controller) ^ fd,
+ TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "fd", fd);
TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION heap_profiler_scope(
controller->created_from_location().file_name());
diff --git a/chromium/base/message_loop/message_pump_libevent_unittest.cc b/chromium/base/message_loop/message_pump_libevent_unittest.cc
index f5dbd4006fb..ae5e011f70c 100644
--- a/chromium/base/message_loop/message_pump_libevent_unittest.cc
+++ b/chromium/base/message_loop/message_pump_libevent_unittest.cc
@@ -12,6 +12,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_pump_type.h"
#include "base/posix/eintr_wrapper.h"
diff --git a/chromium/base/message_loop/message_pump_mac.h b/chromium/base/message_loop/message_pump_mac.h
index 909b946dc8e..f9c9db9696f 100644
--- a/chromium/base/message_loop/message_pump_mac.h
+++ b/chromium/base/message_loop/message_pump_mac.h
@@ -36,9 +36,7 @@
#include <CoreFoundation/CoreFoundation.h>
#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
#include "base/message_loop/timer_slack.h"
-#include "base/optional.h"
#include "build/build_config.h"
#if defined(__OBJC__)
@@ -145,10 +143,6 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// Get the current mode mask from |enabled_modes_|.
int GetModeMask() const;
- // Controls whether the timer invalidation performance optimization is
- // allowed.
- void SetTimerInvalidationAllowed(bool allowed);
-
private:
class ScopedModeEnabler;
@@ -159,25 +153,6 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
// avoids querying Now() for key callers.
void ScheduleDelayedWorkImpl(TimeDelta delta);
- // Marking timers as invalid at the right time helps significantly reduce
- // power use (see the comment in RunDelayedWorkTimer()), however there is no
- // public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which
- // the above timer invalidation functions are based, can change from release
- // to release and should not be accessed directly (this struct last changed at
- // least in 2008 in CF-476).
- //
- // This function uses private API to modify a test timer's valid state and
- // uses public API to confirm that the private API changed the right bit.
- static bool CanInvalidateCFRunLoopTimers();
-
- // Sets a Core Foundation object's "invalid" bit to |valid|. Based on code
- // from CFRunLoop.c.
- static void ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid);
-
- // Controls the validity of the delayed work timer. Does nothing if timer
- // invalidation is disallowed.
- void SetDelayedWorkTimerValid(bool valid);
-
// Timer callback scheduled by ScheduleDelayedWork. This does not do any
// work, but it signals |work_source_| so that delayed work can be performed
// within the appropriate priority constraints.
@@ -280,14 +255,6 @@ class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
bool delegateless_work_;
bool delegateless_idle_work_;
- // Whether or not timer invalidation can be used in order to reduce the number
- // of reschedulings.
- bool allow_timer_invalidation_;
-
- // If changing timer validitity was attempted while it was disallowed, this
- // value tracks the desired state of the timer.
- Optional<bool> pending_timer_validity_;
-
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase);
};
diff --git a/chromium/base/message_loop/message_pump_mac.mm b/chromium/base/message_loop/message_pump_mac.mm
index cbda789a0d2..1d0ac66a917 100644
--- a/chromium/base/message_loop/message_pump_mac.mm
+++ b/chromium/base/message_loop/message_pump_mac.mm
@@ -53,48 +53,6 @@ bool g_not_using_cr_app = false;
// The MessagePump controlling [NSApp run].
MessagePumpNSApplication* g_app_pump;
-
-Feature kMessagePumpTimerInvalidation{"MessagePumpMacTimerInvalidation",
- FEATURE_ENABLED_BY_DEFAULT};
-
-// Various CoreFoundation definitions.
-typedef struct __CFRuntimeBase {
- uintptr_t _cfisa;
- uint8_t _cfinfo[4];
- uint32_t _rc;
-} CFRuntimeBase;
-
-#if defined(__BIG_ENDIAN__)
-#define __CF_BIG_ENDIAN__ 1
-#define __CF_LITTLE_ENDIAN__ 0
-#endif
-
-#if defined(__LITTLE_ENDIAN__)
-#define __CF_LITTLE_ENDIAN__ 1
-#define __CF_BIG_ENDIAN__ 0
-#endif
-
-#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__)*3)
-
-#define __CFBitfieldMask(N1, N2) \
- ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1))
-#define __CFBitfieldSetValue(V, N1, N2, X) \
- ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | \
- (((X) << (N2)) & __CFBitfieldMask(N1, N2)))
-
-// Marking timers as invalid at the right time by flipping their valid bit helps
-// significantly reduce power use (see the explanation in
-// RunDelayedWorkTimer()), however there is no public API for doing so.
-// CFRuntime.h states that CFRuntimeBase can change from release to release
-// and should not be accessed directly. The last known change of this struct
-// occurred in 2008 in CF-476 / 10.5; unfortunately the source for 10.11 and
-// 10.12 is not available for inspection at this time.
-// CanInvalidateCFRunLoopTimers() will at least prevent us from invalidating
-// timers if this function starts flipping the wrong bit on a future OS release.
-void __ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid) {
- __CFBitfieldSetValue(((CFRuntimeBase*)timer)->_cfinfo[CF_INFO_BITS], 3, 3,
- valid);
-}
#endif // !defined(OS_IOS)
} // namespace
@@ -217,16 +175,6 @@ void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
}
void MessagePumpCFRunLoopBase::ScheduleDelayedWorkImpl(TimeDelta delta) {
- // Flip the timer's validation bit just before setting the new fire time. Do
- // this now because CFRunLoopTimerSetNextFireDate() likely checks the validity
- // of a timer before proceeding to set its fire date. Making the timer valid
- // now won't have any side effects (such as a premature firing of the timer)
- // because we're only flipping a bit.
- //
- // Please see the comment in RunDelayedWorkTimer() for more info on the whys
- // of invalidation.
- SetDelayedWorkTimerValid(true);
-
// The tolerance needs to be set before the fire date or it may be ignored.
if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
@@ -256,8 +204,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int initial_mode_mask)
deepest_nesting_level_(0),
keep_running_(true),
delegateless_work_(false),
- delegateless_idle_work_(false),
- allow_timer_invalidation_(true) {
+ delegateless_idle_work_(false) {
run_loop_ = CFRunLoopGetCurrent();
CFRetain(run_loop_);
@@ -368,100 +315,11 @@ int MessagePumpCFRunLoopBase::GetModeMask() const {
return mask;
}
-#if !defined(OS_IOS)
-// This function uses private API to modify a test timer's valid state and
-// uses public API to confirm that the private API changed the correct bit.
-// static
-bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() {
- if (!FeatureList::IsEnabled(kMessagePumpTimerInvalidation)) {
- return false;
- }
-
- CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
- timer_context.info = nullptr;
- ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(
- CFRunLoopTimerCreate(NULL, // allocator
- kCFTimeIntervalMax, // fire time
- kCFTimeIntervalMax, // interval
- 0, // flags
- 0, // priority
- nullptr, &timer_context));
- // Should be valid from the start.
- if (!CFRunLoopTimerIsValid(test_timer)) {
- return false;
- }
- // Confirm that the private API can mark the timer invalid.
- __ChromeCFRunLoopTimerSetValid(test_timer, false);
- if (CFRunLoopTimerIsValid(test_timer)) {
- return false;
- }
- // Confirm that the private API can mark the timer valid.
- __ChromeCFRunLoopTimerSetValid(test_timer, true);
- return CFRunLoopTimerIsValid(test_timer);
-}
-#endif // !defined(OS_IOS)
-
-// static
-void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(
- CFRunLoopTimerRef timer,
- bool valid) {
-#if !defined(OS_IOS)
- static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers();
- if (can_invalidate_timers) {
- __ChromeCFRunLoopTimerSetValid(timer, valid);
- }
-#endif // !defined(OS_IOS)
-}
-
-void MessagePumpCFRunLoopBase::SetDelayedWorkTimerValid(bool valid) {
- if (allow_timer_invalidation_) {
- ChromeCFRunLoopTimerSetValid(delayed_work_timer_, valid);
- } else {
- pending_timer_validity_ = valid;
- }
-}
-
-void MessagePumpCFRunLoopBase::SetTimerInvalidationAllowed(bool allowed) {
- if (!allowed)
- ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true);
- allow_timer_invalidation_ = allowed;
- if (allowed && pending_timer_validity_.has_value()) {
- SetDelayedWorkTimerValid(*pending_timer_validity_);
- pending_timer_validity_ = nullopt;
- }
-}
-
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
-
- // The message pump's timer needs to fire at changing and unpredictable
- // intervals. Creating a new timer for each firing time is very expensive, so
- // the message pump instead uses a repeating timer with a very large repeat
- // rate. After each firing of the timer, the run loop sets the timer's next
- // firing time to the distant future, essentially pausing the timer until the
- // pump sets the next firing time. This is the solution recommended by Apple.
- //
- // It turns out, however, that scheduling timers is also quite expensive, and
- // that every one of the message pump's timer firings incurs two
- // reschedulings. The first rescheduling occurs in ScheduleDelayedWork(),
- // which sets the desired next firing time. The second comes after exiting
- // this method (the timer's callback method), when the run loop sets the
- // timer's next firing time to far in the future.
- //
- // The code in __CFRunLoopDoTimer() inside CFRunLoop.c calls the timer's
- // callback, confirms that the timer is valid, and then sets its future
- // firing time based on its repeat frequency. Flipping the valid bit here
- // causes the __CFRunLoopDoTimer() to skip setting the future firing time.
- // Note that there's public API to invalidate a timer but it goes beyond
- // flipping the valid bit, making the timer unusable in the future.
- //
- // ScheduleDelayedWork() flips the valid bit back just before setting the
- // timer's new firing time.
- self->SetDelayedWorkTimerValid(false);
-
// The timer fired, assume we have work and let RunWork() figure out what to
// do and what to schedule after.
base::mac::CallWithEHFrame(^{
@@ -793,14 +651,11 @@ ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
if ([NSApp modalWindow])
return;
g_app_pump->SetModeMask(kAllModesMask);
- // Disable timer invalidation to avoid hangs. See crbug.com/912273.
- g_app_pump->SetTimerInvalidationAllowed(false);
}
ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() {
DCHECK(g_app_pump);
g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask);
- g_app_pump->SetTimerInvalidationAllowed(true);
}
int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() {
diff --git a/chromium/base/message_loop/message_pump_mac_unittest.mm b/chromium/base/message_loop/message_pump_mac_unittest.mm
index 85f4779868a..840a3f5f8eb 100644
--- a/chromium/base/message_loop/message_pump_mac_unittest.mm
+++ b/chromium/base/message_loop/message_pump_mac_unittest.mm
@@ -29,102 +29,6 @@ constexpr int kNSApplicationModalSafeModeMask = 0x3;
namespace base {
-class TestMessagePumpCFRunLoopBase {
- public:
- bool TestCanInvalidateTimers() {
- return MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers();
- }
- static void SetTimerValid(CFRunLoopTimerRef timer, bool valid) {
- MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(timer, valid);
- }
-
- static void PerformTimerCallback(CFRunLoopTimerRef timer, void* info) {
- TestMessagePumpCFRunLoopBase* self =
- static_cast<TestMessagePumpCFRunLoopBase*>(info);
- self->timer_callback_called_ = true;
-
- if (self->invalidate_timer_in_callback_) {
- SetTimerValid(timer, false);
- }
- }
-
- bool invalidate_timer_in_callback_;
-
- bool timer_callback_called_;
-};
-
-TEST(MessagePumpMacTest, TestCanInvalidateTimers) {
- TestMessagePumpCFRunLoopBase message_pump_test;
-
- // Catch whether or not the use of private API ever starts failing.
- EXPECT_TRUE(message_pump_test.TestCanInvalidateTimers());
-}
-
-TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) {
- TestMessagePumpCFRunLoopBase message_pump_test;
-
- CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
- timer_context.info = &message_pump_test;
- const CFTimeInterval kCFTimeIntervalMax =
- std::numeric_limits<CFTimeInterval>::max();
- ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(CFRunLoopTimerCreate(
- NULL, // allocator
- kCFTimeIntervalMax, // fire time
- kCFTimeIntervalMax, // interval
- 0, // flags
- 0, // priority
- TestMessagePumpCFRunLoopBase::PerformTimerCallback, &timer_context));
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), test_timer,
- kMessageLoopExclusiveRunLoopMode);
-
- // Sanity check.
- EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer));
-
- // Confirm that the timer fires as expected, and that it's not a one-time-use
- // timer (those timers are invalidated after they fire).
- CFAbsoluteTime next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01;
- CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time);
- message_pump_test.timer_callback_called_ = false;
- message_pump_test.invalidate_timer_in_callback_ = false;
- CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true);
- EXPECT_TRUE(message_pump_test.timer_callback_called_);
- EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer));
-
- // As a repeating timer, the timer should have a new fire date set in the
- // future.
- EXPECT_GT(CFRunLoopTimerGetNextFireDate(test_timer), next_fire_time);
-
- // Try firing the timer, and invalidating it within its callback.
- next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01;
- CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time);
- message_pump_test.timer_callback_called_ = false;
- message_pump_test.invalidate_timer_in_callback_ = true;
- CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true);
- EXPECT_TRUE(message_pump_test.timer_callback_called_);
- EXPECT_FALSE(CFRunLoopTimerIsValid(test_timer));
-
- // The CFRunLoop believes the timer is invalid, so it should not have a
- // fire date.
- EXPECT_EQ(0, CFRunLoopTimerGetNextFireDate(test_timer));
-
- // Now mark the timer as valid and confirm that it still fires correctly.
- TestMessagePumpCFRunLoopBase::SetTimerValid(test_timer, true);
- EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer));
- next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01;
- CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time);
- message_pump_test.timer_callback_called_ = false;
- message_pump_test.invalidate_timer_in_callback_ = false;
- CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true);
- EXPECT_TRUE(message_pump_test.timer_callback_called_);
- EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer));
-
- // Confirm that the run loop again gave it a new fire date in the future.
- EXPECT_GT(CFRunLoopTimerGetNextFireDate(test_timer), next_fire_time);
-
- CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer,
- kMessageLoopExclusiveRunLoopMode);
-}
-
namespace {
// PostedTasks are only executed while the message pump has a delegate. That is,
@@ -215,84 +119,6 @@ TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) {
EXPECT_EQ(NSAlertFirstButtonReturn, result);
}
-// This is a regression test for a scenario where the invalidation of the
-// delayed work timer (using non-public APIs) causes a nested native run loop to
-// hang. The exact root cause of the hang is unknown since it involves the
-// closed-source Core Foundation runtime, but the steps needed to trigger it
-// are:
-//
-// 1. Post a delayed task that will run some time after step #4.
-// 2. Allow Chrome tasks to run in nested run loops (with
-// ScopedNestableTaskAllower).
-// 3. Allow running Chrome tasks during private run loop modes (with
-// ScopedPumpMessagesInPrivateModes).
-// 4. Open a pop-up menu via [NSMenu popupContextMenu]. This will start a
-// private native run loop to process menu interaction.
-// 5. In a posted task, close the menu with [NSMenu cancelTracking].
-//
-// At this point the menu closes visually but the nested run loop (flakily)
-// hangs forever in a live-lock, i.e., Chrome tasks keep executing but the
-// NSMenu call in #4 never returns.
-//
-// The workaround is to avoid timer invalidation during nested native run loops.
-//
-// DANGER: As the pop-up menu captures keyboard input, the bug will make the
-// machine's keyboard inoperable during the live-lock. Use a TTY-based remote
-// terminal such as SSH (as opposed to Chromoting) to investigate the issue.
-//
-TEST(MessagePumpMacTest, DontInvalidateTimerInNativeRunLoop) {
- test::SingleThreadTaskEnvironment task_environment(
- test::SingleThreadTaskEnvironment::MainThreadType::UI);
- NSWindow* window =
- [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
- styleMask:NSBorderlessWindowMask
- backing:NSBackingStoreBuffered
- defer:NO] autorelease];
- NSMenu* menu = [[NSMenu alloc] initWithTitle:@"Test menu"];
- [menu insertItemWithTitle:@"Dummy item"
- action:@selector(dummy)
- keyEquivalent:@"a"
- atIndex:0];
- NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
- location:NSZeroPoint
- modifierFlags:0
- timestamp:0
- windowNumber:0
- context:nil
- subtype:0
- data1:0
- data2:0];
-
- // Post a task to open the menu. This needs to be a separate task so that
- // nested task execution can be allowed.
- ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(
- [](NSWindow* window, NSMenu* menu, NSEvent* event) {
- MessageLoopCurrent::ScopedNestableTaskAllower allow;
- ScopedPumpMessagesInPrivateModes pump_private;
- // When the bug triggers, this call never returns.
- [NSMenu popUpContextMenu:menu
- withEvent:event
- forView:[window contentView]];
- },
- window, menu, event));
-
- // Post another task to close the menu. The 100ms delay was determined
- // experimentally on a 2013 Mac Pro.
- RunLoop run_loop;
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(
- [](RunLoop* run_loop, NSMenu* menu) {
- [menu cancelTracking];
- run_loop->Quit();
- },
- &run_loop, menu),
- base::TimeDelta::FromMilliseconds(100));
-
- EXPECT_NO_FATAL_FAILURE(run_loop.Run());
-}
-
TEST(MessagePumpMacTest, QuitWithModalWindow) {
test::SingleThreadTaskEnvironment task_environment(
test::SingleThreadTaskEnvironment::MainThreadType::UI);
diff --git a/chromium/base/message_loop/message_pump_win.cc b/chromium/base/message_loop/message_pump_win.cc
index 83cbec19c29..4c500064bcd 100644
--- a/chromium/base/message_loop/message_pump_win.cc
+++ b/chromium/base/message_loop/message_pump_win.cc
@@ -14,7 +14,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/numerics/ranges.h"
#include "base/numerics/safe_conversions.h"
-#include "base/trace_event/trace_event.h"
+#include "base/trace_event/base_tracing.h"
namespace base {
@@ -24,11 +24,12 @@ namespace {
// opportunity to yield to other threads according to some heuristics (e.g.
// presumably when there's no input but perhaps a single WM_USER message posted
// later than another thread was readied). MessagePumpForUI doesn't intend to
-// give this opportunity to the kernel when invoking ::PeekMessage however as it
-// runs most tasks out-of-band. Hence, PM_NOYIELD should be used to tell
-// ::PeekMessage it's not the only source of work for this thread.
-const Feature kNoYieldFromNativePeek{"NoYieldFromNativePeek",
- FEATURE_DISABLED_BY_DEFAULT};
+// give this opportunity to the kernel when invoking ::PeekMessage however. This
+// experiment attempts to regain control of the pump (behind an experiment
+// because of how fragile this code is -- experiments help external contributors
+// diagnose regressions, e.g. crbug.com/1078475).
+const Feature kPreventMessagePumpHangs{"PreventMessagePumpHangs",
+ FEATURE_DISABLED_BY_DEFAULT};
enum MessageLoopProblems {
MESSAGE_POST_ERROR,
@@ -114,7 +115,7 @@ void MessagePumpForUI::ScheduleWork() {
return; // Someone else continued the pumping.
// Make sure the MessagePump does some work for us.
- BOOL ret = PostMessage(message_window_.hwnd(), kMsgHaveWork, 0, 0);
+ const BOOL ret = ::PostMessage(message_window_.hwnd(), kMsgHaveWork, 0, 0);
if (ret)
return; // There was room in the Window Message queue.
@@ -131,6 +132,8 @@ void MessagePumpForUI::ScheduleWork() {
work_scheduled_ = false;
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR,
MESSAGE_LOOP_PROBLEM_MAX);
+ TRACE_EVENT_INSTANT0("base", "Chrome.MessageLoopProblem.MESSAGE_POST_ERROR",
+ TRACE_EVENT_SCOPE_THREAD);
}
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
@@ -252,8 +255,6 @@ void MessagePumpForUI::DoRunLoop() {
if (more_work_is_plausible)
continue;
- // WaitForWork() does some work itself, so notify the delegate of it.
- state_->delegate->BeforeWait();
WaitForWork(next_work_info);
}
}
@@ -267,6 +268,8 @@ void MessagePumpForUI::WaitForWork(Delegate::NextWorkInfo next_work_info) {
for (DWORD delay = GetSleepTimeoutMs(next_work_info.delayed_run_time,
next_work_info.recent_now);
delay != 0; delay = GetSleepTimeoutMs(next_work_info.delayed_run_time)) {
+ state_->delegate->BeforeWait();
+
// Tell the optimizer to retain these values to simplify analyzing hangs.
base::debug::Alias(&delay);
base::debug::Alias(&wait_flags);
@@ -296,13 +299,15 @@ void MessagePumpForUI::WaitForWork(Delegate::NextWorkInfo next_work_info) {
}
{
- static const auto kAdditionalFlags =
- FeatureList::IsEnabled(kNoYieldFromNativePeek) ? PM_NOYIELD : 0x0;
+ // ::GetQueueStatus() above may racily miss a sent-message and
+ // ::PeekMessage() below may thus process one and/or internal events per
+ // its doc.
+ state_->delegate->BeforeDoInternalWork();
MSG msg;
// Trace as in ProcessNextWindowsMessage().
TRACE_EVENT0("base", "MessagePumpForUI::WaitForWork PeekMessage");
- if (::PeekMessage(&msg, nullptr, 0, 0, kAdditionalFlags | PM_NOREMOVE))
+ if (::PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE))
return;
}
@@ -341,6 +346,7 @@ void MessagePumpForUI::HandleWorkMessage() {
if (next_work_info.is_immediate()) {
ScheduleWork();
} else {
+ state_->delegate->BeforeWait();
ScheduleNativeTimer(next_work_info);
}
}
@@ -372,6 +378,7 @@ void MessagePumpForUI::HandleTimerMessage() {
if (next_work_info.is_immediate()) {
ScheduleWork();
} else {
+ state_->delegate->BeforeWait();
ScheduleNativeTimer(next_work_info);
}
}
@@ -426,18 +433,22 @@ void MessagePumpForUI::ScheduleNativeTimer(
// Tell the optimizer to retain the delay to simplify analyzing hangs.
base::debug::Alias(&delay_msec);
- UINT_PTR ret =
+ const UINT_PTR ret =
::SetTimer(message_window_.hwnd(), reinterpret_cast<UINT_PTR>(this),
delay_msec, nullptr);
- installed_native_timer_ = next_work_info.delayed_run_time;
- if (ret)
+ if (ret) {
+ installed_native_timer_ = next_work_info.delayed_run_time;
return;
- // If we can't set timers, we are in big trouble... but cross our fingers
- // for now.
- // TODO(jar): If we don't see this error, use a CHECK() here instead.
+ }
+ // This error is likely similar to MESSAGE_POST_ERROR (i.e. native queue is
+ // full). Since we only use ScheduleNativeTimer() in native nested loops
+ // this likely means this pump will not be given a chance to run application
+ // tasks until the nested loop completes.
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR,
MESSAGE_LOOP_PROBLEM_MAX);
+ TRACE_EVENT_INSTANT0("base", "Chrome.MessageLoopProblem.SET_TIMER_ERROR",
+ TRACE_EVENT_SCOPE_THREAD);
}
}
@@ -480,17 +491,13 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() {
// when ::PeekMessage turns out to be a no-op).
state_->delegate->BeforeDoInternalWork();
- static const auto kAdditionalFlags =
- FeatureList::IsEnabled(kNoYieldFromNativePeek) ? PM_NOYIELD : 0x0;
-
// PeekMessage can run a message if there are sent messages, trace that and
// emit the boolean param to see if it ever janks independently (ref.
// comment on GetQueueStatus).
TRACE_EVENT1("base",
"MessagePumpForUI::ProcessNextWindowsMessage PeekMessage",
"sent_messages_in_queue", more_work_is_plausible);
- has_msg = ::PeekMessage(&msg, nullptr, 0, 0,
- kAdditionalFlags | PM_REMOVE) != FALSE;
+ has_msg = ::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE;
}
if (has_msg)
more_work_is_plausible |= ProcessMessageHelper(msg);
@@ -545,13 +552,33 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() {
// that peeked replacement. Note that the re-post of kMsgHaveWork may be
// asynchronous to this thread!!
- // As in ProcessNextWindowsMessage() since ::PeekMessage() may process
- // sent-messages.
+ // Bump the work id since ::PeekMessage may process internal events.
state_->delegate->BeforeDoInternalWork();
+ // The system headers don't define this; it's equivalent to PM_QS_INPUT |
+ // PM_QS_PAINT | PM_QS_POSTMESSAGE. i.e., anything but QS_SENDMESSAGE. Since
+ // we're looking to replace our kMsgHaveWork posted message, we can ignore
+ // sent messages (which never compete with posted messages in the initial
+ // PeekMessage call).
+ constexpr auto PM_QS_ALLEVENTS = QS_ALLEVENTS << 16;
+ static_assert(
+ PM_QS_ALLEVENTS == (PM_QS_INPUT | PM_QS_PAINT | PM_QS_POSTMESSAGE), "");
+ static_assert((PM_QS_ALLEVENTS & PM_QS_SENDMESSAGE) == 0, "");
+
MSG msg;
- const bool have_message =
- ::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE;
+ bool have_message = false;
+ {
+ TRACE_EVENT0("base",
+ "MessagePumpForUI::ProcessPumpReplacementMessage PeekMessage");
+
+ static const auto peek_replacement_message_modifier =
+ base::FeatureList::IsEnabled(kPreventMessagePumpHangs) ? PM_QS_ALLEVENTS
+ : 0;
+
+ have_message =
+ ::PeekMessage(&msg, nullptr, 0, 0,
+ PM_REMOVE | peek_replacement_message_modifier) != FALSE;
+ }
// Expect no message or a message different than kMsgHaveWork.
DCHECK(!have_message || kMsgHaveWork != msg.message ||
@@ -623,9 +650,9 @@ void MessagePumpForIO::ScheduleWork() {
return; // Someone else continued the pumping.
// Make sure the MessagePump does some work for us.
- BOOL ret = ::PostQueuedCompletionStatus(port_.Get(), 0,
- reinterpret_cast<ULONG_PTR>(this),
- reinterpret_cast<OVERLAPPED*>(this));
+ const BOOL ret = ::PostQueuedCompletionStatus(
+ port_.Get(), 0, reinterpret_cast<ULONG_PTR>(this),
+ reinterpret_cast<OVERLAPPED*>(this));
if (ret)
return; // Post worked perfectly.
@@ -634,6 +661,9 @@ void MessagePumpForIO::ScheduleWork() {
work_scheduled_ = false; // Clarify that we didn't succeed.
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR,
MESSAGE_LOOP_PROBLEM_MAX);
+ TRACE_EVENT_INSTANT0("base",
+ "Chrome.MessageLoopProblem.COMPLETION_POST_ERROR",
+ TRACE_EVENT_SCOPE_THREAD);
}
void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {