diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-12 16:35:02 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-12 16:35:02 +0000 |
commit | 3fee57b39da2e99e44d65021263d057a7b239683 (patch) | |
tree | 06f7887456a61bc27f04a2a1965925ef3713c76e /base | |
parent | 660e19c045e1cd43fe64f0adb8caf678143a461b (diff) | |
download | chromium_src-3fee57b39da2e99e44d65021263d057a7b239683.zip chromium_src-3fee57b39da2e99e44d65021263d057a7b239683.tar.gz chromium_src-3fee57b39da2e99e44d65021263d057a7b239683.tar.bz2 |
Implementation of MessagePump for Mojo.
I've refactored a bunch of the common MessagePump code into
message_pump_test. This allows us to share tests for custom
messagePumps and should allow moving MessagePumpForUI out later
on. Sadly it results in a ginormous macro. On the positive side it's
easy to use.
BUG=none
TEST=none
R=darin@chromium.org
Review URL: https://codereview.chromium.org/66193007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234540 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gyp | 13 | ||||
-rw-r--r-- | base/message_loop/message_loop.cc | 89 | ||||
-rw-r--r-- | base/message_loop/message_loop.h | 6 | ||||
-rw-r--r-- | base/message_loop/message_loop_test.cc | 1069 | ||||
-rw-r--r-- | base/message_loop/message_loop_test.h | 137 | ||||
-rw-r--r-- | base/message_loop/message_loop_unittest.cc | 1057 |
6 files changed, 1288 insertions, 1083 deletions
diff --git a/base/base.gyp b/base/base.gyp index de397d6..2f8a144 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -350,6 +350,18 @@ ], }, { + 'target_name': 'base_message_loop_tests', + 'type': 'static_library', + 'dependencies': [ + 'base', + '../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'message_loop/message_loop_test.cc', + 'message_loop/message_loop_test.h', + ], + }, + { 'target_name': 'base_prefs', 'type': '<(component)', 'variables': { @@ -676,6 +688,7 @@ 'dependencies': [ 'base', 'base_i18n', + 'base_message_loop_tests', 'base_prefs', 'base_prefs_test_support', 'base_static', diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index 6d3d59c..e035726 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -146,49 +146,7 @@ MessageLoop::MessageLoop(Type type) run_loop_(NULL) { Init(); -// TODO(rvargas): Get rid of the OS guards. -#if defined(OS_WIN) -#define MESSAGE_PUMP_UI new MessagePumpForUI() -#define MESSAGE_PUMP_IO new MessagePumpForIO() -#elif defined(OS_IOS) -#define MESSAGE_PUMP_UI MessagePumpMac::Create() -#define MESSAGE_PUMP_IO new MessagePumpIOSForIO() -#elif defined(OS_MACOSX) -#define MESSAGE_PUMP_UI MessagePumpMac::Create() -#define MESSAGE_PUMP_IO new MessagePumpLibevent() -#elif defined(OS_NACL) -// Currently NaCl doesn't have a UI MessageLoop. -// TODO(abarth): Figure out if we need this. -#define MESSAGE_PUMP_UI NULL -// ipc_channel_nacl.cc uses a worker thread to do socket reads currently, and -// doesn't require extra support for watching file descriptors. -#define MESSAGE_PUMP_IO new MessagePumpDefault() -#elif defined(OS_POSIX) // POSIX but not MACOSX. -#define MESSAGE_PUMP_UI new MessagePumpForUI() -#define MESSAGE_PUMP_IO new MessagePumpLibevent() -#else -#error Not implemented -#endif - - if (type_ == TYPE_UI) { - if (message_pump_for_ui_factory_) - pump_.reset(message_pump_for_ui_factory_()); - else - pump_.reset(MESSAGE_PUMP_UI); - } else if (type_ == TYPE_IO) { - pump_.reset(MESSAGE_PUMP_IO); -#if defined(TOOLKIT_GTK) - } else if (type_ == TYPE_GPU) { - pump_.reset(new MessagePumpX11()); -#endif -#if defined(OS_ANDROID) - } else if (type_ == TYPE_JAVA) { - pump_.reset(MESSAGE_PUMP_UI); -#endif - } else { - DCHECK_EQ(TYPE_DEFAULT, type_); - pump_.reset(new MessagePumpDefault()); - } + pump_.reset(CreateMessagePumpForType(type)); } MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump) @@ -264,6 +222,51 @@ bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) { return true; } +// static +MessagePump* MessageLoop::CreateMessagePumpForType(Type type) { +// TODO(rvargas): Get rid of the OS guards. +#if defined(OS_WIN) +#define MESSAGE_PUMP_UI new MessagePumpForUI() +#define MESSAGE_PUMP_IO new MessagePumpForIO() +#elif defined(OS_IOS) +#define MESSAGE_PUMP_UI MessagePumpMac::Create() +#define MESSAGE_PUMP_IO new MessagePumpIOSForIO() +#elif defined(OS_MACOSX) +#define MESSAGE_PUMP_UI MessagePumpMac::Create() +#define MESSAGE_PUMP_IO new MessagePumpLibevent() +#elif defined(OS_NACL) +// Currently NaCl doesn't have a UI MessageLoop. +// TODO(abarth): Figure out if we need this. +#define MESSAGE_PUMP_UI NULL +// ipc_channel_nacl.cc uses a worker thread to do socket reads currently, and +// doesn't require extra support for watching file descriptors. +#define MESSAGE_PUMP_IO new MessagePumpDefault() +#elif defined(OS_POSIX) // POSIX but not MACOSX. +#define MESSAGE_PUMP_UI new MessagePumpForUI() +#define MESSAGE_PUMP_IO new MessagePumpLibevent() +#else +#error Not implemented +#endif + + if (type == MessageLoop::TYPE_UI) { + if (message_pump_for_ui_factory_) + return message_pump_for_ui_factory_(); + return MESSAGE_PUMP_UI; + } + if (type == MessageLoop::TYPE_IO) + return MESSAGE_PUMP_IO; +#if defined(TOOLKIT_GTK) + if (type == MessageLoop::TYPE_GPU) + return new MessagePumpX11(); +#endif +#if defined(OS_ANDROID) + if (type == MessageLoop::TYPE_JAVA) + return MESSAGE_PUMP_UI; +#endif + DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type); + return new MessagePumpDefault(); +} + void MessageLoop::AddDestructionObserver( DestructionObserver* destruction_observer) { DCHECK_EQ(this, current()); diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index 84e1449..e307d36 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -163,6 +163,12 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // was successfully registered. static bool InitMessagePumpForUIFactory(MessagePumpFactory* factory); + // Creates the default MessagePump based on |type|. Caller owns return + // value. + // TODO(sky): convert this and InitMessagePumpForUIFactory() to return a + // scoped_ptr. + static MessagePump* CreateMessagePumpForType(Type type); + // A DestructionObserver is notified when the current MessageLoop is being // destroyed. These observers are notified prior to MessageLoop::current() // being changed to return NULL. This gives interested parties the chance to diff --git a/base/message_loop/message_loop_test.cc b/base/message_loop/message_loop_test.cc new file mode 100644 index 0000000..9ac5b72 --- /dev/null +++ b/base/message_loop/message_loop_test.cc @@ -0,0 +1,1069 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop/message_loop_test.h" + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/run_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" + +namespace base { +namespace test { + +namespace { + +class Foo : public RefCounted<Foo> { + public: + Foo() : test_count_(0) { + } + + void Test0() { + ++test_count_; + } + + void Test1ConstRef(const std::string& a) { + ++test_count_; + result_.append(a); + } + + void Test1Ptr(std::string* a) { + ++test_count_; + result_.append(*a); + } + + void Test1Int(int a) { + test_count_ += a; + } + + void Test2Ptr(std::string* a, std::string* b) { + ++test_count_; + result_.append(*a); + result_.append(*b); + } + + void Test2Mixed(const std::string& a, std::string* b) { + ++test_count_; + result_.append(a); + result_.append(*b); + } + + int test_count() const { return test_count_; } + const std::string& result() const { return result_; } + + private: + friend class RefCounted<Foo>; + + ~Foo() {} + + int test_count_; + std::string result_; + + DISALLOW_COPY_AND_ASSIGN(Foo); +}; + +// This function runs slowly to simulate a large amount of work being done. +void SlowFunc(TimeDelta pause, int* quit_counter) { + PlatformThread::Sleep(pause); + if (--(*quit_counter) == 0) + MessageLoop::current()->QuitWhenIdle(); +} + +// This function records the time when Run was called in a Time object, which is +// useful for building a variety of MessageLoop tests. +// TODO(sky): remove? +void RecordRunTimeFunc(Time* run_time, int* quit_counter) { + *run_time = Time::Now(); + + // Cause our Run function to take some time to execute. As a result we can + // count on subsequent RecordRunTimeFunc()s running at a future time, + // without worry about the resolution of our system clock being an issue. + SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter); +} + +} // namespace + +void RunTest_PostTask(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + // Add tests to message loop + scoped_refptr<Foo> foo(new Foo()); + std::string a("a"), b("b"), c("c"), d("d"); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test0, foo.get())); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test1ConstRef, foo.get(), a)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test1Ptr, foo.get(), &b)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test1Int, foo.get(), 100)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test2Ptr, foo.get(), &a, &c)); + + // TryPost with no contention. It must succeed. + EXPECT_TRUE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind( + &Foo::Test2Mixed, foo.get(), a, &d))); + + // TryPost with simulated contention. It must fail. We wait for a helper + // thread to lock the queue, we TryPost on this thread and finally we + // signal the helper to unlock and exit. + WaitableEvent wait(true, false); + WaitableEvent signal(true, false); + Thread thread("RunTest_PostTask_helper"); + thread.Start(); + thread.message_loop()->PostTask( + FROM_HERE, + Bind(&MessageLoop::LockWaitUnLockForTesting, + base::Unretained(MessageLoop::current()), + &wait, + &signal)); + + wait.Wait(); + EXPECT_FALSE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind( + &Foo::Test2Mixed, foo.get(), a, &d))); + signal.Signal(); + + // After all tests, post a message that will shut down the message loop + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &MessageLoop::Quit, Unretained(MessageLoop::current()))); + + // Now kick things off + MessageLoop::current()->Run(); + + EXPECT_EQ(foo->test_count(), 105); + EXPECT_EQ(foo->result(), "abacad"); +} + +void RunTest_PostTask_SEH(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // Add tests to message loop + scoped_refptr<Foo> foo(new Foo()); + std::string a("a"), b("b"), c("c"), d("d"); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test0, foo.get())); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test1ConstRef, foo.get(), a)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test1Ptr, foo.get(), &b)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test1Int, foo.get(), 100)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test2Ptr, foo.get(), &a, &c)); + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &Foo::Test2Mixed, foo.get(), a, &d)); + + // After all tests, post a message that will shut down the message loop + MessageLoop::current()->PostTask(FROM_HERE, Bind( + &MessageLoop::Quit, Unretained(MessageLoop::current()))); + + // Now kick things off with the SEH block active. + MessageLoop::current()->set_exception_restoration(true); + MessageLoop::current()->Run(); + MessageLoop::current()->set_exception_restoration(false); + + EXPECT_EQ(foo->test_count(), 105); + EXPECT_EQ(foo->result(), "abacad"); +} + +void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // Test that PostDelayedTask results in a delayed task. + + const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); + + int num_tasks = 1; + Time run_time; + + loop.PostDelayedTask( + FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks), + kDelay); + + Time time_before_run = Time::Now(); + loop.Run(); + Time time_after_run = Time::Now(); + + EXPECT_EQ(0, num_tasks); + EXPECT_LT(kDelay, time_after_run - time_before_run); +} + +void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // Test that two tasks with different delays run in the right order. + int num_tasks = 2; + Time run_time1, run_time2; + + loop.PostDelayedTask( + FROM_HERE, + Bind(&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.PostDelayedTask( + FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), + TimeDelta::FromMilliseconds(10)); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time2 < run_time1); +} + +void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // 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; + Time run_time1, run_time2; + + loop.PostDelayedTask( + FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay); + loop.PostDelayedTask( + FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time1 < run_time2); +} + +void RunTest_PostDelayedTask_InPostOrder_2(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // 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; + Time run_time; + + loop.PostTask(FROM_HERE, Bind(&SlowFunc, kPause, &num_tasks)); + loop.PostDelayedTask( + FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time, &num_tasks), + TimeDelta::FromMilliseconds(10)); + + Time time_before_run = Time::Now(); + loop.Run(); + Time time_after_run = Time::Now(); + + EXPECT_EQ(0, num_tasks); + + EXPECT_LT(kPause, time_after_run - time_before_run); +} + +void RunTest_PostDelayedTask_InPostOrder_3(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // 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; + Time run_time1, run_time2; + + // Clutter the ML with tasks. + for (int i = 1; i < num_tasks; ++i) + loop.PostTask(FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time1, &num_tasks)); + + loop.PostDelayedTask( + FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), + TimeDelta::FromMilliseconds(1)); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time2 > run_time1); +} + +void RunTest_PostDelayedTask_SharedTimer(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + // 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; + Time run_time1, run_time2; + + loop.PostDelayedTask( + FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), + TimeDelta::FromSeconds(1000)); + loop.PostDelayedTask( + FROM_HERE, + Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), + TimeDelta::FromMilliseconds(10)); + + Time start_time = Time::Now(); + + loop.Run(); + EXPECT_EQ(0, 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_time1.is_null()); + EXPECT_FALSE(run_time2.is_null()); +} + +// This is used to inject a test point for recording the destructor calls for +// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we +// are trying to hook the actual destruction, which is not a common operation. +class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> { + public: + RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted) + : post_on_delete_(post_on_delete), was_deleted_(was_deleted) { + } + void Run() {} + + private: + friend class RefCounted<RecordDeletionProbe>; + + ~RecordDeletionProbe() { + *was_deleted_ = true; + if (post_on_delete_.get()) + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_.get())); + } + + scoped_refptr<RecordDeletionProbe> post_on_delete_; + bool* was_deleted_; +}; + +void RunTest_EnsureDeletion(MessagePumpFactory factory) { + bool a_was_deleted = false; + bool b_was_deleted = false; + { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + loop.PostTask( + FROM_HERE, Bind(&RecordDeletionProbe::Run, + new RecordDeletionProbe(NULL, &a_was_deleted))); + // TODO(ajwong): Do we really need 1000ms here? + loop.PostDelayedTask( + FROM_HERE, Bind(&RecordDeletionProbe::Run, + new RecordDeletionProbe(NULL, &b_was_deleted)), + TimeDelta::FromMilliseconds(1000)); + } + EXPECT_TRUE(a_was_deleted); + EXPECT_TRUE(b_was_deleted); +} + +void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory) { + bool a_was_deleted = false; + bool b_was_deleted = false; + bool c_was_deleted = false; + { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + // The scoped_refptr for each of the below is held either by the chained + // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback. + RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted); + RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted); + RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted); + loop.PostTask(FROM_HERE, Bind(&RecordDeletionProbe::Run, c)); + } + EXPECT_TRUE(a_was_deleted); + EXPECT_TRUE(b_was_deleted); + EXPECT_TRUE(c_was_deleted); +} + +void NestingFunc(int* depth) { + if (*depth > 0) { + *depth -= 1; + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&NestingFunc, depth)); + + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); + } + MessageLoop::current()->QuitWhenIdle(); +} + +void RunTest_Nesting(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + int depth = 100; + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&NestingFunc, &depth)); + MessageLoop::current()->Run(); + EXPECT_EQ(depth, 0); +} + +enum TaskType { + MESSAGEBOX, + ENDDIALOG, + RECURSIVE, + TIMEDMESSAGELOOP, + QUITMESSAGELOOP, + ORDERED, + PUMPS, + SLEEP, + RUNS, +}; + +struct TaskItem { + TaskItem(TaskType t, int c, bool s) + : type(t), + cookie(c), + start(s) { + } + + TaskType type; + int cookie; + bool start; + + bool operator == (const TaskItem& other) const { + return type == other.type && cookie == other.cookie && start == other.start; + } +}; + +std::ostream& operator <<(std::ostream& os, TaskType type) { + switch (type) { + case MESSAGEBOX: os << "MESSAGEBOX"; break; + case ENDDIALOG: os << "ENDDIALOG"; break; + case RECURSIVE: os << "RECURSIVE"; break; + case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break; + case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break; + case ORDERED: os << "ORDERED"; break; + case PUMPS: os << "PUMPS"; break; + case SLEEP: os << "SLEEP"; break; + default: + NOTREACHED(); + os << "Unknown TaskType"; + break; + } + return os; +} + +std::ostream& operator <<(std::ostream& os, const TaskItem& item) { + if (item.start) + return os << item.type << " " << item.cookie << " starts"; + else + return os << item.type << " " << item.cookie << " ends"; +} + +class TaskList { + public: + void RecordStart(TaskType type, int cookie) { + TaskItem item(type, cookie, true); + DVLOG(1) << item; + task_list_.push_back(item); + } + + void RecordEnd(TaskType type, int cookie) { + TaskItem item(type, cookie, false); + DVLOG(1) << item; + task_list_.push_back(item); + } + + size_t Size() { + return task_list_.size(); + } + + TaskItem Get(int n) { + return task_list_[n]; + } + + private: + std::vector<TaskItem> task_list_; +}; + +void RecursiveFunc(TaskList* order, int cookie, int depth, + bool is_reentrant) { + order->RecordStart(RECURSIVE, cookie); + if (depth > 0) { + if (is_reentrant) + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant)); + } + order->RecordEnd(RECURSIVE, cookie); +} + +void QuitFunc(TaskList* order, int cookie) { + order->RecordStart(QUITMESSAGELOOP, cookie); + MessageLoop::current()->QuitWhenIdle(); + order->RecordEnd(QUITMESSAGELOOP, cookie); +} +void RunTest_RecursiveDenial1(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); + TaskList order; + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&RecursiveFunc, &order, 1, 2, false)); + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&RecursiveFunc, &order, 2, 2, false)); + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&QuitFunc, &order, 3)); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(14U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); + EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false)); +} + +void RecursiveSlowFunc(TaskList* order, int cookie, int depth, + bool is_reentrant) { + RecursiveFunc(order, cookie, depth, is_reentrant); + PlatformThread::Sleep(TimeDelta::FromMilliseconds(10)); +} + +void OrderedFunc(TaskList* order, int cookie) { + order->RecordStart(ORDERED, cookie); + order->RecordEnd(ORDERED, cookie); +} + +void RunTest_RecursiveDenial3(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); + TaskList order; + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&RecursiveSlowFunc, &order, 1, 2, false)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&RecursiveSlowFunc, &order, 2, 2, false)); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + Bind(&OrderedFunc, &order, 3), + TimeDelta::FromMilliseconds(5)); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + Bind(&QuitFunc, &order, 4), + TimeDelta::FromMilliseconds(5)); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(16U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true)); + EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false)); + EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false)); +} + +void RunTest_RecursiveSupport1(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, true)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, true)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&QuitFunc, &order, 3)); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(14U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); + EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false)); +} + +// Tests that non nestable tasks run in FIFO if there are no nested loops. +void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + MessageLoop::current()->PostNonNestableTask( + FROM_HERE, + Bind(&OrderedFunc, &order, 1)); + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&QuitFunc, &order, 3)); + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(6U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); +} + +void FuncThatPumps(TaskList* order, int cookie) { + order->RecordStart(PUMPS, cookie); + { + MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); + RunLoop().RunUntilIdle(); + } + order->RecordEnd(PUMPS, cookie); +} + +void SleepFunc(TaskList* order, int cookie, TimeDelta delay) { + order->RecordStart(SLEEP, cookie); + PlatformThread::Sleep(delay); + order->RecordEnd(SLEEP, cookie); +} + +// Tests that non nestable tasks don't run when there's code in the call stack. +void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory, + bool use_delayed) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&FuncThatPumps, &order, 1)); + if (use_delayed) { + MessageLoop::current()->PostNonNestableDelayedTask( + FROM_HERE, + Bind(&OrderedFunc, &order, 2), + TimeDelta::FromMilliseconds(1)); + } else { + MessageLoop::current()->PostNonNestableTask( + FROM_HERE, + Bind(&OrderedFunc, &order, 2)); + } + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&OrderedFunc, &order, 3)); + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50))); + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&OrderedFunc, &order, 5)); + if (use_delayed) { + MessageLoop::current()->PostNonNestableDelayedTask( + FROM_HERE, + Bind(&QuitFunc, &order, 6), + TimeDelta::FromMilliseconds(2)); + } else { + MessageLoop::current()->PostNonNestableTask( + FROM_HERE, + Bind(&QuitFunc, &order, 6)); + } + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(12U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true)); + EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false)); + EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true)); + EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false)); + EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false)); + EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true)); + EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false)); +} + +void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) { + order->RecordStart(RUNS, cookie); + { + MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); + run_loop->Run(); + } + order->RecordEnd(RUNS, cookie); +} + +void FuncThatQuitsNow() { + MessageLoop::current()->QuitNow(); +} +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +void RunTest_QuitNow(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop run_loop; + + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop))); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&FuncThatQuitsNow)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 3)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&FuncThatQuitsNow)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 4)); // never runs + + MessageLoop::current()->Run(); + + ASSERT_EQ(6U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +void RunTest_RunLoopQuitTop(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop; + + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + MessageLoop::current()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); + + outer_run_loop.Run(); + + ASSERT_EQ(4U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +void RunTest_RunLoopQuitNested(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop; + + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + MessageLoop::current()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + + outer_run_loop.Run(); + + ASSERT_EQ(4U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +void RunTest_RunLoopQuitBogus(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_run_loop; + RunLoop bogus_run_loop; + + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); + MessageLoop::current()->PostTask( + FROM_HERE, bogus_run_loop.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); + + outer_run_loop.Run(); + + ASSERT_EQ(4U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. +void RunTest_RunLoopQuitDeep(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop outer_run_loop; + RunLoop nested_loop1; + RunLoop nested_loop2; + RunLoop nested_loop3; + RunLoop nested_loop4; + + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 1, Unretained(&nested_loop1))); + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 2, Unretained(&nested_loop2))); + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 3, Unretained(&nested_loop3))); + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 4, Unretained(&nested_loop4))); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 5)); + MessageLoop::current()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 6)); + MessageLoop::current()->PostTask( + FROM_HERE, nested_loop1.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 7)); + MessageLoop::current()->PostTask( + FROM_HERE, nested_loop2.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 8)); + MessageLoop::current()->PostTask( + FROM_HERE, nested_loop3.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 9)); + MessageLoop::current()->PostTask( + FROM_HERE, nested_loop4.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 10)); + + outer_run_loop.Run(); + + ASSERT_EQ(18U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit works before RunWithID. +void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop run_loop; + + run_loop.Quit(); + + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 1)); // never runs + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs + + run_loop.Run(); + + ASSERT_EQ(0U, order.Size()); +} + +// Tests RunLoopQuit works during RunWithID. +void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop run_loop; + + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 1)); + MessageLoop::current()->PostTask( + FROM_HERE, run_loop.QuitClosure()); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 2)); // never runs + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs + + run_loop.Run(); + + ASSERT_EQ(2U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +// Tests RunLoopQuit works after RunWithID. +void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory) { + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + + TaskList order; + + RunLoop run_loop; + + MessageLoop::current()->PostTask(FROM_HERE, + Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop))); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&FuncThatQuitsNow)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 3)); + MessageLoop::current()->PostTask( + FROM_HERE, run_loop.QuitClosure()); // has no affect + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&OrderedFunc, &order, 4)); + MessageLoop::current()->PostTask( + FROM_HERE, Bind(&FuncThatQuitsNow)); + + RunLoop outer_run_loop; + outer_run_loop.Run(); + + ASSERT_EQ(8U, order.Size()); + int task_index = 0; + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true)); + EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false)); + EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); +} + +void PostNTasksThenQuit(int posts_remaining) { + if (posts_remaining > 1) { + MessageLoop::current()->PostTask( + FROM_HERE, + Bind(&PostNTasksThenQuit, posts_remaining - 1)); + } else { + MessageLoop::current()->QuitWhenIdle(); + } +} + +// There was a bug in the MessagePumpGLib where posting tasks recursively +// caused the message loop to hang, due to the buffer of the internal pipe +// becoming full. Test all MessageLoop types to ensure this issue does not +// exist in other MessagePumps. +// +// On Linux, the pipe buffer size is 64KiB by default. The bug caused one +// byte accumulated in the pipe per two posts, so we should repeat 128K +// times to reproduce the bug. +void RunTest_RecursivePosts(MessagePumpFactory factory) { + const int kNumTimes = 1 << 17; + scoped_ptr<MessagePump> pump(factory()); + MessageLoop loop(pump.Pass()); + loop.PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumTimes)); + loop.Run(); +} + +} // namespace test +} // namespace base diff --git a/base/message_loop/message_loop_test.h b/base/message_loop/message_loop_test.h new file mode 100644 index 0000000..5d1a4f5 --- /dev/null +++ b/base/message_loop/message_loop_test.h @@ -0,0 +1,137 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_ +#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_ + +#include "base/message_loop/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +// This file consists of tests meant to exercise the combination of MessageLoop +// and MessagePump. To use these define the macro RUN_MESSAGE_LOOP_TESTS using +// an ID appropriate for your MessagePump, eg +// RUN_MESSAGE_LOOP_TESTS(UI, factory). Factory is a function called to create +// the MessagePump. +namespace base { +namespace test { + +typedef MessageLoop::MessagePumpFactory MessagePumpFactory; + +void RunTest_PostTask(MessagePumpFactory factory); +void RunTest_PostTask_SEH(MessagePumpFactory factory); +void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory); +void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory); +void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory); +void RunTest_PostDelayedTask_InPostOrder_2(MessagePumpFactory factory); +void RunTest_PostDelayedTask_InPostOrder_3(MessagePumpFactory factory); +void RunTest_PostDelayedTask_SharedTimer(MessagePumpFactory factory); +void RunTest_EnsureDeletion(MessagePumpFactory factory); +void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory); +void RunTest_Nesting(MessagePumpFactory factory); +void RunTest_RecursiveDenial1(MessagePumpFactory factory); +void RunTest_RecursiveDenial3(MessagePumpFactory factory); +void RunTest_RecursiveSupport1(MessagePumpFactory factory); +void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory); +void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory, + bool use_delayed); +void RunTest_QuitNow(MessagePumpFactory factory); +void RunTest_RunLoopQuitTop(MessagePumpFactory factory); +void RunTest_RunLoopQuitNested(MessagePumpFactory factory); +void RunTest_RunLoopQuitBogus(MessagePumpFactory factory); +void RunTest_RunLoopQuitDeep(MessagePumpFactory factory); +void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory); +void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory); +void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory); +void RunTest_RecursivePosts(MessagePumpFactory factory); + +} // namespace test +} // namespace base + +#define RUN_MESSAGE_LOOP_TESTS(id, factory) \ + TEST(MessageLoopTestType##id, PostTask) { \ + base::test::RunTest_PostTask(factory); \ + } \ + TEST(MessageLoopTestType##id, PostTask_SEH) { \ + base::test::RunTest_PostTask_SEH(factory); \ + } \ + TEST(MessageLoopTestType##id, PostDelayedTask_Basic) { \ + base::test::RunTest_PostDelayedTask_Basic(factory); \ + } \ + TEST(MessageLoopTestType##id, PostDelayedTask_InDelayOrder) { \ + base::test::RunTest_PostDelayedTask_InDelayOrder(factory); \ + } \ + TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder) { \ + base::test::RunTest_PostDelayedTask_InPostOrder(factory); \ + } \ + TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder_2) { \ + base::test::RunTest_PostDelayedTask_InPostOrder_2(factory); \ + } \ + TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder_3) { \ + base::test::RunTest_PostDelayedTask_InPostOrder_3(factory); \ + } \ + TEST(MessageLoopTestType##id, PostDelayedTask_SharedTimer) { \ + base::test::RunTest_PostDelayedTask_SharedTimer(factory); \ + } \ + /* TODO(darin): MessageLoop does not support deleting all tasks in the */ \ + /* destructor. */ \ + /* Fails, http://crbug.com/50272. */ \ + TEST(MessageLoopTestType##id, DISABLED_EnsureDeletion) { \ + base::test::RunTest_EnsureDeletion(factory); \ + } \ + /* TODO(darin): MessageLoop does not support deleting all tasks in the */ \ + /* destructor. */ \ + /* Fails, http://crbug.com/50272. */ \ + TEST(MessageLoopTestType##id, DISABLED_EnsureDeletion_Chain) { \ + base::test::RunTest_EnsureDeletion_Chain(factory); \ + } \ + TEST(MessageLoopTestType##id, Nesting) { \ + base::test::RunTest_Nesting(factory); \ + } \ + TEST(MessageLoopTestType##id, RecursiveDenial1) { \ + base::test::RunTest_RecursiveDenial1(factory); \ + } \ + TEST(MessageLoopTestType##id, RecursiveDenial3) { \ + base::test::RunTest_RecursiveDenial3(factory); \ + } \ + TEST(MessageLoopTestType##id, RecursiveSupport1) { \ + base::test::RunTest_RecursiveSupport1(factory); \ + } \ + TEST(MessageLoopTestType##id, NonNestableWithNoNesting) { \ + base::test::RunTest_NonNestableWithNoNesting(factory); \ + } \ + TEST(MessageLoopTestType##id, NonNestableInNestedLoop) { \ + base::test::RunTest_NonNestableInNestedLoop(factory, false); \ + } \ + TEST(MessageLoopTestType##id, NonNestableDelayedInNestedLoop) { \ + base::test::RunTest_NonNestableInNestedLoop(factory, true); \ + } \ + TEST(MessageLoopTestType##id, QuitNow) { \ + base::test::RunTest_QuitNow(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitTop) { \ + base::test::RunTest_RunLoopQuitTop(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitNested) { \ + base::test::RunTest_RunLoopQuitNested(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitBogus) { \ + base::test::RunTest_RunLoopQuitBogus(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitDeep) { \ + base::test::RunTest_RunLoopQuitDeep(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitOrderBefore) { \ + base::test::RunTest_RunLoopQuitOrderBefore(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitOrderDuring) { \ + base::test::RunTest_RunLoopQuitOrderDuring(factory); \ + } \ + TEST(MessageLoopTestType##id, RunLoopQuitOrderAfter) { \ + base::test::RunTest_RunLoopQuitOrderAfter(factory); \ + } \ + TEST(MessageLoopTestType##id, RecursivePosts) { \ + base::test::RunTest_RecursivePosts(factory); \ + } \ + +#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_ diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc index bd464fa..fe2d728 100644 --- a/base/message_loop/message_loop_unittest.cc +++ b/base/message_loop/message_loop_unittest.cc @@ -11,6 +11,7 @@ #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy_impl.h" +#include "base/message_loop/message_loop_test.h" #include "base/pending_task.h" #include "base/posix/eintr_wrapper.h" #include "base/run_loop.h" @@ -32,6 +33,18 @@ namespace base { namespace { +MessagePump* TypeDefaultMessagePumpFactory() { + return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT); +} + +MessagePump* TypeIOMessagePumpFactory() { + return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_IO); +} + +MessagePump* TypeUIMessagePumpFactory() { + return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_UI); +} + class Foo : public RefCounted<Foo> { public: Foo() : test_count_(0) { @@ -79,88 +92,7 @@ class Foo : public RefCounted<Foo> { std::string result_; }; -void RunTest_PostTask(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // Add tests to message loop - scoped_refptr<Foo> foo(new Foo()); - std::string a("a"), b("b"), c("c"), d("d"); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test0, foo.get())); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1ConstRef, foo.get(), a)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1Ptr, foo.get(), &b)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1Int, foo.get(), 100)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test2Ptr, foo.get(), &a, &c)); - - // TryPost with no contention. It must succeed. - EXPECT_TRUE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind( - &Foo::Test2Mixed, foo.get(), a, &d))); - - // TryPost with simulated contention. It must fail. We wait for a helper - // thread to lock the queue, we TryPost on this thread and finally we - // signal the helper to unlock and exit. - WaitableEvent wait(true, false); - WaitableEvent signal(true, false); - Thread thread("RunTest_PostTask_helper"); - thread.Start(); - thread.message_loop()->PostTask( - FROM_HERE, - Bind(&MessageLoop::LockWaitUnLockForTesting, - base::Unretained(MessageLoop::current()), - &wait, - &signal)); - - wait.Wait(); - EXPECT_FALSE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind( - &Foo::Test2Mixed, foo.get(), a, &d))); - signal.Signal(); - - // After all tests, post a message that will shut down the message loop - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &MessageLoop::Quit, Unretained(MessageLoop::current()))); - - // Now kick things off - MessageLoop::current()->Run(); - - EXPECT_EQ(foo->test_count(), 105); - EXPECT_EQ(foo->result(), "abacad"); -} - -void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // Add tests to message loop - scoped_refptr<Foo> foo(new Foo()); - std::string a("a"), b("b"), c("c"), d("d"); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test0, foo.get())); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1ConstRef, foo.get(), a)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1Ptr, foo.get(), &b)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test1Int, foo.get(), 100)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test2Ptr, foo.get(), &a, &c)); - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &Foo::Test2Mixed, foo.get(), a, &d)); - - // After all tests, post a message that will shut down the message loop - MessageLoop::current()->PostTask(FROM_HERE, Bind( - &MessageLoop::Quit, Unretained(MessageLoop::current()))); - - // Now kick things off with the SEH block active. - MessageLoop::current()->set_exception_restoration(true); - MessageLoop::current()->Run(); - MessageLoop::current()->set_exception_restoration(false); - - EXPECT_EQ(foo->test_count(), 105); - EXPECT_EQ(foo->result(), "abacad"); -} +#if defined(OS_WIN) // This function runs slowly to simulate a large amount of work being done. static void SlowFunc(TimeDelta pause, int* quit_counter) { @@ -180,180 +112,6 @@ static void RecordRunTimeFunc(Time* run_time, int* quit_counter) { SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter); } -void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // Test that PostDelayedTask results in a delayed task. - - const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); - - int num_tasks = 1; - Time run_time; - - loop.PostDelayedTask( - FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks), - kDelay); - - Time time_before_run = Time::Now(); - loop.Run(); - Time time_after_run = Time::Now(); - - EXPECT_EQ(0, num_tasks); - EXPECT_LT(kDelay, time_after_run - time_before_run); -} - -void RunTest_PostDelayedTask_InDelayOrder( - MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // Test that two tasks with different delays run in the right order. - int num_tasks = 2; - Time run_time1, run_time2; - - loop.PostDelayedTask( - FROM_HERE, - Bind(&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.PostDelayedTask( - FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), - TimeDelta::FromMilliseconds(10)); - - loop.Run(); - EXPECT_EQ(0, num_tasks); - - EXPECT_TRUE(run_time2 < run_time1); -} - -void RunTest_PostDelayedTask_InPostOrder( - MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // 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; - Time run_time1, run_time2; - - loop.PostDelayedTask( - FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay); - loop.PostDelayedTask( - FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay); - - loop.Run(); - EXPECT_EQ(0, num_tasks); - - EXPECT_TRUE(run_time1 < run_time2); -} - -void RunTest_PostDelayedTask_InPostOrder_2( - MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // 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; - Time run_time; - - loop.PostTask(FROM_HERE, Bind(&SlowFunc, kPause, &num_tasks)); - loop.PostDelayedTask( - FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time, &num_tasks), - TimeDelta::FromMilliseconds(10)); - - Time time_before_run = Time::Now(); - loop.Run(); - Time time_after_run = Time::Now(); - - EXPECT_EQ(0, num_tasks); - - EXPECT_LT(kPause, time_after_run - time_before_run); -} - -void RunTest_PostDelayedTask_InPostOrder_3( - MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // 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; - Time run_time1, run_time2; - - // Clutter the ML with tasks. - for (int i = 1; i < num_tasks; ++i) - loop.PostTask(FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time1, &num_tasks)); - - loop.PostDelayedTask( - FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), - TimeDelta::FromMilliseconds(1)); - - loop.Run(); - EXPECT_EQ(0, num_tasks); - - EXPECT_TRUE(run_time2 > run_time1); -} - -void RunTest_PostDelayedTask_SharedTimer( - MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - // 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; - Time run_time1, run_time2; - - loop.PostDelayedTask( - FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), - TimeDelta::FromSeconds(1000)); - loop.PostDelayedTask( - FROM_HERE, - Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), - TimeDelta::FromMilliseconds(10)); - - Time start_time = Time::Now(); - - loop.Run(); - EXPECT_EQ(0, 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_time1.is_null()); - EXPECT_FALSE(run_time2.is_null()); -} - -#if defined(OS_WIN) - void SubPumpFunc() { MessageLoop::current()->SetNestableTasksAllowed(true); MSG msg; @@ -407,82 +165,6 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() { EXPECT_TRUE(run_time.is_null()); } -#endif // defined(OS_WIN) - -// 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()) - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_.get())); - } - - scoped_refptr<RecordDeletionProbe> post_on_delete_; - bool* was_deleted_; -}; - -void RunTest_EnsureDeletion(MessageLoop::Type message_loop_type) { - bool a_was_deleted = false; - bool b_was_deleted = false; - { - MessageLoop loop(message_loop_type); - loop.PostTask( - FROM_HERE, Bind(&RecordDeletionProbe::Run, - new RecordDeletionProbe(NULL, &a_was_deleted))); - // TODO(ajwong): Do we really need 1000ms here? - loop.PostDelayedTask( - FROM_HERE, Bind(&RecordDeletionProbe::Run, - new RecordDeletionProbe(NULL, &b_was_deleted)), - TimeDelta::FromMilliseconds(1000)); - } - EXPECT_TRUE(a_was_deleted); - EXPECT_TRUE(b_was_deleted); -} - -void RunTest_EnsureDeletion_Chain(MessageLoop::Type message_loop_type) { - bool a_was_deleted = false; - bool b_was_deleted = false; - bool c_was_deleted = false; - { - MessageLoop loop(message_loop_type); - // The scoped_refptr for each of the below is held either by the chained - // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback. - RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted); - RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted); - RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted); - loop.PostTask(FROM_HERE, Bind(&RecordDeletionProbe::Run, c)); - } - EXPECT_TRUE(a_was_deleted); - EXPECT_TRUE(b_was_deleted); - EXPECT_TRUE(c_was_deleted); -} - -void NestingFunc(int* depth) { - if (*depth > 0) { - *depth -= 1; - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&NestingFunc, depth)); - - MessageLoop::current()->SetNestableTasksAllowed(true); - MessageLoop::current()->Run(); - } - MessageLoop::current()->QuitWhenIdle(); -} - -#if defined(OS_WIN) - LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) { ADD_FAILURE() << "bad exception handler"; ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode); @@ -597,21 +279,7 @@ void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) { ::SetUnhandledExceptionFilter(old_SEH_filter); } -#endif // defined(OS_WIN) - -void RunTest_Nesting(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - int depth = 100; - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&NestingFunc, &depth)); - MessageLoop::current()->Run(); - EXPECT_EQ(depth, 0); -} - -#if defined(OS_WIN) const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test"; -#endif // defined(OS_WIN) enum TaskType { MESSAGEBOX, @@ -693,14 +361,6 @@ class TaskList { std::vector<TaskItem> task_list_; }; -// Saves the order the tasks ran. -void OrderedFunc(TaskList* order, int cookie) { - order->RecordStart(ORDERED, cookie); - order->RecordEnd(ORDERED, cookie); -} - -#if defined(OS_WIN) - // MessageLoop implicitly start a "modal message loop". Modal dialog boxes, // common controls (like OpenFile) and StartDoc printing function can cause // implicit message loops. @@ -724,8 +384,6 @@ void EndDialogFunc(TaskList* order, int cookie) { } } -#endif // defined(OS_WIN) - void RecursiveFunc(TaskList* order, int cookie, int depth, bool is_reentrant) { order->RecordStart(RECURSIVE, cookie); @@ -739,25 +397,12 @@ void RecursiveFunc(TaskList* order, int cookie, int depth, order->RecordEnd(RECURSIVE, cookie); } -void RecursiveSlowFunc(TaskList* order, int cookie, int depth, - bool is_reentrant) { - RecursiveFunc(order, cookie, depth, is_reentrant); - PlatformThread::Sleep(TimeDelta::FromMilliseconds(10)); -} - void QuitFunc(TaskList* order, int cookie) { order->RecordStart(QUITMESSAGELOOP, cookie); MessageLoop::current()->QuitWhenIdle(); order->RecordEnd(QUITMESSAGELOOP, cookie); } -void SleepFunc(TaskList* order, int cookie, TimeDelta delay) { - order->RecordStart(SLEEP, cookie); - PlatformThread::Sleep(delay); - order->RecordEnd(SLEEP, cookie); -} - -#if defined(OS_WIN) void RecursiveFuncWin(MessageLoop* target, HANDLE event, bool expect_window, @@ -803,115 +448,6 @@ void RecursiveFuncWin(MessageLoop* target, } } -#endif // defined(OS_WIN) - -void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); - TaskList order; - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&RecursiveFunc, &order, 1, 2, false)); - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&RecursiveFunc, &order, 2, 2, false)); - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&QuitFunc, &order, 3)); - - MessageLoop::current()->Run(); - - // FIFO order. - ASSERT_EQ(14U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); - EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false)); -} - -void RunTest_RecursiveDenial3(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); - TaskList order; - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&RecursiveSlowFunc, &order, 1, 2, false)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&RecursiveSlowFunc, &order, 2, 2, false)); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - Bind(&OrderedFunc, &order, 3), - TimeDelta::FromMilliseconds(5)); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - Bind(&QuitFunc, &order, 4), - TimeDelta::FromMilliseconds(5)); - - MessageLoop::current()->Run(); - - // FIFO order. - ASSERT_EQ(16U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true)); - EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false)); - EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true)); - EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false)); - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false)); -} - -void RunTest_RecursiveSupport1(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, true)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, true)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&QuitFunc, &order, 3)); - - MessageLoop::current()->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)); -} - -#if defined(OS_WIN) // TODO(darin): These tests need to be ported since they test critical // message loop functionality. @@ -1009,387 +545,6 @@ void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) { #endif // defined(OS_WIN) -void FuncThatPumps(TaskList* order, int cookie) { - order->RecordStart(PUMPS, cookie); - { - MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); - RunLoop().RunUntilIdle(); - } - order->RecordEnd(PUMPS, cookie); -} - -void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) { - order->RecordStart(RUNS, cookie); - { - MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); - run_loop->Run(); - } - order->RecordEnd(RUNS, cookie); -} - -void FuncThatQuitsNow() { - MessageLoop::current()->QuitNow(); -} - -// Tests that non nestable tasks run in FIFO if there are no nested loops. -void RunTest_NonNestableWithNoNesting( - MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - MessageLoop::current()->PostNonNestableTask( - FROM_HERE, - Bind(&OrderedFunc, &order, 1)); - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&OrderedFunc, &order, 2)); - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&QuitFunc, &order, 3)); - MessageLoop::current()->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)); -} - -// Tests that non nestable tasks don't run when there's code in the call stack. -void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type, - bool use_delayed) { - MessageLoop loop(message_loop_type); - - TaskList order; - - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&FuncThatPumps, &order, 1)); - if (use_delayed) { - MessageLoop::current()->PostNonNestableDelayedTask( - FROM_HERE, - Bind(&OrderedFunc, &order, 2), - TimeDelta::FromMilliseconds(1)); - } else { - MessageLoop::current()->PostNonNestableTask( - FROM_HERE, - Bind(&OrderedFunc, &order, 2)); - } - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&OrderedFunc, &order, 3)); - MessageLoop::current()->PostTask( - FROM_HERE, - Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50))); - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&OrderedFunc, &order, 5)); - if (use_delayed) { - MessageLoop::current()->PostNonNestableDelayedTask( - FROM_HERE, - Bind(&QuitFunc, &order, 6), - TimeDelta::FromMilliseconds(2)); - } else { - MessageLoop::current()->PostNonNestableTask( - FROM_HERE, - Bind(&QuitFunc, &order, 6)); - } - - MessageLoop::current()->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)); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -void RunTest_QuitNow(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop run_loop; - - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop))); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 2)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&FuncThatQuitsNow)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 3)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&FuncThatQuitsNow)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 4)); // never runs - - MessageLoop::current()->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 works before RunWithID. -void RunTest_RunLoopQuitOrderBefore(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop run_loop; - - run_loop.Quit(); - - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 1)); // never runs - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs - - run_loop.Run(); - - ASSERT_EQ(0U, order.Size()); -} - -// Tests RunLoopQuit works during RunWithID. -void RunTest_RunLoopQuitOrderDuring(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop run_loop; - - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 1)); - MessageLoop::current()->PostTask( - FROM_HERE, run_loop.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 2)); // never runs - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs - - run_loop.Run(); - - ASSERT_EQ(2U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false)); - EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); -} - -// Tests RunLoopQuit works after RunWithID. -void RunTest_RunLoopQuitOrderAfter(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop run_loop; - - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop))); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 2)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&FuncThatQuitsNow)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 3)); - MessageLoop::current()->PostTask( - FROM_HERE, run_loop.QuitClosure()); // has no affect - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 4)); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&FuncThatQuitsNow)); - - RunLoop outer_run_loop; - outer_run_loop.Run(); - - ASSERT_EQ(8U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false)); - EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -void RunTest_RunLoopQuitTop(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - MessageLoop::current()->PostTask( - FROM_HERE, outer_run_loop.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 2)); - MessageLoop::current()->PostTask( - FROM_HERE, nested_run_loop.QuitClosure()); - - outer_run_loop.Run(); - - ASSERT_EQ(4U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -void RunTest_RunLoopQuitNested(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - MessageLoop::current()->PostTask( - FROM_HERE, nested_run_loop.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 2)); - MessageLoop::current()->PostTask( - FROM_HERE, outer_run_loop.QuitClosure()); - - outer_run_loop.Run(); - - ASSERT_EQ(4U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -void RunTest_RunLoopQuitBogus(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - RunLoop bogus_run_loop; - - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - MessageLoop::current()->PostTask( - FROM_HERE, bogus_run_loop.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 2)); - MessageLoop::current()->PostTask( - FROM_HERE, outer_run_loop.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, nested_run_loop.QuitClosure()); - - outer_run_loop.Run(); - - ASSERT_EQ(4U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(static_cast<size_t>(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -void RunTest_RunLoopQuitDeep(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_loop1; - RunLoop nested_loop2; - RunLoop nested_loop3; - RunLoop nested_loop4; - - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 1, Unretained(&nested_loop1))); - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 2, Unretained(&nested_loop2))); - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 3, Unretained(&nested_loop3))); - MessageLoop::current()->PostTask(FROM_HERE, - Bind(&FuncThatRuns, &order, 4, Unretained(&nested_loop4))); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 5)); - MessageLoop::current()->PostTask( - FROM_HERE, outer_run_loop.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 6)); - MessageLoop::current()->PostTask( - FROM_HERE, nested_loop1.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 7)); - MessageLoop::current()->PostTask( - FROM_HERE, nested_loop2.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 8)); - MessageLoop::current()->PostTask( - FROM_HERE, nested_loop3.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&OrderedFunc, &order, 9)); - MessageLoop::current()->PostTask( - FROM_HERE, nested_loop4.QuitClosure()); - MessageLoop::current()->PostTask( - FROM_HERE, Bind(&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()); -} - void PostNTasksThenQuit(int posts_remaining) { if (posts_remaining > 1) { MessageLoop::current()->PostTask( @@ -1400,13 +555,6 @@ void PostNTasksThenQuit(int posts_remaining) { } } -void RunTest_RecursivePosts(MessageLoop::Type message_loop_type, - int num_times) { - MessageLoop loop(message_loop_type); - loop.PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, num_times)); - loop.Run(); -} - #if defined(OS_WIN) class DispatcherImpl : public MessageLoopForUI::Dispatcher { @@ -1622,79 +770,15 @@ void RunTest_WaitForIO() { // that message loops work properly in all configurations. Of course, in some // cases, a unit test may only be for a particular type of loop. -TEST(MessageLoopTest, PostTask) { - RunTest_PostTask(MessageLoop::TYPE_DEFAULT); - RunTest_PostTask(MessageLoop::TYPE_UI); - RunTest_PostTask(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostTask_SEH) { - RunTest_PostTask_SEH(MessageLoop::TYPE_DEFAULT); - RunTest_PostTask_SEH(MessageLoop::TYPE_UI); - RunTest_PostTask_SEH(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostDelayedTask_Basic) { - RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_DEFAULT); - RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_UI); - RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostDelayedTask_InDelayOrder) { - RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_DEFAULT); - RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_UI); - RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostDelayedTask_InPostOrder) { - RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_DEFAULT); - RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_UI); - RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostDelayedTask_InPostOrder_2) { - RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_DEFAULT); - RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_UI); - RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostDelayedTask_InPostOrder_3) { - RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_DEFAULT); - RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_UI); - RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, PostDelayedTask_SharedTimer) { - RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_DEFAULT); - RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_UI); - RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_IO); -} +RUN_MESSAGE_LOOP_TESTS(Default, &TypeDefaultMessagePumpFactory); +RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory); +RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory); #if defined(OS_WIN) TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { RunTest_PostDelayedTask_SharedTimer_SubPump(); } -#endif - -// TODO(darin): MessageLoop does not support deleting all tasks in the -// destructor. -// Fails, http://crbug.com/50272. -TEST(MessageLoopTest, DISABLED_EnsureDeletion) { - RunTest_EnsureDeletion(MessageLoop::TYPE_DEFAULT); - RunTest_EnsureDeletion(MessageLoop::TYPE_UI); - RunTest_EnsureDeletion(MessageLoop::TYPE_IO); -} - -// TODO(darin): MessageLoop does not support deleting all tasks in the -// destructor. -// Fails, http://crbug.com/50272. -TEST(MessageLoopTest, DISABLED_EnsureDeletion_Chain) { - RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_DEFAULT); - RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_UI); - RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_IO); -} -#if defined(OS_WIN) TEST(MessageLoopTest, Crasher) { RunTest_Crasher(MessageLoop::TYPE_DEFAULT); RunTest_Crasher(MessageLoop::TYPE_UI); @@ -1706,33 +790,7 @@ TEST(MessageLoopTest, CrasherNasty) { RunTest_CrasherNasty(MessageLoop::TYPE_UI); RunTest_CrasherNasty(MessageLoop::TYPE_IO); } -#endif // defined(OS_WIN) - -TEST(MessageLoopTest, Nesting) { - RunTest_Nesting(MessageLoop::TYPE_DEFAULT); - RunTest_Nesting(MessageLoop::TYPE_UI); - RunTest_Nesting(MessageLoop::TYPE_IO); -} -TEST(MessageLoopTest, RecursiveDenial1) { - RunTest_RecursiveDenial1(MessageLoop::TYPE_DEFAULT); - RunTest_RecursiveDenial1(MessageLoop::TYPE_UI); - RunTest_RecursiveDenial1(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RecursiveDenial3) { - RunTest_RecursiveDenial3(MessageLoop::TYPE_DEFAULT); - RunTest_RecursiveDenial3(MessageLoop::TYPE_UI); - RunTest_RecursiveDenial3(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RecursiveSupport1) { - RunTest_RecursiveSupport1(MessageLoop::TYPE_DEFAULT); - RunTest_RecursiveSupport1(MessageLoop::TYPE_UI); - RunTest_RecursiveSupport1(MessageLoop::TYPE_IO); -} - -#if defined(OS_WIN) // This test occasionally hangs http://crbug.com/44567 TEST(MessageLoopTest, DISABLED_RecursiveDenial2) { RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT); @@ -1746,72 +804,6 @@ TEST(MessageLoopTest, RecursiveSupport2) { } #endif // defined(OS_WIN) -TEST(MessageLoopTest, NonNestableWithNoNesting) { - RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_DEFAULT); - RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_UI); - RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, NonNestableInNestedLoop) { - RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT, false); - RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI, false); - RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, false); -} - -TEST(MessageLoopTest, NonNestableDelayedInNestedLoop) { - RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT, true); - RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI, true); - RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, true); -} - -TEST(MessageLoopTest, QuitNow) { - RunTest_QuitNow(MessageLoop::TYPE_DEFAULT); - RunTest_QuitNow(MessageLoop::TYPE_UI); - RunTest_QuitNow(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitTop) { - RunTest_RunLoopQuitTop(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitTop(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitTop(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitNested) { - RunTest_RunLoopQuitNested(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitNested(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitNested(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitBogus) { - RunTest_RunLoopQuitBogus(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitBogus(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitBogus(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitDeep) { - RunTest_RunLoopQuitDeep(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitDeep(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitDeep(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitOrderBefore) { - RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitOrderDuring) { - RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_IO); -} - -TEST(MessageLoopTest, RunLoopQuitOrderAfter) { - RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_DEFAULT); - RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_UI); - RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_IO); -} - class DummyTaskObserver : public MessageLoop::TaskObserver { public: explicit DummyTaskObserver(int num_tasks) @@ -2088,19 +1080,4 @@ TEST(MessageLoopTest, IsType) { EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT)); } -TEST(MessageLoopTest, RecursivePosts) { - // 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. - const int kNumTimes = 1 << 17; - RunTest_RecursivePosts(MessageLoop::TYPE_DEFAULT, kNumTimes); - RunTest_RecursivePosts(MessageLoop::TYPE_UI, kNumTimes); - RunTest_RecursivePosts(MessageLoop::TYPE_IO, kNumTimes); -} - } // namespace base |