OLD | NEW |
(Empty) | |
| 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/checks.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 RTC_DCHECK(unique_id_ < unique_id_seed_); |
| 35 } |
| 36 |
| 37 Task::~Task() { |
| 38 // Is this task being deleted in the correct manner? |
| 39 #if RTC_DCHECK_IS_ON |
| 40 RTC_DCHECK(!done_ || GetRunner()->is_ok_to_delete(this)); |
| 41 #endif |
| 42 RTC_DCHECK(state_ == STATE_INIT || done_); |
| 43 RTC_DCHECK(state_ == STATE_INIT || blocked_); |
| 44 |
| 45 // If the task is being deleted without being done, it |
| 46 // means that it hasn't been removed from its parent. |
| 47 // This happens if a task is deleted outside of TaskRunner. |
| 48 if (!done_) { |
| 49 Stop(); |
| 50 } |
| 51 } |
| 52 |
| 53 int64_t Task::CurrentTime() { |
| 54 return GetRunner()->CurrentTime(); |
| 55 } |
| 56 |
| 57 int64_t Task::ElapsedTime() { |
| 58 return CurrentTime() - start_time_; |
| 59 } |
| 60 |
| 61 void Task::Start() { |
| 62 if (state_ != STATE_INIT) |
| 63 return; |
| 64 // Set the start time before starting the task. Otherwise if the task |
| 65 // finishes quickly and deletes the Task object, setting start_time_ |
| 66 // will crash. |
| 67 start_time_ = CurrentTime(); |
| 68 GetRunner()->StartTask(this); |
| 69 } |
| 70 |
| 71 void Task::Step() { |
| 72 if (done_) { |
| 73 #if RTC_DCHECK_IS_ON |
| 74 // we do not know how !blocked_ happens when done_ - should be impossible. |
| 75 // But it causes problems, so in retail build, we force blocked_, and |
| 76 // under debug we assert. |
| 77 RTC_DCHECK(blocked_); |
| 78 #else |
| 79 blocked_ = true; |
| 80 #endif |
| 81 return; |
| 82 } |
| 83 |
| 84 // Async Error() was called |
| 85 if (error_) { |
| 86 done_ = true; |
| 87 state_ = STATE_ERROR; |
| 88 blocked_ = true; |
| 89 // obsolete - an errored task is not considered done now |
| 90 // SignalDone(); |
| 91 |
| 92 Stop(); |
| 93 #if RTC_DCHECK_IS_ON |
| 94 // verify that stop removed this from its parent |
| 95 RTC_DCHECK(!parent()->IsChildTask(this)); |
| 96 #endif |
| 97 return; |
| 98 } |
| 99 |
| 100 busy_ = true; |
| 101 int new_state = Process(state_); |
| 102 busy_ = false; |
| 103 |
| 104 if (aborted_) { |
| 105 Abort(true); // no need to wake because we're awake |
| 106 return; |
| 107 } |
| 108 |
| 109 if (new_state == STATE_BLOCKED) { |
| 110 blocked_ = true; |
| 111 // Let the timeout continue |
| 112 } else { |
| 113 state_ = new_state; |
| 114 blocked_ = false; |
| 115 ResetTimeout(); |
| 116 } |
| 117 |
| 118 if (new_state == STATE_DONE) { |
| 119 done_ = true; |
| 120 } else if (new_state == STATE_ERROR) { |
| 121 done_ = true; |
| 122 error_ = true; |
| 123 } |
| 124 |
| 125 if (done_) { |
| 126 // obsolete - call this yourself |
| 127 // SignalDone(); |
| 128 |
| 129 Stop(); |
| 130 #if RTC_DCHECK_IS_ON |
| 131 // verify that stop removed this from its parent |
| 132 RTC_DCHECK(!parent()->IsChildTask(this)); |
| 133 #endif |
| 134 blocked_ = true; |
| 135 } |
| 136 } |
| 137 |
| 138 void Task::Abort(bool nowake) { |
| 139 // Why only check for done_ (instead of "aborted_ || done_")? |
| 140 // |
| 141 // If aborted_ && !done_, it means the logic for aborting still |
| 142 // needs to be executed (because busy_ must have been true when |
| 143 // Abort() was previously called). |
| 144 if (done_) |
| 145 return; |
| 146 aborted_ = true; |
| 147 if (!busy_) { |
| 148 done_ = true; |
| 149 blocked_ = true; |
| 150 error_ = true; |
| 151 |
| 152 // "done_" is set before calling "Stop()" to ensure that this code |
| 153 // doesn't execute more than once (recursively) for the same task. |
| 154 Stop(); |
| 155 #if RTC_DCHECK_IS_ON |
| 156 // verify that stop removed this from its parent |
| 157 RTC_DCHECK(!parent()->IsChildTask(this)); |
| 158 #endif |
| 159 if (!nowake) { |
| 160 // WakeTasks to self-delete. |
| 161 // Don't call Wake() because it is a no-op after "done_" is set. |
| 162 // Even if Wake() did run, it clears "blocked_" which isn't desireable. |
| 163 GetRunner()->WakeTasks(); |
| 164 } |
| 165 } |
| 166 } |
| 167 |
| 168 void Task::Wake() { |
| 169 if (done_) |
| 170 return; |
| 171 if (blocked_) { |
| 172 blocked_ = false; |
| 173 GetRunner()->WakeTasks(); |
| 174 } |
| 175 } |
| 176 |
| 177 void Task::Error() { |
| 178 if (error_ || done_) |
| 179 return; |
| 180 error_ = true; |
| 181 Wake(); |
| 182 } |
| 183 |
| 184 std::string Task::GetStateName(int state) const { |
| 185 switch (state) { |
| 186 case STATE_BLOCKED: return "BLOCKED"; |
| 187 case STATE_INIT: return "INIT"; |
| 188 case STATE_START: return "START"; |
| 189 case STATE_DONE: return "DONE"; |
| 190 case STATE_ERROR: return "ERROR"; |
| 191 case STATE_RESPONSE: return "RESPONSE"; |
| 192 } |
| 193 return "??"; |
| 194 } |
| 195 |
| 196 int Task::Process(int state) { |
| 197 int newstate = STATE_ERROR; |
| 198 |
| 199 if (TimedOut()) { |
| 200 ClearTimeout(); |
| 201 newstate = OnTimeout(); |
| 202 SignalTimeout(); |
| 203 } else { |
| 204 switch (state) { |
| 205 case STATE_INIT: |
| 206 newstate = STATE_START; |
| 207 break; |
| 208 case STATE_START: |
| 209 newstate = ProcessStart(); |
| 210 break; |
| 211 case STATE_RESPONSE: |
| 212 newstate = ProcessResponse(); |
| 213 break; |
| 214 case STATE_DONE: |
| 215 case STATE_ERROR: |
| 216 newstate = STATE_BLOCKED; |
| 217 break; |
| 218 } |
| 219 } |
| 220 |
| 221 return newstate; |
| 222 } |
| 223 |
| 224 void Task::Stop() { |
| 225 // No need to wake because we're either awake or in abort |
| 226 TaskParent::OnStopped(this); |
| 227 } |
| 228 |
| 229 int Task::ProcessResponse() { |
| 230 return STATE_DONE; |
| 231 } |
| 232 |
| 233 void Task::set_timeout_seconds(const int timeout_seconds) { |
| 234 timeout_seconds_ = timeout_seconds; |
| 235 ResetTimeout(); |
| 236 } |
| 237 |
| 238 bool Task::TimedOut() { |
| 239 return timeout_seconds_ && |
| 240 timeout_time_ && |
| 241 CurrentTime() >= timeout_time_; |
| 242 } |
| 243 |
| 244 void Task::ResetTimeout() { |
| 245 int64_t previous_timeout_time = timeout_time_; |
| 246 bool timeout_allowed = (state_ != STATE_INIT) |
| 247 && (state_ != STATE_DONE) |
| 248 && (state_ != STATE_ERROR); |
| 249 if (timeout_seconds_ && timeout_allowed && !timeout_suspended_) |
| 250 timeout_time_ = CurrentTime() + |
| 251 (timeout_seconds_ * kSecToMsec * kMsecTo100ns); |
| 252 else |
| 253 timeout_time_ = 0; |
| 254 |
| 255 GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); |
| 256 } |
| 257 |
| 258 void Task::ClearTimeout() { |
| 259 int64_t previous_timeout_time = timeout_time_; |
| 260 timeout_time_ = 0; |
| 261 GetRunner()->UpdateTaskTimeout(this, previous_timeout_time); |
| 262 } |
| 263 |
| 264 void Task::SuspendTimeout() { |
| 265 if (!timeout_suspended_) { |
| 266 timeout_suspended_ = true; |
| 267 ResetTimeout(); |
| 268 } |
| 269 } |
| 270 |
| 271 void Task::ResumeTimeout() { |
| 272 if (timeout_suspended_) { |
| 273 timeout_suspended_ = false; |
| 274 ResetTimeout(); |
| 275 } |
| 276 } |
| 277 |
| 278 int Task::OnTimeout() { |
| 279 // by default, we are finished after timing out |
| 280 return STATE_DONE; |
| 281 } |
| 282 |
| 283 } // namespace rtc |
OLD | NEW |