[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Lua+Coco on Tiny Devices
- From: Mike Pall <mikelu-0904@...>
- Date: Wed, 15 Apr 2009 15:36:09 +0200
James Snyder wrote:
> Good point. What about situations where you have control over what the
> coroutine consists of. One model we're thinking of is using this to deal
> with hardware events (button presses, changes in ADC signal level,
> etc..).
One of the lessons learned from GUI frameworks is that controllers
should be reactive and not active.
It turns out that an active controller (using a coroutine or
thread) cannot adequately represent the inherent complexity in UI
handling. It's tempting to use a coroutine/thread for this, but in
general you have many inputs that interfere with linear control
flow in complex ways. You'll end up with a mess of event-driven
(i.e. yield to scheduler) and polled APIs.
Even a simple use case like "record a signal in regular intervals
until a button is pressed" turns out to be tricky. Implemented
with a coroutine this would probably look like this:
local function recorder_coro(adc, button, rate)
local t = {}
for value in adc:sample(rate) do -- Iterator yields to scheduler.
t[#t+1] = value
if button:pressed() then break end -- Polled? Queued event?
end
end
As you can see, the check for the pressed button doesn't integrate
nicely. A button press is really an event -- you might miss some
events if you're just polling the button state (if the sample rate
is very low). Ok, so let's queue button press events from an
interrupt. But then you get in trouble if multiple consumers want
to receive the same event. And that still doesn't handle the case
of an early abort (the loop is never entered until the end of a
sampling interval).
Ok, so let's teach the scheduler to wait on multiple events, like
"next sample available OR button pressed". But then you need to
find out which event triggered -- this thoroughly messes up the
nice and easy API. And simple boolean scheduling decisions just
don't suffice for the more complex cases. And so on ...
But there's a better way:
A reactive controller is purely event-driven. It can be
implemented Erlang-style as a coroutine with a central event
dispatch loop. But since Lua has decent support for closure-based
or OO-style programming, it's more comfortable to decompose the
controller into invidivual event handlers. Something like:
local function recorder_start(adc, button, rate)
local t = {}
adc:onsample(function(value) t[#t+1] = value end)
button:onpress(function() adc:stop() ... process data ... end)
adc:start(rate)
end
For a more flexible design, one could borrow the Qt-Style
signals/slots model, e.g.:
local recorder = {}
function recorder:sample(value) self[#self+1] = value end
function recorder:stop() ... process data ... end
function recorder:start(adc, button, rate)
attach(adc.sample, recorder, "sample")
attach(button.press, adc, "stop") -- adc:stop() detaches receivers, too.
attach(button.press, recorder, "stop")
adc:start(rate)
end
All the control logic is now inside the individual controllers,
where it belongs. Writing a scheduler for an event-driven model is
much simpler. A first cut of a trivial scheduler could just poll
all event sources and call the associated handlers. Later it can
be converted into an interrupt-driven design (to save power). No
change to the controllers should be necessary.
I strongly suggest to _first_ collect several typical use cases of
varying complexity. Then write pseudo-code for the controllers
using the different paradigms. Find out which framework best
handles your needs. Only then design the framework and decide on
the implementation details (such as the use of coroutines).
--Mike