You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
TaskManager: without new thread works but blocks REPL
aiorepl (asyncio REPL) works but it's pretty limited It's probably fine for production, but it means the user has to sys.exit() in aiorepl before landing on the real interactive REPL, with asyncio tasks stopped.
This commit is contained in:
@@ -130,10 +130,14 @@ class AppStore(Activity):
|
||||
print("Downloading icons...")
|
||||
for app in self.apps:
|
||||
if not self.has_foreground():
|
||||
print(f"App is stopping, aborting icon downloads.")
|
||||
print(f"App is stopping, aborting icon downloads.") # maybe this can continue? but then update_ui_threadsafe is needed
|
||||
break
|
||||
#if not app.icon_data:
|
||||
app.icon_data = await self.download_url(app.icon_url)
|
||||
if not app.icon_data:
|
||||
try:
|
||||
app.icon_data = await TaskManager.wait_for(self.download_url(app.icon_url), 5) # max 5 seconds per icon
|
||||
except Exception as e:
|
||||
print(f"Download of {app.icon_url} got exception: {e}")
|
||||
continue
|
||||
if app.icon_data:
|
||||
print("download_icons has icon_data, showing it...")
|
||||
image_icon_widget = None
|
||||
@@ -146,7 +150,7 @@ class AppStore(Activity):
|
||||
'data_size': len(app.icon_data),
|
||||
'data': app.icon_data
|
||||
})
|
||||
self.update_ui_threadsafe_if_foreground(image_icon_widget.set_src, image_dsc) # error: 'App' object has no attribute 'image'
|
||||
self.update_ui_threadsafe_if_foreground(image_icon_widget.set_src, image_dsc) # add update_ui_threadsafe() for background?
|
||||
print("Finished downloading icons.")
|
||||
|
||||
def show_app_detail(self, app):
|
||||
@@ -156,7 +160,7 @@ class AppStore(Activity):
|
||||
|
||||
async def download_url(self, url):
|
||||
print(f"Downloading {url}")
|
||||
#await TaskManager.sleep(1)
|
||||
#await TaskManager.sleep(4) # test slowness
|
||||
try:
|
||||
async with self.aiohttp_session.get(url) as response:
|
||||
if response.status >= 200 and response.status < 400:
|
||||
|
||||
@@ -72,8 +72,6 @@ except Exception as e:
|
||||
# This will throw an exception if there is already a "/builtin" folder present
|
||||
print("main.py: WARNING: could not import/run freezefs_mount_builtin: ", e)
|
||||
|
||||
mpos.TaskManager()
|
||||
|
||||
try:
|
||||
from mpos.net.wifi_service import WifiService
|
||||
_thread.stack_size(mpos.apps.good_stack_size())
|
||||
@@ -89,11 +87,31 @@ auto_start_app = prefs.get_string("auto_start_app", None)
|
||||
if auto_start_app and launcher_app.fullname != auto_start_app:
|
||||
mpos.apps.start_app(auto_start_app)
|
||||
|
||||
if not started_launcher:
|
||||
print(f"WARNING: launcher {launcher_app} failed to start, not cancelling OTA update rollback")
|
||||
else:
|
||||
# Create limited aiorepl because it's better than nothing:
|
||||
import aiorepl
|
||||
print("Starting very limited asyncio REPL task. Use sys.exit() to stop all asyncio tasks and go to real REPL...")
|
||||
mpos.TaskManager.create_task(aiorepl.task()) # only gets started when mpos.TaskManager() is created
|
||||
|
||||
async def ota_rollback_cancel():
|
||||
try:
|
||||
import ota.rollback
|
||||
ota.rollback.cancel()
|
||||
except Exception as e:
|
||||
print("main.py: warning: could not mark this update as valid:", e)
|
||||
|
||||
if not started_launcher:
|
||||
print(f"WARNING: launcher {launcher_app} failed to start, not cancelling OTA update rollback")
|
||||
else:
|
||||
mpos.TaskManager.create_task(ota_rollback_cancel()) # only gets started when mpos.TaskManager() is created
|
||||
|
||||
while True:
|
||||
try:
|
||||
mpos.TaskManager() # do this at the end because it doesn't return
|
||||
except KeyboardInterrupt as k:
|
||||
print(f"mpos.TaskManager() got KeyboardInterrupt, falling back to REPL shell...") # only works if no aiorepl is running
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"mpos.TaskManager() got exception: {e}")
|
||||
print("Restarting mpos.TaskManager() after 10 seconds...")
|
||||
import time
|
||||
time.sleep(10)
|
||||
|
||||
@@ -8,14 +8,17 @@ class TaskManager:
|
||||
|
||||
def __init__(self):
|
||||
print("TaskManager starting asyncio_thread")
|
||||
_thread.stack_size(mpos.apps.good_stack_size()) # tiny stack size of 1024 is fine for tasks that do nothing but for real-world usage, it needs more
|
||||
_thread.start_new_thread(asyncio.run, (self._asyncio_thread(), ))
|
||||
# tiny stack size of 1024 is fine for tasks that do nothing
|
||||
# but for real-world usage, it needs more:
|
||||
#_thread.stack_size(mpos.apps.good_stack_size())
|
||||
#_thread.start_new_thread(asyncio.run, (self._asyncio_thread(100), ))
|
||||
asyncio.run(self._asyncio_thread(10)) # this actually works, but it blocks the real REPL (aiorepl works, but that's limited)
|
||||
|
||||
async def _asyncio_thread(self):
|
||||
async def _asyncio_thread(self, ms_to_sleep):
|
||||
print("asyncio_thread started")
|
||||
while True:
|
||||
#print("asyncio_thread tick")
|
||||
await asyncio.sleep_ms(100) # This delay determines how quickly new tasks can be started, so keep it below human reaction speed
|
||||
await asyncio.sleep_ms(ms_to_sleep) # This delay determines how quickly new tasks can be started, so keep it below human reaction speed
|
||||
print("WARNING: asyncio_thread exited, this shouldn't happen because now asyncio.create_task() won't work anymore!")
|
||||
|
||||
@classmethod
|
||||
@@ -38,3 +41,7 @@ class TaskManager:
|
||||
@staticmethod
|
||||
def notify_event():
|
||||
return asyncio.Event()
|
||||
|
||||
@staticmethod
|
||||
def wait_for(awaitable, timeout):
|
||||
return asyncio.wait_for(awaitable, timeout)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import lvgl as lv
|
||||
|
||||
import mpos.ui
|
||||
import mpos.time
|
||||
import mpos.battery_voltage
|
||||
from .display import (get_display_width, get_display_height)
|
||||
from .util import (get_foreground_app)
|
||||
|
||||
Reference in New Issue
Block a user