Index: base/message_loop.cc |
diff --git a/base/message_loop.cc b/base/message_loop.cc |
index 1154c3e91476160e7f34e643c2e17f7cc8e8c889..bf0d25da975981b361ab576ddb6eeb6b26cc7021 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 <gdk/gdk.h> |
-#include <gdk/gdkx.h> |
-#endif |
- |
#include <algorithm> |
+#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 <gdk/gdk.h> |
+#include <gdk/gdkx.h> |
#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<TaskClosureAdapter> { |
+ 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>; |
+ |
+ ~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 |