blob: d35cd20e98a86a52c7ea854e4750fdcbb6a23cd8 [file] [log] [blame]
Ben Fredricksond69f38b2015-01-28 20:06:15 -08001#ifndef AOS_COMMON_ACTIONS_ACTIONS_H_
2#define AOS_COMMON_ACTIONS_ACTIONS_H_
3
4#include <inttypes.h>
5#include <sys/types.h>
6#include <unistd.h>
7
8#include <type_traits>
9#include <atomic>
10#include <memory>
11
12#include "aos/common/logging/logging.h"
13#include "aos/common/queue.h"
14
15namespace aos {
16namespace common {
17namespace actions {
18
19class Action;
20
21// A queue which queues Actions, verifies a single action is running, and
22// cancels Actions.
23class ActionQueue {
24 public:
25 // Queues up an action for sending.
26 void EnqueueAction(::std::unique_ptr<Action> action);
27
28 // Cancels the current action, and runs the next one after the current one has
29 // finished and the action queue ticks again.
30 void CancelCurrentAction();
31
32 // Cancels all running actions.
33 void CancelAllActions();
34
Austin Schuh0f9db8a2015-02-22 21:48:16 -080035 // Runs the next action when the current one is finished running and polls the
36 // running action state for use by Running.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080037 void Tick();
38
39 // Returns true if any action is running or could be running.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080040 bool Running();
41
42 // Retrieves the internal state of the current action for testing.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080043 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080044 bool GetCurrentActionState(bool* has_started, bool* sent_started,
45 bool* sent_cancel, bool* interrupted,
46 uint32_t* run_value, uint32_t* old_run_value);
47
48 // Retrieves the internal state of the next action for testing.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080049 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080050 bool GetNextActionState(bool* has_started, bool* sent_started,
51 bool* sent_cancel, bool* interrupted,
52 uint32_t* run_value, uint32_t* old_run_value);
53
54 private:
55 ::std::unique_ptr<Action> current_action_;
56 ::std::unique_ptr<Action> next_action_;
57};
58
59// The basic interface an ActionQueue can access.
60class Action {
61 public:
62 virtual ~Action() {}
63
64 // Cancels the action.
65 void Cancel() { DoCancel(); }
66 // Returns true if the action is currently running.
67 bool Running() { return DoRunning(); }
68 // Starts the action.
69 void Start() { DoStart(); }
70
71 // Waits until the action has finished.
72 void WaitUntilDone() { DoWaitUntilDone(); }
73
Ben Fredrickson32e6c252015-02-21 23:53:38 -080074 // Run all the checks for one iteration of waiting. Will return true when the
75 // action has completed, successfully or not. This is non-blocking.
76 bool CheckIteration() { return DoCheckIteration(false); }
77
Ben Fredricksond69f38b2015-01-28 20:06:15 -080078 // Retrieves the internal state of the action for testing.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080079 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080080 void GetState(bool* has_started, bool* sent_started, bool* sent_cancel,
81 bool* interrupted, uint32_t* run_value,
82 uint32_t* old_run_value) {
83 DoGetState(has_started, sent_started, sent_cancel, interrupted, run_value,
84 old_run_value);
85 }
86
87 private:
88 // Cancels the action.
89 virtual void DoCancel() = 0;
90 // Returns true if the action is running or we don't have an initial response
91 // back from it to signal whether or not it is running.
92 virtual bool DoRunning() = 0;
93 // Starts the action if a goal has been created.
94 virtual void DoStart() = 0;
95 // Blocks until complete.
96 virtual void DoWaitUntilDone() = 0;
Ben Fredrickson32e6c252015-02-21 23:53:38 -080097 // Updates status for one cycle of waiting
98 virtual bool DoCheckIteration(bool blocking) = 0;
Ben Fredricksond69f38b2015-01-28 20:06:15 -080099 // For testing we will need to get the internal state.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800100 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800101 virtual void DoGetState(bool* has_started, bool* sent_started,
102 bool* sent_cancel, bool* interrupted,
103 uint32_t* run_value, uint32_t* old_run_value) = 0;
104};
105
106// Templated subclass to hold the type information.
107template <typename T>
108class TypedAction : public Action {
109 public:
110 // A convenient way to refer to the type of our goals.
111 typedef typename std::remove_reference<decltype(
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800112 *(static_cast<T*>(nullptr)->goal.MakeMessage().get()))>::type GoalType;
113 typedef typename std::remove_reference<
114 decltype(static_cast<GoalType*>(nullptr)->params)>::type ParamType;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800115
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800116 TypedAction(T* queue_group, const ParamType &params)
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800117 : queue_group_(queue_group),
118 goal_(queue_group_->goal.MakeMessage()),
119 // This adds 1 to the counter (atomically because it's potentially
120 // shared across threads) and then bitwise-ORs the bottom of the PID to
121 // differentiate it from other processes's values (ie a unique id).
122 run_value_(run_counter_.fetch_add(1, ::std::memory_order_relaxed) |
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800123 ((getpid() & 0xFFFF) << 16)),
124 params_(params) {
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800125 LOG(INFO, "Action %" PRIx32 " created on queue %s\n", run_value_,
126 queue_group_->goal.name());
127 // Clear out any old status messages from before now.
128 queue_group_->status.FetchLatest();
129 }
130
131 virtual ~TypedAction() {
132 LOG(DEBUG, "Calling destructor of %" PRIx32 "\n", run_value_);
133 DoCancel();
134 }
135
136 // Returns the current goal that will be sent when the action is sent.
137 GoalType* GetGoal() { return goal_.get(); }
138
139 private:
140 void DoCancel() override;
141
142 bool DoRunning() override;
143
144 void DoWaitUntilDone() override;
145
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800146 bool DoCheckIteration(bool blocking);
147
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800148 // Sets the started flag (also possibly the interrupted flag).
149 void CheckStarted();
150
151 // Checks for interrupt.
152 void CheckInterrupted();
153
154 void DoStart() override;
155
156 void DoGetState(bool* has_started, bool* sent_started, bool* sent_cancel,
157 bool* interrupted, uint32_t* run_value,
158 uint32_t* old_run_value) override {
159 if (has_started != nullptr) *has_started = has_started_;
160 if (sent_started != nullptr) *sent_started = sent_started_;
161 if (sent_cancel != nullptr) *sent_cancel = sent_cancel_;
162 if (interrupted != nullptr) *interrupted = interrupted_;
163 if (run_value != nullptr) *run_value = run_value_;
164 if (old_run_value != nullptr) *old_run_value = old_run_value_;
165 }
166
167 T* const queue_group_;
168 ::aos::ScopedMessagePtr<GoalType> goal_;
169
170 // Track if we have seen a response to the start message.
171 bool has_started_ = false;
172 // Track if we have sent an initial start message.
173 bool sent_started_ = false;
174
175 bool sent_cancel_ = false;
176
177 // Gets set to true if we ever see somebody else's value in running.
178 bool interrupted_ = false;
179
180 // The value we're going to use for goal.run etc.
181 const uint32_t run_value_;
182
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800183 // flag passed to action in order to have differing types
184 const ParamType params_;
185
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800186 // The old value for running that we may have seen. If we see any value other
187 // than this or run_value_, somebody else got in the way and we're done. 0 if
188 // there was nothing there to start with. Only valid after sent_started_
189 // changes to true.
190 uint32_t old_run_value_;
191
192 static ::std::atomic<uint16_t> run_counter_;
193};
194
195template <typename T>
196::std::atomic<uint16_t> TypedAction<T>::run_counter_{0};
197
198template <typename T>
199void TypedAction<T>::DoCancel() {
200 if (!sent_started_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800201 LOG(INFO, "Action %" PRIx32 " on queue %s was never started\n", run_value_,
202 queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800203 } else {
204 if (interrupted_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800205 LOG(INFO,
206 "Action %" PRIx32 " on queue %s was interrupted -> not cancelling\n",
207 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800208 } else {
209 if (sent_cancel_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800210 LOG(INFO, "Action %" PRIx32 " on queue %s already cancelled\n",
211 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800212 } else {
213 LOG(INFO, "Canceling action %" PRIx32 " on queue %s\n", run_value_,
214 queue_group_->goal.name());
215 queue_group_->goal.MakeWithBuilder().run(0).Send();
216 sent_cancel_ = true;
217 }
218 }
219 }
220}
221
222template <typename T>
223bool TypedAction<T>::DoRunning() {
224 if (!sent_started_) return false;
225 if (has_started_) {
226 queue_group_->status.FetchNext();
227 CheckInterrupted();
228 } else if (queue_group_->status.FetchLatest()) {
229 CheckStarted();
230 }
231 if (interrupted_) return false;
232 // We've asked it to start but haven't gotten confirmation that it's started
233 // yet.
234 if (!has_started_) return true;
235 return queue_group_->status.get() &&
236 queue_group_->status->running == run_value_;
237}
238
239template <typename T>
240void TypedAction<T>::DoWaitUntilDone() {
241 CHECK(sent_started_);
242 queue_group_->status.FetchNext();
243 CheckInterrupted();
244 while (true) {
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800245 if (DoCheckIteration(true)) {
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800246 return;
247 }
248 }
249}
250
251template <typename T>
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800252bool TypedAction<T>::DoCheckIteration(bool blocking) {
253 CHECK(sent_started_);
254 if (interrupted_) return true;
255 CheckStarted();
256 if (blocking) {
257 queue_group_->status.FetchAnother();
258 } else {
259 if (!queue_group_->status.FetchNext()) {
260 return false;
261 }
262 }
263 CheckStarted();
264 CheckInterrupted();
265 if (has_started_ && (queue_group_->status.get() &&
266 queue_group_->status->running != run_value_)) {
267 return true;
268 }
269 return false;
270}
271
272template <typename T>
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800273void TypedAction<T>::CheckStarted() {
274 if (has_started_) return;
275 if (queue_group_->status.get()) {
276 if (queue_group_->status->running == run_value_ ||
277 (queue_group_->status->running == 0 &&
278 queue_group_->status->last_running == run_value_)) {
279 // It's currently running our instance.
280 has_started_ = true;
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800281 LOG(DEBUG, "Action %" PRIx32 " on queue %s has been started\n",
282 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800283 } else if (queue_group_->status->running == old_run_value_) {
284 // It's still running an old instance (or still doing nothing).
285 } else {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800286 LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
287 " before starting\n",
288 run_value_, queue_group_->goal.name(), queue_group_->status->running);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800289 has_started_ = true;
290 interrupted_ = true;
291 }
292 } else {
293 LOG(WARNING, "No status message recieved.\n");
294 }
295}
296
297template <typename T>
298void TypedAction<T>::CheckInterrupted() {
299 if (!interrupted_ && has_started_ && queue_group_->status.get()) {
300 if (queue_group_->status->running != 0 &&
301 queue_group_->status->running != run_value_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800302 LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
303 " after starting\n",
304 run_value_, queue_group_->goal.name(), queue_group_->status->running);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800305 }
306 }
307}
308
309template <typename T>
310void TypedAction<T>::DoStart() {
311 if (goal_) {
312 LOG(INFO, "Starting action %" PRIx32 "\n", run_value_);
313 goal_->run = run_value_;
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800314 goal_->params = params_;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800315 sent_started_ = true;
316 if (!goal_.Send()) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800317 LOG(ERROR, "sending goal for action %" PRIx32 " on queue %s failed\n",
318 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800319 // Don't wait to see a message with it.
320 has_started_ = true;
321 }
322 queue_group_->status.FetchLatest();
323 if (queue_group_->status.get() && queue_group_->status->running != 0) {
324 old_run_value_ = queue_group_->status->running;
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800325 LOG(INFO, "Action %" PRIx32 " on queue %s already running\n",
326 old_run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800327 } else {
328 old_run_value_ = 0;
329 }
330 } else {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800331 LOG(WARNING, "Action %" PRIx32 " on queue %s already started\n", run_value_,
332 queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800333 }
334}
335
336} // namespace actions
337} // namespace common
338} // namespace aos
339
340#endif // AOS_COMMON_ACTIONS_ACTIONS_H_