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