2026-01-24 19:10:51 +01:00
|
|
|
# Lightweight replacement for CPython's Thread module
|
|
|
|
|
|
2025-05-19 14:44:28 +02:00
|
|
|
import _thread
|
|
|
|
|
|
2026-01-24 19:13:02 +01:00
|
|
|
from mpos.task_manager import TaskManager
|
2025-05-27 22:43:43 +02:00
|
|
|
|
2025-05-19 14:44:28 +02:00
|
|
|
class Thread:
|
2025-05-19 15:09:30 +02:00
|
|
|
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, daemon=None):
|
2025-05-19 14:44:28 +02:00
|
|
|
self.target = target
|
|
|
|
|
self.args = args
|
|
|
|
|
self.kwargs = {} if kwargs is None else kwargs
|
2025-05-19 15:09:30 +02:00
|
|
|
self.name = name
|
|
|
|
|
self.daemon = daemon # Store daemon attribute (True, False, or None)
|
2025-05-19 14:44:28 +02:00
|
|
|
|
|
|
|
|
def start(self):
|
2025-05-19 15:09:30 +02:00
|
|
|
# In MicroPython, _thread.start_new_thread doesn't support daemon threads directly
|
|
|
|
|
# We store the daemon attribute for compatibility, but it may not affect termination
|
2025-05-20 11:48:20 +02:00
|
|
|
# 18KB or more causes "can't create thread" when starting relay.queue_worker thread
|
|
|
|
|
# 16KB still too much
|
|
|
|
|
# _thread.stack_size(32*1024)
|
|
|
|
|
#_thread.stack_size(10*1024) # might not be enough
|
2025-05-20 21:29:24 +02:00
|
|
|
#stacksize = 12*1024
|
|
|
|
|
# small stack sizes 8KB gives segfault directly
|
2025-05-21 12:27:02 +02:00
|
|
|
# 22KB or less is too tight on desktop, 23KB and more is fine
|
2025-05-27 22:43:43 +02:00
|
|
|
#stacksize = 24*1024
|
2026-01-24 19:10:51 +01:00
|
|
|
stacksize = TaskManager.good_stack_size()
|
2025-05-22 21:06:36 +02:00
|
|
|
#stacksize = 20*1024
|
2025-05-20 11:48:20 +02:00
|
|
|
print(f"starting thread with stacksize {stacksize}")
|
|
|
|
|
_thread.stack_size(stacksize)
|
2025-05-19 14:44:28 +02:00
|
|
|
_thread.start_new_thread(self.run, ())
|
|
|
|
|
|
|
|
|
|
def run(self):
|
2025-05-19 15:09:30 +02:00
|
|
|
try:
|
|
|
|
|
self.target(*self.args, **self.kwargs)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# Basic error handling to prevent silent failures
|
|
|
|
|
print(f"Thread {self.name or ''} failed: {e}")
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def daemon(self):
|
|
|
|
|
return self._daemon
|
|
|
|
|
|
|
|
|
|
@daemon.setter
|
|
|
|
|
def daemon(self, value):
|
|
|
|
|
self._daemon = value if value is not None else False
|
2025-05-19 14:44:28 +02:00
|
|
|
|
2025-05-19 12:07:42 +02:00
|
|
|
|
|
|
|
|
class Lock:
|
|
|
|
|
def __init__(self):
|
2025-05-19 15:09:30 +02:00
|
|
|
self._lock = _thread.allocate_lock()
|
2025-05-19 12:07:42 +02:00
|
|
|
|
|
|
|
|
def __enter__(self):
|
|
|
|
|
if self._lock:
|
|
|
|
|
self._lock.acquire()
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
|
|
|
if self._lock:
|
|
|
|
|
self._lock.release()
|
|
|
|
|
|
|
|
|
|
def acquire(self):
|
|
|
|
|
if self._lock:
|
|
|
|
|
return self._lock.acquire()
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def release(self):
|
|
|
|
|
if self._lock:
|
|
|
|
|
self._lock.release()
|