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_t 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_t Task::CurrentTime() {
     52   return GetRunner()->CurrentTime();
     53 }
     54 
     55 int64_t 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 #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 
    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 
    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 int Task::ProcessResponse() {
    228   return STATE_DONE;
    229 }
    230 
    231 void Task::set_timeout_seconds(const int timeout_seconds) {
    232   timeout_seconds_ = timeout_seconds;
    233   ResetTimeout();
    234 }
    235 
    236 bool Task::TimedOut() {
    237   return timeout_seconds_ &&
    238     timeout_time_ &&
    239     CurrentTime() >= timeout_time_;
    240 }
    241 
    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 
    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 
    262 void Task::SuspendTimeout() {
    263   if (!timeout_suspended_) {
    264     timeout_suspended_ = true;
    265     ResetTimeout();
    266   }
    267 }
    268 
    269 void Task::ResumeTimeout() {
    270   if (timeout_suspended_) {
    271     timeout_suspended_ = false;
    272     ResetTimeout();
    273   }
    274 }
    275 
    276 int Task::OnTimeout() {
    277   // by default, we are finished after timing out
    278   return STATE_DONE;
    279 }
    280 
    281 } // namespace rtc
    282