• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 Task::CurrentTime() {
52   return GetRunner()->CurrentTime();
53 }
54 
ElapsedTime()55 int64 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 #ifdef _DEBUG
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 #ifdef _DEBUG
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 _DEBUG
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 #ifdef _DEBUG
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 
set_timeout_seconds(const int timeout_seconds)227 void Task::set_timeout_seconds(const int timeout_seconds) {
228   timeout_seconds_ = timeout_seconds;
229   ResetTimeout();
230 }
231 
TimedOut()232 bool Task::TimedOut() {
233   return timeout_seconds_ &&
234     timeout_time_ &&
235     CurrentTime() >= timeout_time_;
236 }
237 
ResetTimeout()238 void Task::ResetTimeout() {
239   int64 previous_timeout_time = timeout_time_;
240   bool timeout_allowed = (state_ != STATE_INIT)
241                       && (state_ != STATE_DONE)
242                       && (state_ != STATE_ERROR);
243   if (timeout_seconds_ && timeout_allowed && !timeout_suspended_)
244     timeout_time_ = CurrentTime() +
245                     (timeout_seconds_ * kSecToMsec * kMsecTo100ns);
246   else
247     timeout_time_ = 0;
248 
249   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
250 }
251 
ClearTimeout()252 void Task::ClearTimeout() {
253   int64 previous_timeout_time = timeout_time_;
254   timeout_time_ = 0;
255   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
256 }
257 
SuspendTimeout()258 void Task::SuspendTimeout() {
259   if (!timeout_suspended_) {
260     timeout_suspended_ = true;
261     ResetTimeout();
262   }
263 }
264 
ResumeTimeout()265 void Task::ResumeTimeout() {
266   if (timeout_suspended_) {
267     timeout_suspended_ = false;
268     ResetTimeout();
269   }
270 }
271 
272 } // namespace rtc
273