blob: b26f7320b9253facce9a0c980439fe675ab9cb64 [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"
Brian Silverman51675e62015-03-19 23:37:13 -070014#include "aos/common/logging/queue_logging.h"
Ben Fredricksond69f38b2015-01-28 20:06:15 -080015
16namespace aos {
17namespace common {
18namespace actions {
19
20class Action;
21
22// A queue which queues Actions, verifies a single action is running, and
23// cancels Actions.
24class ActionQueue {
25 public:
26 // Queues up an action for sending.
27 void EnqueueAction(::std::unique_ptr<Action> action);
28
29 // Cancels the current action, and runs the next one after the current one has
30 // finished and the action queue ticks again.
31 void CancelCurrentAction();
32
33 // Cancels all running actions.
34 void CancelAllActions();
35
Austin Schuh0f9db8a2015-02-22 21:48:16 -080036 // Runs the next action when the current one is finished running and polls the
37 // running action state for use by Running.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080038 void Tick();
39
40 // Returns true if any action is running or could be running.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080041 bool Running();
42
43 // Retrieves the internal state of the current action for testing.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080044 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080045 bool GetCurrentActionState(bool* has_started, bool* sent_started,
46 bool* sent_cancel, bool* interrupted,
47 uint32_t* run_value, uint32_t* old_run_value);
48
49 // Retrieves the internal state of the next action for testing.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080050 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080051 bool GetNextActionState(bool* has_started, bool* sent_started,
52 bool* sent_cancel, bool* interrupted,
53 uint32_t* run_value, uint32_t* old_run_value);
54
55 private:
56 ::std::unique_ptr<Action> current_action_;
57 ::std::unique_ptr<Action> next_action_;
58};
59
60// The basic interface an ActionQueue can access.
61class Action {
62 public:
63 virtual ~Action() {}
64
65 // Cancels the action.
66 void Cancel() { DoCancel(); }
67 // Returns true if the action is currently running.
68 bool Running() { return DoRunning(); }
69 // Starts the action.
70 void Start() { DoStart(); }
71
72 // Waits until the action has finished.
73 void WaitUntilDone() { DoWaitUntilDone(); }
74
Ben Fredrickson32e6c252015-02-21 23:53:38 -080075 // Run all the checks for one iteration of waiting. Will return true when the
76 // action has completed, successfully or not. This is non-blocking.
77 bool CheckIteration() { return DoCheckIteration(false); }
78
Ben Fredricksond69f38b2015-01-28 20:06:15 -080079 // Retrieves the internal state of the action for testing.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -080080 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -080081 void GetState(bool* has_started, bool* sent_started, bool* sent_cancel,
82 bool* interrupted, uint32_t* run_value,
83 uint32_t* old_run_value) {
84 DoGetState(has_started, sent_started, sent_cancel, interrupted, run_value,
85 old_run_value);
86 }
87
88 private:
89 // Cancels the action.
90 virtual void DoCancel() = 0;
91 // Returns true if the action is running or we don't have an initial response
92 // back from it to signal whether or not it is running.
93 virtual bool DoRunning() = 0;
94 // Starts the action if a goal has been created.
95 virtual void DoStart() = 0;
96 // Blocks until complete.
97 virtual void DoWaitUntilDone() = 0;
Ben Fredrickson32e6c252015-02-21 23:53:38 -080098 // Updates status for one cycle of waiting
99 virtual bool DoCheckIteration(bool blocking) = 0;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800100 // For testing we will need to get the internal state.
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800101 // See comments on the private members of TypedAction<T, S> for details.
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800102 virtual void DoGetState(bool* has_started, bool* sent_started,
103 bool* sent_cancel, bool* interrupted,
104 uint32_t* run_value, uint32_t* old_run_value) = 0;
105};
106
107// Templated subclass to hold the type information.
108template <typename T>
109class TypedAction : public Action {
110 public:
111 // A convenient way to refer to the type of our goals.
112 typedef typename std::remove_reference<decltype(
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800113 *(static_cast<T*>(nullptr)->goal.MakeMessage().get()))>::type GoalType;
114 typedef typename std::remove_reference<
115 decltype(static_cast<GoalType*>(nullptr)->params)>::type ParamType;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800116
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800117 TypedAction(T* queue_group, const ParamType &params)
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800118 : queue_group_(queue_group),
119 goal_(queue_group_->goal.MakeMessage()),
120 // This adds 1 to the counter (atomically because it's potentially
121 // shared across threads) and then bitwise-ORs the bottom of the PID to
122 // differentiate it from other processes's values (ie a unique id).
123 run_value_(run_counter_.fetch_add(1, ::std::memory_order_relaxed) |
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800124 ((getpid() & 0xFFFF) << 16)),
125 params_(params) {
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800126 LOG(INFO, "Action %" PRIx32 " created on queue %s\n", run_value_,
127 queue_group_->goal.name());
128 // Clear out any old status messages from before now.
129 queue_group_->status.FetchLatest();
Brian Silverman51675e62015-03-19 23:37:13 -0700130 if (queue_group_->status.get()) {
131 LOG_STRUCT(DEBUG, "have status", *queue_group_->status);
132 }
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800133 }
134
135 virtual ~TypedAction() {
136 LOG(DEBUG, "Calling destructor of %" PRIx32 "\n", run_value_);
137 DoCancel();
138 }
139
140 // Returns the current goal that will be sent when the action is sent.
141 GoalType* GetGoal() { return goal_.get(); }
142
143 private:
144 void DoCancel() override;
145
146 bool DoRunning() override;
147
148 void DoWaitUntilDone() override;
149
Brian Silvermanf1cff392015-10-11 19:36:18 -0400150 bool DoCheckIteration(bool blocking) override;
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800151
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800152 // Sets the started flag (also possibly the interrupted flag).
153 void CheckStarted();
154
155 // Checks for interrupt.
156 void CheckInterrupted();
157
158 void DoStart() override;
159
160 void DoGetState(bool* has_started, bool* sent_started, bool* sent_cancel,
161 bool* interrupted, uint32_t* run_value,
162 uint32_t* old_run_value) override {
163 if (has_started != nullptr) *has_started = has_started_;
164 if (sent_started != nullptr) *sent_started = sent_started_;
165 if (sent_cancel != nullptr) *sent_cancel = sent_cancel_;
166 if (interrupted != nullptr) *interrupted = interrupted_;
167 if (run_value != nullptr) *run_value = run_value_;
168 if (old_run_value != nullptr) *old_run_value = old_run_value_;
169 }
170
171 T* const queue_group_;
172 ::aos::ScopedMessagePtr<GoalType> goal_;
173
174 // Track if we have seen a response to the start message.
175 bool has_started_ = false;
176 // Track if we have sent an initial start message.
177 bool sent_started_ = false;
178
179 bool sent_cancel_ = false;
180
181 // Gets set to true if we ever see somebody else's value in running.
182 bool interrupted_ = false;
183
184 // The value we're going to use for goal.run etc.
185 const uint32_t run_value_;
186
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800187 // flag passed to action in order to have differing types
188 const ParamType params_;
189
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800190 // The old value for running that we may have seen. If we see any value other
191 // than this or run_value_, somebody else got in the way and we're done. 0 if
192 // there was nothing there to start with. Only valid after sent_started_
193 // changes to true.
194 uint32_t old_run_value_;
195
196 static ::std::atomic<uint16_t> run_counter_;
197};
198
199template <typename T>
200::std::atomic<uint16_t> TypedAction<T>::run_counter_{0};
201
202template <typename T>
203void TypedAction<T>::DoCancel() {
204 if (!sent_started_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800205 LOG(INFO, "Action %" PRIx32 " on queue %s was never started\n", run_value_,
206 queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800207 } else {
208 if (interrupted_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800209 LOG(INFO,
210 "Action %" PRIx32 " on queue %s was interrupted -> not cancelling\n",
211 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800212 } else {
213 if (sent_cancel_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800214 LOG(INFO, "Action %" PRIx32 " on queue %s already cancelled\n",
215 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800216 } else {
217 LOG(INFO, "Canceling action %" PRIx32 " on queue %s\n", run_value_,
218 queue_group_->goal.name());
219 queue_group_->goal.MakeWithBuilder().run(0).Send();
220 sent_cancel_ = true;
221 }
222 }
223 }
224}
225
226template <typename T>
227bool TypedAction<T>::DoRunning() {
Brian Silverman3f050292015-03-28 18:29:32 -0400228 if (!sent_started_) {
229 LOG(DEBUG, "haven't sent start message yet\n");
230 return false;
231 }
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800232 if (has_started_) {
233 queue_group_->status.FetchNext();
234 CheckInterrupted();
Brian Silverman51675e62015-03-19 23:37:13 -0700235 } else {
236 while (queue_group_->status.FetchNext()) {
237 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
238 CheckStarted();
239 if (has_started_) CheckInterrupted();
240 }
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800241 }
242 if (interrupted_) return false;
243 // We've asked it to start but haven't gotten confirmation that it's started
244 // yet.
245 if (!has_started_) return true;
246 return queue_group_->status.get() &&
247 queue_group_->status->running == run_value_;
248}
249
250template <typename T>
251void TypedAction<T>::DoWaitUntilDone() {
252 CHECK(sent_started_);
253 queue_group_->status.FetchNext();
Brian Silverman51675e62015-03-19 23:37:13 -0700254 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800255 CheckInterrupted();
256 while (true) {
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800257 if (DoCheckIteration(true)) {
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800258 return;
259 }
260 }
261}
262
263template <typename T>
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800264bool TypedAction<T>::DoCheckIteration(bool blocking) {
265 CHECK(sent_started_);
266 if (interrupted_) return true;
267 CheckStarted();
268 if (blocking) {
Brian Silverman51675e62015-03-19 23:37:13 -0700269 queue_group_->status.FetchNextBlocking();
270 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800271 } else {
272 if (!queue_group_->status.FetchNext()) {
273 return false;
274 }
Brian Silverman51675e62015-03-19 23:37:13 -0700275 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800276 }
277 CheckStarted();
278 CheckInterrupted();
279 if (has_started_ && (queue_group_->status.get() &&
280 queue_group_->status->running != run_value_)) {
281 return true;
282 }
283 return false;
284}
285
286template <typename T>
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800287void TypedAction<T>::CheckStarted() {
288 if (has_started_) return;
289 if (queue_group_->status.get()) {
290 if (queue_group_->status->running == run_value_ ||
291 (queue_group_->status->running == 0 &&
292 queue_group_->status->last_running == run_value_)) {
293 // It's currently running our instance.
294 has_started_ = true;
Brian Silverman51675e62015-03-19 23:37:13 -0700295 LOG(INFO, "Action %" PRIx32 " on queue %s has been started\n",
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800296 run_value_, queue_group_->goal.name());
Brian Silvermana2ae62d2015-03-15 15:55:22 -0700297 } else if (old_run_value_ != 0 &&
298 queue_group_->status->running == old_run_value_) {
299 LOG(DEBUG, "still running old instance %" PRIx32 "\n", old_run_value_);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800300 } else {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800301 LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
302 " before starting\n",
303 run_value_, queue_group_->goal.name(), queue_group_->status->running);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800304 has_started_ = true;
305 interrupted_ = true;
306 }
307 } else {
308 LOG(WARNING, "No status message recieved.\n");
309 }
310}
311
312template <typename T>
313void TypedAction<T>::CheckInterrupted() {
314 if (!interrupted_ && has_started_ && queue_group_->status.get()) {
315 if (queue_group_->status->running != 0 &&
316 queue_group_->status->running != run_value_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800317 LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
318 " after starting\n",
319 run_value_, queue_group_->goal.name(), queue_group_->status->running);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800320 }
321 }
322}
323
324template <typename T>
325void TypedAction<T>::DoStart() {
326 if (goal_) {
327 LOG(INFO, "Starting action %" PRIx32 "\n", run_value_);
328 goal_->run = run_value_;
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800329 goal_->params = params_;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800330 sent_started_ = true;
331 if (!goal_.Send()) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800332 LOG(ERROR, "sending goal for action %" PRIx32 " on queue %s failed\n",
333 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800334 // Don't wait to see a message with it.
335 has_started_ = true;
336 }
Brian Silverman51675e62015-03-19 23:37:13 -0700337 queue_group_->status.FetchNext();
338 if (queue_group_->status.get()) {
339 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
340 }
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800341 if (queue_group_->status.get() && queue_group_->status->running != 0) {
342 old_run_value_ = queue_group_->status->running;
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800343 LOG(INFO, "Action %" PRIx32 " on queue %s already running\n",
344 old_run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800345 } else {
346 old_run_value_ = 0;
347 }
348 } else {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800349 LOG(WARNING, "Action %" PRIx32 " on queue %s already started\n", run_value_,
350 queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800351 }
352}
353
354} // namespace actions
355} // namespace common
356} // namespace aos
357
358#endif // AOS_COMMON_ACTIONS_ACTIONS_H_