• 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   static const std::string STR_BLOCKED("BLOCKED");
201   static const std::string STR_INIT("INIT");
202   static const std::string STR_START("START");
203   static const std::string STR_DONE("DONE");
204   static const std::string STR_ERROR("ERROR");
205   static const std::string STR_RESPONSE("RESPONSE");
206   static const std::string STR_HUH("??");
207   switch (state) {
208     case STATE_BLOCKED: return STR_BLOCKED;
209     case STATE_INIT: return STR_INIT;
210     case STATE_START: return STR_START;
211     case STATE_DONE: return STR_DONE;
212     case STATE_ERROR: return STR_ERROR;
213     case STATE_RESPONSE: return STR_RESPONSE;
214   }
215   return STR_HUH;
216 }
217 
Process(int state)218 int Task::Process(int state) {
219   int newstate = STATE_ERROR;
220 
221   if (TimedOut()) {
222     ClearTimeout();
223     newstate = OnTimeout();
224     SignalTimeout();
225   } else {
226     switch (state) {
227       case STATE_INIT:
228         newstate = STATE_START;
229         break;
230       case STATE_START:
231         newstate = ProcessStart();
232         break;
233       case STATE_RESPONSE:
234         newstate = ProcessResponse();
235         break;
236       case STATE_DONE:
237       case STATE_ERROR:
238         newstate = STATE_BLOCKED;
239         break;
240     }
241   }
242 
243   return newstate;
244 }
245 
Stop()246 void Task::Stop() {
247   // No need to wake because we're either awake or in abort
248   TaskParent::OnStopped(this);
249 }
250 
set_timeout_seconds(const int timeout_seconds)251 void Task::set_timeout_seconds(const int timeout_seconds) {
252   timeout_seconds_ = timeout_seconds;
253   ResetTimeout();
254 }
255 
TimedOut()256 bool Task::TimedOut() {
257   return timeout_seconds_ &&
258     timeout_time_ &&
259     CurrentTime() >= timeout_time_;
260 }
261 
ResetTimeout()262 void Task::ResetTimeout() {
263   int64 previous_timeout_time = timeout_time_;
264   bool timeout_allowed = (state_ != STATE_INIT)
265                       && (state_ != STATE_DONE)
266                       && (state_ != STATE_ERROR);
267   if (timeout_seconds_ && timeout_allowed && !timeout_suspended_)
268     timeout_time_ = CurrentTime() +
269                     (timeout_seconds_ * kSecToMsec * kMsecTo100ns);
270   else
271     timeout_time_ = 0;
272 
273   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
274 }
275 
ClearTimeout()276 void Task::ClearTimeout() {
277   int64 previous_timeout_time = timeout_time_;
278   timeout_time_ = 0;
279   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
280 }
281 
SuspendTimeout()282 void Task::SuspendTimeout() {
283   if (!timeout_suspended_) {
284     timeout_suspended_ = true;
285     ResetTimeout();
286   }
287 }
288 
ResumeTimeout()289 void Task::ResumeTimeout() {
290   if (timeout_suspended_) {
291     timeout_suspended_ = false;
292     ResetTimeout();
293   }
294 }
295 
296 } // namespace talk_base
297