| /* | 
 |  *  Copyright 2004 The WebRTC Project Authors. All rights reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "webrtc/base/task.h" | 
 | #include "webrtc/base/common.h" | 
 | #include "webrtc/base/taskrunner.h" | 
 |  | 
 | namespace rtc { | 
 |  | 
 | int32_t Task::unique_id_seed_ = 0; | 
 |  | 
 | Task::Task(TaskParent *parent) | 
 |     : TaskParent(this, parent), | 
 |       state_(STATE_INIT), | 
 |       blocked_(false), | 
 |       done_(false), | 
 |       aborted_(false), | 
 |       busy_(false), | 
 |       error_(false), | 
 |       start_time_(0), | 
 |       timeout_time_(0), | 
 |       timeout_seconds_(0), | 
 |       timeout_suspended_(false)  { | 
 |   unique_id_ = unique_id_seed_++; | 
 |  | 
 |   // sanity check that we didn't roll-over our id seed | 
 |   ASSERT(unique_id_ < unique_id_seed_); | 
 | } | 
 |  | 
 | Task::~Task() { | 
 |   // Is this task being deleted in the correct manner? | 
 |   ASSERT(!done_ || GetRunner()->is_ok_to_delete(this)); | 
 |   ASSERT(state_ == STATE_INIT || done_); | 
 |   ASSERT(state_ == STATE_INIT || blocked_); | 
 |  | 
 |   // If the task is being deleted without being done, it | 
 |   // means that it hasn't been removed from its parent. | 
 |   // This happens if a task is deleted outside of TaskRunner. | 
 |   if (!done_) { | 
 |     Stop(); | 
 |   } | 
 | } | 
 |  | 
 | int64_t Task::CurrentTime() { | 
 |   return GetRunner()->CurrentTime(); | 
 | } | 
 |  | 
 | int64_t Task::ElapsedTime() { | 
 |   return CurrentTime() - start_time_; | 
 | } | 
 |  | 
 | void Task::Start() { | 
 |   if (state_ != STATE_INIT) | 
 |     return; | 
 |   // Set the start time before starting the task.  Otherwise if the task | 
 |   // finishes quickly and deletes the Task object, setting start_time_ | 
 |   // will crash. | 
 |   start_time_ = CurrentTime(); | 
 |   GetRunner()->StartTask(this); | 
 | } | 
 |  | 
 | void Task::Step() { | 
 |   if (done_) { | 
 | #if !defined(NDEBUG) | 
 |     // we do not know how !blocked_ happens when done_ - should be impossible. | 
 |     // But it causes problems, so in retail build, we force blocked_, and | 
 |     // under debug we assert. | 
 |     ASSERT(blocked_); | 
 | #else | 
 |     blocked_ = true; | 
 | #endif | 
 |     return; | 
 |   } | 
 |  | 
 |   // Async Error() was called | 
 |   if (error_) { | 
 |     done_ = true; | 
 |     state_ = STATE_ERROR; | 
 |     blocked_ = true; | 
 | //   obsolete - an errored task is not considered done now | 
 | //   SignalDone(); | 
 |  | 
 |     Stop(); | 
 | #if !defined(NDEBUG) | 
 |     // verify that stop removed this from its parent | 
 |     ASSERT(!parent()->IsChildTask(this)); | 
 | #endif | 
 |     return; | 
 |   } | 
 |  | 
 |   busy_ = true; | 
 |   int new_state = Process(state_); | 
 |   busy_ = false; | 
 |  | 
 |   if (aborted_) { | 
 |     Abort(true);  // no need to wake because we're awake | 
 |     return; | 
 |   } | 
 |  | 
 |   if (new_state == STATE_BLOCKED) { | 
 |     blocked_ = true; | 
 |     // Let the timeout continue | 
 |   } else { | 
 |     state_ = new_state; | 
 |     blocked_ = false; | 
 |     ResetTimeout(); | 
 |   } | 
 |  | 
 |   if (new_state == STATE_DONE) { | 
 |     done_ = true; | 
 |   } else if (new_state == STATE_ERROR) { | 
 |     done_ = true; | 
 |     error_ = true; | 
 |   } | 
 |  | 
 |   if (done_) { | 
 | //  obsolete - call this yourself | 
 | //    SignalDone(); | 
 |  | 
 |     Stop(); | 
 | #if !defined(NDEBUG) | 
 |     // verify that stop removed this from its parent | 
 |     ASSERT(!parent()->IsChildTask(this)); | 
 | #endif | 
 |     blocked_ = true; | 
 |   } | 
 | } | 
 |  | 
 | void Task::Abort(bool nowake) { | 
 |   // Why only check for done_ (instead of "aborted_ || done_")? | 
 |   // | 
 |   // If aborted_ && !done_, it means the logic for aborting still | 
 |   // needs to be executed (because busy_ must have been true when | 
 |   // Abort() was previously called). | 
 |   if (done_) | 
 |     return; | 
 |   aborted_ = true; | 
 |   if (!busy_) { | 
 |     done_ = true; | 
 |     blocked_ = true; | 
 |     error_ = true; | 
 |  | 
 |     // "done_" is set before calling "Stop()" to ensure that this code  | 
 |     // doesn't execute more than once (recursively) for the same task. | 
 |     Stop(); | 
 | #if !defined(NDEBUG) | 
 |     // verify that stop removed this from its parent | 
 |     ASSERT(!parent()->IsChildTask(this)); | 
 | #endif | 
 |     if (!nowake) { | 
 |       // WakeTasks to self-delete. | 
 |       // Don't call Wake() because it is a no-op after "done_" is set. | 
 |       // Even if Wake() did run, it clears "blocked_" which isn't desireable. | 
 |       GetRunner()->WakeTasks(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void Task::Wake() { | 
 |   if (done_) | 
 |     return; | 
 |   if (blocked_) { | 
 |     blocked_ = false; | 
 |     GetRunner()->WakeTasks(); | 
 |   } | 
 | } | 
 |  | 
 | void Task::Error() { | 
 |   if (error_ || done_) | 
 |     return; | 
 |   error_ = true; | 
 |   Wake(); | 
 | } | 
 |  | 
 | std::string Task::GetStateName(int state) const { | 
 |   switch (state) { | 
 |     case STATE_BLOCKED: return "BLOCKED"; | 
 |     case STATE_INIT: return "INIT"; | 
 |     case STATE_START: return "START"; | 
 |     case STATE_DONE: return "DONE"; | 
 |     case STATE_ERROR: return "ERROR"; | 
 |     case STATE_RESPONSE: return "RESPONSE"; | 
 |   } | 
 |   return "??"; | 
 | } | 
 |  | 
 | int Task::Process(int state) { | 
 |   int newstate = STATE_ERROR; | 
 |  | 
 |   if (TimedOut()) { | 
 |     ClearTimeout(); | 
 |     newstate = OnTimeout(); | 
 |     SignalTimeout(); | 
 |   } else { | 
 |     switch (state) { | 
 |       case STATE_INIT: | 
 |         newstate = STATE_START; | 
 |         break; | 
 |       case STATE_START: | 
 |         newstate = ProcessStart(); | 
 |         break; | 
 |       case STATE_RESPONSE: | 
 |         newstate = ProcessResponse(); | 
 |         break; | 
 |       case STATE_DONE: | 
 |       case STATE_ERROR: | 
 |         newstate = STATE_BLOCKED; | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   return newstate; | 
 | } | 
 |  | 
 | void Task::Stop() { | 
 |   // No need to wake because we're either awake or in abort | 
 |   TaskParent::OnStopped(this); | 
 | } | 
 |  | 
 | int Task::ProcessResponse() { | 
 |   return STATE_DONE; | 
 | } | 
 |  | 
 | void Task::set_timeout_seconds(const int timeout_seconds) { | 
 |   timeout_seconds_ = timeout_seconds; | 
 |   ResetTimeout(); | 
 | } | 
 |  | 
 | bool Task::TimedOut() { | 
 |   return timeout_seconds_ && | 
 |     timeout_time_ && | 
 |     CurrentTime() >= timeout_time_; | 
 | } | 
 |  | 
 | void Task::ResetTimeout() { | 
 |   int64_t previous_timeout_time = timeout_time_; | 
 |   bool timeout_allowed = (state_ != STATE_INIT) | 
 |                       && (state_ != STATE_DONE) | 
 |                       && (state_ != STATE_ERROR); | 
 |   if (timeout_seconds_ && timeout_allowed && !timeout_suspended_) | 
 |     timeout_time_ = CurrentTime() + | 
 |                     (timeout_seconds_ * kSecToMsec * kMsecTo100ns); | 
 |   else | 
 |     timeout_time_ = 0; | 
 |  | 
 |   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); | 
 | } | 
 |  | 
 | void Task::ClearTimeout() { | 
 |   int64_t previous_timeout_time = timeout_time_; | 
 |   timeout_time_ = 0; | 
 |   GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); | 
 | } | 
 |  | 
 | void Task::SuspendTimeout() { | 
 |   if (!timeout_suspended_) { | 
 |     timeout_suspended_ = true; | 
 |     ResetTimeout(); | 
 |   } | 
 | } | 
 |  | 
 | void Task::ResumeTimeout() { | 
 |   if (timeout_suspended_) { | 
 |     timeout_suspended_ = false; | 
 |     ResetTimeout(); | 
 |   } | 
 | } | 
 |  | 
 | int Task::OnTimeout() { | 
 |   // by default, we are finished after timing out | 
 |   return STATE_DONE; | 
 | } | 
 |  | 
 | } // namespace rtc |