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#ifndef WEBRTC_BASE_TASK_H__ 12#define WEBRTC_BASE_TASK_H__ 13 14#include <string> 15#include "webrtc/base/basictypes.h" 16#include "webrtc/base/scoped_ptr.h" 17#include "webrtc/base/sigslot.h" 18#include "webrtc/base/taskparent.h" 19 20///////////////////////////////////////////////////////////////////// 21// 22// TASK 23// 24///////////////////////////////////////////////////////////////////// 25// 26// Task is a state machine infrastructure. States are pushed forward by 27// pushing forwards a TaskRunner that holds on to all Tasks. The purpose 28// of Task is threefold: 29// 30// (1) It manages ongoing work on the UI thread. Multitasking without 31// threads, keeping it easy, keeping it real. :-) It does this by 32// organizing a set of states for each task. When you return from your 33// Process*() function, you return an integer for the next state. You do 34// not go onto the next state yourself. Every time you enter a state, 35// you check to see if you can do anything yet. If not, you return 36// STATE_BLOCKED. If you _could_ do anything, do not return 37// STATE_BLOCKED - even if you end up in the same state, return 38// STATE_mysamestate. When you are done, return STATE_DONE and then the 39// task will self-delete sometime afterwards. 40// 41// (2) It helps you avoid all those reentrancy problems when you chain 42// too many triggers on one thread. Basically if you want to tell a task 43// to process something for you, you feed your task some information and 44// then you Wake() it. Don't tell it to process it right away. If it 45// might be working on something as you send it information, you may want 46// to have a queue in the task. 47// 48// (3) Finally it helps manage parent tasks and children. If a parent 49// task gets aborted, all the children tasks are too. The nice thing 50// about this, for example, is if you have one parent task that 51// represents, say, and Xmpp connection, then you can spawn a whole bunch 52// of infinite lifetime child tasks and now worry about cleaning them up. 53// When the parent task goes to STATE_DONE, the task engine will make 54// sure all those children are aborted and get deleted. 55// 56// Notice that Task has a few built-in states, e.g., 57// 58// STATE_INIT - the task isn't running yet 59// STATE_START - the task is in its first state 60// STATE_RESPONSE - the task is in its second state 61// STATE_DONE - the task is done 62// 63// STATE_ERROR - indicates an error - we should audit the error code in 64// light of any usage of it to see if it should be improved. When I 65// first put down the task stuff I didn't have a good sense of what was 66// needed for Abort and Error, and now the subclasses of Task will ground 67// the design in a stronger way. 68// 69// STATE_NEXT - the first undefined state number. (like WM_USER) - you 70// can start defining more task states there. 71// 72// When you define more task states, just override Process(int state) and 73// add your own switch statement. If you want to delegate to 74// Task::Process, you can effectively delegate to its switch statement. 75// No fancy method pointers or such - this is all just pretty low tech, 76// easy to debug, and fast. 77// 78// Also notice that Task has some primitive built-in timeout functionality. 79// 80// A timeout is defined as "the task stays in STATE_BLOCKED longer than 81// timeout_seconds_." 82// 83// Descendant classes can override this behavior by calling the 84// various protected methods to change the timeout behavior. For 85// instance, a descendand might call SuspendTimeout() when it knows 86// that it isn't waiting for anything that might timeout, but isn't 87// yet in the STATE_DONE state. 88// 89 90namespace rtc { 91 92// Executes a sequence of steps 93class Task : public TaskParent { 94 public: 95 Task(TaskParent *parent); 96 ~Task() override; 97 98 int32_t unique_id() { return unique_id_; } 99 100 void Start(); 101 void Step(); 102 int GetState() const { return state_; } 103 bool HasError() const { return (GetState() == STATE_ERROR); } 104 bool Blocked() const { return blocked_; } 105 bool IsDone() const { return done_; } 106 int64_t ElapsedTime(); 107 108 // Called from outside to stop task without any more callbacks 109 void Abort(bool nowake = false); 110 111 bool TimedOut(); 112 113 int64_t timeout_time() const { return timeout_time_; } 114 int timeout_seconds() const { return timeout_seconds_; } 115 void set_timeout_seconds(int timeout_seconds); 116 117 sigslot::signal0<> SignalTimeout; 118 119 // Called inside the task to signal that the task may be unblocked 120 void Wake(); 121 122 protected: 123 124 enum { 125 STATE_BLOCKED = -1, 126 STATE_INIT = 0, 127 STATE_START = 1, 128 STATE_DONE = 2, 129 STATE_ERROR = 3, 130 STATE_RESPONSE = 4, 131 STATE_NEXT = 5, // Subclasses which need more states start here and higher 132 }; 133 134 // Called inside to advise that the task should wake and signal an error 135 void Error(); 136 137 int64_t CurrentTime(); 138 139 virtual std::string GetStateName(int state) const; 140 virtual int Process(int state); 141 virtual void Stop(); 142 virtual int ProcessStart() = 0; 143 virtual int ProcessResponse(); 144 145 void ResetTimeout(); 146 void ClearTimeout(); 147 148 void SuspendTimeout(); 149 void ResumeTimeout(); 150 151 protected: 152 virtual int OnTimeout(); 153 154 private: 155 void Done(); 156 157 int state_; 158 bool blocked_; 159 bool done_; 160 bool aborted_; 161 bool busy_; 162 bool error_; 163 int64_t start_time_; 164 int64_t timeout_time_; 165 int timeout_seconds_; 166 bool timeout_suspended_; 167 int32_t unique_id_; 168 169 static int32_t unique_id_seed_; 170}; 171 172} // namespace rtc 173 174#endif // WEBRTC_BASE_TASK_H__ 175