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