Hi Juan,
The current RIOT scheduler will only switch between threads with the
same priority if there is an explicit yield, or if there occurs a
preemption by a higher priority thread.
RIOT's scheduler used to work like that: any preemption by a higher
priority thread would advance the current priority's circular runqueue.
That has been fixed years ago.
Nowadays, only if a thread calls explicit thread_yield() or it blocks,
it's "friends" will be scheduled.
Coroutines make it possible to program asynchronous code in a
blocking style - see "await". This is more natural and easier that
using callbacks.
How does it compare to sending / receiving messages?
This is almost cooperative multithreading (within the same "priority
group"), except there is no guarantee that after a thread in a group is
preempted, it will be that thread an not another with the same priority
that will get resumed. Maybe that is the current behavior, but I'm
having some trouble understanding the scheduler code.
It *should* be current behaviour, but I'm sure there are both platforms
and modules that call "thread_yield()" instead of "thread_yield_higher()".
Assigning the same priority to two or more threads is usually not a
good idea.
Can't really argue with that, can you?
More seriously, that note can definitely be improved.
A good starting point would be to guarantee threads with the same
priority get cooperatively scheduled 100% of the time. This means that
if one thread is preempted by a higher priority task, then no other
thread but that one will get resumed. In other words, the only way to
switch between threads with the same priority is explicitly yielding
from one.
As said, that guarantee *should* be in place, if calling a blocking
function can be considered "yielding".
We might consider to make even blocking of a thread not advance that
thread priority's run queue, but I'd be reluctant to change the
semantics that much.
Coroutines / Fibers do not have to be full fledged threads. The TCB can
be simpler and some objects can be shared with other fibers.
I count 12 bytes minimum for the TCB on 32bit architectures. There's not
much more that can be shaved off, and it would not have significant
impact, as already the stack space necessary for a thread's registers
when storing the context, apart from a stack itself, is the largest part
of a thread's overhead.
See the PR
on a thread-safe implementation of newlib
(https://github.com/RIOT-OS/RIOT/pull/8619) for an idea of the overhead
that thread-safety imposes.
This is not the overhead of thread-safety per se, but of the C library
functions that were designed to save state in global variables, when
there was no multi-threading. There are thread-safe alternatives to each
one of them in the C library, which do not impose the overhead.
Kaspar