Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions api_drivers/common_api_drivers/frozen/other/task_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ def __init__(
self._task_handler_ref = self._task_handler
self.max_scheduled = max_scheduled

self._start_time = time.ticks_ms() # NOQA
self._last_tick = time.ticks_ms() # NOQA
# Fairness: how long the last handler pass took and when it ended,
# so the timer can leave the main thread a share of the CPU.
self._last_run_ms = 0
self._last_end = self._last_tick
self._timer.init(
mode=Timer.PERIODIC,
period=self.duration,
Expand Down Expand Up @@ -102,6 +106,7 @@ def is_running(cls):
return cls._current_instance is not None

def _task_handler(self, _):
run_start = time.ticks_ms() # NOQA
try:
self._scheduled -= 1

Expand All @@ -128,12 +133,6 @@ def _task_handler(self, _):
print(f"TaskHandler callback {cb} threw exception, disabling it")
self.remove_event_cb(cb)

stop_time = time.ticks_ms() # NOQA

ticks_diff = time.ticks_diff(stop_time, self._start_time) # NOQA
self._start_time = stop_time
lv.tick_inc(ticks_diff)

if run_update:
try:
lv.task_handler()
Expand All @@ -142,8 +141,6 @@ def _task_handler(self, _):
sys.print_exception(e)
# LVGL UI still hangs

start_time = time.ticks_ms() # NOQA

for cb, evt, data in self._callbacks:
if not evt & TASK_HANDLER_FINISHED:
continue
Expand All @@ -159,10 +156,6 @@ def _task_handler(self, _):
else:
sys.print_exception(err) # NOQA

stop_time = time.ticks_ms() # NOQA
ticks_diff = time.ticks_diff(stop_time, start_time) # NOQA
lv.tick_inc(ticks_diff)

self._running = False

except Exception as e:
Expand All @@ -171,11 +164,31 @@ def _task_handler(self, _):
if self.exception_hook:
self.exception_hook(e)

finally:
end = time.ticks_ms() # NOQA
self._last_run_ms = time.ticks_diff(end, run_start) # NOQA
self._last_end = end

def _timer_cb(self, _):
lv.tick_inc(self.duration)
# The only place LVGL time advances. Feed it the real elapsed time
# rather than the nominal period: timer callbacks are delivered via the
# scheduler and coalesce or drop under load, and _task_handler must not
# add ticks of its own or LVGL time runs ~2x faster than wall clock.
now = time.ticks_ms() # NOQA
lv.tick_inc(time.ticks_diff(now, self._last_tick)) # NOQA
self._last_tick = now
if self._running:
return

# Fairness: a handler pass that outlasts the timer period would
# otherwise be rescheduled back-to-back, starving everything else on
# the main thread (the REPL, touch and app tasks). Wait until at
# least a quarter of the last pass's duration has elapsed since it
# ended, so the main thread always keeps ~20% of the CPU.
gap = self._last_run_ms >> 2
if gap > self.duration and time.ticks_diff(now, self._last_end) < gap: # NOQA
return

if self._scheduled < self.max_scheduled:
try:
micropython.schedule(self._task_handler_ref, 0)
Expand Down