From 43eb8220c871e998a9aa6dbacb931cc352d55807 Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Sat, 24 Jan 2026 19:10:51 +0100 Subject: [PATCH] Move mpos.apps.good_stack_size() to TaskManager.good_stack_size() Trying to get every app-facing API as part of an object. --- .../com.micropythonos.wifi/assets/wifi.py | 7 ++-- internal_filesystem/lib/mpos/apps.py | 35 ------------------- .../lib/mpos/audio/audioflinger.py | 8 ++--- .../lib/mpos/board/fri3d_2024.py | 3 +- internal_filesystem/lib/mpos/main.py | 5 +-- internal_filesystem/lib/mpos/task_manager.py | 13 ++++--- internal_filesystem/lib/threading.py | 5 ++- tests/test_multi_connect.py | 8 ++--- tests/test_multi_websocket_with_bad_ones.py | 4 +-- 9 files changed, 30 insertions(+), 58 deletions(-) diff --git a/internal_filesystem/builtin/apps/com.micropythonos.wifi/assets/wifi.py b/internal_filesystem/builtin/apps/com.micropythonos.wifi/assets/wifi.py index ddeb9f32..75a6e7d9 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.wifi/assets/wifi.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.wifi/assets/wifi.py @@ -2,8 +2,7 @@ import time import lvgl as lv import _thread -from mpos import Activity, Intent, MposKeyboard, WifiService, CameraActivity, DisplayMetrics, CameraManager -import mpos.apps +from mpos import Activity, Intent, MposKeyboard, WifiService, CameraActivity, DisplayMetrics, CameraManager, TaskManager class WiFi(Activity): """ @@ -101,7 +100,7 @@ class WiFi(Activity): self.busy_scanning = True self.scan_button.add_state(lv.STATE.DISABLED) self.scan_button_label.set_text(self.scan_button_scanning_text) - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self.scan_networks_thread, ()) def refresh_list(self): @@ -179,7 +178,7 @@ class WiFi(Activity): print("Not attempting connect because busy_connecting.") else: self.busy_connecting = True - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self.attempt_connecting_thread, (ssid, password)) def attempt_connecting_thread(self, ssid, password): diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index 31a319fb..f48b69a0 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -6,13 +6,6 @@ import traceback import mpos.info import mpos.ui -def good_stack_size(): - stacksize = 24*1024 # less than 20KB crashes on desktop when doing heavy apps, like LightningPiggy's Wallet connections - import sys - if sys.platform == "esp32": - stacksize = 16*1024 - return stacksize - # Run the script in the current thread: # Returns True if successful def execute_script(script_source, is_file, classname, cwd=None): @@ -81,34 +74,6 @@ def execute_script(script_source, is_file, classname, cwd=None): traceback.print_exception(type(e), e, tb) return False -""" Unused: -# Run the script in a new thread: -# NOTE: check if the script exists here instead of launching a new thread? -def execute_script_new_thread(scriptname, is_file): - print(f"main.py: execute_script_new_thread({scriptname},{is_file})") - try: - # 168KB maximum at startup but 136KB after loading display, drivers, LVGL gui etc so let's go for 128KB for now, still a lot... - # But then no additional threads can be created. A stacksize of 32KB allows for 4 threads, so 3 in the app itself, which might be tight. - # 16KB allows for 10 threads in the apps, but seems too tight for urequests on unix (desktop) targets - # 32KB seems better for the camera, but it forced me to lower other app threads from 16 to 12KB - #_thread.stack_size(24576) # causes camera issue... - # NOTE: This doesn't do anything if apps are started in the same thread! - if "camtest" in scriptname: - print("Starting camtest with extra stack size!") - stack=32*1024 - elif "appstore" in scriptname: - print("Starting appstore with extra stack size!") - stack=24*1024 # this doesn't do anything because it's all started in the same thread - else: - stack=16*1024 # 16KB doesn't seem to be enough for the AppStore app on desktop - stack = mpos.apps.good_stack_size() - print(f"app.py: setting stack size for script to {stack}") - _thread.stack_size(stack) - _thread.start_new_thread(execute_script, (scriptname, is_file)) - except Exception as e: - print("main.py: execute_script_new_thread(): error starting new thread thread: ", e) -""" - # Returns True if successful def start_app(fullname): from .content.package_manager import PackageManager diff --git a/internal_filesystem/lib/mpos/audio/audioflinger.py b/internal_filesystem/lib/mpos/audio/audioflinger.py index 4affc144..27a1119b 100644 --- a/internal_filesystem/lib/mpos/audio/audioflinger.py +++ b/internal_filesystem/lib/mpos/audio/audioflinger.py @@ -6,7 +6,7 @@ # Uses _thread for non-blocking background playback/recording (separate thread from UI) import _thread -import mpos.apps +from ..task_manager import TaskManager class AudioFlinger: @@ -164,7 +164,7 @@ class AudioFlinger: on_complete=on_complete ) - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self._playback_thread, (stream,)) return True @@ -208,7 +208,7 @@ class AudioFlinger: on_complete=on_complete ) - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self._playback_thread, (stream,)) return True @@ -285,7 +285,7 @@ class AudioFlinger: ) print("AudioFlinger: Starting recording thread...") - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self._recording_thread, (stream,)) print("AudioFlinger: Recording thread started successfully") return True diff --git a/internal_filesystem/lib/mpos/board/fri3d_2024.py b/internal_filesystem/lib/mpos/board/fri3d_2024.py index 8fbd4317..530e9f13 100644 --- a/internal_filesystem/lib/mpos/board/fri3d_2024.py +++ b/internal_filesystem/lib/mpos/board/fri3d_2024.py @@ -16,6 +16,7 @@ import task_handler import mpos.ui import mpos.ui.focus_direction +from ..task_manager import TaskManager # Pin configuration SPI_BUS = 2 @@ -386,7 +387,7 @@ def startup_wow_effect(): except Exception as e: print(f"Startup effect error: {e}") -_thread.stack_size(mpos.apps.good_stack_size()) # default stack size won't work, crashes! +_thread.stack_size(TaskManager.good_stack_size()) # default stack size won't work, crashes! _thread.start_new_thread(startup_wow_effect, ()) print("fri3d_2024.py finished") diff --git a/internal_filesystem/lib/mpos/main.py b/internal_filesystem/lib/mpos/main.py index 8252559d..11f245c1 100644 --- a/internal_filesystem/lib/mpos/main.py +++ b/internal_filesystem/lib/mpos/main.py @@ -9,8 +9,9 @@ import mpos.ui from .content.package_manager import PackageManager from .ui.appearance_manager import AppearanceManager from .ui.display_metrics import DisplayMetrics -import mpos.ui.topmenu +import mpos.ui.topmenu +from .task_manager import TaskManager # White text on black logo works (for dark mode) and can be inverted (for light mode) @@ -124,7 +125,7 @@ except Exception as e: try: from mpos.net.wifi_service import WifiService - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(WifiService.auto_connect, ()) except Exception as e: print(f"Couldn't start WifiService.auto_connect thread because: {e}") diff --git a/internal_filesystem/lib/mpos/task_manager.py b/internal_filesystem/lib/mpos/task_manager.py index 995bb5b1..032276e4 100644 --- a/internal_filesystem/lib/mpos/task_manager.py +++ b/internal_filesystem/lib/mpos/task_manager.py @@ -26,10 +26,6 @@ class TaskManager: print("Not starting TaskManager because it's been disabled.") return cls.keep_running = True - # New thread works but LVGL isn't threadsafe so it's preferred to do this in the same thread: - #_thread.stack_size(mpos.apps.good_stack_size()) - #_thread.start_new_thread(asyncio.run, (self._asyncio_thread(100), )) - # Same thread works, although it blocks the real REPL, but aiorepl works: asyncio.run(TaskManager._asyncio_thread(10)) # 100ms is too high, causes lag. 10ms is fine. not sure if 1ms would be better... @classmethod @@ -70,3 +66,12 @@ class TaskManager: @staticmethod def wait_for(awaitable, timeout): return asyncio.wait_for(awaitable, timeout) + + @staticmethod + def good_stack_size(): + stacksize = 24*1024 # less than 20KB crashes on desktop when doing heavy apps, like LightningPiggy's Wallet connections + import sys + if sys.platform == "esp32": + stacksize = 16*1024 + return stacksize + diff --git a/internal_filesystem/lib/threading.py b/internal_filesystem/lib/threading.py index fb509768..25e07b7e 100644 --- a/internal_filesystem/lib/threading.py +++ b/internal_filesystem/lib/threading.py @@ -1,5 +1,8 @@ +# Lightweight replacement for CPython's Thread module + import _thread +from .task_manager import TaskManager import mpos.apps class Thread: @@ -21,7 +24,7 @@ class Thread: # small stack sizes 8KB gives segfault directly # 22KB or less is too tight on desktop, 23KB and more is fine #stacksize = 24*1024 - stacksize = mpos.apps.good_stack_size() + stacksize = TaskManager.good_stack_size() #stacksize = 20*1024 print(f"starting thread with stacksize {stacksize}") _thread.stack_size(stacksize) diff --git a/tests/test_multi_connect.py b/tests/test_multi_connect.py index 5669d037..6d7fc0cc 100644 --- a/tests/test_multi_connect.py +++ b/tests/test_multi_connect.py @@ -2,12 +2,10 @@ import unittest import _thread import time -from mpos import App, PackageManager -import mpos.apps +from mpos import App, PackageManager, TaskManager from websocket import WebSocketApp - # demo_multiple_ws.py import asyncio import aiohttp @@ -137,7 +135,7 @@ class TestTwoWebsockets(unittest.TestCase): asyncio.run(self.main()) def test_it(self): - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self.newthread, ()) time.sleep(10) @@ -253,6 +251,6 @@ class TestCrashingSeparateThreads(): # Disabled def test_it(self): for url in self.WS_URLS: - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self.newthread, (url,)) time.sleep(15) diff --git a/tests/test_multi_websocket_with_bad_ones.py b/tests/test_multi_websocket_with_bad_ones.py index d2cc0cca..9d50c511 100644 --- a/tests/test_multi_websocket_with_bad_ones.py +++ b/tests/test_multi_websocket_with_bad_ones.py @@ -3,7 +3,7 @@ import _thread import time from mpos import App, PackageManager -import mpos.apps +from mpos import TaskManager from websocket import WebSocketApp @@ -136,7 +136,7 @@ class TestTwoWebsockets(unittest.TestCase): asyncio.run(self.main()) def test_it(self): - _thread.stack_size(mpos.apps.good_stack_size()) + _thread.stack_size(TaskManager.good_stack_size()) _thread.start_new_thread(self.newthread, ()) time.sleep(10)