File: task.h

package info (click to toggle)
golang-github-google-certificate-transparency 0.0~git20160709.0.0f6e3d1~ds1-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster
  • size: 5,676 kB
  • sloc: cpp: 35,278; python: 11,838; java: 1,911; sh: 1,885; makefile: 950; xml: 520; ansic: 225
file content (209 lines) | stat: -rw-r--r-- 7,458 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// This class is used to coordinate asynchronous work. It runs a
// callback once the work is done, and also supports cancellation, as
// well as memory management.
//
// Typically, a function/method that starts an asynchronous operation
// will take a pointer to a util::Task. The caller keeps ownership of
// the util::Task, and once the operation is complete, the callee
// calls util::Task::Return(), with the status in case of an
// error.
//
// The task object can help with the implementation of the callee, by
// providing memory management, and notifying it when a cancellation
// has been requested.
//
// A task is provided with a util::Executor which it will use to run
// its callbacks. The executor will not be accessed after the done
// callback has started.
//
// Once util::Task::Return() is called, the done callback is run on
// the executor.

#ifndef CERT_TRANS_UTIL_TASK_H_
#define CERT_TRANS_UTIL_TASK_H_

#include <functional>
#include <memory>
#include <mutex>
#include <vector>

#include "base/macros.h"
#include "util/executor.h"
#include "util/status.h"

namespace util {

// The task can be in one of three states: ACTIVE (the initial state),
// PREPARED (the task has a status), or DONE (the done callback can
// run).
//
// A task enters the PREPARED state on the first Return() call.
//
// The task changes from PREPARED to DONE when the following
// conditions are met:
//
//  - there are no remaining holds on the task
//  - all cancellation callbacks have returned
//  - all child task done callbacks have returned
//
class Task {
 public:
  Task(const std::function<void(Task*)>& done_callback, Executor* executor);

  // REQUIRES: task is in DONE state.
  // Tasks can be deleted in their done callback.
  ~Task();

  // Returns the executor passed into the constructor, which will be
  // used for callbacks.
  Executor* executor() const {
    return executor_;
  }

  // Requests that the asynchronous operation (and all its
  // descendants) be cancelled. There is no guarantee that the task is
  // PREPARED or DONE by the time this method returns. Also, the
  // cancellation is merely a request, and could be completely
  // ignored.
  void Cancel();

  // REQUIRES: Return() has been called, which can be verified by
  // calling IsActive().
  Status status() const;

  // Methods used by the implementer of an asynchronous operation (the
  // callee).

  // If the task is ACTIVE, prepares it with the specified Status
  // object, returning true. If the task is no longer ACTIVE (meaning
  // that Return() has already been called), the task is not changed,
  // and false is returned.
  //
  // Note that once Return() is called, the task can reach the DONE
  // state asynchronously and run the callbacks for this task, which
  // might delete the task and state used by the callee. So you must
  // be careful with what is used after calling Return(), including
  // through destructors of locally scoped objects (such as
  // std::lock_guard, for example). An option is to use a TaskHold to
  // ensure the task does not reach the DONE state prematurely.
  bool Return(const Status& status = Status::OK);

  // This can be used to prevent the task from advancing to the DONE
  // state.
  void AddHold();
  void RemoveHold();

  // These two methods allow inspecting the current state of the task.
  bool IsActive() const;
  bool IsDone() const;

  // Returns true once Cancel() is called.
  bool CancelRequested() const;

  // The "cancel_cb" callback will be called the first time Cancel()
  // is called. If Cancel() is never called, then it will just be
  // destroyed without being called. So this function could be called
  // zero or one time, and should thus not be solely responsible for
  // memory management.
  //
  // It is okay to call WhenCancelled() more than once on the same
  // task. There is no ordering guarantee, and since they are called
  // using the executor, they could even be run concurrently.
  //
  // If this method is called after the Cancel() has been called, the
  // callback will be sent to the executor immediately. If the task is
  // not ACTIVE anymore (in other words, if Return() has already been
  // called), the callback will not be run.
  //
  // All cancellation callbacks will be complete before the task
  // enters the DONE state. Effectively, each cancellation callback
  // has a hold on the task while they are running. That hold is
  // removed once the cancellation callback returns, but the callback
  // is allowed to take a hold of its own, if it wants to delay the
  // DONE state further.
  void WhenCancelled(const std::function<void()>& cancel_cb);

  // Child tasks are owned by this task (the child task will be
  // deleted automatically after their done callback has run). All
  // child tasks will be cancelled automatically if this task is
  // cancelled or enters the PREPARED state (when Return() is
  // called). Each child task has a hold on this task that is released
  // once the child's done callback returns, so the parent task will
  // not reach the DONE state until all of its child tasks have
  // finished running. The executor of the child task is the same as
  // that of the parent.
  Task* AddChild(const std::function<void(Task*)>& done_callback) {
    return AddChildWithExecutor(done_callback, executor_);
  }

  // Variant of AddChild() that allows setting a different executor.
  Task* AddChildWithExecutor(const std::function<void(Task*)>& done_callback,
                             Executor* executor);

  // Functions to call once the task is DONE. This could be called
  // before, during, or after the done callback, and so cannot rely on
  // the task itself. An example would be to free the memory that was
  // used to execute the asynchronous operation.
  void CleanupWhenDone(const std::function<void()>& cleanup_cb);

  // Arranges to delete the object once the task is DONE. As this
  // could be run before the done callback, this is meant to be used
  // by the implementation of the asynchronous operation, rather than
  // for data used by the caller.
  template <class T>
  void DeleteWhenDone(T* obj) {
    CleanupWhenDone(std::bind(std::default_delete<T>(), obj));
  }

 private:
  enum State {
    ACTIVE = 0,
    PREPARED = 1,
    DONE = 2,
  };

  void TryDoneTransition(std::unique_lock<std::mutex>* lock);
  void RunCancelCallback(const std::function<void()>& cb);
  void RunCleanupAndDoneCallbacks();
  void RunChildDoneCallback(const std::function<void(Task*)>& done_callback,
                            Task* child_task);

  const std::function<void(Task*)> done_callback_;
  Executor* const executor_;

  mutable std::mutex lock_;
  State state_;
  Status status_;  // not protected by lock_
  bool cancelled_;
  int holds_;
  // References to child tasks are kept as shared pointers to avoid
  // some races.
  std::vector<std::shared_ptr<Task>> child_tasks_;
  std::vector<std::function<void()>> cancel_callbacks_;
  std::vector<std::function<void()>> cleanup_callbacks_;

  DISALLOW_COPY_AND_ASSIGN(Task);
};


// Helper class, that adds a hold on a task, and automatically removes
// it when it goes out of scope.
class TaskHold {
 public:
  TaskHold(Task* task) : task_(task) {
    task_->AddHold();
  }
  ~TaskHold() {
    task_->RemoveHold();
  }

 private:
  Task* const task_;

  DISALLOW_COPY_AND_ASSIGN(TaskHold);
};


}  // namespace util

#endif  // CERT_TRANS_UTIL_TASK_H_