Home | History | Annotate | Download | only in base
      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 
     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 
     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 
     68 int64 Task::CurrentTime() {
     69   return GetRunner()->CurrentTime();
     70 }
     71 
     72 int64 Task::ElapsedTime() {
     73   return CurrentTime() - start_time_;
     74 }
     75 
     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 
     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 
    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 
    183 void Task::Wake() {
    184   if (done_)
    185     return;
    186   if (blocked_) {
    187     blocked_ = false;
    188     GetRunner()->WakeTasks();
    189   }
    190 }
    191 
    192 void Task::Error() {
    193   if (error_ || done_)
    194     return;
    195   error_ = true;
    196   Wake();
    197 }
    198 
    199 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 
    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 
    246 void Task::Stop() {
    247   // No need to wake because we're either awake or in abort
    248   TaskParent::OnStopped(this);
    249 }
    250 
    251 void Task::set_timeout_seconds(const int timeout_seconds) {
    252   timeout_seconds_ = timeout_seconds;
    253   ResetTimeout();
    254 }
    255 
    256 bool Task::TimedOut() {
    257   return timeout_seconds_ &&
    258     timeout_time_ &&
    259     CurrentTime() >= timeout_time_;
    260 }
    261 
    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 
    276 void Task::ClearTimeout() {
    277   int64 previous_timeout_time = timeout_time_;
    278   timeout_time_ = 0;
    279   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time);
    280 }
    281 
    282 void Task::SuspendTimeout() {
    283   if (!timeout_suspended_) {
    284     timeout_suspended_ = true;
    285     ResetTimeout();
    286   }
    287 }
    288 
    289 void Task::ResumeTimeout() {
    290   if (timeout_suspended_) {
    291     timeout_suspended_ = false;
    292     ResetTimeout();
    293   }
    294 }
    295 
    296 } // namespace talk_base
    297