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" |
| 14 | |
| 15 | namespace aos { |
| 16 | namespace common { |
| 17 | namespace actions { |
| 18 | |
| 19 | class Action; |
| 20 | |
| 21 | // A queue which queues Actions, verifies a single action is running, and |
| 22 | // cancels Actions. |
| 23 | class 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 Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 35 | // Runs the next action when the current one is finished running and polls the |
| 36 | // running action state for use by Running. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 37 | void Tick(); |
| 38 | |
| 39 | // Returns true if any action is running or could be running. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 40 | bool Running(); |
| 41 | |
| 42 | // Retrieves the internal state of the current action for testing. |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 43 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 44 | 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 49 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 50 | 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. |
| 60 | class 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 Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 74 | // 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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 78 | // Retrieves the internal state of the action for testing. |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 79 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 80 | 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 Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 97 | // Updates status for one cycle of waiting |
| 98 | virtual bool DoCheckIteration(bool blocking) = 0; |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 99 | // For testing we will need to get the internal state. |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 100 | // See comments on the private members of TypedAction<T, S> for details. |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 101 | 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. |
| 107 | template <typename T> |
| 108 | class 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 112 | *(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 Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 115 | |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 116 | TypedAction(T* queue_group, const ParamType ¶ms) |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 117 | : 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 123 | ((getpid() & 0xFFFF) << 16)), |
| 124 | params_(params) { |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 125 | 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 Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 146 | bool DoCheckIteration(bool blocking); |
| 147 | |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 148 | // 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 Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 183 | // flag passed to action in order to have differing types |
| 184 | const ParamType params_; |
| 185 | |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 186 | // 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 | |
| 195 | template <typename T> |
| 196 | ::std::atomic<uint16_t> TypedAction<T>::run_counter_{0}; |
| 197 | |
| 198 | template <typename T> |
| 199 | void TypedAction<T>::DoCancel() { |
| 200 | if (!sent_started_) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 201 | LOG(INFO, "Action %" PRIx32 " on queue %s was never started\n", run_value_, |
| 202 | queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 203 | } else { |
| 204 | if (interrupted_) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 205 | LOG(INFO, |
| 206 | "Action %" PRIx32 " on queue %s was interrupted -> not cancelling\n", |
| 207 | run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 208 | } else { |
| 209 | if (sent_cancel_) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 210 | LOG(INFO, "Action %" PRIx32 " on queue %s already cancelled\n", |
| 211 | run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 212 | } 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 | |
| 222 | template <typename T> |
| 223 | bool 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 | |
| 239 | template <typename T> |
| 240 | void TypedAction<T>::DoWaitUntilDone() { |
| 241 | CHECK(sent_started_); |
| 242 | queue_group_->status.FetchNext(); |
| 243 | CheckInterrupted(); |
| 244 | while (true) { |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 245 | if (DoCheckIteration(true)) { |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 246 | return; |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | template <typename T> |
Ben Fredrickson | 32e6c25 | 2015-02-21 23:53:38 -0800 | [diff] [blame] | 252 | bool 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 | |
| 272 | template <typename T> |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 273 | void 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 Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 281 | LOG(DEBUG, "Action %" PRIx32 " on queue %s has been started\n", |
| 282 | run_value_, queue_group_->goal.name()); |
Brian Silverman | a2ae62d | 2015-03-15 15:55:22 -0700 | [diff] [blame^] | 283 | } else if (old_run_value_ != 0 && |
| 284 | queue_group_->status->running == old_run_value_) { |
| 285 | LOG(DEBUG, "still running old instance %" PRIx32 "\n", old_run_value_); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 286 | } else { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 287 | LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32 |
| 288 | " before starting\n", |
| 289 | run_value_, queue_group_->goal.name(), queue_group_->status->running); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 290 | has_started_ = true; |
| 291 | interrupted_ = true; |
| 292 | } |
| 293 | } else { |
| 294 | LOG(WARNING, "No status message recieved.\n"); |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | template <typename T> |
| 299 | void TypedAction<T>::CheckInterrupted() { |
| 300 | if (!interrupted_ && has_started_ && queue_group_->status.get()) { |
| 301 | if (queue_group_->status->running != 0 && |
| 302 | queue_group_->status->running != run_value_) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 303 | LOG(WARNING, "Action %" PRIx32 " on queue %s interrupted by %" PRIx32 |
| 304 | " after starting\n", |
| 305 | run_value_, queue_group_->goal.name(), queue_group_->status->running); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 306 | } |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | template <typename T> |
| 311 | void TypedAction<T>::DoStart() { |
| 312 | if (goal_) { |
| 313 | LOG(INFO, "Starting action %" PRIx32 "\n", run_value_); |
| 314 | goal_->run = run_value_; |
Ben Fredrickson | 9fb2ab1 | 2015-02-16 16:42:08 -0800 | [diff] [blame] | 315 | goal_->params = params_; |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 316 | sent_started_ = true; |
| 317 | if (!goal_.Send()) { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 318 | LOG(ERROR, "sending goal for action %" PRIx32 " on queue %s failed\n", |
| 319 | run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 320 | // Don't wait to see a message with it. |
| 321 | has_started_ = true; |
| 322 | } |
| 323 | queue_group_->status.FetchLatest(); |
| 324 | if (queue_group_->status.get() && queue_group_->status->running != 0) { |
| 325 | old_run_value_ = queue_group_->status->running; |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 326 | LOG(INFO, "Action %" PRIx32 " on queue %s already running\n", |
| 327 | old_run_value_, queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 328 | } else { |
| 329 | old_run_value_ = 0; |
| 330 | } |
| 331 | } else { |
Austin Schuh | 0f9db8a | 2015-02-22 21:48:16 -0800 | [diff] [blame] | 332 | LOG(WARNING, "Action %" PRIx32 " on queue %s already started\n", run_value_, |
| 333 | queue_group_->goal.name()); |
Ben Fredrickson | d69f38b | 2015-01-28 20:06:15 -0800 | [diff] [blame] | 334 | } |
| 335 | } |
| 336 | |
| 337 | } // namespace actions |
| 338 | } // namespace common |
| 339 | } // namespace aos |
| 340 | |
| 341 | #endif // AOS_COMMON_ACTIONS_ACTIONS_H_ |