AceRoutine  0.2
A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.
Coroutine.h
Go to the documentation of this file.
1 /*
2 MIT License
3 
4 Copyright (c) 2018 Brian T. Park
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 
25 #ifndef ACE_ROUTINE_COROUTINE_H
26 #define ACE_ROUTINE_COROUTINE_H
27 
28 #include <stdint.h> // UINT16_MAX
29 #include <Print.h> // Print
30 #include "Flash.h" // ACE_ROUTINE_F()
31 #include "FCString.h"
32 
64 #define COROUTINE(...) \
65  GET_COROUTINE(__VA_ARGS__, COROUTINE2, COROUTINE1)(__VA_ARGS__)
66 
67 #define GET_COROUTINE(_1, _2, NAME, ...) NAME
68 
69 #define COROUTINE1(name) \
70 struct Coroutine_##name : ace_routine::Coroutine { \
71  Coroutine_##name(); \
72  virtual int runCoroutine() override \
73  __attribute__((__noinline__,__noclone__)); \
74 } name; \
75 Coroutine_##name :: Coroutine_##name() { \
76  setupCoroutine(ACE_ROUTINE_F(#name)); \
77 } \
78 int Coroutine_##name :: runCoroutine()
79 
80 #define COROUTINE2(className, name) \
81 struct className##_##name : className { \
82  className##_##name(); \
83  virtual int runCoroutine() override \
84  __attribute__((__noinline__,__noclone__)); \
85 } name; \
86 className##_##name :: className##_##name() { \
87  setupCoroutine(ACE_ROUTINE_F(#name)); \
88 } \
89 int className##_##name :: runCoroutine()
90 
99 #define EXTERN_COROUTINE(...) \
100  GET_EXTERN_COROUTINE(\
101  __VA_ARGS__, EXTERN_COROUTINE2, EXTERN_COROUTINE1)(__VA_ARGS__)
102 
103 #define GET_EXTERN_COROUTINE(_1, _2, NAME, ...) NAME
104 
105 #define EXTERN_COROUTINE1(name) \
106 struct Coroutine_##name : ace_routine::Coroutine { \
107  Coroutine_##name(); \
108  virtual int runCoroutine() override \
109  __attribute__((__noinline__,__noclone__)); \
110 }; \
111 extern Coroutine_##name name
112 
113 #define EXTERN_COROUTINE2(className, name) \
114 struct className##_##name : className { \
115  className##_##name(); \
116  virtual int runCoroutine() override \
117  __attribute__((__noinline__,__noclone__)); \
118 }; \
119 extern className##_##name name
120 
122 #define COROUTINE_BEGIN() \
123  void* p = getJump(); \
124  if (p != nullptr) { \
125  goto *p; \
126  }
127 
132 #define COROUTINE_LOOP() \
133  COROUTINE_BEGIN(); \
134  while (true) \
135 
136 #define COROUTINE_YIELD_INTERNAL() \
137  do { \
138  __label__ jumpLabel; \
139  setJump(&& jumpLabel); \
140  return 0; \
141  jumpLabel: ; \
142  } while (false)
143 
145 #define COROUTINE_YIELD() \
146  do { \
147  setYielding(); \
148  COROUTINE_YIELD_INTERNAL(); \
149  setRunning(); \
150  } while (false)
151 
162 #define COROUTINE_AWAIT(condition) \
163  do { \
164  while (!(condition)) { \
165  setYielding(); \
166  COROUTINE_YIELD_INTERNAL(); \
167  } \
168  setRunning(); \
169  } while (false)
170 
186 #define COROUTINE_DELAY(delayMillis) \
187  do { \
188  setDelay(delayMillis); \
189  while (!isDelayExpired()) { \
190  setDelaying(); \
191  COROUTINE_YIELD_INTERNAL(); \
192  } \
193  setRunning(); \
194  } while (false)
195 
242 #define COROUTINE_DELAY_SECONDS(loopCounter, delaySeconds) \
243  do { \
244  loopCounter = delaySeconds; \
245  while (loopCounter-- > 0) { \
246  COROUTINE_DELAY(1000); \
247  } \
248  } while (false)
249 
254 #define COROUTINE_END() \
255  do { \
256  __label__ jumpLabel; \
257  setEnding(); \
258  setJump(&& jumpLabel); \
259  jumpLabel: ; \
260  return 0; \
261  } while (false)
262 
263 class StatusStringTest;
264 
265 namespace ace_routine {
266 
271 class Coroutine {
272  friend class CoroutineScheduler;
273  friend class ::StatusStringTest;
274 
275  public:
281  static Coroutine** getRoot();
282 
288  Coroutine** getNext() { return &mNext; }
289 
291  const FCString& getName() const { return mName; }
292 
303  virtual int runCoroutine() = 0;
304 
309  virtual unsigned long millis() const;
310 
321  void suspend() {
322  if (isDone()) return;
323  mStatus = kStatusSuspended;
324  }
325 
332  void resume();
333 
335  bool isDelayExpired() {
336  uint16_t elapsedMillis = millis() - mDelayStartMillis;
337  return elapsedMillis >= mDelayDurationMillis;
338  }
339 
341  bool isSuspended() const { return mStatus == kStatusSuspended; }
342 
344  bool isYielding() const { return mStatus == kStatusYielding; }
345 
347  bool isDelaying() const { return mStatus == kStatusDelaying; }
348 
350  bool isRunning() const { return mStatus == kStatusRunning; }
351 
357  bool isEnding() const { return mStatus == kStatusEnding; }
358 
365  bool isTerminated() const { return mStatus == kStatusTerminated; }
366 
372  bool isDone() const {
373  return mStatus == kStatusEnding || mStatus == kStatusTerminated;
374  }
375 
390  void setupCoroutine(const char* name) {
391  mName = FCString(name);
392  mStatus = kStatusYielding;
393  insertSorted();
394  }
395 
409  void setupCoroutine(const __FlashStringHelper* name) {
410  mName = FCString(name);
411  mStatus = kStatusYielding;
412  insertSorted();
413  }
414 
415  protected:
446  typedef uint8_t Status;
447 
454  static const Status kStatusSuspended = 0;
455 
457  static const Status kStatusYielding = 1;
458 
460  static const Status kStatusDelaying = 2;
461 
463  static const Status kStatusRunning = 3;
464 
466  static const Status kStatusEnding = 4;
467 
469  static const Status kStatusTerminated = 5;
470 
482 
484  Status getStatus() const { return mStatus; }
485 
487  void statusPrintTo(Print& printer) {
488  printer.print(sStatusStrings[mStatus]);
489  }
490 
495  void setJump(void* jumpPoint) { mJumpPoint = jumpPoint; }
496 
501  void* getJump() const { return mJumpPoint; }
502 
504  void setRunning() { mStatus = kStatusRunning; }
505 
507  void setYielding() { mStatus = kStatusYielding; }
508 
510  void setDelaying() { mStatus = kStatusDelaying; }
511 
524  void setDelay(uint16_t delayMillisDuration) {
525  mDelayStartMillis = millis();
526  mDelayDurationMillis = (delayMillisDuration >= UINT16_MAX / 2)
527  ? UINT16_MAX / 2
528  : delayMillisDuration;
529  }
530 
532  void setEnding() { mStatus = kStatusEnding; }
533 
538  void setTerminated() { mStatus = kStatusTerminated; }
539 
540  private:
541  // Disable copy-constructor and assignment operator
542  Coroutine(const Coroutine&) = delete;
543  Coroutine& operator=(const Coroutine&) = delete;
544 
546  static const __FlashStringHelper* const sStatusStrings[];
547 
560  void insertSorted();
561 
562  FCString mName;
563  Coroutine* mNext = nullptr;
564  void* mJumpPoint = nullptr;
565  Status mStatus = kStatusSuspended;
566  uint16_t mDelayStartMillis;
567  uint16_t mDelayDurationMillis;
568 };
569 
570 }
571 
572 #endif
void resume()
Add a Suspended coroutine into the head of the scheduler linked list, and change the state to Yieldin...
Definition: Coroutine.cpp:52
Coroutine()
Constructor.
Definition: Coroutine.h:481
void setupCoroutine(const char *name)
Initialize the coroutine for the CoroutineScheduler, set it to Yielding state, and add it to the link...
Definition: Coroutine.h:390
Class that manages instances of the Coroutine class, and executes them in a round-robin fashion...
void setTerminated()
Set status to indicate that the Coroutine has been removed from the Scheduler queue.
Definition: Coroutine.h:538
static const Status kStatusEnding
Coroutine executed the COROUTINE_END() statement.
Definition: Coroutine.h:466
void setJump(void *jumpPoint)
Pointer to label where execute will start on the next call to runCoroutine().
Definition: Coroutine.h:495
virtual int runCoroutine()=0
The body of the coroutine.
void * getJump() const
Pointer to label where execute will start on the next call to runCoroutine().
Definition: Coroutine.h:501
bool isDone() const
The coroutine is either Ending or Terminated.
Definition: Coroutine.h:372
const FCString & getName() const
Human-readable name of the coroutine.
Definition: Coroutine.h:291
bool isTerminated() const
The coroutine was terminated by the scheduler with a call to setTerminated().
Definition: Coroutine.h:365
bool isDelaying() const
The coroutine returned using COROUTINE_DELAY().
Definition: Coroutine.h:347
static const Status kStatusRunning
Coroutine is currenly running.
Definition: Coroutine.h:463
bool isEnding() const
The coroutine returned using COROUTINE_END().
Definition: Coroutine.h:357
static const Status kStatusYielding
Coroutine returned using the COROUTINE_YIELD() statement.
Definition: Coroutine.h:457
virtual unsigned long millis() const
Returns the current millisecond clock.
Definition: Coroutine.cpp:68
static const Status kStatusSuspended
Coroutine has been suspended using suspend() and the scheduler should remove it from the queue upon t...
Definition: Coroutine.h:454
void setupCoroutine(const __FlashStringHelper *name)
Same as setupCoroutine(const char*) except using flash string type.
Definition: Coroutine.h:409
Status getStatus() const
Return the status of the coroutine.
Definition: Coroutine.h:484
static const Status kStatusTerminated
Coroutine has ended and no longer in the scheduler queue.
Definition: Coroutine.h:469
uint8_t Status
The execution status of the coroutine, corresponding to the COROUTINE_YIELD(), COROUTINE_DELAY(), COROUTINE_AWAIT() and COROUTINE_END() macros.
Definition: Coroutine.h:446
A union of (const char*) and (const __FlashStringHelper*) with a discriminator.
Definition: FCString.h:53
static const Status kStatusDelaying
Coroutine returned using the COROUTINE_DELAY() statement.
Definition: Coroutine.h:460
void setDelay(uint16_t delayMillisDuration)
Configure the delay timer.
Definition: Coroutine.h:524
void setYielding()
Set the kStatusDelaying state.
Definition: Coroutine.h:507
bool isRunning() const
The coroutine is currently running.
Definition: Coroutine.h:350
bool isYielding() const
The coroutine returned using COROUTINE_YIELD().
Definition: Coroutine.h:344
void setRunning()
Set the kStatusRunning state.
Definition: Coroutine.h:504
void statusPrintTo(Print &printer)
Print the human-readable string of the Status.
Definition: Coroutine.h:487
void setEnding()
Set the kStatusEnding state.
Definition: Coroutine.h:532
bool isSuspended() const
The coroutine was suspended with a call to suspend().
Definition: Coroutine.h:341
bool isDelayExpired()
Check if delay time is over.
Definition: Coroutine.h:335
Coroutine ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Definition: Coroutine.h:288
Base class of all coroutines.
Definition: Coroutine.h:271
static Coroutine ** getRoot()
Get the pointer to the root pointer.
Definition: Coroutine.cpp:34
Various macros to smooth over the differences among the various platforms with regards to their suppo...
void setDelaying()
Set the kStatusDelaying state.
Definition: Coroutine.h:510
void suspend()
Suspend the coroutine at the next scheduler iteration.
Definition: Coroutine.h:321