blob: 1fe3af484089f61f820e6a0b732b5ce10ccb32eb [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
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"
nisseede5da42017-01-12 05:15:36 -080012#include "webrtc/base/checks.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000013#include "webrtc/base/taskrunner.h"
14
15namespace rtc {
16
Peter Boström0c4e06b2015-10-07 12:23:21 +020017int32_t Task::unique_id_seed_ = 0;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000018
19Task::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
nisseede5da42017-01-12 05:15:36 -080034 RTC_DCHECK(unique_id_ < unique_id_seed_);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000035}
36
37Task::~Task() {
38 // Is this task being deleted in the correct manner?
nisseede5da42017-01-12 05:15:36 -080039#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_);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000044
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
Peter Boström0c4e06b2015-10-07 12:23:21 +020053int64_t Task::CurrentTime() {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000054 return GetRunner()->CurrentTime();
55}
56
Peter Boström0c4e06b2015-10-07 12:23:21 +020057int64_t Task::ElapsedTime() {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000058 return CurrentTime() - start_time_;
59}
60
61void 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
71void Task::Step() {
72 if (done_) {
nisseede5da42017-01-12 05:15:36 -080073#if RTC_DCHECK_IS_ON
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000074 // 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.
nisseede5da42017-01-12 05:15:36 -080077 RTC_DCHECK(blocked_);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000078#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();
nisseede5da42017-01-12 05:15:36 -080093#if RTC_DCHECK_IS_ON
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000094 // verify that stop removed this from its parent
nisseede5da42017-01-12 05:15:36 -080095 RTC_DCHECK(!parent()->IsChildTask(this));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000096#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();
nisseede5da42017-01-12 05:15:36 -0800130#if RTC_DCHECK_IS_ON
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000131 // verify that stop removed this from its parent
nisseede5da42017-01-12 05:15:36 -0800132 RTC_DCHECK(!parent()->IsChildTask(this));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000133#endif
134 blocked_ = true;
135 }
136}
137
138void 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();
nisseede5da42017-01-12 05:15:36 -0800155#if RTC_DCHECK_IS_ON
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000156 // verify that stop removed this from its parent
nisseede5da42017-01-12 05:15:36 -0800157 RTC_DCHECK(!parent()->IsChildTask(this));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000158#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
168void Task::Wake() {
169 if (done_)
170 return;
171 if (blocked_) {
172 blocked_ = false;
173 GetRunner()->WakeTasks();
174 }
175}
176
177void Task::Error() {
178 if (error_ || done_)
179 return;
180 error_ = true;
181 Wake();
182}
183
184std::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
196int 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
224void Task::Stop() {
225 // No need to wake because we're either awake or in abort
226 TaskParent::OnStopped(this);
227}
228
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000229int Task::ProcessResponse() {
230 return STATE_DONE;
231}
232
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000233void Task::set_timeout_seconds(const int timeout_seconds) {
234 timeout_seconds_ = timeout_seconds;
235 ResetTimeout();
236}
237
238bool Task::TimedOut() {
239 return timeout_seconds_ &&
240 timeout_time_ &&
241 CurrentTime() >= timeout_time_;
242}
243
244void Task::ResetTimeout() {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200245 int64_t previous_timeout_time = timeout_time_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000246 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
258void Task::ClearTimeout() {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200259 int64_t previous_timeout_time = timeout_time_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000260 timeout_time_ = 0;
261 GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
262}
263
264void Task::SuspendTimeout() {
265 if (!timeout_suspended_) {
266 timeout_suspended_ = true;
267 ResetTimeout();
268 }
269}
270
271void Task::ResumeTimeout() {
272 if (timeout_suspended_) {
273 timeout_suspended_ = false;
274 ResetTimeout();
275 }
276}
277
kwiberg@webrtc.org67186fe2015-03-09 22:21:53 +0000278int Task::OnTimeout() {
279 // by default, we are finished after timing out
280 return STATE_DONE;
281}
282
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000283} // namespace rtc