rework the event loop

This commit is contained in:
Jan Pochyla 2016-04-29 21:19:25 +02:00 committed by Pavol Rusnak
parent 71496913ba
commit e7bfb64502
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
1 changed files with 74 additions and 140 deletions

View File

@ -1,16 +1,79 @@
import utime
import uheapq
from uheapq import heappop, heappush
from .utils import type_gen
from . import msg
from . import ui
import log
q = []
cnt = 0
EVT_TSTART = const(-1)
EVT_TMOVE = const(-2)
EVT_TEND = const(-3)
EVT_MSG = const(-4)
evt_handlers = { EVT_TSTART: None,
EVT_TMOVE: None,
EVT_TEND: None,
EVT_MSG: None, }
time_queue = []
def run_forever():
delay_max = const(1000000)
while True:
if time_queue:
t, _, _ = time_queue[0]
delay = t - utime.ticks_us()
else:
delay = delay_max
event = wait_for_event(delay)
if event:
# run interrupt handler
raise NotImplementedError()
else:
# run something from the time queue
_, gen, args = heappop(time_queue)
try:
if not args:
args = (None,)
ret = gen.send(*args)
except StopIteration as e:
# gen ended, forget it and go on
continue
if isinstance(ret, type_gen):
# generator, run it and call us asap
call_at(None, ret)
call_at(None, gen, *args)
elif isinstance(ret, int):
if ret >= 0:
# sleep in microseconds, call us later
call_at(utime.ticks_us() + ret, gen, *args)
else:
# wait for event
raise NotImplementedError()
elif ret is None:
# just call us asap
call_at(None, gen, *args)
def call_at(time, gen, *args):
if __debug__:
log.debug("Scheduling %s", (time, gen, args))
if not time:
time = utime.ticks_us()
heappush(time_queue, (time, gen, args))
# For performance stats
if __debug__:
# For performance stats
import array
@ -18,149 +81,20 @@ if __debug__:
log_delay_rb_len = const(10)
log_delay_rb = array.array('i', [0] * log_delay_rb_len)
def call_soon(callback, *args):
call_at(0, callback, *args)
def call_later(delay, callback, *args):
call_at(utime.ticks_us() + delay, callback, *args)
def call_at(time, callback, *args):
global cnt
if __debug__:
log.debug("Scheduling %s", (int(time), cnt, callback, args))
# Including self.cnt is a workaround per heapq docs
uheapq.heappush(q, (int(time), cnt, callback, args))
cnt += 1
def wait(delay):
def wait_for_event(timeout_us):
if __debug__:
# Adding delay to ring buffer for performance stats
global log_delay_pos
global log_delay_rb
global log_delay_rb_len
log_delay_rb[log_delay_pos] = delay
log_delay_rb[log_delay_pos] = timeout_us
log_delay_pos = (log_delay_pos + 1) % log_delay_rb_len
m = msg.select(delay)
if m:
event = msg.select(timeout_us)
if event:
# print('msg:', m)
# utime.sleep_us(10000)
if m[0] == 2:
ui.display.bar(m[1], m[2], 2, 2, ui.BLACK)
return m
def run_forever():
global q, cnt
while True:
if q:
t, cnt, cb, args = uheapq.heappop(q)
if __debug__:
log.debug("Next coroutine to run: %s", (t, cnt, cb, args))
tnow = utime.ticks_us()
delay = t - tnow
if delay > 0:
m = wait(delay)
else:
m = wait(0)
# Assuming IO completion scheduled some tasks
continue
if callable(cb):
ret = cb(*args)
if __debug__ and isinstance(ret, type_gen):
log.warning("Callback produced generator, which will never run.")
else:
delay = 0
try:
if args == ():
args = (None,)
if __debug__:
log.debug("Coroutine %s send args: %s", cb, args)
ret = cb.send(*args)
if __debug__:
log.debug("Coroutine %s yield result: %s", cb, ret)
if isinstance(ret, SysCall1):
arg = ret.arg
if isinstance(ret, Sleep):
delay = arg
elif isinstance(ret, StopLoop):
return arg
# elif isinstance(ret, IORead):
# self.add_reader(arg.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, arg)
# self.add_reader(arg.fileno(), lambda c, f: self.call_soon(c, f), cb, arg)
# self.add_reader(arg.fileno(), lambda cb: self.call_soon(cb), cb)
# self.add_reader(arg.fileno(), cb)
# continue
# elif isinstance(ret, IOWrite):
# self.add_writer(arg.fileno(), lambda cb: self.call_soon(cb), cb)
# self.add_writer(arg.fileno(), cb)
# continue
# elif isinstance(ret, IOReadDone):
# self.remove_reader(arg.fileno())
# elif isinstance(ret, IOWriteDone):
# self.remove_writer(arg.fileno())
elif isinstance(ret, type_gen):
call_soon(ret)
elif ret is None:
# Just reschedule
pass
else:
assert False, "Unsupported yield value: %r (of type %r)" % (ret, type(ret))
except StopIteration as e:
if __debug__:
log.debug("Coroutine finished: %s", cb)
continue
call_later(delay, cb, *args)
# def run_until_complete(self, coro):
# def _run_and_stop():
# yield from coro
# yield StopLoop(0)
# self.call_soon(_run_and_stop())
# self.run_forever()
# class SysCall:
# def __init__(self, *args):
# self.args = args
# def handle(self):
# raise NotImplementedError
# Optimized syscall with 1 arg
class SysCall1:
def __init__(self, arg):
self.arg = arg
def handle(self):
raise NotImplementedError
#class IOButton(SysCall):
# pass
class StopLoop(SysCall1):
pass
class Sleep(SysCall1):
pass
# class IORead(SysCall1):
# pass
# class IOWrite(SysCall1):
# pass
# class IOReadDone(SysCall1):
# pass
# class IOWriteDone(SysCall1):
# pass
# _event_loop = None
# _event_loop_class = EventLoop
# def get_event_loop():
# global _event_loop
# if _event_loop is None:
# _event_loop = _event_loop_class()
# return _event_loop
if event[0] == 2:
ui.display.bar(event[1], event[2], 2, 2, ui.BLACK)
return event