OLD | NEW |
---|---|
(Empty) | |
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_); | |
jar1
2013/07/29 23:30:50
Calling to acquire the lock here is IMO much too e
alexeypa (please no reviews)
2013/07/30 00:31:13
Before I made this change, there were two locks. O
| |
26 PendingTask pending_task( | |
27 from_here, task, CalculateDelayedRuntime(delay), nestable); | |
jar1
2013/07/29 23:30:50
The rule with locks is to try to protect data, and
| |
28 return PostPendingTask(&pending_task); | |
jar1
2013/07/29 23:30:50
Here again we're calling to do work while holding
| |
29 } | |
30 | |
31 bool IncomingTaskQueue::TryAddToIncomingQueue( | |
32 const tracked_objects::Location& from_here, | |
33 const Closure& task) { | |
34 if (!incoming_queue_lock_.Try()) { | |
35 // Reset |task|. | |
36 Closure local_task = task; | |
37 return false; | |
38 } | |
39 | |
40 AutoLock locked(incoming_queue_lock_, AutoLock::AlreadyAcquired()); | |
41 PendingTask pending_task( | |
42 from_here, task, CalculateDelayedRuntime(TimeDelta()), true); | |
43 return PostPendingTask(&pending_task); | |
44 } | |
45 | |
46 bool IncomingTaskQueue::IsHighResolutionTimerEnabledForTesting() { | |
47 #if defined(OS_WIN) | |
48 return !high_resolution_timer_expiration_.is_null(); | |
49 #else | |
50 return true; | |
51 #endif | |
52 } | |
53 | |
54 bool IncomingTaskQueue::IsIdleForTesting() { | |
55 AutoLock lock(incoming_queue_lock_); | |
56 return incoming_queue_.empty(); | |
57 } | |
58 | |
59 void IncomingTaskQueue::LockWaitUnLockForTesting(WaitableEvent* caller_wait, | |
60 WaitableEvent* caller_signal) { | |
61 AutoLock lock(incoming_queue_lock_); | |
62 caller_wait->Signal(); | |
63 caller_signal->Wait(); | |
64 } | |
65 | |
66 void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { | |
67 // Make sure no tasks are lost. | |
68 DCHECK(work_queue->empty()); | |
69 | |
70 // Acquire all we can from the inter-thread queue with one lock acquisition. | |
71 AutoLock lock(incoming_queue_lock_); | |
72 if (!incoming_queue_.empty()) | |
73 incoming_queue_.Swap(work_queue); // Constant time | |
74 | |
75 DCHECK(incoming_queue_.empty()); | |
76 } | |
77 | |
78 void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { | |
79 #if defined(OS_WIN) | |
80 // If we left the high-resolution timer activated, deactivate it now. | |
81 // Doing this is not-critical, it is mainly to make sure we track | |
82 // the high resolution timer activations properly in our unit tests. | |
83 if (!high_resolution_timer_expiration_.is_null()) { | |
84 Time::ActivateHighResolutionTimer(false); | |
85 high_resolution_timer_expiration_ = TimeTicks(); | |
86 } | |
87 #endif | |
88 | |
89 AutoLock lock(incoming_queue_lock_); | |
90 message_loop_ = NULL; | |
91 } | |
92 | |
93 IncomingTaskQueue::~IncomingTaskQueue() { | |
94 // Verify that WillDestroyCurrentMessageLoop() has been called. | |
95 DCHECK(!message_loop_); | |
96 } | |
97 | |
98 TimeTicks IncomingTaskQueue::CalculateDelayedRuntime(TimeDelta delay) { | |
99 TimeTicks delayed_run_time; | |
100 if (delay > TimeDelta()) { | |
101 delayed_run_time = TimeTicks::Now() + delay; | |
102 | |
103 #if defined(OS_WIN) | |
104 if (high_resolution_timer_expiration_.is_null()) { | |
105 // Windows timers are granular to 15.6ms. If we only set high-res | |
106 // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms, | |
107 // which as a percentage is pretty inaccurate. So enable high | |
108 // res timers for any timer which is within 2x of the granularity. | |
109 // This is a tradeoff between accuracy and power management. | |
110 bool needs_high_res_timers = delay.InMilliseconds() < | |
111 (2 * Time::kMinLowResolutionThresholdMs); | |
112 if (needs_high_res_timers) { | |
113 if (Time::ActivateHighResolutionTimer(true)) { | |
114 high_resolution_timer_expiration_ = TimeTicks::Now() + | |
115 TimeDelta::FromMilliseconds( | |
116 MessageLoop::kHighResolutionTimerModeLeaseTimeMs); | |
117 } | |
118 } | |
119 } | |
120 #endif | |
121 } else { | |
122 DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative"; | |
123 } | |
124 | |
125 #if defined(OS_WIN) | |
126 if (!high_resolution_timer_expiration_.is_null()) { | |
127 if (TimeTicks::Now() > high_resolution_timer_expiration_) { | |
128 Time::ActivateHighResolutionTimer(false); | |
129 high_resolution_timer_expiration_ = TimeTicks(); | |
130 } | |
131 } | |
132 #endif | |
133 | |
134 return delayed_run_time; | |
135 } | |
136 | |
137 bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { | |
138 // Warning: Don't try to short-circuit, and handle this thread's tasks more | |
139 // directly, as it could starve handling of foreign threads. Put every task | |
140 // into this queue. | |
141 | |
142 // This should only be called while the lock is taken. | |
143 incoming_queue_lock_.AssertAcquired(); | |
jar1
2013/07/29 23:30:50
It is good that you had this.... but if this stays
| |
144 | |
145 if (!message_loop_) { | |
146 pending_task->task.Reset(); | |
147 return false; | |
148 } | |
149 | |
150 // Initialize the sequence number. The sequence number is used for delayed | |
151 // tasks (to faciliate FIFO sorting when two tasks have the same | |
152 // delayed_run_time value) and for identifying the task in about:tracing. | |
153 pending_task->sequence_num = next_sequence_num_++; | |
154 | |
155 TRACE_EVENT_FLOW_BEGIN0("task", "MessageLoop::PostTask", | |
156 TRACE_ID_MANGLE(message_loop_->GetTaskTraceID(*pending_task))); | |
157 | |
158 bool was_empty = incoming_queue_.empty(); | |
159 incoming_queue_.push(*pending_task); | |
160 pending_task->task.Reset(); | |
161 | |
162 // Wake up the pump. | |
163 message_loop_->ScheduleWork(was_empty); | |
jar1
2013/07/29 23:30:50
This is where things really get dicey. The lock w
alexeypa (please no reviews)
2013/07/30 00:31:13
This is new code, so not really. The lock was inte
| |
164 | |
165 return true; | |
166 } | |
167 | |
168 } // namespace internal | |
169 } // namespace base | |
OLD | NEW |