AceRoutine  0.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 "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 run() override \
73  __attribute__((__noinline__,__noclone__)); \
74 } name; \
75 Coroutine_##name :: Coroutine_##name() { \
76  init(ACE_ROUTINE_F(#name)); \
77 } \
78 int Coroutine_##name :: run()
79 
80 #define COROUTINE2(className, name) \
81 struct className##_##name : className { \
82  className##_##name(); \
83  virtual int run() override \
84  __attribute__((__noinline__,__noclone__)); \
85 } name; \
86 className##_##name :: className##_##name() { \
87  init(ACE_ROUTINE_F(#name)); \
88 } \
89 int className##_##name :: run()
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 run() 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 run() 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 
164 #define COROUTINE_AWAIT(condition) \
165  do { \
166  while (!(condition)) { \
167  setAwaiting(); \
168  COROUTINE_YIELD_INTERNAL(); \
169  } \
170  setRunning(); \
171  } while (false)
172 
188 #define COROUTINE_DELAY(delayMillis) \
189  do { \
190  setDelay(delayMillis); \
191  while (!isDelayExpired()) { \
192  setDelaying(); \
193  COROUTINE_YIELD_INTERNAL(); \
194  } \
195  setRunning(); \
196  } while (false)
197 
202 #define COROUTINE_END() \
203  do { \
204  __label__ jumpLabel; \
205  setEnding(); \
206  setJump(&& jumpLabel); \
207  jumpLabel: ; \
208  return 0; \
209  } while (false)
210 
211 namespace ace_routine {
212 
217 class Coroutine {
218  friend class CoroutineScheduler;
219 
220  public:
226  static Coroutine** getRoot();
227 
233  Coroutine** getNext() { return &mNext; }
234 
236  const FCString& getName() const { return mName; }
237 
248  virtual int run() = 0;
249 
254  virtual unsigned long millis() const;
255 
266  void suspend() {
267  if (isDone()) return;
268  mStatus = kStatusSuspended;
269  }
270 
277  void resume();
278 
280  bool isDelayExpired() {
281  uint16_t elapsedMillis = millis() - mDelayStartMillis;
282  return elapsedMillis >= mDelayDurationMillis;
283  }
284 
286  bool isSuspended() const { return mStatus == kStatusSuspended; }
287 
289  bool isYielding() const { return mStatus == kStatusYielding; }
290 
292  bool isAwaiting() const { return mStatus == kStatusAwaiting; }
293 
295  bool isDelaying() const { return mStatus == kStatusDelaying; }
296 
298  bool isRunning() const { return mStatus == kStatusRunning; }
299 
305  bool isEnding() const { return mStatus == kStatusEnding; }
306 
313  bool isTerminated() const { return mStatus == kStatusTerminated; }
314 
320  bool isDone() const {
321  return mStatus == kStatusEnding || mStatus == kStatusTerminated;
322  }
323 
324  protected:
355  typedef uint8_t Status;
356 
363  static const Status kStatusSuspended = 0;
364 
366  static const Status kStatusYielding = 1;
367 
369  static const Status kStatusAwaiting = 2;
370 
372  static const Status kStatusDelaying = 3;
373 
375  static const Status kStatusRunning = 4;
376 
378  static const Status kStatusEnding = 5;
379 
381  static const Status kStatusTerminated = 6;
382 
385 
392  void init(const char* name) {
393  mName = FCString(name);
394  mStatus = kStatusYielding;
395  insertSorted();
396  }
397 
399  void init(const __FlashStringHelper* name) {
400  mName = FCString(name);
401  mStatus = kStatusYielding;
402  insertSorted();
403  }
404 
406  Status getStatus() const { return mStatus; }
407 
409  void setJump(void* jumpPoint) { mJumpPoint = jumpPoint; }
410 
412  void* getJump() const { return mJumpPoint; }
413 
415  void setRunning() { mStatus = kStatusRunning; }
416 
418  void setYielding() { mStatus = kStatusYielding; }
419 
421  void setAwaiting() { mStatus = kStatusAwaiting; }
422 
424  void setDelaying() { mStatus = kStatusDelaying; }
425 
437  void setDelay(uint16_t delayMillisDuration) {
438  mDelayStartMillis = millis();
439  mDelayDurationMillis = (delayMillisDuration >= UINT16_MAX / 2)
440  ? UINT16_MAX / 2
441  : delayMillisDuration;
442  }
443 
445  void setEnding() { mStatus = kStatusEnding; }
446 
451  void setTerminated() { mStatus = kStatusTerminated; }
452 
453  private:
454  // Disable copy-constructor and assignment operator
455  Coroutine(const Coroutine&) = delete;
456  Coroutine& operator=(const Coroutine&) = delete;
457 
470  void insertSorted();
471 
472  FCString mName;
473  Coroutine* mNext = nullptr;
474  void* mJumpPoint = nullptr;
475  Status mStatus = kStatusSuspended;
476  uint16_t mDelayStartMillis;
477  uint16_t mDelayDurationMillis;
478 };
479 
480 }
481 
482 #endif
void resume()
Add a Suspended coroutine into the head of the scheduler linked list, and change the state to Yieldin...
Definition: Coroutine.cpp:51
bool isAwaiting() const
The coroutine returned using COROUTINE_AWAIT().
Definition: Coroutine.h:292
Coroutine()
Constructor.
Definition: Coroutine.h:384
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:451
static const Status kStatusEnding
Coroutine executed the COROUTINE_END() statement.
Definition: Coroutine.h:378
void setJump(void *jumpPoint)
Pointer to label where execute will start on the next call to run().
Definition: Coroutine.h:409
void init(const __FlashStringHelper *name)
Same as init(const char*) except using flash string type.
Definition: Coroutine.h:399
void * getJump() const
Pointer to label where execute will start on the next call to run().
Definition: Coroutine.h:412
bool isDone() const
The coroutine is either Ending or Terminated.
Definition: Coroutine.h:320
const FCString & getName() const
Human-readable name of the coroutine.
Definition: Coroutine.h:236
bool isTerminated() const
The coroutine was terminated by the scheduler with a call to setTerminated().
Definition: Coroutine.h:313
bool isDelaying() const
The coroutine returned using COROUTINE_DELAY().
Definition: Coroutine.h:295
static const Status kStatusRunning
Coroutine is currenly running.
Definition: Coroutine.h:375
bool isEnding() const
The coroutine returned using COROUTINE_END().
Definition: Coroutine.h:305
static const Status kStatusYielding
Coroutine returned using the COROUTINE_YIELD() statement.
Definition: Coroutine.h:366
virtual unsigned long millis() const
Returns the current millisecond clock.
Definition: Coroutine.cpp:66
static const Status kStatusSuspended
Coroutine has been suspended using suspend() and the scheduler should remove it from the queue upon t...
Definition: Coroutine.h:363
Status getStatus() const
Return the status of the coroutine.
Definition: Coroutine.h:406
static const Status kStatusTerminated
Coroutine has ended and no longer in the scheduler queue.
Definition: Coroutine.h:381
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:355
virtual int run()=0
The body of the coroutine.
A union of (const char*) and (const __FlashStringHelper*) with a discriminator.
Definition: FCString.h:56
static const Status kStatusDelaying
Coroutine returned using the COROUTINE_DELAY() statement.
Definition: Coroutine.h:372
void setDelay(uint16_t delayMillisDuration)
Configure the delay timer.
Definition: Coroutine.h:437
void setYielding()
Set the kStatusDelaying state.
Definition: Coroutine.h:418
bool isRunning() const
The coroutine is currently running.
Definition: Coroutine.h:298
static const Status kStatusAwaiting
Coroutine returned using the COROUTINE_AWAIT() statement.
Definition: Coroutine.h:369
bool isYielding() const
The coroutine returned using COROUTINE_YIELD().
Definition: Coroutine.h:289
void setRunning()
Set the kStatusRunning state.
Definition: Coroutine.h:415
void init(const char *name)
Initialize the coroutine, set it to Yielding state, and add it to the linked list of coroutines...
Definition: Coroutine.h:392
void setEnding()
Set the kStatusEnding state.
Definition: Coroutine.h:445
bool isSuspended() const
The coroutine was suspended with a call to suspend().
Definition: Coroutine.h:286
bool isDelayExpired()
Check if delay time is over.
Definition: Coroutine.h:280
Coroutine ** getNext()
Return the next pointer as a pointer to the pointer, similar to getRoot().
Definition: Coroutine.h:233
Base class of all coroutines.
Definition: Coroutine.h:217
static Coroutine ** getRoot()
Get the pointer to the root pointer.
Definition: Coroutine.cpp:33
Various macros to smooth over the differences among the various platforms with regards to their suppo...
void setAwaiting()
Set the kStatusAwaiting state.
Definition: Coroutine.h:421
void setDelaying()
Set the kStatusDelaying state.
Definition: Coroutine.h:424
void suspend()
Suspend the coroutine at the next scheduler iteration.
Definition: Coroutine.h:266