From b224f79da757434cee55b721806ff8553fc80b0a Mon Sep 17 00:00:00 2001 From: "ajwong@chromium.org" Date: Wed, 20 Apr 2011 16:02:23 +0000 Subject: Add support for base::Closure in the MessageLoop, and reimplement the whole sucker on top of base::Closure. After this, all Task objects that are posted will be wrapped in a closure prior to dispatch. BUG=35223 TEST=unittests. Review URL: http://codereview.chromium.org/6463013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82300 0039d316-1c4b-4281-b951-d872f2087c98 --- base/bind_helpers.h | 2 +- base/bind_internal_win.h | 7 + base/bind_internal_win.h.pump | 7 + base/message_loop.cc | 228 ++++++--- base/message_loop.h | 92 +++- base/message_loop_unittest.cc | 1072 +++++++++++++++++++---------------------- 6 files changed, 766 insertions(+), 642 deletions(-) (limited to 'base') diff --git a/base/bind_helpers.h b/base/bind_helpers.h index 3293dbb..27434e0 100644 --- a/base/bind_helpers.h +++ b/base/bind_helpers.h @@ -145,7 +145,7 @@ class SupportsAddRefAndRelease { #pragma warning(default:4624) #endif - template struct Helper {}; + template struct Helper {}; template static No& Check(Helper<&C::AddRef>*, Helper<&C::Release>*); diff --git a/base/bind_internal_win.h b/base/bind_internal_win.h index 976a4d7..85ba2ae 100644 --- a/base/bind_internal_win.h +++ b/base/bind_internal_win.h @@ -15,6 +15,11 @@ #define BASE_BIND_INTERNAL_WIN_H_ #pragma once +// In the x64 architecture in Windows, __fastcall, __stdcall, etc, are all +// the same as __cdecl which would turn the following specializations into +// multiple definitions. +#if !defined(ARCH_CPU_X86_64) + namespace base { namespace internal { @@ -180,4 +185,6 @@ struct FunctionTraits { } // namespace internal } // namespace base +#endif // !defined(ARCH_CPU_X86_64) + #endif // BASE_BIND_INTERNAL_WIN_H_ diff --git a/base/bind_internal_win.h.pump b/base/bind_internal_win.h.pump index 06ceaca1..d293fcc 100644 --- a/base/bind_internal_win.h.pump +++ b/base/bind_internal_win.h.pump @@ -18,6 +18,11 @@ $var MAX_ARITY = 6 #define BASE_BIND_INTERNAL_WIN_H_ #pragma once +// In the x64 architecture in Windows, __fastcall, __stdcall, etc, are all +// the same as __cdecl which would turn the following specializations into +// multiple definitions. +#if !defined(ARCH_CPU_X86_64) + namespace base { namespace internal { @@ -69,4 +74,6 @@ $for ARG [[ } // namespace internal } // namespace base +#endif // !defined(ARCH_CPU_X86_64) + #endif // BASE_BIND_INTERNAL_WIN_H_ diff --git a/base/message_loop.cc b/base/message_loop.cc index 1154c3e..bf0d25d 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -4,20 +4,18 @@ #include "base/message_loop.h" -#if defined(OS_POSIX) && !defined(OS_MACOSX) -#include -#include -#endif - #include +#include "base/bind.h" #include "base/compiler_specific.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/message_pump_default.h" #include "base/metrics/histogram.h" +#include "base/scoped_ptr.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_local.h" +#include "base/tracked_objects.h" #if defined(OS_MACOSX) #include "base/message_pump_mac.h" @@ -26,6 +24,8 @@ #include "base/message_pump_libevent.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) +#include +#include #include "base/message_pump_glib.h" #endif #if defined(TOUCH_UI) @@ -81,6 +81,40 @@ const base::LinearHistogram::DescriptionPair event_descriptions_[] = { bool enable_histogrammer_ = false; +// TODO(ajwong): This is one use case for having a Owned() tag that behaves +// like a "Unique" pointer. If we had that, and Tasks were always safe to +// delete on MessageLoop shutdown, this class could just be a function. +class TaskClosureAdapter : public base::RefCounted { + public: + // |should_leak_task| points to a flag variable that can be used to determine + // if this class should leak the Task on destruction. This is important + // at MessageLoop shutdown since not all tasks can be safely deleted without + // running. See MessageLoop::DeletePendingTasks() for the exact behavior + // of when a Task should be deleted. It is subtle. + TaskClosureAdapter(Task* task, bool* should_leak_task) + : task_(task), + should_leak_task_(should_leak_task) { + } + + void Run() { + task_->Run(); + delete task_; + task_ = NULL; + } + + private: + friend class base::RefCounted; + + ~TaskClosureAdapter() { + if (!*should_leak_task_) { + delete task_; + } + } + + Task* task_; + bool* should_leak_task_; +}; + } // namespace //------------------------------------------------------------------------------ @@ -124,6 +158,7 @@ MessageLoop::MessageLoop(Type type) exception_restoration_(false), message_histogram_(NULL), state_(NULL), + should_leak_tasks_(true), #ifdef OS_WIN os_modal_loop_(false), #endif // OS_WIN @@ -220,22 +255,74 @@ void MessageLoop::RemoveDestructionObserver( void MessageLoop::PostTask( const tracked_objects::Location& from_here, Task* task) { - PostTask_Helper(from_here, task, 0, true); + PendingTask pending_task( + base::Bind(&TaskClosureAdapter::Run, + new TaskClosureAdapter(task, &should_leak_tasks_)), + from_here, + CalculateDelayedRuntime(0), true); + AddToIncomingQueue(&pending_task); } void MessageLoop::PostDelayedTask( const tracked_objects::Location& from_here, Task* task, int64 delay_ms) { - PostTask_Helper(from_here, task, delay_ms, true); + PendingTask pending_task( + base::Bind(&TaskClosureAdapter::Run, + new TaskClosureAdapter(task, &should_leak_tasks_)), + from_here, + CalculateDelayedRuntime(delay_ms), true); + AddToIncomingQueue(&pending_task); } void MessageLoop::PostNonNestableTask( const tracked_objects::Location& from_here, Task* task) { - PostTask_Helper(from_here, task, 0, false); + PendingTask pending_task( + base::Bind(&TaskClosureAdapter::Run, + new TaskClosureAdapter(task, &should_leak_tasks_)), + from_here, + CalculateDelayedRuntime(0), false); + AddToIncomingQueue(&pending_task); } void MessageLoop::PostNonNestableDelayedTask( const tracked_objects::Location& from_here, Task* task, int64 delay_ms) { - PostTask_Helper(from_here, task, delay_ms, false); + PendingTask pending_task( + base::Bind(&TaskClosureAdapter::Run, + new TaskClosureAdapter(task, &should_leak_tasks_)), + from_here, + CalculateDelayedRuntime(delay_ms), false); + AddToIncomingQueue(&pending_task); +} + +void MessageLoop::PostTask( + const tracked_objects::Location& from_here, const base::Closure& task) { + DCHECK(!task.is_null()); + PendingTask pending_task(task, from_here, CalculateDelayedRuntime(0), true); + AddToIncomingQueue(&pending_task); +} + +void MessageLoop::PostDelayedTask( + const tracked_objects::Location& from_here, const base::Closure& task, + int64 delay_ms) { + DCHECK(!task.is_null()); + PendingTask pending_task(task, from_here, + CalculateDelayedRuntime(delay_ms), true); + AddToIncomingQueue(&pending_task); +} + +void MessageLoop::PostNonNestableTask( + const tracked_objects::Location& from_here, const base::Closure& task) { + DCHECK(!task.is_null()); + PendingTask pending_task(task, from_here, CalculateDelayedRuntime(0), false); + AddToIncomingQueue(&pending_task); +} + +void MessageLoop::PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, const base::Closure& task, + int64 delay_ms) { + DCHECK(!task.is_null()); + PendingTask pending_task(task, from_here, + CalculateDelayedRuntime(delay_ms), false); + AddToIncomingQueue(&pending_task); } void MessageLoop::Run() { @@ -353,31 +440,40 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() { if (deferred_non_nestable_work_queue_.empty()) return false; - Task* task = deferred_non_nestable_work_queue_.front().task; + PendingTask pending_task = deferred_non_nestable_work_queue_.front(); deferred_non_nestable_work_queue_.pop(); - RunTask(task); + RunTask(pending_task); return true; } -void MessageLoop::RunTask(Task* task) { +void MessageLoop::RunTask(const PendingTask& pending_task) { DCHECK(nestable_tasks_allowed_); // Execute the task and assume the worst: It is probably not reentrant. nestable_tasks_allowed_ = false; HistogramEvent(kTaskRunEvent); FOR_EACH_OBSERVER(TaskObserver, task_observers_, - WillProcessTask(task)); - task->Run(); - FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask(task)); - delete task; + WillProcessTask(pending_task.time_posted)); + pending_task.task.Run(); + FOR_EACH_OBSERVER(TaskObserver, task_observers_, + DidProcessTask(pending_task.time_posted)); + +#if defined(TRACK_ALL_TASK_OBJECTS) + if (tracked_objects::ThreadData::IsActive() && pending_task.post_births) { + tracked_objects::ThreadData::current()->TallyADeath( + *pending_task.post_births, + TimeTicks::Now() - pending_task.time_posted); + } +#endif // defined(TRACK_ALL_TASK_OBJECTS) nestable_tasks_allowed_ = true; } -bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) { +bool MessageLoop::DeferOrRunPendingTask( + const PendingTask& pending_task) { if (pending_task.nestable || state_->run_depth == 1) { - RunTask(pending_task.task); + RunTask(pending_task); // Show that we ran a task (Note: a new one might arrive as a // consequence!). return true; @@ -419,6 +515,18 @@ void MessageLoop::ReloadWorkQueue() { bool MessageLoop::DeletePendingTasks() { bool did_work = !work_queue_.empty(); + // TODO(darin): Delete all tasks once it is safe to do so. + // Until it is totally safe, just do it when running Purify or + // Valgrind. + // + // See http://crbug.com/61131 + // +#if defined(PURIFY) || defined(USE_HEAPCHECKER) + should_leak_tasks_ = false; +#else + if (RunningOnValgrind()) + should_leak_tasks_ = false; +#endif // defined(OS_POSIX) while (!work_queue_.empty()) { PendingTask pending_task = work_queue_.front(); work_queue_.pop(); @@ -427,52 +535,31 @@ bool MessageLoop::DeletePendingTasks() { // normally be deleted in case of any funny dependencies between delayed // tasks. AddToDelayedWorkQueue(pending_task); - } else { - // TODO(darin): Delete all tasks once it is safe to do so. - // Until it is totally safe, just do it when running Purify or - // Valgrind. -#if defined(PURIFY) || defined(USE_HEAPCHECKER) - delete pending_task.task; -#else - if (RunningOnValgrind()) - delete pending_task.task; -#endif // defined(OS_POSIX) } } did_work |= !deferred_non_nestable_work_queue_.empty(); while (!deferred_non_nestable_work_queue_.empty()) { - // TODO(darin): Delete all tasks once it is safe to do so. - // Until it is totaly safe, only delete them under Purify and Valgrind. - Task* task = NULL; -#if defined(PURIFY) || defined(USE_HEAPCHECKER) - task = deferred_non_nestable_work_queue_.front().task; -#else - if (RunningOnValgrind()) - task = deferred_non_nestable_work_queue_.front().task; -#endif deferred_non_nestable_work_queue_.pop(); - if (task) - delete task; } did_work |= !delayed_work_queue_.empty(); + + // Historically, we always delete the task regardless of valgrind status. It's + // not completely clear why we want to leak them in the loops above. This + // code is replicating legacy behavior, and should not be considered + // absolutely "correct" behavior. See TODO above about deleting all tasks + // when it's safe. + should_leak_tasks_ = false; while (!delayed_work_queue_.empty()) { - Task* task = delayed_work_queue_.top().task; delayed_work_queue_.pop(); - delete task; } + should_leak_tasks_ = true; return did_work; } -// Possibly called on a background thread! -void MessageLoop::PostTask_Helper( - const tracked_objects::Location& from_here, Task* task, int64 delay_ms, - bool nestable) { - task->SetBirthPlace(from_here); - - PendingTask pending_task(task, nestable); - +TimeTicks MessageLoop::CalculateDelayedRuntime(int64 delay_ms) { + TimeTicks delayed_run_time; if (delay_ms > 0) { - pending_task.delayed_run_time = + delayed_run_time = TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms); #if defined(OS_WIN) @@ -504,6 +591,11 @@ void MessageLoop::PostTask_Helper( } #endif + return delayed_run_time; +} + +// Possibly called on a background thread! +void MessageLoop::AddToIncomingQueue(PendingTask* pending_task) { // Warning: Don't try to short-circuit, and handle this thread's tasks more // directly, as it could starve handling of foreign threads. Put every task // into this queue. @@ -513,7 +605,8 @@ void MessageLoop::PostTask_Helper( base::AutoLock locked(incoming_queue_lock_); bool was_empty = incoming_queue_.empty(); - incoming_queue_.push(pending_task); + incoming_queue_.push(*pending_task); + pending_task->task.Reset(); if (!was_empty) return; // Someone else should have started the sub-pump. @@ -566,8 +659,8 @@ bool MessageLoop::DoWork() { work_queue_.pop(); if (!pending_task.delayed_run_time.is_null()) { AddToDelayedWorkQueue(pending_task); - // If we changed the topmost task, then it is time to re-schedule. - if (delayed_work_queue_.top().task == pending_task.task) + // If we changed the topmost task, then it is time to reschedule. + if (delayed_work_queue_.top().task.Equals(pending_task.task)) pump_->ScheduleDelayedWork(pending_task.delayed_run_time); } else { if (DeferOrRunPendingTask(pending_task)) @@ -580,7 +673,7 @@ bool MessageLoop::DoWork() { return false; } -bool MessageLoop::DoDelayedWork(base::TimeTicks* next_delayed_work_time) { +bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { recent_time_ = *next_delayed_work_time = TimeTicks(); return false; @@ -648,6 +741,33 @@ MessageLoop::AutoRunState::~AutoRunState() { //------------------------------------------------------------------------------ // MessageLoop::PendingTask +MessageLoop::PendingTask::PendingTask( + const base::Closure& task, + const tracked_objects::Location& posted_from, + TimeTicks delayed_run_time, + bool nestable) + : task(task), + time_posted(TimeTicks::Now()), + delayed_run_time(delayed_run_time), + sequence_num(0), + nestable(nestable) { +#if defined(TRACK_ALL_TASK_OBJECTS) + if (tracked_objects::ThreadData::IsActive()) { + tracked_objects::ThreadData* current_thread_data = + tracked_objects::ThreadData::current(); + if (current_thread_data) { + post_births = current_thread_data->TallyABirth(posted_from); + } else { + // Shutdown started, and this thread wasn't registered. + post_births = NULL; + } + } +#endif // defined(TRACK_ALL_TASK_OBJECTS) +} + +MessageLoop::PendingTask::~PendingTask() { +} + bool MessageLoop::PendingTask::operator<(const PendingTask& other) const { // Since the top of a priority queue is defined as the "greatest" element, we // need to invert the comparison here. We want the smaller time to be at the diff --git a/base/message_loop.h b/base/message_loop.h index 519e4a3..b601d9e 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -11,11 +11,14 @@ #include "base/base_api.h" #include "base/basictypes.h" +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/message_pump.h" #include "base/observer_list.h" #include "base/synchronization/lock.h" #include "base/task.h" +#include "base/time.h" +#include "base/tracked.h" #if defined(OS_WIN) // We need this to declare base::MessagePumpWin::Dispatcher, which we should @@ -36,6 +39,12 @@ namespace base { class Histogram; } +#if defined(TRACK_ALL_TASK_OBJECTS) +namespace tracked_objects { +class Births; +} +#endif // defined(TRACK_ALL_TASK_OBJECTS) + // A MessageLoop is used to process events for a particular thread. There is // at most one MessageLoop instance per thread. // @@ -163,6 +172,29 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { void PostNonNestableDelayedTask( const tracked_objects::Location& from_here, Task* task, int64 delay_ms); + // TODO(ajwong): Remove the functions above once the Task -> Closure migration + // is complete. + // + // There are 2 sets of Post*Task functions, one which takes the older Task* + // function object representation, and one that takes the newer base::Closure. + // We have this overload to allow a staged transition between the two systems. + // Once the transition is done, the functions above should be deleted. + void PostTask( + const tracked_objects::Location& from_here, + const base::Closure& task); + + void PostDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, int64 delay_ms); + + void PostNonNestableTask( + const tracked_objects::Location& from_here, + const base::Closure& task); + + void PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, int64 delay_ms); + // A variant on PostTask that deletes the given object. This is useful // if the object needs to live until the next run of the MessageLoop (for // example, deleting a RenderProcessHost from within an IPC callback is not @@ -289,10 +321,10 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { TaskObserver(); // This method is called before processing a task. - virtual void WillProcessTask(const Task* task) = 0; + virtual void WillProcessTask(base::TimeTicks time_posted) = 0; // This method is called after processing a task. - virtual void DidProcessTask(const Task* task) = 0; + virtual void DidProcessTask(base::TimeTicks time_posted) = 0; protected: virtual ~TaskObserver(); @@ -356,17 +388,34 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { // This structure is copied around by value. struct PendingTask { - PendingTask(Task* task, bool nestable) - : task(task), sequence_num(0), nestable(nestable) { - } + PendingTask(const base::Closure& task, + const tracked_objects::Location& posted_from, + base::TimeTicks delayed_run_time, + bool nestable); + ~PendingTask(); // Used to support sorting. bool operator<(const PendingTask& other) const; - Task* task; // The task to run. - base::TimeTicks delayed_run_time; // The time when the task should be run. - int sequence_num; // Secondary sort key for run time. - bool nestable; // OK to dispatch from a nested loop. + // The task to run. + base::Closure task; + +#if defined(TRACK_ALL_TASK_OBJECTS) + // Counter for location where the Closure was posted from. + tracked_objects::Births* post_births; +#endif // defined(TRACK_ALL_TASK_OBJECTS) + + // Time this PendingTask was posted. + base::TimeTicks time_posted; + + // The time when the task should be run. + base::TimeTicks delayed_run_time; + + // Secondary sort key for run time. + int sequence_num; + + // OK to dispatch from a nested loop. + bool nestable; }; class TaskQueue : public std::queue { @@ -406,8 +455,8 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { // Called to process any delayed non-nestable tasks. bool ProcessNextDelayedNonNestableTask(); - // Runs the specified task and deletes it. - void RunTask(Task* task); + // Runs the specified PendingTask. + void RunTask(const PendingTask& pending_task); // Calls RunTask or queues the pending_task on the deferred task list if it // cannot be run right now. Returns true if the task was run. @@ -416,6 +465,14 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { // Adds the pending task to delayed_work_queue_. void AddToDelayedWorkQueue(const PendingTask& pending_task); + // Adds the pending task to our incoming_queue_. + // + // Caller retains ownership of |pending_task|, but this function will + // reset the value of pending_task->task. This is needed to ensure + // that the posting call stack does not retain pending_task->task + // beyond this function call. + void AddToIncomingQueue(PendingTask* pending_task); + // Load tasks from the incoming_queue_ into work_queue_ if the latter is // empty. The former requires a lock to access, while the latter is directly // accessible on this thread. @@ -426,9 +483,8 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { // true if some work was done. bool DeletePendingTasks(); - // Post a task to our incomming queue. - void PostTask_Helper(const tracked_objects::Location& from_here, Task* task, - int64 delay_ms, bool nestable); + // Calcuates the time at which a PendingTask should run. + base::TimeTicks CalculateDelayedRuntime(int64 delay_ms); // Start recording histogram info about events and action IF it was enabled // and IF the statistics recorder can accept a registration of our histogram. @@ -477,14 +533,18 @@ class BASE_API MessageLoop : public base::MessagePump::Delegate { // A null terminated list which creates an incoming_queue of tasks that are // acquired under a mutex for processing on this instance's thread. These - // tasks have not yet been sorted out into items for our work_queue_ vs - // items that will be handled by the TimerManager. + // tasks have not yet been sorted out into items for our work_queue_ vs items + // that will be handled by the TimerManager. TaskQueue incoming_queue_; // Protect access to incoming_queue_. mutable base::Lock incoming_queue_lock_; RunState* state_; + // The need for this variable is subtle. Please see implementation comments + // around where it is used. + bool should_leak_tasks_; + #if defined(OS_WIN) base::TimeTicks high_resolution_timer_expiration_; // Should be set to true before calling Windows APIs like TrackPopupMenu, etc diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc index cd681f3..be40f39 100644 --- a/base/message_loop_unittest.cc +++ b/base/message_loop_unittest.cc @@ -4,6 +4,9 @@ #include +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/compiler_specific.h" #include "base/eintr_wrapper.h" #include "base/logging.h" #include "base/memory/ref_counted.h" @@ -25,6 +28,7 @@ using base::PlatformThread; using base::Thread; using base::Time; using base::TimeDelta; +using base::TimeTicks; // TODO(darin): Platform-specific MessageLoop tests should be grouped together // to avoid chopping this file up with so many #ifdefs. @@ -80,19 +84,9 @@ class Foo : public base::RefCounted { std::string result_; }; -class QuitMsgLoop : public base::RefCounted { - public: - void QuitNow() { - MessageLoop::current()->Quit(); - } - - private: - friend class base::RefCounted; - - ~QuitMsgLoop() {} -}; - -void RunTest_PostTask(MessageLoop::Type message_loop_type) { +// TODO(ajwong): Remove this once we've finished getting rid of the PostTask() +// compatibility methods. +void RunTest_PostLegacyTask(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); // Add tests to message loop @@ -112,9 +106,38 @@ void RunTest_PostTask(MessageLoop::Type message_loop_type) { foo.get(), &Foo::Test2Mixed, a, &d)); // After all tests, post a message that will shut down the message loop - scoped_refptr quit(new QuitMsgLoop()); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - quit.get(), &QuitMsgLoop::QuitNow)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &MessageLoop::Quit, base::Unretained(MessageLoop::current()))); + + // Now kick things off + MessageLoop::current()->Run(); + + EXPECT_EQ(foo->test_count(), 105); + EXPECT_EQ(foo->result(), "abacad"); +} + +void RunTest_PostTask(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Add tests to message loop + scoped_refptr foo(new Foo()); + std::string a("a"), b("b"), c("c"), d("d"); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test0, foo.get())); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test1ConstRef, foo.get(), a)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test1Ptr, foo.get(), &b)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test1Int, foo.get(), 100)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test2Ptr, foo.get(), &a, &c)); + MessageLoop::current()->PostTask(FROM_HERE, base::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, base::Bind( + &MessageLoop::Quit, base::Unretained(MessageLoop::current()))); // Now kick things off MessageLoop::current()->Run(); @@ -129,23 +152,22 @@ void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) { // Add tests to message loop scoped_refptr foo(new Foo()); std::string a("a"), b("b"), c("c"), d("d"); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - foo.get(), &Foo::Test0)); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - foo.get(), &Foo::Test1ConstRef, a)); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - foo.get(), &Foo::Test1Ptr, &b)); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - foo.get(), &Foo::Test1Int, 100)); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - foo.get(), &Foo::Test2Ptr, &a, &c)); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - foo.get(), &Foo::Test2Mixed, a, &d)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test0, foo.get())); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test1ConstRef, foo.get(), a)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test1Ptr, foo.get(), &b)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test1Int, foo.get(), 100)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test2Ptr, foo.get(), &a, &c)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &Foo::Test2Mixed, foo.get(), a, &d)); // After all tests, post a message that will shut down the message loop - scoped_refptr quit(new QuitMsgLoop()); - MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( - quit.get(), &QuitMsgLoop::QuitNow)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &MessageLoop::Quit, base::Unretained(MessageLoop::current()))); // Now kick things off with the SEH block active. MessageLoop::current()->set_exception_restoration(true); @@ -156,39 +178,23 @@ void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) { EXPECT_EQ(foo->result(), "abacad"); } -// This class runs slowly to simulate a large amount of work being done. -class SlowTask : public Task { - public: - SlowTask(int pause_ms, int* quit_counter) - : pause_ms_(pause_ms), quit_counter_(quit_counter) { - } - virtual void Run() { - PlatformThread::Sleep(pause_ms_); - if (--(*quit_counter_) == 0) +// This function runs slowly to simulate a large amount of work being done. +static void SlowFunc(int pause_ms, int* quit_counter) { + PlatformThread::Sleep(pause_ms); + if (--(*quit_counter) == 0) MessageLoop::current()->Quit(); - } - private: - int pause_ms_; - int* quit_counter_; -}; +} -// This class records the time when Run was called in a Time object, which is +// This function records the time when Run was called in a Time object, which is // useful for building a variety of MessageLoop tests. -class RecordRunTimeTask : public SlowTask { - public: - RecordRunTimeTask(Time* run_time, int* quit_counter) - : SlowTask(10, quit_counter), run_time_(run_time) { - } - virtual void Run() { - *run_time_ = Time::Now(); +static 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 RecordRunTimeTask objects running at a future time, + // count on subsequent RecordRunTimeFunc()s running at a future time, // without worry about the resolution of our system clock being an issue. - SlowTask::Run(); - } - private: - Time* run_time_; -}; + SlowFunc(10, quit_counter); +} void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); @@ -201,7 +207,8 @@ void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) { Time run_time; loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), kDelayMS); + FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time, &num_tasks), + kDelayMS); Time time_before_run = Time::Now(); loop.Run(); @@ -211,20 +218,20 @@ void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) { EXPECT_LT(kDelayMS, (time_after_run - time_before_run).InMilliseconds()); } -void RunTest_PostDelayedTask_InDelayOrder(MessageLoop::Type message_loop_type) { +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, new RecordRunTimeTask(&run_time1, &num_tasks), 200); + FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), 200); // If we get a large pause in execution (due to a context switch) here, this // test could fail. loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 10); + FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), 10); loop.Run(); EXPECT_EQ(0, num_tasks); @@ -232,7 +239,8 @@ void RunTest_PostDelayedTask_InDelayOrder(MessageLoop::Type message_loop_type) { EXPECT_TRUE(run_time2 < run_time1); } -void RunTest_PostDelayedTask_InPostOrder(MessageLoop::Type message_loop_type) { +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 @@ -249,9 +257,11 @@ void RunTest_PostDelayedTask_InPostOrder(MessageLoop::Type message_loop_type) { Time run_time1, run_time2; loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), kDelayMS); + FROM_HERE, + base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelayMS); loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), kDelayMS); + FROM_HERE, + base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelayMS); loop.Run(); EXPECT_EQ(0, num_tasks); @@ -271,10 +281,9 @@ void RunTest_PostDelayedTask_InPostOrder_2( int num_tasks = 2; Time run_time; - loop.PostTask( - FROM_HERE, new SlowTask(kPauseMS, &num_tasks)); + loop.PostTask(FROM_HERE, base::Bind(&SlowFunc, kPauseMS, &num_tasks)); loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), 10); + FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time, &num_tasks), 10); Time time_before_run = Time::Now(); loop.Run(); @@ -300,10 +309,11 @@ void RunTest_PostDelayedTask_InPostOrder_3( // Clutter the ML with tasks. for (int i = 1; i < num_tasks; ++i) - loop.PostTask(FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks)); + loop.PostTask(FROM_HERE, + base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks)); loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 1); + FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), 1); loop.Run(); EXPECT_EQ(0, num_tasks); @@ -311,7 +321,8 @@ void RunTest_PostDelayedTask_InPostOrder_3( EXPECT_TRUE(run_time2 > run_time1); } -void RunTest_PostDelayedTask_SharedTimer(MessageLoop::Type message_loop_type) { +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 @@ -323,9 +334,11 @@ void RunTest_PostDelayedTask_SharedTimer(MessageLoop::Type message_loop_type) { Time run_time1, run_time2; loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), 1000000); + FROM_HERE, + base::Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), + 1000000); loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 10); + FROM_HERE, base::Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), 10); Time start_time = Time::Now(); @@ -348,27 +361,15 @@ void RunTest_PostDelayedTask_SharedTimer(MessageLoop::Type message_loop_type) { #if defined(OS_WIN) -class SubPumpTask : public Task { - public: - virtual void Run() { - MessageLoop::current()->SetNestableTasksAllowed(true); - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - MessageLoop::current()->Quit(); - } -}; - -class SubPumpQuitTask : public Task { - public: - SubPumpQuitTask() { - } - virtual void Run() { - PostQuitMessage(0); +void SubPumpFunc() { + MessageLoop::current()->SetNestableTasksAllowed(true); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); } -}; + MessageLoop::current()->Quit(); +} void RunTest_PostDelayedTask_SharedTimer_SubPump() { MessageLoop loop(MessageLoop::TYPE_UI); @@ -381,15 +382,16 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() { int num_tasks = 1; Time run_time; - loop.PostTask(FROM_HERE, new SubPumpTask()); + loop.PostTask(FROM_HERE, base::Bind(&SubPumpFunc)); // This very delayed task should never run. loop.PostDelayedTask( - FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), 1000000); + FROM_HERE, + base::Bind(&RecordRunTimeFunc, &run_time, &num_tasks), + 1000000); - // This slightly delayed task should run from within SubPumpTask::Run(). - loop.PostDelayedTask( - FROM_HERE, new SubPumpQuitTask(), 10); + // This slightly delayed task should run from within SubPumpFunc). + loop.PostDelayedTask(FROM_HERE, base::Bind(&PostQuitMessage, 0), 10); Time start_time = Time::Now(); @@ -411,69 +413,73 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() { #endif // defined(OS_WIN) -class RecordDeletionTask : public Task { +// 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 base::RefCounted { public: - RecordDeletionTask(Task* post_on_delete, bool* was_deleted) + RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted) : post_on_delete_(post_on_delete), was_deleted_(was_deleted) { } - ~RecordDeletionTask() { + ~RecordDeletionProbe() { *was_deleted_ = true; if (post_on_delete_) - MessageLoop::current()->PostTask(FROM_HERE, post_on_delete_); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&RecordDeletionProbe::Run, post_on_delete_.get())); } virtual void Run() {} private: - Task* post_on_delete_; + scoped_refptr post_on_delete_; bool* was_deleted_; }; -void RunTest_EnsureTaskDeletion(MessageLoop::Type message_loop_type) { +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, new RecordDeletionTask(NULL, &a_was_deleted)); + FROM_HERE, base::Bind(&RecordDeletionProbe::Run, + new RecordDeletionProbe(NULL, &a_was_deleted))); loop.PostDelayedTask( - FROM_HERE, new RecordDeletionTask(NULL, &b_was_deleted), 1000); + FROM_HERE, base::Bind(&RecordDeletionProbe::Run, + new RecordDeletionProbe(NULL, &b_was_deleted)), + 1000); // TODO(ajwong): Do we really need 1000ms here? } EXPECT_TRUE(a_was_deleted); EXPECT_TRUE(b_was_deleted); } -void RunTest_EnsureTaskDeletion_Chain(MessageLoop::Type message_loop_type) { +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); - RecordDeletionTask* a = new RecordDeletionTask(NULL, &a_was_deleted); - RecordDeletionTask* b = new RecordDeletionTask(a, &b_was_deleted); - RecordDeletionTask* c = new RecordDeletionTask(b, &c_was_deleted); - loop.PostTask(FROM_HERE, c); + // 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, base::Bind(&RecordDeletionProbe::Run, c)); } EXPECT_TRUE(a_was_deleted); EXPECT_TRUE(b_was_deleted); EXPECT_TRUE(c_was_deleted); } -class NestingTest : public Task { - public: - explicit NestingTest(int* depth) : depth_(depth) { - } - void Run() { - if (*depth_ > 0) { - *depth_ -= 1; - MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(depth_)); +void NestingFunc(int* depth) { + if (*depth > 0) { + *depth -= 1; + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&NestingFunc, depth)); - MessageLoop::current()->SetNestableTasksAllowed(true); - MessageLoop::current()->Run(); - } - MessageLoop::current()->Quit(); + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); } - private: - int* depth_; -}; + MessageLoop::current()->Quit(); +} #if defined(OS_WIN) @@ -485,13 +491,14 @@ LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) { // This task throws an SEH exception: initially write to an invalid address. // If the right SEH filter is installed, it will fix the error. -class CrasherTask : public Task { +class Crasher : public base::RefCounted { public: // Ctor. If trash_SEH_handler is true, the task will override the unhandled // exception handler with one sure to crash this test. - explicit CrasherTask(bool trash_SEH_handler) + explicit Crasher(bool trash_SEH_handler) : trash_SEH_handler_(trash_SEH_handler) { } + void Run() { PlatformThread::Sleep(1); if (trash_SEH_handler_) @@ -502,7 +509,7 @@ class CrasherTask : public Task { #if defined(_M_IX86) __asm { - mov eax, dword ptr [CrasherTask::bad_array_] + mov eax, dword ptr [Crasher::bad_array_] mov byte ptr [eax], 66 } @@ -527,17 +534,17 @@ class CrasherTask : public Task { static char valid_store_; }; -volatile char* CrasherTask::bad_array_ = 0; -char CrasherTask::valid_store_ = 0; +volatile char* Crasher::bad_array_ = 0; +char Crasher::valid_store_ = 0; // This SEH filter fixes the problem and retries execution. Fixing requires -// that the last instruction: mov eax, [CrasherTask::bad_array_] to be retried +// that the last instruction: mov eax, [Crasher::bad_array_] to be retried // so we move the instruction pointer 5 bytes back. -LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) { +LONG WINAPI HandleCrasherException(EXCEPTION_POINTERS *ex_info) { if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) return EXCEPTION_EXECUTE_HANDLER; - CrasherTask::FixError(); + Crasher::FixError(); #if defined(_M_IX86) @@ -559,9 +566,11 @@ void RunTest_Crasher(MessageLoop::Type message_loop_type) { return; LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter = - ::SetUnhandledExceptionFilter(&HandleCrasherTaskException); + ::SetUnhandledExceptionFilter(&HandleCrasherException); - MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(false)); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&Crasher::Run, new Crasher(false))); MessageLoop::current()->set_exception_restoration(true); MessageLoop::current()->Run(); MessageLoop::current()->set_exception_restoration(false); @@ -576,9 +585,11 @@ void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) { return; LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter = - ::SetUnhandledExceptionFilter(&HandleCrasherTaskException); + ::SetUnhandledExceptionFilter(&HandleCrasherException); - MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(true)); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&Crasher::Run, new Crasher(true))); MessageLoop::current()->set_exception_restoration(true); MessageLoop::current()->Run(); MessageLoop::current()->set_exception_restoration(false); @@ -592,7 +603,8 @@ void RunTest_Nesting(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); int depth = 100; - MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(&depth)); + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&NestingFunc, &depth)); MessageLoop::current()->Run(); EXPECT_EQ(depth, 0); } @@ -627,8 +639,6 @@ struct TaskItem { } }; -typedef std::vector TaskList; - std::ostream& operator <<(std::ostream& os, TaskType type) { switch (type) { case MESSAGEBOX: os << "MESSAGEBOX"; break; @@ -654,223 +664,141 @@ std::ostream& operator <<(std::ostream& os, const TaskItem& item) { return os << item.type << " " << item.cookie << " ends"; } -// Saves the order the tasks ran. -class OrderedTasks : public Task { +class TaskList { public: - OrderedTasks(TaskList* order, int cookie) - : order_(order), - type_(ORDERERD), - cookie_(cookie) { - } - OrderedTasks(TaskList* order, TaskType type, int cookie) - : order_(order), - type_(type), - cookie_(cookie) { - } - - void RunStart() { - TaskItem item(type_, cookie_, true); - DVLOG(1) << item; - order_->push_back(item); - } - void RunEnd() { - TaskItem item(type_, cookie_, false); + void RecordStart(TaskType type, int cookie) { + TaskItem item(type, cookie, true); DVLOG(1) << item; - order_->push_back(item); + task_list_.push_back(item); } - virtual void Run() { - RunStart(); - RunEnd(); + void RecordEnd(TaskType type, int cookie) { + TaskItem item(type, cookie, false); + DVLOG(1) << item; + task_list_.push_back(item); } - protected: - TaskList* order() const { - return order_; + size_t Size() { + return task_list_.size(); } - int cookie() const { - return cookie_; + TaskItem Get(int n) { + return task_list_[n]; } private: - TaskList* order_; - TaskType type_; - int cookie_; + std::vector task_list_; }; +// Saves the order the tasks ran. +void OrderedFunc(TaskList* order, int cookie) { + order->RecordStart(ORDERERD, cookie); + order->RecordEnd(ORDERERD, 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. -class MessageBoxTask : public OrderedTasks { - public: - MessageBoxTask(TaskList* order, int cookie, bool is_reentrant) - : OrderedTasks(order, MESSAGEBOX, cookie), - is_reentrant_(is_reentrant) { - } - - virtual void Run() { - RunStart(); - if (is_reentrant_) - MessageLoop::current()->SetNestableTasksAllowed(true); - MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK); - RunEnd(); - } - - private: - bool is_reentrant_; -}; +void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) { + order->RecordStart(MESSAGEBOX, cookie); + if (is_reentrant) + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK); + order->RecordEnd(MESSAGEBOX, cookie); +} // Will end the MessageBox. -class EndDialogTask : public OrderedTasks { - public: - EndDialogTask(TaskList* order, int cookie) - : OrderedTasks(order, ENDDIALOG, cookie) { - } - - virtual void Run() { - RunStart(); - 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. - RunEnd(); - } +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); } -}; +} #endif // defined(OS_WIN) -class RecursiveTask : public OrderedTasks { - public: - RecursiveTask(int depth, TaskList* order, int cookie, bool is_reentrant) - : OrderedTasks(order, RECURSIVE, cookie), - depth_(depth), - is_reentrant_(is_reentrant) { - } - - virtual void Run() { - RunStart(); - if (depth_ > 0) { - if (is_reentrant_) - MessageLoop::current()->SetNestableTasksAllowed(true); - MessageLoop::current()->PostTask(FROM_HERE, - new RecursiveTask(depth_ - 1, order(), cookie(), is_reentrant_)); - } - RunEnd(); - } - - private: - int depth_; - bool is_reentrant_; -}; - -class RecursiveSlowTask : public RecursiveTask { - public: - RecursiveSlowTask(int depth, TaskList* order, int cookie, bool is_reentrant) - : RecursiveTask(depth, order, cookie, is_reentrant) { - } - - virtual void Run() { - RecursiveTask::Run(); - PlatformThread::Sleep(10); // milliseconds - } -}; - -class QuitTask : public OrderedTasks { - public: - QuitTask(TaskList* order, int cookie) - : OrderedTasks(order, QUITMESSAGELOOP, cookie) { - } - - virtual void Run() { - RunStart(); - MessageLoop::current()->Quit(); - RunEnd(); +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, + base::Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant)); } -}; + order->RecordEnd(RECURSIVE, cookie); +} -class SleepTask : public OrderedTasks { - public: - SleepTask(TaskList* order, int cookie, int ms) - : OrderedTasks(order, SLEEP, cookie), ms_(ms) { - } +void RecursiveSlowFunc(TaskList* order, int cookie, int depth, + bool is_reentrant) { + RecursiveFunc(order, cookie, depth, is_reentrant); + PlatformThread::Sleep(10); // milliseconds +} - virtual void Run() { - RunStart(); - PlatformThread::Sleep(ms_); - RunEnd(); - } +void QuitFunc(TaskList* order, int cookie) { + order->RecordStart(QUITMESSAGELOOP, cookie); + MessageLoop::current()->Quit(); + order->RecordEnd(QUITMESSAGELOOP, cookie); +} - private: - int ms_; -}; +void SleepFunc(TaskList* order, int cookie, int ms) { + order->RecordStart(SLEEP, cookie); + PlatformThread::Sleep(ms); + order->RecordEnd(SLEEP, cookie); +} #if defined(OS_WIN) - -class Recursive2Tasks : public Task { - public: - Recursive2Tasks(MessageLoop* target, - HANDLE event, - bool expect_window, - TaskList* order, - bool is_reentrant) - : target_(target), - event_(event), - expect_window_(expect_window), - order_(order), - is_reentrant_(is_reentrant) { - } - - virtual void Run() { - target_->PostTask(FROM_HERE, - new RecursiveTask(2, order_, 1, is_reentrant_)); - target_->PostTask(FROM_HERE, - new MessageBoxTask(order_, 2, is_reentrant_)); - target_->PostTask(FROM_HERE, - new RecursiveTask(2, order_, 3, 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. - target_->PostTask(FROM_HERE, new EndDialogTask(order_, 4)); - target_->PostTask(FROM_HERE, new QuitTask(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; - } +void RecursiveFuncWin(MessageLoop* target, + HANDLE event, + bool expect_window, + TaskList* order, + bool is_reentrant) { + target->PostTask(FROM_HERE, + base::Bind(&RecursiveFunc, order, 1, 2, is_reentrant)); + target->PostTask(FROM_HERE, + base::Bind(&MessageBoxFunc, order, 2, is_reentrant)); + target->PostTask(FROM_HERE, + base::Bind(&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. + target->PostTask(FROM_HERE, + base::Bind(&EndDialogFunc, order, 4)); + target->PostTask(FROM_HERE, + base::Bind(&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; } + break; } } - - private: - MessageLoop* target_; - HANDLE event_; - TaskList* order_; - bool expect_window_; - bool is_reentrant_; -}; +} #endif // defined(OS_WIN) @@ -879,30 +807,34 @@ void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) { EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); TaskList order; - MessageLoop::current()->PostTask(FROM_HERE, - new RecursiveTask(2, &order, 1, false)); - MessageLoop::current()->PostTask(FROM_HERE, - new RecursiveTask(2, &order, 2, false)); - MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3)); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&RecursiveFunc, &order, 1, 2, false)); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&RecursiveFunc, &order, 2, 2, false)); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&QuitFunc, &order, 3)); MessageLoop::current()->Run(); // FIFO order. - ASSERT_EQ(14U, order.size()); - EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false)); - EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false)); + 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) { @@ -910,66 +842,66 @@ void RunTest_RecursiveDenial3(MessageLoop::Type message_loop_type) { EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); TaskList order; - MessageLoop::current()->PostTask(FROM_HERE, - new RecursiveSlowTask(2, &order, 1, false)); - MessageLoop::current()->PostTask(FROM_HERE, - new RecursiveSlowTask(2, &order, 2, false)); - MessageLoop::current()->PostDelayedTask(FROM_HERE, - new OrderedTasks(&order, 3), 5); - MessageLoop::current()->PostDelayedTask(FROM_HERE, - new QuitTask(&order, 4), 5); + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&RecursiveSlowFunc, &order, 1, 2, false)); + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&RecursiveSlowFunc, &order, 2, 2, false)); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&OrderedFunc, &order, 3), 5); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&QuitFunc, &order, 4), 5); MessageLoop::current()->Run(); // FIFO order. - ASSERT_EQ(16U, order.size()); - EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 3, true)); - EXPECT_EQ(order[ 7], TaskItem(ORDERERD, 3, false)); - EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order[10], TaskItem(QUITMESSAGELOOP, 4, true)); - EXPECT_EQ(order[11], TaskItem(QUITMESSAGELOOP, 4, false)); - EXPECT_EQ(order[12], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[14], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[15], TaskItem(RECURSIVE, 2, false)); + 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(ORDERERD, 3, true)); + EXPECT_EQ(order.Get(7), TaskItem(ORDERERD, 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, - new RecursiveTask(2, &order, 1, true)); - MessageLoop::current()->PostTask(FROM_HERE, - new RecursiveTask(2, &order, 2, true)); - MessageLoop::current()->PostTask(FROM_HERE, - new QuitTask(&order, 3)); + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&RecursiveFunc, &order, 1, 2, true)); + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&RecursiveFunc, &order, 2, 2, true)); + MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&QuitFunc, &order, 3)); MessageLoop::current()->Run(); // FIFO order. - ASSERT_EQ(14U, order.size()); - EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false)); - EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false)); + 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) @@ -987,35 +919,36 @@ void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) { TaskList order; base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); worker.message_loop()->PostTask(FROM_HERE, - new Recursive2Tasks(MessageLoop::current(), - event, - true, - &order, - false)); + base::Bind(&RecursiveFuncWin, + MessageLoop::current(), + event.Get(), + true, + &order, + false)); // Let the other thread execute. WaitForSingleObject(event, INFINITE); MessageLoop::current()->Run(); - ASSERT_EQ(order.size(), 17); - EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true)); - EXPECT_EQ(order[ 3], TaskItem(MESSAGEBOX, 2, false)); - EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 3, false)); - // When EndDialogTask is processed, the window is already dismissed, hence no + ASSERT_EQ(order.Size(), 17); + 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[ 6], TaskItem(ENDDIALOG, 4, true)); - EXPECT_EQ(order[ 7], TaskItem(QUITMESSAGELOOP, 5, true)); - EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, false)); - EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[11], TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, false)); - EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[15], TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, false)); + 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)); } // A side effect of this test is the generation a beep. Sorry. This test also @@ -1030,80 +963,78 @@ void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) { TaskList order; base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); worker.message_loop()->PostTask(FROM_HERE, - new Recursive2Tasks(MessageLoop::current(), - event, - false, - &order, - true)); + base::Bind(&RecursiveFuncWin, + MessageLoop::current(), + event.Get(), + false, + &order, + true)); // Let the other thread execute. WaitForSingleObject(event, INFINITE); MessageLoop::current()->Run(); - ASSERT_EQ(order.size(), 18); - EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true)); + ASSERT_EQ(order.Size(), 18); + 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[ 3], TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, false)); - EXPECT_EQ(order[ 5], TaskItem(ENDDIALOG, 4, true)); - EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, false)); - EXPECT_EQ(order[ 7], TaskItem(MESSAGEBOX, 2, false)); - /* The order can subtly change here. The reason is that when RecursiveTask(1) + 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, QuitTask) execution, the order of task execution can - change. We don't care anyway that the order isn't correct. - EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true)); - EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false)); - EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false)); + PostTask(FROM_HERE, base::Bind(&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[12], TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order[13], TaskItem(RECURSIVE, 3, false)); - EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order[15], TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order[17], TaskItem(RECURSIVE, 3, 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) -class TaskThatPumps : public OrderedTasks { - public: - TaskThatPumps(TaskList* order, int cookie) - : OrderedTasks(order, PUMPS, cookie) { - } - - virtual void Run() { - RunStart(); - bool old_state = MessageLoop::current()->NestableTasksAllowed(); - MessageLoop::current()->SetNestableTasksAllowed(true); - MessageLoop::current()->RunAllPending(); - MessageLoop::current()->SetNestableTasksAllowed(old_state); - RunEnd(); - } -}; +void FuncThatPumps(TaskList* order, int cookie) { + order->RecordStart(PUMPS, cookie); + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->RunAllPending(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + order->RecordEnd(PUMPS, cookie); +} // Tests that non nestable tasks run in FIFO if there are no nested loops. -void RunTest_NonNestableWithNoNesting(MessageLoop::Type message_loop_type) { +void RunTest_NonNestableWithNoNesting( + MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); TaskList order; - Task* task = new OrderedTasks(&order, 1); - MessageLoop::current()->PostNonNestableTask(FROM_HERE, task); - MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 2)); - MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3)); + MessageLoop::current()->PostNonNestableTask( + FROM_HERE, + base::Bind(&OrderedFunc, &order, 1)); + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&OrderedFunc, &order, 2)); + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&QuitFunc, &order, 3)); MessageLoop::current()->Run(); // FIFO order. - ASSERT_EQ(6U, order.size()); - EXPECT_EQ(order[ 0], TaskItem(ORDERERD, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 1, false)); - EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 2, true)); - EXPECT_EQ(order[ 3], TaskItem(ORDERERD, 2, false)); - EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false)); + ASSERT_EQ(6U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(ORDERERD, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(ORDERERD, 1, false)); + EXPECT_EQ(order.Get(2), TaskItem(ORDERERD, 2, true)); + EXPECT_EQ(order.Get(3), TaskItem(ORDERERD, 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. @@ -1113,42 +1044,52 @@ void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type, TaskList order; - MessageLoop::current()->PostTask(FROM_HERE, - new TaskThatPumps(&order, 1)); - Task* task = new OrderedTasks(&order, 2); + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&FuncThatPumps, &order, 1)); if (use_delayed) { - MessageLoop::current()->PostNonNestableDelayedTask(FROM_HERE, task, 1); + MessageLoop::current()->PostNonNestableDelayedTask( + FROM_HERE, + base::Bind(&OrderedFunc, &order, 2), + 1); } else { - MessageLoop::current()->PostNonNestableTask(FROM_HERE, task); + MessageLoop::current()->PostNonNestableTask( + FROM_HERE, + base::Bind(&OrderedFunc, &order, 2)); } - MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 3)); - MessageLoop::current()->PostTask(FROM_HERE, new SleepTask(&order, 4, 50)); - MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 5)); - Task* non_nestable_quit = new QuitTask(&order, 6); + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&OrderedFunc, &order, 3)); + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&SleepFunc, &order, 4, 50)); + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&OrderedFunc, &order, 5)); if (use_delayed) { - MessageLoop::current()->PostNonNestableDelayedTask(FROM_HERE, - non_nestable_quit, - 2); + MessageLoop::current()->PostNonNestableDelayedTask( + FROM_HERE, + base::Bind(&QuitFunc, &order, 6), + 2); } else { - MessageLoop::current()->PostNonNestableTask(FROM_HERE, non_nestable_quit); + MessageLoop::current()->PostNonNestableTask( + FROM_HERE, + base::Bind(&QuitFunc, &order, 6)); } MessageLoop::current()->Run(); // FIFO order. - ASSERT_EQ(12U, order.size()); - EXPECT_EQ(order[ 0], TaskItem(PUMPS, 1, true)); - EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 3, true)); - EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 3, false)); - EXPECT_EQ(order[ 3], TaskItem(SLEEP, 4, true)); - EXPECT_EQ(order[ 4], TaskItem(SLEEP, 4, false)); - EXPECT_EQ(order[ 5], TaskItem(ORDERERD, 5, true)); - EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 5, false)); - EXPECT_EQ(order[ 7], TaskItem(PUMPS, 1, false)); - EXPECT_EQ(order[ 8], TaskItem(ORDERERD, 2, true)); - EXPECT_EQ(order[ 9], TaskItem(ORDERERD, 2, false)); - EXPECT_EQ(order[10], TaskItem(QUITMESSAGELOOP, 6, true)); - EXPECT_EQ(order[11], TaskItem(QUITMESSAGELOOP, 6, false)); + ASSERT_EQ(12U, order.Size()); + EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true)); + EXPECT_EQ(order.Get(1), TaskItem(ORDERERD, 3, true)); + EXPECT_EQ(order.Get(2), TaskItem(ORDERERD, 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(ORDERERD, 5, true)); + EXPECT_EQ(order.Get(6), TaskItem(ORDERERD, 5, false)); + EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false)); + EXPECT_EQ(order.Get(8), TaskItem(ORDERERD, 2, true)); + EXPECT_EQ(order.Get(9), TaskItem(ORDERERD, 2, false)); + EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true)); + EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false)); } #if defined(OS_WIN) @@ -1171,18 +1112,16 @@ class DispatcherImpl : public MessageLoopForUI::Dispatcher { int dispatch_count_; }; +void MouseDownUp() { + PostMessage(NULL, WM_LBUTTONDOWN, 0, 0); + PostMessage(NULL, WM_LBUTTONUP, 'A', 0); +} + void RunTest_Dispatcher(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); - class MyTask : public Task { - public: - virtual void Run() { - PostMessage(NULL, WM_LBUTTONDOWN, 0, 0); - PostMessage(NULL, WM_LBUTTONUP, 'A', 0); - } - }; - Task* task = new MyTask(); - MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MouseDownUp), 100); DispatcherImpl dispatcher; MessageLoopForUI::current()->Run(&dispatcher); ASSERT_EQ(2, dispatcher.dispatch_count_); @@ -1200,15 +1139,8 @@ LRESULT CALLBACK MsgFilterProc(int code, WPARAM wparam, LPARAM lparam) { void RunTest_DispatcherWithMessageHook(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); - class MyTask : public Task { - public: - virtual void Run() { - PostMessage(NULL, WM_LBUTTONDOWN, 0, 0); - PostMessage(NULL, WM_LBUTTONUP, 'A', 0); - } - }; - Task* task = new MyTask(); - MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MouseDownUp), 100); HHOOK msg_hook = SetWindowsHookEx(WH_MSGFILTER, MsgFilterProc, NULL, @@ -1271,17 +1203,6 @@ void TestIOHandler::WaitForIO() { EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(400, this)); } -class IOHandlerTask : public Task { - public: - explicit IOHandlerTask(TestIOHandler* handler) : handler_(handler) {} - virtual void Run() { - handler_->Init(); - } - - private: - TestIOHandler* handler_; -}; - void RunTest_IOHandler() { base::win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL)); ASSERT_TRUE(callback_called.IsValid()); @@ -1300,8 +1221,8 @@ void RunTest_IOHandler() { ASSERT_TRUE(NULL != thread_loop); TestIOHandler handler(kPipeName, callback_called, false); - IOHandlerTask* task = new IOHandlerTask(&handler); - thread_loop->PostTask(FROM_HERE, task); + thread_loop->PostTask(FROM_HERE, base::Bind(&TestIOHandler::Init, + base::Unretained(&handler))); Sleep(100); // Make sure the thread runs and sleeps for lack of work. const char buffer[] = "Hello there!"; @@ -1341,11 +1262,12 @@ void RunTest_WaitForIO() { TestIOHandler handler1(kPipeName1, callback1_called, false); TestIOHandler handler2(kPipeName2, callback2_called, true); - IOHandlerTask* task1 = new IOHandlerTask(&handler1); - IOHandlerTask* task2 = new IOHandlerTask(&handler2); - thread_loop->PostTask(FROM_HERE, task1); + thread_loop->PostTask(FROM_HERE, base::Bind(&TestIOHandler::Init, + base::Unretained(&handler1))); + // TODO(ajwong): Do we really need such long Sleeps in ths function? Sleep(100); // Make sure the thread runs and sleeps for lack of work. - thread_loop->PostTask(FROM_HERE, task2); + thread_loop->PostTask(FROM_HERE, base::Bind(&TestIOHandler::Init, + base::Unretained(&handler2))); Sleep(100); // At this time handler1 is waiting to be called, and the thread is waiting @@ -1376,6 +1298,12 @@ 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, PostLegacyTask) { + RunTest_PostLegacyTask(MessageLoop::TYPE_DEFAULT); + RunTest_PostLegacyTask(MessageLoop::TYPE_UI); + RunTest_PostLegacyTask(MessageLoop::TYPE_IO); +} + TEST(MessageLoopTest, PostTask) { RunTest_PostTask(MessageLoop::TYPE_DEFAULT); RunTest_PostTask(MessageLoop::TYPE_UI); @@ -1433,19 +1361,19 @@ TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { // TODO(darin): MessageLoop does not support deleting all tasks in the // destructor. // Fails, http://crbug.com/50272. -TEST(MessageLoopTest, FAILS_EnsureTaskDeletion) { - RunTest_EnsureTaskDeletion(MessageLoop::TYPE_DEFAULT); - RunTest_EnsureTaskDeletion(MessageLoop::TYPE_UI); - RunTest_EnsureTaskDeletion(MessageLoop::TYPE_IO); +TEST(MessageLoopTest, FAILS_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, FAILS_EnsureTaskDeletion_Chain) { - RunTest_EnsureTaskDeletion_Chain(MessageLoop::TYPE_DEFAULT); - RunTest_EnsureTaskDeletion_Chain(MessageLoop::TYPE_UI); - RunTest_EnsureTaskDeletion_Chain(MessageLoop::TYPE_IO); +TEST(MessageLoopTest, FAILS_EnsureDeletion_Chain) { + RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_DEFAULT); + RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_UI); + RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_IO); } #if defined(OS_WIN) @@ -1518,23 +1446,15 @@ TEST(MessageLoopTest, NonNestableDelayedInNestedLoop) { RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, true); } -class DummyTask : public Task { - public: - explicit DummyTask(int num_tasks) : num_tasks_(num_tasks) {} - - virtual void Run() { - if (num_tasks_ > 1) { - MessageLoop::current()->PostTask( - FROM_HERE, - new DummyTask(num_tasks_ - 1)); - } else { - MessageLoop::current()->Quit(); - } +void PostNTasksThenQuit(int posts_remaining) { + if (posts_remaining > 1) { + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PostNTasksThenQuit, posts_remaining - 1)); + } else { + MessageLoop::current()->Quit(); } - - private: - const int num_tasks_; -}; +} class DummyTaskObserver : public MessageLoop::TaskObserver { public: @@ -1545,16 +1465,16 @@ class DummyTaskObserver : public MessageLoop::TaskObserver { virtual ~DummyTaskObserver() {} - virtual void WillProcessTask(const Task* task) { + virtual void WillProcessTask(TimeTicks time_posted) OVERRIDE { num_tasks_started_++; - EXPECT_TRUE(task != NULL); + EXPECT_TRUE(time_posted != TimeTicks()); EXPECT_LE(num_tasks_started_, num_tasks_); EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1); } - virtual void DidProcessTask(const Task* task) { + virtual void DidProcessTask(TimeTicks time_posted) OVERRIDE { num_tasks_processed_++; - EXPECT_TRUE(task != NULL); + EXPECT_TRUE(time_posted != TimeTicks()); EXPECT_LE(num_tasks_started_, num_tasks_); EXPECT_EQ(num_tasks_started_, num_tasks_processed_); } @@ -1571,17 +1491,17 @@ class DummyTaskObserver : public MessageLoop::TaskObserver { }; TEST(MessageLoopTest, TaskObserver) { - const int kNumTasks = 6; - DummyTaskObserver observer(kNumTasks); + const int kNumPosts = 6; + DummyTaskObserver observer(kNumPosts); MessageLoop loop; loop.AddTaskObserver(&observer); - loop.PostTask(FROM_HERE, new DummyTask(kNumTasks)); + loop.PostTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, kNumPosts)); loop.Run(); loop.RemoveTaskObserver(&observer); - EXPECT_EQ(kNumTasks, observer.num_tasks_started()); - EXPECT_EQ(kNumTasks, observer.num_tasks_processed()); + EXPECT_EQ(kNumPosts, observer.num_tasks_started()); + EXPECT_EQ(kNumPosts, observer.num_tasks_processed()); } #if defined(OS_WIN) @@ -1612,13 +1532,15 @@ TEST(MessageLoopTest, HighResolutionTimer) { EXPECT_FALSE(loop.high_resolution_timers_enabled()); // Post a fast task to enable the high resolution timers. - loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kFastTimerMs); + loop.PostDelayedTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, 1), + kFastTimerMs); loop.Run(); EXPECT_TRUE(loop.high_resolution_timers_enabled()); // Post a slow task and verify high resolution timers // are still enabled. - loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kSlowTimerMs); + loop.PostDelayedTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, 1), + kSlowTimerMs); loop.Run(); EXPECT_TRUE(loop.high_resolution_timers_enabled()); @@ -1626,7 +1548,8 @@ TEST(MessageLoopTest, HighResolutionTimer) { Sleep(MessageLoop::kHighResolutionTimerModeLeaseTimeMs); // Post a slow task to disable the high resolution timers. - loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kSlowTimerMs); + loop.PostDelayedTask(FROM_HERE, base::Bind(&PostNTasksThenQuit, 1), + kSlowTimerMs); loop.Run(); EXPECT_FALSE(loop.high_resolution_timers_enabled()); } @@ -1706,13 +1629,18 @@ TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop) { #endif // defined(OS_POSIX) && !defined(OS_NACL) namespace { -class RunAtDestructionTask : public Task { +// 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 base::RefCounted { public: - RunAtDestructionTask(bool* task_destroyed, bool* destruction_observer_called) + DestructionObserverProbe(bool* task_destroyed, + bool* destruction_observer_called) : task_destroyed_(task_destroyed), destruction_observer_called_(destruction_observer_called) { } - ~RunAtDestructionTask() { + virtual ~DestructionObserverProbe() { EXPECT_FALSE(*destruction_observer_called_); *task_destroyed_ = true; } @@ -1760,7 +1688,9 @@ TEST(MessageLoopTest, DestructionObserverTest) { loop->AddDestructionObserver(&observer); loop->PostDelayedTask( FROM_HERE, - new RunAtDestructionTask(&task_destroyed, &destruction_observer_called), + base::Bind(&DestructionObserverProbe::Run, + new DestructionObserverProbe(&task_destroyed, + &destruction_observer_called)), kDelayMS); delete loop; EXPECT_TRUE(observer.task_destroyed_before_message_loop()); -- cgit v1.1