Fri Aug 14 06:53:02 EDT 2020
Converting a condition machine into an event machine
Premise: it is much easier to design a round-robin condition polling
machine, than it is to design an event driven machine. At least, it
is in a bare-metal setting.
A task in that context blocks on conditions, not events. E.g. wait
for this uart transmit ready flag to be set.
Is there a mechanical way to translate one into the other? Preferably
by only changing the scheduling, and not the code?
The scheduler should somehow know what condition is being blocked on,
and only resume the task if there was a change.
This smells like FRP, but isn't really beacause I really do have
state. Maybe read Conal's paper again for inspiration.
EDIT: The core problem:
- convert the blocking condition into an event (differential)
- sub/unsub: e.g. distinguish between being interested in the event,
and not caring.
Let's fix the polarity of the conditions for true to mean continue,
and false to mean block. A task can do this:
1. inspect a condition. if true, continue and don't register an event.
2. if false, register the false->true wakeup, and block
To find a shape, implement it manually a couple of times and focus on
how to convert conditions into events. It seems that this can be
relaxed a lot by treating the events as optimizations, e.g. their only
effect is to schedule a poll, so they could be implemented as a dumb
What I want to know is what kind of eventing mechanism this should be
placed into? It is clearly not stateless, so FRP doesn't seem to be
It seems that some kind of pub/sub would be more appropriate. In my
specific case: interrupt routines and a broker. The broker will
receive events from hardware, will filter them based on what tasks are
interested. Then each task can send change notifications to the
broker if it changed a condition.
So this looks like a mix between actor and csp:
- the relation from broker -> tasks is non-buffered. there is only a
single channel that carries the "something might have changed"
- the relation from event source -> broker is buffered.
Let's zoom in on the concrete condition variables in the current
application. For now I have the following actual condtions:
- Token stream ready (uc_tools cbuf)
- Timer expired (uc_tools 32 bit cycle_counter poll)
- Hardware ready flag event (e.g. uart)
- Software ready flag (e.g. some state machine has reached its final
It's probably most appropriate to do this as part of sm.h because it
is in that contect that I am faced with the need for opaque polling.
These all need to be inverted, meaning that the entitiy that causes
the change needs to schedule a notification somewhere. How to
- For cbuf writes, it could go straight into the cbuf data structure.
Just add a callback that is invoked whenever there is data in the
buffer after a write.
- Software ready flag is also just a callback. Same case as cbuf.
- Hardware ready flag can use peripheral interrupts + main loop
- Timer expiration needs an actual abstraction. I.e. a software timer
driven by a hardware timer.
So this is quite a bit of work to extract due to the variety of
events. Hence: it is a lot eaasier to just poll if the energy
consumption is not a problem.
This is also the central issue: if power saving is necessary, then
hardware event conversion to interrupt is necessary. Basically,
conditions need to be inverted into events.
EDIT: One remark wrt. timer: there is no consequential difference
between letting the main loop poll, and letting the task poll. The
only real change here is to turn timers into events, either using
separate hardware timers, or using a single timer to implement a
heap-based software timer.