AceRoutine  1.2.1
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 <AceCommon.h> // FCString
31 
32 class AceRoutineTest_statusStrings;
33 class SuspendTest_suspendAndResume;
34 
66 #define COROUTINE(...) \
67  GET_COROUTINE(__VA_ARGS__, COROUTINE2, COROUTINE1)(__VA_ARGS__)
68 
70 #define GET_COROUTINE(_1, _2, NAME, ...) NAME
71 
73 #define COROUTINE1(name) \
74 struct Coroutine_##name : ace_routine::Coroutine { \
75  Coroutine_##name(); \
76  int runCoroutine() override; \
77 } name; \
78 Coroutine_##name :: Coroutine_##name() { \
79  setupCoroutine(F(#name)); \
80 } \
81 int Coroutine_##name :: runCoroutine()
82 
84 #define COROUTINE2(className, name) \
85 struct className##_##name : className { \
86  className##_##name(); \
87  int runCoroutine() override; \
88 } name; \
89 className##_##name :: className##_##name() { \
90  setupCoroutine(F(#name)); \
91 } \
92 int className##_##name :: runCoroutine()
93 
102 #define EXTERN_COROUTINE(...) \
103  GET_EXTERN_COROUTINE(\
104  __VA_ARGS__, EXTERN_COROUTINE2, EXTERN_COROUTINE1)(__VA_ARGS__)
105 
109 #define GET_EXTERN_COROUTINE(_1, _2, NAME, ...) NAME
110 
112 #define EXTERN_COROUTINE1(name) \
113 struct Coroutine_##name : ace_routine::Coroutine { \
114  Coroutine_##name(); \
115  int runCoroutine() override; \
116 }; \
117 extern Coroutine_##name name
118 
120 #define EXTERN_COROUTINE2(className, name) \
121 struct className##_##name : className { \
122  className##_##name(); \
123  int runCoroutine() override; \
124 }; \
125 extern className##_##name name
126 
128 #define COROUTINE_BEGIN() \
129  void* p = getJump(); \
130  if (p != nullptr) { \
131  goto *p; \
132  }
133 
138 #define COROUTINE_LOOP() \
139  COROUTINE_BEGIN(); \
140  while (true) \
141 
142 
146 #define COROUTINE_YIELD_INTERNAL() \
147  do { \
148  __label__ jumpLabel; \
149  setJump(&& jumpLabel); \
150  return 0; \
151  jumpLabel: ; \
152  } while (false)
153 
155 #define COROUTINE_YIELD() \
156  do { \
157  setYielding(); \
158  COROUTINE_YIELD_INTERNAL(); \
159  setRunning(); \
160  } while (false)
161 
172 #define COROUTINE_AWAIT(condition) \
173  do { \
174  setYielding(); \
175  do { \
176  COROUTINE_YIELD_INTERNAL(); \
177  } while (!(condition)); \
178  setRunning(); \
179  } while (false)
180 
197 #define COROUTINE_DELAY(delayMillis) \
198  do { \
199  setDelayMillis(delayMillis); \
200  setDelaying(); \
201  do { \
202  COROUTINE_YIELD_INTERNAL(); \
203  } while (!isDelayExpired()); \
204  setRunning(); \
205  } while (false)
206 
208 #define COROUTINE_DELAY_MICROS(delayMicros) \
209  do { \
210  setDelayMicros(delayMicros); \
211  setDelaying(); \
212  do { \
213  COROUTINE_YIELD_INTERNAL(); \
214  } while (!isDelayExpired()); \
215  setRunning(); \
216  } while (false)
217 
236 #define COROUTINE_DELAY_SECONDS(delaySeconds) \
237  do { \
238  setDelaySeconds(delaySeconds); \
239  setDelaying(); \
240  do { \
241  COROUTINE_YIELD_INTERNAL(); \
242  } while (!isDelayExpired()); \
243  setRunning(); \
244  } while (false)
245 
250 #define COROUTINE_END() \
251  do { \
252  __label__ jumpLabel; \
253  setEnding(); \
254  setJump(&& jumpLabel); \
255  jumpLabel: ; \
256  return 0; \
257  } while (false)
258 
259 namespace ace_routine {
260 
265 class Coroutine {
266  friend class CoroutineScheduler;
267  friend class ::AceRoutineTest_statusStrings;
268  friend class ::SuspendTest_suspendAndResume;
269 
270  public:
272  const ace_common::FCString& getName() const { return mName; }
273 
284  virtual int runCoroutine() = 0;
285 
290  virtual unsigned long coroutineMillis() const;
291 
296  virtual unsigned long coroutineMicros() const;
297 
304  virtual unsigned long coroutineSeconds() const;
305 
316  void suspend() {
317  if (isDone()) return;
318  mStatus = kStatusSuspended;
319  }
320 
327  void resume();
328 
342  void reset() {
343  mStatus = kStatusYielding;
344  mJumpPoint = nullptr;
345  }
346 
348  bool isDelayExpired() const;
349 
351  bool isSuspended() const { return mStatus == kStatusSuspended; }
352 
354  bool isYielding() const { return mStatus == kStatusYielding; }
355 
357  bool isDelaying() const { return mStatus == kStatusDelaying; }
358 
360  bool isRunning() const { return mStatus == kStatusRunning; }
361 
367  bool isEnding() const { return mStatus == kStatusEnding; }
368 
375  bool isTerminated() const { return mStatus == kStatusTerminated; }
376 
382  bool isDone() const {
383  return mStatus == kStatusEnding || mStatus == kStatusTerminated;
384  }
385 
400  void setupCoroutine(const char* name);
401 
417  void setupCoroutine(const __FlashStringHelper* name);
418 
427  void setupCoroutineOrderedByName(const char* name);
428 
438  void setupCoroutineOrderedByName(const __FlashStringHelper* name);
439 
440  protected:
471  typedef uint8_t Status;
472 
479  static const Status kStatusSuspended = 0;
480 
482  static const Status kStatusYielding = 1;
483 
485  static const Status kStatusDelaying = 2;
486 
488  static const Status kStatusRunning = 3;
489 
491  static const Status kStatusEnding = 4;
492 
494  static const Status kStatusTerminated = 5;
495 
497  static const uint8_t kDelayTypeMillis = 0;
498 
500  static const uint8_t kDelayTypeMicros = 1;
501 
503  static const uint8_t kDelayTypeSeconds = 2;
504 
516 
518  virtual ~Coroutine() {}
519 
521  Status getStatus() const { return mStatus; }
522 
524  void statusPrintTo(Print& printer) {
525  printer.print(sStatusStrings[mStatus]);
526  }
527 
532  void setJump(void* jumpPoint) { mJumpPoint = jumpPoint; }
533 
538  void* getJump() const { return mJumpPoint; }
539 
541  void setRunning() { mStatus = kStatusRunning; }
542 
544  void setYielding() { mStatus = kStatusYielding; }
545 
547  void setDelaying() { mStatus = kStatusDelaying; }
548 
550  void setEnding() { mStatus = kStatusEnding; }
551 
556  void setTerminated() { mStatus = kStatusTerminated; }
557 
571  void setDelayMillis(uint16_t delayMillis) {
572  mDelayType = kDelayTypeMillis;
573  mDelayStart = coroutineMillis();
574  mDelayDuration = (delayMillis >= UINT16_MAX / 2)
575  ? UINT16_MAX / 2
576  : delayMillis;
577  }
578 
583  void setDelayMicros(uint16_t delayMicros) {
584  mDelayType = kDelayTypeMicros;
585  mDelayStart = coroutineMicros();
586  mDelayDuration = (delayMicros >= UINT16_MAX / 2)
587  ? UINT16_MAX / 2
588  : delayMicros;
589  }
590 
595  void setDelaySeconds(uint16_t delaySeconds) {
596  mDelayType = kDelayTypeSeconds;
597  mDelayStart = coroutineSeconds();
598  mDelayDuration = (delaySeconds >= UINT16_MAX / 2)
599  ? UINT16_MAX / 2
600  : delaySeconds;
601  }
602 
603  private:
604  // Disable copy-constructor and assignment operator
605  Coroutine(const Coroutine&) = delete;
606  Coroutine& operator=(const Coroutine&) = delete;
607 
609  static const __FlashStringHelper* const sStatusStrings[];
610 
616  static Coroutine** getRoot();
617 
624  Coroutine** getNext() { return &mNext; }
625 
638  void insertSorted();
639 
646  void insertAtRoot();
647 
648  ace_common::FCString mName;
649  Coroutine* mNext = nullptr;
650  void* mJumpPoint = nullptr;
651  Status mStatus = kStatusYielding;
652  uint8_t mDelayType;
653  uint16_t mDelayStart; // millis or micros
654  uint16_t mDelayDuration; // millis or micros
655 };
656 
657 }
658 
659 #endif
ace_routine::Coroutine::setDelayMillis
void setDelayMillis(uint16_t delayMillis)
Configure the delay timer for delayMillis.
Definition: Coroutine.h:571
ace_routine::Coroutine::getStatus
Status getStatus() const
Return the status of the coroutine.
Definition: Coroutine.h:521
ace_routine::Coroutine::reset
void reset()
Reset the coroutine to its initial state.
Definition: Coroutine.h:342
ace_routine::Coroutine::suspend
void suspend()
Suspend the coroutine at the next scheduler iteration.
Definition: Coroutine.h:316
ace_routine::Coroutine::setRunning
void setRunning()
Set the kStatusRunning state.
Definition: Coroutine.h:541
ace_routine::Coroutine::setEnding
void setEnding()
Set the kStatusEnding state.
Definition: Coroutine.h:550
ace_routine::Coroutine::kDelayTypeSeconds
static const uint8_t kDelayTypeSeconds
Delay using units of seconds.
Definition: Coroutine.h:503
ace_routine::Coroutine::resume
void resume()
Add a Suspended coroutine into the head of the scheduler linked list, and change the state to Yieldin...
Definition: Coroutine.cpp:103
ace_routine::Coroutine::kDelayTypeMillis
static const uint8_t kDelayTypeMillis
Delay using units of millis.
Definition: Coroutine.h:497
ace_routine::Coroutine::getJump
void * getJump() const
Pointer to label where execute will start on the next call to runCoroutine().
Definition: Coroutine.h:538
ace_routine::CoroutineScheduler
Class that manages instances of the Coroutine class, and executes them in a round-robin fashion.
Definition: CoroutineScheduler.h:37
ace_routine::Coroutine::setDelayMicros
void setDelayMicros(uint16_t delayMicros)
Configure the delay timer for delayMicros.
Definition: Coroutine.h:583
ace_routine::Coroutine::setTerminated
void setTerminated()
Set status to indicate that the Coroutine has been removed from the Scheduler queue.
Definition: Coroutine.h:556
ace_routine::Coroutine::kStatusRunning
static const Status kStatusRunning
Coroutine is currenly running.
Definition: Coroutine.h:488
ace_routine::Coroutine::coroutineMicros
virtual unsigned long coroutineMicros() const
Returns the current millisecond clock.
Definition: Coroutine.cpp:118
ace_routine::Coroutine::kStatusSuspended
static const Status kStatusSuspended
Coroutine has been suspended using suspend() and the scheduler should remove it from the queue upon t...
Definition: Coroutine.h:479
ace_routine::Coroutine::isEnding
bool isEnding() const
The coroutine returned using COROUTINE_END().
Definition: Coroutine.h:367
ace_routine::Coroutine::setDelaying
void setDelaying()
Set the kStatusDelaying state.
Definition: Coroutine.h:547
ace_routine::Coroutine::coroutineSeconds
virtual unsigned long coroutineSeconds() const
Returns the current clock in unit of seconds, truncated to the lower 16-bits.
Definition: Coroutine.cpp:122
ace_routine::Coroutine::isRunning
bool isRunning() const
The coroutine is currently running.
Definition: Coroutine.h:360
ace_routine::Coroutine::kStatusTerminated
static const Status kStatusTerminated
Coroutine has ended and no longer in the scheduler queue.
Definition: Coroutine.h:494
ace_routine::Coroutine::Status
uint8_t Status
The execution status of the coroutine, corresponding to the COROUTINE_YIELD(), COROUTINE_DELAY(),...
Definition: Coroutine.h:471
ace_routine::Coroutine::setJump
void setJump(void *jumpPoint)
Pointer to label where execute will start on the next call to runCoroutine().
Definition: Coroutine.h:532
ace_routine::Coroutine::kStatusEnding
static const Status kStatusEnding
Coroutine executed the COROUTINE_END() statement.
Definition: Coroutine.h:491
ace_routine::Coroutine::getName
const ace_common::FCString & getName() const
Human-readable name of the coroutine.
Definition: Coroutine.h:272
ace_routine::Coroutine::setDelaySeconds
void setDelaySeconds(uint16_t delaySeconds)
Configure the delay timer for delaySeconds.
Definition: Coroutine.h:595
ace_routine::Coroutine::isDone
bool isDone() const
The coroutine is either Ending or Terminated.
Definition: Coroutine.h:382
ace_routine::Coroutine::kStatusDelaying
static const Status kStatusDelaying
Coroutine returned using the COROUTINE_DELAY() statement.
Definition: Coroutine.h:485
ace_routine::Coroutine::kDelayTypeMicros
static const uint8_t kDelayTypeMicros
Delay using units of micros.
Definition: Coroutine.h:500
ace_routine::Coroutine::isDelayExpired
bool isDelayExpired() const
Check if delay time is over.
Definition: Coroutine.cpp:33
ace_routine::Coroutine::setYielding
void setYielding()
Set the kStatusDelaying state.
Definition: Coroutine.h:544
ace_routine::Coroutine::setupCoroutineOrderedByName
void setupCoroutineOrderedByName(const char *name)
A version of setupCoroutine(const char*) where the ordering of the coroutines executed by CoroutineSc...
Definition: Coroutine.cpp:65
ace_routine::Coroutine
Base class of all coroutines.
Definition: Coroutine.h:265
ace_routine::Coroutine::~Coroutine
virtual ~Coroutine()
Destructor.
Definition: Coroutine.h:518
ace_routine::Coroutine::isYielding
bool isYielding() const
The coroutine returned using COROUTINE_YIELD().
Definition: Coroutine.h:354
ace_routine::Coroutine::runCoroutine
virtual int runCoroutine()=0
The body of the coroutine.
ace_routine::Coroutine::statusPrintTo
void statusPrintTo(Print &printer)
Print the human-readable string of the Status.
Definition: Coroutine.h:524
ace_routine::Coroutine::isDelaying
bool isDelaying() const
The coroutine returned using COROUTINE_DELAY().
Definition: Coroutine.h:357
ace_routine::Coroutine::isSuspended
bool isSuspended() const
The coroutine was suspended with a call to suspend().
Definition: Coroutine.h:351
ace_routine::Coroutine::Coroutine
Coroutine()
Constructor.
Definition: Coroutine.h:515
ace_routine::Coroutine::kStatusYielding
static const Status kStatusYielding
Coroutine returned using the COROUTINE_YIELD() statement.
Definition: Coroutine.h:482
ace_routine::Coroutine::isTerminated
bool isTerminated() const
The coroutine was terminated by the scheduler with a call to setTerminated().
Definition: Coroutine.h:375
ace_routine::Coroutine::coroutineMillis
virtual unsigned long coroutineMillis() const
Returns the current millisecond clock.
Definition: Coroutine.cpp:114
ace_routine::Coroutine::setupCoroutine
void setupCoroutine(const char *name)
Initialize the coroutine for the CoroutineScheduler, set it to Yielding state, and add it to the link...
Definition: Coroutine.cpp:53