Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 1 | #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 Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 14 | #include "aos/common/logging/queue_logging.h" |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 15 | |
| 16 | namespace aos { |
| 17 | namespace common { |
| 18 | namespace actions { |
| 19 | |
| 20 | class Action; |
| 21 | |
| 22 | // A queue which queues Actions, verifies a single action is running, and |
| 23 | // cancels Actions. |
| 24 | class 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 Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 36 | // Runs the next action when the current one is finished running and polls the |
| 37 | // running action state for use by Running. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 38 | void Tick(); |
| 39 | |
| 40 | // Returns true if any action is running or could be running. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 41 | bool Running(); |
| 42 | |
| 43 | // Retrieves the internal state of the current action for testing. |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 44 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 45 | 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 50 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 51 | 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. |
| 61 | class 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 Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 75 | // 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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 79 | // Retrieves the internal state of the action for testing. |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 80 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 81 | 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 Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 98 | // Updates status for one cycle of waiting |
| 99 | virtual bool DoCheckIteration(bool blocking) = 0; |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 100 | // For testing we will need to get the internal state. |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 101 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 102 | 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. |
| 108 | template <typename T> |
| 109 | class 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 113 | *(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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 116 | |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 117 | TypedAction(T* queue_group, const ParamType ¶ms) |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 118 | : 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 124 | ((getpid() & 0xFFFF) << 16)), |
| 125 | params_(params) { |
Austin Schuh | dae6377 | 2016-04-03 21:34:36 -0700 | [diff] [blame^] | 126 | LOG(DEBUG, "Action %" PRIx32 " created on queue %s\n", run_value_, |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 127 | queue_group_->goal.name()); |
| 128 | // Clear out any old status messages from before now. |
| 129 | queue_group_->status.FetchLatest(); |
Brian Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 130 | if (queue_group_->status.get()) { |
| 131 | LOG_STRUCT(DEBUG, "have status", *queue_group_->status); |
| 132 | } |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 133 | } |
| 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 Silverman | f1cff39 | 2015-10-11 19:36:18 -0400 | [diff] [blame] | 150 | bool DoCheckIteration(bool blocking) override; |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 151 | |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 152 | // 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 187 | // flag passed to action in order to have differing types |
| 188 | const ParamType params_; |
| 189 | |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 190 | // 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 | |
| 199 | template <typename T> |
| 200 | ::std::atomic<uint16_t> TypedAction<T>::run_counter_{0}; |
| 201 | |
| 202 | template <typename T> |
| 203 | void TypedAction<T>::DoCancel() { |
| 204 | if (!sent_started_) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 205 | LOG(INFO, "Action %" PRIx32 " on queue %s was never started\n", run_value_, |
| 206 | queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 207 | } else { |
| 208 | if (interrupted_) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 209 | LOG(INFO, |
| 210 | "Action %" PRIx32 " on queue %s was interrupted -> not cancelling\n", |
| 211 | run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 212 | } else { |
| 213 | if (sent_cancel_) { |
Austin Schuh | dae6377 | 2016-04-03 21:34:36 -0700 | [diff] [blame^] | 214 | LOG(DEBUG, "Action %" PRIx32 " on queue %s already cancelled\n", |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 215 | run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 216 | } else { |
Austin Schuh | dae6377 | 2016-04-03 21:34:36 -0700 | [diff] [blame^] | 217 | LOG(DEBUG, "Canceling action %" PRIx32 " on queue %s\n", run_value_, |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 218 | queue_group_->goal.name()); |
| 219 | queue_group_->goal.MakeWithBuilder().run(0).Send(); |
| 220 | sent_cancel_ = true; |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | template <typename T> |
| 227 | bool TypedAction<T>::DoRunning() { |
Brian Silverman | 3f05029 | 2015-03-28 18:29:32 -0400 | [diff] [blame] | 228 | if (!sent_started_) { |
| 229 | LOG(DEBUG, "haven't sent start message yet\n"); |
| 230 | return false; |
| 231 | } |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 232 | if (has_started_) { |
| 233 | queue_group_->status.FetchNext(); |
| 234 | CheckInterrupted(); |
Brian Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 235 | } 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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 241 | } |
| 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 | |
| 250 | template <typename T> |
| 251 | void TypedAction<T>::DoWaitUntilDone() { |
| 252 | CHECK(sent_started_); |
| 253 | queue_group_->status.FetchNext(); |
Brian Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 254 | LOG_STRUCT(DEBUG, "got status", *queue_group_->status); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 255 | CheckInterrupted(); |
| 256 | while (true) { |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 257 | if (DoCheckIteration(true)) { |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 258 | return; |
| 259 | } |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | template <typename T> |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 264 | bool TypedAction<T>::DoCheckIteration(bool blocking) { |
| 265 | CHECK(sent_started_); |
| 266 | if (interrupted_) return true; |
| 267 | CheckStarted(); |
| 268 | if (blocking) { |
Brian Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 269 | queue_group_->status.FetchNextBlocking(); |
| 270 | LOG_STRUCT(DEBUG, "got status", *queue_group_->status); |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 271 | } else { |
| 272 | if (!queue_group_->status.FetchNext()) { |
| 273 | return false; |
| 274 | } |
Brian Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 275 | LOG_STRUCT(DEBUG, "got status", *queue_group_->status); |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 276 | } |
| 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 | |
| 286 | template <typename T> |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 287 | void 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; |
Austin Schuh | dae6377 | 2016-04-03 21:34:36 -0700 | [diff] [blame^] | 295 | LOG(DEBUG, "Action %" PRIx32 " on queue %s has been started\n", |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 296 | run_value_, queue_group_->goal.name()); |
Brian Silverman | a2ae62d | 2015-03-15 15:55:22 -0700 | [diff] [blame] | 297 | } 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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 300 | } else { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 301 | 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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 304 | has_started_ = true; |
| 305 | interrupted_ = true; |
| 306 | } |
| 307 | } else { |
| 308 | LOG(WARNING, "No status message recieved.\n"); |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | template <typename T> |
| 313 | void 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 Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 317 | 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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 320 | } |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | template <typename T> |
| 325 | void TypedAction<T>::DoStart() { |
| 326 | if (goal_) { |
Austin Schuh | dae6377 | 2016-04-03 21:34:36 -0700 | [diff] [blame^] | 327 | LOG(DEBUG, "Starting action %" PRIx32 "\n", run_value_); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 328 | goal_->run = run_value_; |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 329 | goal_->params = params_; |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 330 | sent_started_ = true; |
| 331 | if (!goal_.Send()) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 332 | LOG(ERROR, "sending goal for action %" PRIx32 " on queue %s failed\n", |
| 333 | run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 334 | // Don't wait to see a message with it. |
| 335 | has_started_ = true; |
| 336 | } |
Brian Silverman | 51675e6 | 2015-03-19 23:37:13 -0700 | [diff] [blame] | 337 | queue_group_->status.FetchNext(); |
| 338 | if (queue_group_->status.get()) { |
| 339 | LOG_STRUCT(DEBUG, "got status", *queue_group_->status); |
| 340 | } |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 341 | if (queue_group_->status.get() && queue_group_->status->running != 0) { |
| 342 | old_run_value_ = queue_group_->status->running; |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 343 | LOG(INFO, "Action %" PRIx32 " on queue %s already running\n", |
| 344 | old_run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 345 | } else { |
| 346 | old_run_value_ = 0; |
| 347 | } |
| 348 | } else { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 349 | LOG(WARNING, "Action %" PRIx32 " on queue %s already started\n", run_value_, |
| 350 | queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 351 | } |
| 352 | } |
| 353 | |
| 354 | } // namespace actions |
| 355 | } // namespace common |
| 356 | } // namespace aos |
| 357 | |
| 358 | #endif // AOS_COMMON_ACTIONS_ACTIONS_H_ |