1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/message_loop/incoming_task_queue.h" 6 7 #include "base/debug/trace_event.h" 8 #include "base/location.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/synchronization/waitable_event.h" 11 12 namespace base { 13 namespace internal { 14 15 IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop) 16 : message_loop_(message_loop), 17 next_sequence_num_(0) { 18 } 19 20 bool IncomingTaskQueue::AddToIncomingQueue( 21 const tracked_objects::Location& from_here, 22 const Closure& task, 23 TimeDelta delay, 24 bool nestable) { 25 AutoLock locked(incoming_queue_lock_); 26 PendingTask pending_task( 27 from_here, task, CalculateDelayedRuntime(delay), nestable); 28 return PostPendingTask(&pending_task); 29 } 30 31 bool IncomingTaskQueue::IsHighResolutionTimerEnabledForTesting() { 32 #if defined(OS_WIN) 33 return !high_resolution_timer_expiration_.is_null(); 34 #else 35 return true; 36 #endif 37 } 38 39 bool IncomingTaskQueue::IsIdleForTesting() { 40 AutoLock lock(incoming_queue_lock_); 41 return incoming_queue_.empty(); 42 } 43 44 void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { 45 // Make sure no tasks are lost. 46 DCHECK(work_queue->empty()); 47 48 // Acquire all we can from the inter-thread queue with one lock acquisition. 49 AutoLock lock(incoming_queue_lock_); 50 if (!incoming_queue_.empty()) 51 incoming_queue_.Swap(work_queue); // Constant time 52 53 DCHECK(incoming_queue_.empty()); 54 } 55 56 void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { 57 #if defined(OS_WIN) 58 // If we left the high-resolution timer activated, deactivate it now. 59 // Doing this is not-critical, it is mainly to make sure we track 60 // the high resolution timer activations properly in our unit tests. 61 if (!high_resolution_timer_expiration_.is_null()) { 62 Time::ActivateHighResolutionTimer(false); 63 high_resolution_timer_expiration_ = TimeTicks(); 64 } 65 #endif 66 67 AutoLock lock(incoming_queue_lock_); 68 message_loop_ = NULL; 69 } 70 71 IncomingTaskQueue::~IncomingTaskQueue() { 72 // Verify that WillDestroyCurrentMessageLoop() has been called. 73 DCHECK(!message_loop_); 74 } 75 76 TimeTicks IncomingTaskQueue::CalculateDelayedRuntime(TimeDelta delay) { 77 TimeTicks delayed_run_time; 78 if (delay > TimeDelta()) { 79 delayed_run_time = TimeTicks::Now() + delay; 80 81 #if defined(OS_WIN) 82 if (high_resolution_timer_expiration_.is_null()) { 83 // Windows timers are granular to 15.6ms. If we only set high-res 84 // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms, 85 // which as a percentage is pretty inaccurate. So enable high 86 // res timers for any timer which is within 2x of the granularity. 87 // This is a tradeoff between accuracy and power management. 88 bool needs_high_res_timers = delay.InMilliseconds() < 89 (2 * Time::kMinLowResolutionThresholdMs); 90 if (needs_high_res_timers) { 91 if (Time::ActivateHighResolutionTimer(true)) { 92 high_resolution_timer_expiration_ = TimeTicks::Now() + 93 TimeDelta::FromMilliseconds( 94 MessageLoop::kHighResolutionTimerModeLeaseTimeMs); 95 } 96 } 97 } 98 #endif 99 } else { 100 DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative"; 101 } 102 103 #if defined(OS_WIN) 104 if (!high_resolution_timer_expiration_.is_null()) { 105 if (TimeTicks::Now() > high_resolution_timer_expiration_) { 106 Time::ActivateHighResolutionTimer(false); 107 high_resolution_timer_expiration_ = TimeTicks(); 108 } 109 } 110 #endif 111 112 return delayed_run_time; 113 } 114 115 bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { 116 // Warning: Don't try to short-circuit, and handle this thread's tasks more 117 // directly, as it could starve handling of foreign threads. Put every task 118 // into this queue. 119 120 // This should only be called while the lock is taken. 121 incoming_queue_lock_.AssertAcquired(); 122 123 if (!message_loop_) { 124 pending_task->task.Reset(); 125 return false; 126 } 127 128 // Initialize the sequence number. The sequence number is used for delayed 129 // tasks (to faciliate FIFO sorting when two tasks have the same 130 // delayed_run_time value) and for identifying the task in about:tracing. 131 pending_task->sequence_num = next_sequence_num_++; 132 133 TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), 134 "MessageLoop::PostTask", 135 TRACE_ID_MANGLE(message_loop_->GetTaskTraceID(*pending_task))); 136 137 bool was_empty = incoming_queue_.empty(); 138 incoming_queue_.push(*pending_task); 139 pending_task->task.Reset(); 140 141 // Wake up the pump. 142 message_loop_->ScheduleWork(was_empty); 143 144 return true; 145 } 146 147 } // namespace internal 148 } // namespace base 149