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