diff --git a/src/trezor/loop.py b/src/trezor/loop.py index 7231c481..33213753 100644 --- a/src/trezor/loop.py +++ b/src/trezor/loop.py @@ -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