OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/base/task.h" | |
12 #include "webrtc/base/checks.h" | |
13 #include "webrtc/base/taskrunner.h" | |
14 | |
15 namespace rtc { | |
16 | |
17 int32_t Task::unique_id_seed_ = 0; | |
18 | |
19 Task::Task(TaskParent *parent) | |
20 : TaskParent(this, parent), | |
21 state_(STATE_INIT), | |
22 blocked_(false), | |
23 done_(false), | |
24 aborted_(false), | |
25 busy_(false), | |
26 error_(false), | |
27 start_time_(0), | |
28 timeout_time_(0), | |
29 timeout_seconds_(0), | |
30 timeout_suspended_(false) { | |
31 unique_id_ = unique_id_seed_++; | |
32 | |
33 // sanity check that we didn't roll-over our id seed | |
34 RTC_DCHECK(unique_id_ < unique_id_seed_); | |
35 } | |
36 | |
37 Task::~Task() { | |
38 // Is this task being deleted in the correct manner? | |
39 #if RTC_DCHECK_IS_ON | |
40 RTC_DCHECK(!done_ || GetRunner()->is_ok_to_delete(this)); | |
41 #endif | |
42 RTC_DCHECK(state_ == STATE_INIT || done_); | |
43 RTC_DCHECK(state_ == STATE_INIT || blocked_); | |
44 | |
45 // If the task is being deleted without being done, it | |
46 // means that it hasn't been removed from its parent. | |
47 // This happens if a task is deleted outside of TaskRunner. | |
48 if (!done_) { | |
49 Stop(); | |
50 } | |
51 } | |
52 | |
53 int64_t Task::CurrentTime() { | |
54 return GetRunner()->CurrentTime(); | |
55 } | |
56 | |
57 int64_t Task::ElapsedTime() { | |
58 return CurrentTime() - start_time_; | |
59 } | |
60 | |
61 void Task::Start() { | |
62 if (state_ != STATE_INIT) | |
63 return; | |
64 // Set the start time before starting the task. Otherwise if the task | |
65 // finishes quickly and deletes the Task object, setting start_time_ | |
66 // will crash. | |
67 start_time_ = CurrentTime(); | |
68 GetRunner()->StartTask(this); | |
69 } | |
70 | |
71 void Task::Step() { | |
72 if (done_) { | |
73 #if RTC_DCHECK_IS_ON | |
74 // we do not know how !blocked_ happens when done_ - should be impossible. | |
75 // But it causes problems, so in retail build, we force blocked_, and | |
76 // under debug we assert. | |
77 RTC_DCHECK(blocked_); | |
78 #else | |
79 blocked_ = true; | |
80 #endif | |
81 return; | |
82 } | |
83 | |
84 // Async Error() was called | |
85 if (error_) { | |
86 done_ = true; | |
87 state_ = STATE_ERROR; | |
88 blocked_ = true; | |
89 // obsolete - an errored task is not considered done now | |
90 // SignalDone(); | |
91 | |
92 Stop(); | |
93 #if RTC_DCHECK_IS_ON | |
94 // verify that stop removed this from its parent | |
95 RTC_DCHECK(!parent()->IsChildTask(this)); | |
96 #endif | |
97 return; | |
98 } | |
99 | |
100 busy_ = true; | |
101 int new_state = Process(state_); | |
102 busy_ = false; | |
103 | |
104 if (aborted_) { | |
105 Abort(true); // no need to wake because we're awake | |
106 return; | |
107 } | |
108 | |
109 if (new_state == STATE_BLOCKED) { | |
110 blocked_ = true; | |
111 // Let the timeout continue | |
112 } else { | |
113 state_ = new_state; | |
114 blocked_ = false; | |
115 ResetTimeout(); | |
116 } | |
117 | |
118 if (new_state == STATE_DONE) { | |
119 done_ = true; | |
120 } else if (new_state == STATE_ERROR) { | |
121 done_ = true; | |
122 error_ = true; | |
123 } | |
124 | |
125 if (done_) { | |
126 // obsolete - call this yourself | |
127 // SignalDone(); | |
128 | |
129 Stop(); | |
130 #if RTC_DCHECK_IS_ON | |
131 // verify that stop removed this from its parent | |
132 RTC_DCHECK(!parent()->IsChildTask(this)); | |
133 #endif | |
134 blocked_ = true; | |
135 } | |
136 } | |
137 | |
138 void Task::Abort(bool nowake) { | |
139 // Why only check for done_ (instead of "aborted_ || done_")? | |
140 // | |
141 // If aborted_ && !done_, it means the logic for aborting still | |
142 // needs to be executed (because busy_ must have been true when | |
143 // Abort() was previously called). | |
144 if (done_) | |
145 return; | |
146 aborted_ = true; | |
147 if (!busy_) { | |
148 done_ = true; | |
149 blocked_ = true; | |
150 error_ = true; | |
151 | |
152 // "done_" is set before calling "Stop()" to ensure that this code | |
153 // doesn't execute more than once (recursively) for the same task. | |
154 Stop(); | |
155 #if RTC_DCHECK_IS_ON | |
156 // verify that stop removed this from its parent | |
157 RTC_DCHECK(!parent()->IsChildTask(this)); | |
158 #endif | |
159 if (!nowake) { | |
160 // WakeTasks to self-delete. | |
161 // Don't call Wake() because it is a no-op after "done_" is set. | |
162 // Even if Wake() did run, it clears "blocked_" which isn't desireable. | |
163 GetRunner()->WakeTasks(); | |
164 } | |
165 } | |
166 } | |
167 | |
168 void Task::Wake() { | |
169 if (done_) | |
170 return; | |
171 if (blocked_) { | |
172 blocked_ = false; | |
173 GetRunner()->WakeTasks(); | |
174 } | |
175 } | |
176 | |
177 void Task::Error() { | |
178 if (error_ || done_) | |
179 return; | |
180 error_ = true; | |
181 Wake(); | |
182 } | |
183 | |
184 std::string Task::GetStateName(int state) const { | |
185 switch (state) { | |
186 case STATE_BLOCKED: return "BLOCKED"; | |
187 case STATE_INIT: return "INIT"; | |
188 case STATE_START: return "START"; | |
189 case STATE_DONE: return "DONE"; | |
190 case STATE_ERROR: return "ERROR"; | |
191 case STATE_RESPONSE: return "RESPONSE"; | |
192 } | |
193 return "??"; | |
194 } | |
195 | |
196 int Task::Process(int state) { | |
197 int newstate = STATE_ERROR; | |
198 | |
199 if (TimedOut()) { | |
200 ClearTimeout(); | |
201 newstate = OnTimeout(); | |
202 SignalTimeout(); | |
203 } else { | |
204 switch (state) { | |
205 case STATE_INIT: | |
206 newstate = STATE_START; | |
207 break; | |
208 case STATE_START: | |
209 newstate = ProcessStart(); | |
210 break; | |
211 case STATE_RESPONSE: | |
212 newstate = ProcessResponse(); | |
213 break; | |
214 case STATE_DONE: | |
215 case STATE_ERROR: | |
216 newstate = STATE_BLOCKED; | |
217 break; | |
218 } | |
219 } | |
220 | |
221 return newstate; | |
222 } | |
223 | |
224 void Task::Stop() { | |
225 // No need to wake because we're either awake or in abort | |
226 TaskParent::OnStopped(this); | |
227 } | |
228 | |
229 int Task::ProcessResponse() { | |
230 return STATE_DONE; | |
231 } | |
232 | |
233 void Task::set_timeout_seconds(const int timeout_seconds) { | |
234 timeout_seconds_ = timeout_seconds; | |
235 ResetTimeout(); | |
236 } | |
237 | |
238 bool Task::TimedOut() { | |
239 return timeout_seconds_ && | |
240 timeout_time_ && | |
241 CurrentTime() >= timeout_time_; | |
242 } | |
243 | |
244 void Task::ResetTimeout() { | |
245 int64_t previous_timeout_time = timeout_time_; | |
246 bool timeout_allowed = (state_ != STATE_INIT) | |
247 && (state_ != STATE_DONE) | |
248 && (state_ != STATE_ERROR); | |
249 if (timeout_seconds_ && timeout_allowed && !timeout_suspended_) | |
250 timeout_time_ = CurrentTime() + | |
251 (timeout_seconds_ * kSecToMsec * kMsecTo100ns); | |
252 else | |
253 timeout_time_ = 0; | |
254 | |
255 GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); | |
256 } | |
257 | |
258 void Task::ClearTimeout() { | |
259 int64_t previous_timeout_time = timeout_time_; | |
260 timeout_time_ = 0; | |
261 GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); | |
262 } | |
263 | |
264 void Task::SuspendTimeout() { | |
265 if (!timeout_suspended_) { | |
266 timeout_suspended_ = true; | |
267 ResetTimeout(); | |
268 } | |
269 } | |
270 | |
271 void Task::ResumeTimeout() { | |
272 if (timeout_suspended_) { | |
273 timeout_suspended_ = false; | |
274 ResetTimeout(); | |
275 } | |
276 } | |
277 | |
278 int Task::OnTimeout() { | |
279 // by default, we are finished after timing out | |
280 return STATE_DONE; | |
281 } | |
282 | |
283 } // namespace rtc | |
OLD | NEW |