blob: fa89995614cd5d4b96d7f394b74a6fab46bf38fa [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
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800150 bool DoCheckIteration(bool blocking);
151
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() {
228 if (!sent_started_) return false;
229 if (has_started_) {
230 queue_group_->status.FetchNext();
231 CheckInterrupted();
Brian Silverman51675e62015-03-19 23:37:13 -0700232 } else {
233 while (queue_group_->status.FetchNext()) {
234 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
235 CheckStarted();
236 if (has_started_) CheckInterrupted();
237 }
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800238 }
239 if (interrupted_) return false;
240 // We've asked it to start but haven't gotten confirmation that it's started
241 // yet.
242 if (!has_started_) return true;
243 return queue_group_->status.get() &&
244 queue_group_->status->running == run_value_;
245}
246
247template <typename T>
248void TypedAction<T>::DoWaitUntilDone() {
249 CHECK(sent_started_);
250 queue_group_->status.FetchNext();
Brian Silverman51675e62015-03-19 23:37:13 -0700251 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800252 CheckInterrupted();
253 while (true) {
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800254 if (DoCheckIteration(true)) {
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800255 return;
256 }
257 }
258}
259
260template <typename T>
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800261bool TypedAction<T>::DoCheckIteration(bool blocking) {
262 CHECK(sent_started_);
263 if (interrupted_) return true;
264 CheckStarted();
265 if (blocking) {
Brian Silverman51675e62015-03-19 23:37:13 -0700266 queue_group_->status.FetchNextBlocking();
267 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800268 } else {
269 if (!queue_group_->status.FetchNext()) {
270 return false;
271 }
Brian Silverman51675e62015-03-19 23:37:13 -0700272 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
Ben Fredrickson32e6c252015-02-21 23:53:38 -0800273 }
274 CheckStarted();
275 CheckInterrupted();
276 if (has_started_ && (queue_group_->status.get() &&
277 queue_group_->status->running != run_value_)) {
278 return true;
279 }
280 return false;
281}
282
283template <typename T>
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800284void TypedAction<T>::CheckStarted() {
285 if (has_started_) return;
286 if (queue_group_->status.get()) {
287 if (queue_group_->status->running == run_value_ ||
288 (queue_group_->status->running == 0 &&
289 queue_group_->status->last_running == run_value_)) {
290 // It's currently running our instance.
291 has_started_ = true;
Brian Silverman51675e62015-03-19 23:37:13 -0700292 LOG(INFO, "Action %" PRIx32 " on queue %s has been started\n",
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800293 run_value_, queue_group_->goal.name());
Brian Silvermana2ae62d2015-03-15 15:55:22 -0700294 } else if (old_run_value_ != 0 &&
295 queue_group_->status->running == old_run_value_) {
296 LOG(DEBUG, "still running old instance %" PRIx32 "\n", old_run_value_);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800297 } else {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800298 LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
299 " before starting\n",
300 run_value_, queue_group_->goal.name(), queue_group_->status->running);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800301 has_started_ = true;
302 interrupted_ = true;
303 }
304 } else {
305 LOG(WARNING, "No status message recieved.\n");
306 }
307}
308
309template <typename T>
310void TypedAction<T>::CheckInterrupted() {
311 if (!interrupted_ && has_started_ && queue_group_->status.get()) {
312 if (queue_group_->status->running != 0 &&
313 queue_group_->status->running != run_value_) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800314 LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32
315 " after starting\n",
316 run_value_, queue_group_->goal.name(), queue_group_->status->running);
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800317 }
318 }
319}
320
321template <typename T>
322void TypedAction<T>::DoStart() {
323 if (goal_) {
324 LOG(INFO, "Starting action %" PRIx32 "\n", run_value_);
325 goal_->run = run_value_;
Ben Fredrickson9fb2ab12015-02-16 16:42:08 -0800326 goal_->params = params_;
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800327 sent_started_ = true;
328 if (!goal_.Send()) {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800329 LOG(ERROR, "sending goal for action %" PRIx32 " on queue %s failed\n",
330 run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800331 // Don't wait to see a message with it.
332 has_started_ = true;
333 }
Brian Silverman51675e62015-03-19 23:37:13 -0700334 queue_group_->status.FetchNext();
335 if (queue_group_->status.get()) {
336 LOG_STRUCT(DEBUG, "got status", *queue_group_->status);
337 }
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800338 if (queue_group_->status.get() && queue_group_->status->running != 0) {
339 old_run_value_ = queue_group_->status->running;
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800340 LOG(INFO, "Action %" PRIx32 " on queue %s already running\n",
341 old_run_value_, queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800342 } else {
343 old_run_value_ = 0;
344 }
345 } else {
Austin Schuh0f9db8a2015-02-22 21:48:16 -0800346 LOG(WARNING, "Action %" PRIx32 " on queue %s already started\n", run_value_,
347 queue_group_->goal.name());
Ben Fredricksond69f38b2015-01-28 20:06:15 -0800348 }
349}
350
351} // namespace actions
352} // namespace common
353} // namespace aos
354
355#endif // AOS_COMMON_ACTIONS_ACTIONS_H_