diff options
Diffstat (limited to 'chromium/base/message_loop')
-rw-r--r-- | chromium/base/message_loop/message_loop.cc | 164 | ||||
-rw-r--r-- | chromium/base/message_loop/message_loop.h | 201 | ||||
-rw-r--r-- | chromium/base/message_loop/message_loop_current.cc | 25 | ||||
-rw-r--r-- | chromium/base/message_loop/message_loop_current.h | 57 | ||||
-rw-r--r-- | chromium/base/message_loop/message_loop_unittest.cc | 2270 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump.h | 2 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_fuchsia.cc | 2 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_glib_unittest.cc | 15 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_io_ios.cc | 2 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_io_ios_unittest.cc | 1 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_libevent.cc | 13 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_libevent_unittest.cc | 1 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_mac.h | 33 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_mac.mm | 147 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_mac_unittest.mm | 174 | ||||
-rw-r--r-- | chromium/base/message_loop/message_pump_win.cc | 90 |
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) { |