25 #ifndef ACE_ROUTINE_COROUTINE_H 26 #define ACE_ROUTINE_COROUTINE_H 32 class AceRoutineTest_statusStrings;
65 #define COROUTINE(...) \ 66 GET_COROUTINE(__VA_ARGS__, COROUTINE2, COROUTINE1)(__VA_ARGS__) 68 #define GET_COROUTINE(_1, _2, NAME, ...) NAME 70 #define COROUTINE1(name) \ 71 struct Coroutine_##name : ace_routine::Coroutine { \ 73 int runCoroutine() override; \ 75 Coroutine_##name :: Coroutine_##name() { \ 76 setupCoroutine(F(#name)); \ 78 int Coroutine_##name :: runCoroutine() 80 #define COROUTINE2(className, name) \ 81 struct className##_##name : className { \ 82 className##_##name(); \ 83 int runCoroutine() override; \ 85 className##_##name :: className##_##name() { \ 86 setupCoroutine(F(#name)); \ 88 int className##_##name :: runCoroutine() 98 #define EXTERN_COROUTINE(...) \ 99 GET_EXTERN_COROUTINE(\ 100 __VA_ARGS__, EXTERN_COROUTINE2, EXTERN_COROUTINE1)(__VA_ARGS__) 102 #define GET_EXTERN_COROUTINE(_1, _2, NAME, ...) NAME 104 #define EXTERN_COROUTINE1(name) \ 105 struct Coroutine_##name : ace_routine::Coroutine { \ 106 Coroutine_##name(); \ 107 int runCoroutine() override; \ 109 extern Coroutine_##name name 111 #define EXTERN_COROUTINE2(className, name) \ 112 struct className##_##name : className { \ 113 className##_##name(); \ 114 int runCoroutine() override; \ 116 extern className##_##name name 119 #define COROUTINE_BEGIN() \ 120 void* p = getJump(); \ 121 if (p != nullptr) { \ 129 #define COROUTINE_LOOP() \ 133 #define COROUTINE_YIELD_INTERNAL() \ 135 __label__ jumpLabel; \ 136 setJump(&& jumpLabel); \ 142 #define COROUTINE_YIELD() \ 145 COROUTINE_YIELD_INTERNAL(); \ 159 #define COROUTINE_AWAIT(condition) \ 163 COROUTINE_YIELD_INTERNAL(); \ 164 } while (!(condition)); \ 184 #define COROUTINE_DELAY(delayMillis) \ 186 setDelayMillis(delayMillis); \ 189 COROUTINE_YIELD_INTERNAL(); \ 190 } while (!isDelayExpired()); \ 195 #define COROUTINE_DELAY_MICROS(delayMicros) \ 197 setDelayMicros(delayMicros); \ 200 COROUTINE_YIELD_INTERNAL(); \ 201 } while (!isDelayExpired()); \ 206 #define COROUTINE_DELAY_SECONDS(delaySeconds) \ 208 setDelaySeconds(delaySeconds); \ 211 COROUTINE_YIELD_INTERNAL(); \ 212 } while (!isDelayExpired()); \ 220 #define COROUTINE_END() \ 222 __label__ jumpLabel; \ 224 setJump(&& jumpLabel); \ 244 unsigned long x = (n >> 8);
245 unsigned long y = (x >> 8);
246 unsigned long z = (y >> 8);
247 return (x >> 2) + 3 * (y >> 1) + 9 * z;
257 friend class ::AceRoutineTest_statusStrings;
287 virtual int runCoroutine() = 0;
293 virtual unsigned long coroutineMillis()
const;
299 virtual unsigned long coroutineMicros()
const;
307 virtual unsigned long coroutineSeconds()
const;
320 if (isDone())
return;
321 mStatus = kStatusSuspended;
334 switch (mDelayType) {
335 case kDelayTypeMillis: {
336 uint16_t elapsedMillis = coroutineMillis() - mDelayStart;
337 return elapsedMillis >= mDelayDuration;
339 case kDelayTypeMicros: {
340 uint16_t elapsedMicros = coroutineMicros() - mDelayStart;
341 return elapsedMicros >= mDelayDuration;
343 case kDelayTypeSeconds: {
344 uint16_t elapsedSeconds = coroutineSeconds() - mDelayStart;
345 return elapsedSeconds >= mDelayDuration;
357 bool isYielding()
const {
return mStatus == kStatusYielding; }
360 bool isDelaying()
const {
return mStatus == kStatusDelaying; }
363 bool isRunning()
const {
return mStatus == kStatusRunning; }
370 bool isEnding()
const {
return mStatus == kStatusEnding; }
386 return mStatus == kStatusEnding || mStatus == kStatusTerminated;
405 mStatus = kStatusYielding;
424 mStatus = kStatusYielding;
467 static const Status kStatusSuspended = 0;
470 static const Status kStatusYielding = 1;
473 static const Status kStatusDelaying = 2;
476 static const Status kStatusRunning = 3;
479 static const Status kStatusEnding = 4;
482 static const Status kStatusTerminated = 5;
485 static const uint8_t kDelayTypeMillis = 0;
488 static const uint8_t kDelayTypeMicros = 1;
491 static const uint8_t kDelayTypeSeconds = 2;
513 printer.print(sStatusStrings[mStatus]);
520 void setJump(
void* jumpPoint) { mJumpPoint = jumpPoint; }
560 mDelayType = kDelayTypeMillis;
561 mDelayStart = coroutineMillis();
562 mDelayDuration = (delayMillis >= UINT16_MAX / 2)
572 mDelayType = kDelayTypeMicros;
573 mDelayStart = coroutineMicros();
574 mDelayDuration = (delayMicros >= UINT16_MAX / 2)
584 mDelayType = kDelayTypeSeconds;
585 mDelayStart = coroutineSeconds();
586 mDelayDuration = (delaySeconds >= UINT16_MAX / 2)
597 static const __FlashStringHelper*
const sStatusStrings[];
615 void* mJumpPoint =
nullptr;
616 Status mStatus = kStatusSuspended;
618 uint16_t mDelayStart;
619 uint16_t mDelayDuration;
void setupCoroutine(const char *name)
Initialize the coroutine for the CoroutineScheduler, set it to Yielding state, and add it to the link...
void setDelaySeconds(uint16_t delaySeconds)
Configure the delay timer for delaySeconds.
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.
void setJump(void *jumpPoint)
Pointer to label where execute will start on the next call to runCoroutine().
void setDelayMillis(uint16_t delayMillis)
Configure the delay timer for delayMillis.
void * getJump() const
Pointer to label where execute will start on the next call to runCoroutine().
bool isDone() const
The coroutine is either Ending or Terminated.
const FCString & getName() const
Human-readable name of the coroutine.
bool isTerminated() const
The coroutine was terminated by the scheduler with a call to setTerminated().
bool isDelaying() const
The coroutine returned using COROUTINE_DELAY().
bool isEnding() const
The coroutine returned using COROUTINE_END().
void setupCoroutine(const __FlashStringHelper *name)
Same as setupCoroutine(const char*) except using flash string type.
Status getStatus() const
Return the status of the coroutine.
uint8_t Status
The execution status of the coroutine, corresponding to the COROUTINE_YIELD(), COROUTINE_DELAY(), COROUTINE_AWAIT() and COROUTINE_END() macros.
A union of (const char*) and (const __FlashStringHelper*) with a discriminator.
unsigned long udiv1000(unsigned long n)
Approximate division by 1000.
void setYielding()
Set the kStatusDelaying state.
bool isRunning() const
The coroutine is currently running.
virtual ~Coroutine()
Destructor.
bool isYielding() const
The coroutine returned using COROUTINE_YIELD().
void setRunning()
Set the kStatusRunning state.
void statusPrintTo(Print &printer)
Print the human-readable string of the Status.
void setEnding()
Set the kStatusEnding state.
bool isSuspended() const
The coroutine was suspended with a call to suspend().
bool isDelayExpired()
Check if delay time is over.
Coroutine ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Base class of all coroutines.
void setDelaying()
Set the kStatusDelaying state.
void setDelayMicros(uint16_t delayMicros)
Configure the delay timer for delayMicros.
void suspend()
Suspend the coroutine at the next scheduler iteration.