• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2004--2006, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "talk/base/task.h"
29 #include "talk/base/common.h"
30 #include "talk/base/taskrunner.h"
31 
32 namespace talk_base {
33 
34 int32 Task::unique_id_seed_ = 0;
35 
Task(TaskParent * parent)36 Task::Task(TaskParent *parent)
37     : TaskParent(this, parent),
38       state_(STATE_INIT),
39       blocked_(false),
40       done_(false),
41       aborted_(false),
42       busy_(false),
43       error_(false),
44       start_time_(0),
45       timeout_time_(0),
46       timeout_seconds_(0),
47       timeout_suspended_(false)  {
48   unique_id_ = unique_id_seed_++;
49 
50   // sanity check that we didn't roll-over our id seed
51   ASSERT(unique_id_ < unique_id_seed_);
52 }
53 
~Task()54 Task::~Task() {
55   // Is this task being deleted in the correct manner?
56   ASSERT(!done_ || GetRunner()->is_ok_to_delete(this));
57   ASSERT(state_ == STATE_INIT || done_);
58   ASSERT(state_ == STATE_INIT || blocked_);
59 
60   // If the task is being deleted without being done, it
61   // means that it hasn't been removed from its parent.
62   // This happens if a task is deleted outside of TaskRunner.
63   if (!done_) {
64     Stop();
65   }
66 }
67 
CurrentTime()68 int64 Task::CurrentTime() {
69   return GetRunner()->CurrentTime();
70 }
71 
ElapsedTime()72 int64 Task::ElapsedTime() {
73   return CurrentTime() - start_time_;
74 }
75 
Start()76 void Task::Start() {
77   if (state_ != STATE_INIT)
78     return;
79   // Set the start time before starting the task.  Otherwise if the task
80   // finishes quickly and deletes the Task object, setting start_time_
81   // will crash.
82   start_time_ = CurrentTime();
83   GetRunner()->StartTask(this);
84 }
85 
Step()86 void Task::Step() {
87   if (done_) {
88 #ifdef _DEBUG
89     // we do not know how !blocked_ happens when done_ - should be impossible.
90     // But it causes problems, so in retail build, we force blocked_, and
91     // under debug we assert.
92     ASSERT(blocked_);
93 #else
94     blocked_ = true;
95 #endif
96     return;
97   }
98 
99   // Async Error() was called
100   if (error_) {
101     done_ = true;
102     state_ = STATE_ERROR;
103     blocked_ = true;
104 //   obsolete - an errored task is not considered done now
105 //   SignalDone();
106 
107     Stop();
108 #ifdef _DEBUG
109     // verify that stop removed this from its parent
110     ASSERT(!parent()->IsChildTask(this));
111 #endif
112     return;
113   }
114 
115   busy_ = true;
116   int new_state = Process(state_);
117   busy_ = false;
118 
119   if (aborted_) {
120     Abort(true);  // no need to wake because we're awake
121     return;
122   }
123 
124   if (new_state == STATE_BLOCKED) {
125     blocked_ = true;
126     // Let the timeout continue
127   } else {
128     state_ = new_state;
129     blocked_ = false;
130     ResetTimeout();
131   }
132 
133   if (new_state == STATE_DONE) {
134     done_ = true;
135   } else if (new_state == STATE_ERROR) {
136     done_ = true;
137     error_ = true;
138   }
139 
140   if (done_) {
141 //  obsolete - call this yourself
142 //    SignalDone();
143 
144     Stop();
145 #if _DEBUG
146     // verify that stop removed this from its parent
147     ASSERT(!parent()->IsChildTask(this));
148 #endif
149     blocked_ = true;
150   }
151 }
152 
Abort(bool nowake)153 void Task::Abort(bool nowake) {
154   // Why only check for done_ (instead of "aborted_ || done_")?
155   //
156   // If aborted_ && !done_, it means the logic for aborting still
157   // needs to be executed (because busy_ must have been true when
158   // Abort() was previously called).
159   if (done_)
160     return;
161   aborted_ = true;
162   if (!busy_) {
163     done_ = true;
164     blocked_ = true;
165     error_ = true;
166 
167     // "done_" is set before calling "Stop()" to ensure that this code
168     // doesn't execute more than once (recursively) for the same task.
169     Stop();
170 #ifdef _DEBUG
171     // verify that stop removed this from its parent
172     ASSERT(!parent()->IsChildTask(this));
173 #endif
174     if (!nowake) {
175       // WakeTasks to self-delete.
176       // Don't call Wake() because it is a no-op after "done_" is set.
177       // Even if Wake() did run, it clears "blocked_" which isn't desireable.
178       GetRunner()->WakeTasks();
179     }
180   }
181 }
182 
Wake()183 void Task::Wake() {
184   if (done_)
185     return;
186   if (blocked_) {
187     blocked_ = false;
188     GetRunner()->WakeTasks();
189   }
190 }
191 
Error()192 void Task::Error() {
193   if (error_ || done_)
194     return;
195   error_ = true;
196   Wake();
197 }
198 
GetStateName(int state) const199 std::string Task::GetStateName(int state) const {
200   switch (state) {
201     case STATE_BLOCKED: return "BLOCKED";
202     case STATE_INIT: return "INIT";
203     case STATE_START: return "START";
204     case STATE_DONE: return "DONE";
205     case STATE_ERROR: return "ERROR";
206     case STATE_RESPONSE: return "RESPONSE";
207   }
208   return "??";
209 }
210 
Process(int state)211 int Task::Process(int state) {
212   int newstate = STATE_ERROR;
213 
214   if (TimedOut()) {
215     ClearTimeout();
216     newstate = OnTimeout();
217     SignalTimeout();
218   } else {
219     switch (state) {
220       case STATE_INIT:
221         newstate = STATE_START;
222         break;
223       case STATE_START:
224         newstate = ProcessStart();
225         break;
226       case STATE_RESPONSE:
227         newstate = ProcessResponse();
228         break;
229       case STATE_DONE:
230       case STATE_ERROR:
231         newstate = STATE_BLOCKED;
232         break;
233     }
234   }
235 
236   return newstate;
237 }
238 
Stop()239 void Task::Stop() {
240   // No need to wake because we're either awake or in abort
241   TaskParent::OnStopped(this);
242 }
243 
set_timeout_seconds(const int timeout_seconds)244 void Task::set_timeout_seconds(const int timeout_seconds) {
245   timeout_seconds_ = timeout_seconds;
246   ResetTimeout();
247 }
248 
TimedOut()249 bool Task::TimedOut() {
250   return timeout_seconds_ &&
251     timeout_time_ &&
252     CurrentTime() >= timeout_time_;
253 }
254 
ResetTimeout()255 void Task::ResetTimeout() {
256   int64 previous_timeout_time = timeout_time_;
257   bool timeout_allowed = (state_ != STATE_INIT)
258                       && (state_ != STATE_DONE)
259                       && (state_ != STATE_ERROR);
260   if (timeout_seconds_ && timeout_allowed && !timeout_suspended_)
261     timeout_time_ = CurrentTime() +
262                     (timeout_seconds_ * kSecToMsec * kMsecTo100ns);
263   else
264     timeout_time_ = 0;
265 
266   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
267 }
268 
ClearTimeout()269 void Task::ClearTimeout() {
270   int64 previous_timeout_time = timeout_time_;
271   timeout_time_ = 0;
272   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
273 }
274 
SuspendTimeout()275 void Task::SuspendTimeout() {
276   if (!timeout_suspended_) {
277     timeout_suspended_ = true;
278     ResetTimeout();
279   }
280 }
281 
ResumeTimeout()282 void Task::ResumeTimeout() {
283   if (timeout_suspended_) {
284     timeout_suspended_ = false;
285     ResetTimeout();
286   }
287 }
288 
289 } // namespace talk_base
290