mikaelpatel / Arduino-Scheduler

Portable Cooperative Multi-tasking Scheduler for Arduino

Home Page:https://mikaelpatel.github.io/Arduino-Scheduler/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Integrating with event callback APIs

PaulStoffregen opened this issue · comments

Let's talk about how the Scheduler could/should integrate with event callback APIs, like serialEvent, and (possible future) Arduino events. Mentioned briefly on #14...

If I was (hypothetically) planning to add a major event callback extension to Arduino's API, how should it be done in a way that works best or could be extended to work with the Scheduler and perhaps RTOS systems?

What does the event callback API look like today? If I understand correctly there are callbacks hidden in the Arduino core main() which are polled/called for each loop(). The Teensy core does this (and more?) in yield().

The Scheduler outer yield(), i.e. "C" level function, replaces the core implementation which removes the current Teensy callbacks. The first step would be to preserve this; make the Scheduler perform the callbacks. This implies that the Teensy yield() should actually call a polling/callback function instead of directly implementing that in yield(). This function could be called by the Scheduler outer yield before the context switch. Guess there is already a restriction that a callback may not call a function that calls yield. Recursion blocking would be needed.

The second step could be to introduce a Worker Task that runs in the background and performs the polling/callback. An event queue could be used (as in Cosa). The advantage of this pattern is that the max stack depth becomes easier to estimate/measure (giving higher robustness).

Today Arduino's callback API is just 4 pre-defined function names implemented as weak linker aliases. The names are serialEvent, serialEvent1, serialEvent2, serialEvent3. Teensy adds serialEvent4, serialEvent5, serialEvent6, and calls them from yield() instead of main(), but otherwise the API is identical.

To prevent problems with recursion, I put a simple "running" flag into Teensy's implementation:

void yield(void)
{
    static uint8_t running=0;

    if (running) return; // TODO: does this need to be atomic?
    running = 1;
      // do callbacks
    running = 0;
};

I suppose a first step I should take would be moving all this to a function named something like doEventCallbacks(). I could call that from both yield() and main(), so if the Scheduler overrides yield(), at least the callbacks still get done, the same as Arduino would do.

Or maybe there ought to be a way for the Scheduler to take the responsibility for event callbacks away from main() and/or yield(), so it could do them from another thread?

@PaulStoffregen Alternatively I could copy the callback section from the Teensy yield into the Scheduler yield (with appropriate ifdef). Then the semantics would be maintained until a later date when you intend to refactor. The default stack size on Teensy is 512 bytes which gives some extra head room for the callbacks.

BW: What is the max stack for Teensy 3.5 and 3.6? Right now the Scheduler assumes the same as 3.1, 16 Kbyte.

Maybe best to keep the callback code in the core library? When/if it changes, the Scheduler could easily become out of date.

There shouldn't be any max stack size limit for any 32 bit Teensy boards (or any ARM boards), other than what will fit into the available memory. The stack pointer is implemented with a 32 bit register which is capable of accessing the entire 4GB address space.

The max stack size is actually the total amount of RAM that may be used for tasks (on the stack). For Arduino Due the current limit max stack size is 32K, and Teensy 3.1 16K (which is also the setting for 3.5 and 3.6). The sum of the stack size of allocated tasks may not exceed this limit.

On AVR based Arduino boards the limit is given by the heap limit.

A really nice approach is to have a heap together with the stack for each task (ala Erlang). But this is a totally different discussion.

You could probably increase this to 40K on Teensy 3.2, to 160K on Teensy 3.5, and 220K on Teensy 3.6.

Teensy 3.2 is the current product for the MK20DX256 chip. They're fully software compatible, but 3.2 has an improved hardware design with better voltage regulator. Teensy 3.1 was discontinued some time ago, so probably best to refer to this as Teensy 3.2.