Home | History | Annotate | Download | only in base
      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 
     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 
     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 
     51 int64 Task::CurrentTime() {
     52   return GetRunner()->CurrentTime();
     53 }
     54 
     55 int64 Task::ElapsedTime() {
     56   return CurrentTime() - start_time_;
     57 }
     58 
     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 
     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 
    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 
    166 void Task::Wake() {
    167   if (done_)
    168     return;
    169   if (blocked_) {
    170     blocked_ = false;
    171     GetRunner()->WakeTasks();
    172   }
    173 }
    174 
    175 void Task::Error() {
    176   if (error_ || done_)
    177     return;
    178   error_ = true;
    179   Wake();
    180 }
    181 
    182 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 
    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 
    222 void Task::Stop() {
    223   // No need to wake because we're either awake or in abort
    224   TaskParent::OnStopped(this);
    225 }
    226 
    227 void Task::set_timeout_seconds(const int timeout_seconds) {
    228   timeout_seconds_ = timeout_seconds;
    229   ResetTimeout();
    230 }
    231 
    232 bool Task::TimedOut() {
    233   return timeout_seconds_ &&
    234     timeout_time_ &&
    235     CurrentTime() >= timeout_time_;
    236 }
    237 
    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 
    252 void Task::ClearTimeout() {
    253   int64 previous_timeout_time = timeout_time_;
    254   timeout_time_ = 0;
    255   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
    256 }
    257 
    258 void Task::SuspendTimeout() {
    259   if (!timeout_suspended_) {
    260     timeout_suspended_ = true;
    261     ResetTimeout();
    262   }
    263 }
    264 
    265 void Task::ResumeTimeout() {
    266   if (timeout_suspended_) {
    267     timeout_suspended_ = false;
    268     ResetTimeout();
    269   }
    270 }
    271 
    272 } // namespace rtc
    273