well indeed the magic solution is not yet known
the problem is tradeoff. I mean, if you go with something like FreeRTOS or ChibiOS, you ll get this kind of features all together in a modular form trough a config file, e.g. I remeber that ChibiOS has a cooperative mode.
also there is some sort of incompatibility between RTOS and coop scheduler : RTOS slice the CPU time on regular basis and change the task context and if you want to care about side effect you have to deal with semaphore and make sure the library is ready for re-entrance or atomic code for example.
for coop scheduler, it is more up to the user to decide the context switch, by calling the yield(), therefore accomodating this constrains inside the core program.
sometime the yield() is hidden in a waiting or pooling function like Serial.read() or keypressed() or delay() and this looks easier, but not different in fact. e.g. usleep() in some linux distro.
as soon as you want a task to be launched periodically, like timer based, you have a decision to make if the code of this task takes long CPU time or not. if not, then it looks more like an interrupt and this is the model of ieos2 if I remeber well
If it takes long time, then you need to pass the token to another task after some time and then comes the need for a specific stack for this task; and then this use cas can be handled with a coop scheduler, you only have the concern of the jitter when re-entering in the task.
So I feel you can do a lot with simple schedulers like SchedulerARMAVR or SCoop and then if someone needs more, just invest in learning RTOS, and accept that the Arduino library are probably not usable as-is.
I would be pleased to record in this thread any suggeston for improving SCoop, as long as we respect the inherent border between RTOS and coop scheduler.
for example, one limitation of SCoop is the fact that any interrupts will use the current stack context, therefore we need to size the stack of each task to include the total interrupt stack size. we could imagine a specific context switch to force using the stack of the main loop().
another example is sleep mode. If you have say 3 task in paralell all containing a sort of loop with a delay() then you can for sure put the CPU in sleep mode for a certain duration representing the least or remaining delay of the pending tasks.
I ve tried this successfully on a MSP430 but the code is too big and not clean enough to be published at the moment.
lets engage the discussion!