Preparing for new tagged release.

This commit is contained in:
Storm Dragon
2025-11-23 18:51:02 -05:00
5 changed files with 95 additions and 15 deletions

View File

@@ -13,6 +13,76 @@ from fenrirscreenreader.core import debug
from fenrirscreenreader.core.eventData import FenrirEventType from fenrirscreenreader.core.eventData import FenrirEventType
# Standalone functions for multiprocessing (cannot be instance methods)
def _heart_beat_timer(running):
"""
Standalone heartbeat timer function for multiprocessing.
Returns current timestamp after a short sleep.
"""
try:
time.sleep(0.5)
except Exception as e:
print(f"ProcessManager _heart_beat_timer: Error during sleep: {e}")
return time.time()
def _custom_event_worker_process(
running, event_queue, function, pargs=None, run_once=False
):
"""
Standalone worker function for custom events in multiprocessing.
Cannot use instance methods due to pickle limitations with self.env.
"""
if not callable(function):
return
while running.value:
try:
if pargs:
function(running, event_queue, pargs)
else:
function(running, event_queue)
except Exception as e:
# Cannot use DebugManager in multiprocess context
print(
f"ProcessManager:_custom_event_worker_process:function("
f"{function}):{e}"
)
if run_once:
break
def _simple_event_worker_process(
running, event_queue, event, function, pargs=None, run_once=False
):
"""
Standalone worker function for simple events in multiprocessing.
Cannot use instance methods due to pickle limitations with self.env.
"""
if not isinstance(event, FenrirEventType):
return
if not callable(function):
return
while running.value:
data = None
try:
if pargs:
data = function(running, pargs)
else:
data = function(running)
except Exception as e:
# Cannot use DebugManager in multiprocess context
print(
f"ProcessManager:_simple_event_worker_process:function("
f"{function}):{e}"
)
try:
event_queue.put({"Type": event, "data": data}, timeout=0.1)
except Exception as e:
print(f"ProcessManager: Failed to put event to queue: {e}")
if run_once:
break
class ProcessManager: class ProcessManager:
def __init__(self): def __init__(self):
self._Processes = [] self._Processes = []
@@ -23,7 +93,7 @@ class ProcessManager:
self.running = self.env["runtime"]["EventManager"].get_running() self.running = self.env["runtime"]["EventManager"].get_running()
self.add_simple_event_thread( self.add_simple_event_thread(
FenrirEventType.heart_beat, FenrirEventType.heart_beat,
self.heart_beat_timer, _heart_beat_timer,
multiprocess=True, multiprocess=True,
) )
@@ -60,8 +130,8 @@ class ProcessManager:
original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
if multiprocess: if multiprocess:
t = Process( t = Process(
target=self.custom_event_worker_thread, target=_custom_event_worker_process,
args=(event_queue, function, pargs, run_once), args=(self.running, event_queue, function, pargs, run_once),
) )
self._Processes.append(t) self._Processes.append(t)
else: # use thread instead of process else: # use thread instead of process
@@ -78,9 +148,11 @@ class ProcessManager:
): ):
original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
if multiprocess: if multiprocess:
# Get event queue reference before creating process
event_queue = self.env["runtime"]["EventManager"].get_event_queue()
t = Process( t = Process(
target=self.simple_event_worker_thread, target=_simple_event_worker_process,
args=(event, function, pargs, run_once), args=(self.running, event_queue, event, function, pargs, run_once),
) )
self._Processes.append(t) self._Processes.append(t)
else: else:
@@ -106,12 +178,13 @@ class ProcessManager:
else: else:
function(self.running, event_queue) function(self.running, event_queue)
except Exception as e: except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out( # Cannot use DebugManager in multiprocess context due to
# pickle limitations with file handles
print(
"ProcessManager:custom_event_worker_thread:function(" "ProcessManager:custom_event_worker_thread:function("
+ str(function) + str(function)
+ "):" + "):"
+ str(e), + str(e)
debug.DebugLevel.ERROR,
) )
if run_once: if run_once:
break break
@@ -131,12 +204,13 @@ class ProcessManager:
else: else:
data = function(self.running) data = function(self.running)
except Exception as e: except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out( # Cannot use DebugManager in multiprocess context due to
# pickle limitations with file handles
print(
"ProcessManager:simple_event_worker_thread:function(" "ProcessManager:simple_event_worker_thread:function("
+ str(function) + str(function)
+ "):" + "):"
+ str(e), + str(e)
debug.DebugLevel.ERROR,
) )
self.env["runtime"]["EventManager"].put_to_event_queue(event, data) self.env["runtime"]["EventManager"].put_to_event_queue(event, data)
if run_once: if run_once:

View File

@@ -4,5 +4,5 @@
# Fenrir TTY screen reader # Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors. # By Chrys, Storm Dragon, and contributors.
version = "2025.10.17" version = "2025.11.23"
code_name = "master" code_name = "master"

View File

@@ -20,8 +20,10 @@ class driver(remoteDriver):
def initialize(self, environment): def initialize(self, environment):
self.env = environment self.env = environment
# Use threading instead of multiprocessing to avoid pickle issues
# with self.env (which contains unpicklable file handles)
self.env["runtime"]["ProcessManager"].add_custom_event_thread( self.env["runtime"]["ProcessManager"].add_custom_event_thread(
self.watch_dog, multiprocess=True self.watch_dog, multiprocess=False
) )
def watch_dog(self, active, event_queue): def watch_dog(self, active, event_queue):

View File

@@ -20,8 +20,10 @@ class driver(remoteDriver):
def initialize(self, environment): def initialize(self, environment):
self.env = environment self.env = environment
# Use threading instead of multiprocessing to avoid pickle issues
# with self.env (which contains unpicklable file handles)
self.env["runtime"]["ProcessManager"].add_custom_event_thread( self.env["runtime"]["ProcessManager"].add_custom_event_thread(
self.watch_dog, multiprocess=True self.watch_dog, multiprocess=False
) )
def watch_dog(self, active, event_queue): def watch_dog(self, active, event_queue):

View File

@@ -126,8 +126,10 @@ class driver(screenDriver):
"default", # fontfamily "default", # fontfamily
] ]
) # end attribute ) ) # end attribute )
# Use threading instead of multiprocessing to avoid pickle issues
# with self.env (which contains unpicklable file handles)
self.env["runtime"]["ProcessManager"].add_custom_event_thread( self.env["runtime"]["ProcessManager"].add_custom_event_thread(
self.update_watchdog, multiprocess=True self.update_watchdog, multiprocess=False
) )
def get_curr_screen(self): def get_curr_screen(self):