Index: webrtc/base/task.cc |
diff --git a/webrtc/base/task.cc b/webrtc/base/task.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1fe3af484089f61f820e6a0b732b5ce10ccb32eb |
--- /dev/null |
+++ b/webrtc/base/task.cc |
@@ -0,0 +1,283 @@ |
+/* |
+ * 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/checks.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 |
+ RTC_DCHECK(unique_id_ < unique_id_seed_); |
+} |
+ |
+Task::~Task() { |
+ // Is this task being deleted in the correct manner? |
+#if RTC_DCHECK_IS_ON |
+ RTC_DCHECK(!done_ || GetRunner()->is_ok_to_delete(this)); |
+#endif |
+ RTC_DCHECK(state_ == STATE_INIT || done_); |
+ RTC_DCHECK(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 RTC_DCHECK_IS_ON |
+ // 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. |
+ RTC_DCHECK(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 RTC_DCHECK_IS_ON |
+ // verify that stop removed this from its parent |
+ RTC_DCHECK(!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 RTC_DCHECK_IS_ON |
+ // verify that stop removed this from its parent |
+ RTC_DCHECK(!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 RTC_DCHECK_IS_ON |
+ // verify that stop removed this from its parent |
+ RTC_DCHECK(!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 |