diff --git a/src/fenrirscreenreader/core/processManager.py b/src/fenrirscreenreader/core/processManager.py index 91c9325c..5ec18cdb 100644 --- a/src/fenrirscreenreader/core/processManager.py +++ b/src/fenrirscreenreader/core/processManager.py @@ -13,6 +13,64 @@ from fenrirscreenreader.core import debug from fenrirscreenreader.core.eventData import FenrirEventType +# Standalone worker functions for multiprocessing (cannot be instance methods) +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: def __init__(self): self._Processes = [] @@ -60,8 +118,8 @@ class ProcessManager: original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) if multiprocess: t = Process( - target=self.custom_event_worker_thread, - args=(event_queue, function, pargs, run_once), + target=_custom_event_worker_process, + args=(self.running, event_queue, function, pargs, run_once), ) self._Processes.append(t) else: # use thread instead of process @@ -78,9 +136,11 @@ class ProcessManager: ): original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) if multiprocess: + # Get event queue reference before creating process + event_queue = self.env["runtime"]["EventManager"].get_event_queue() t = Process( - target=self.simple_event_worker_thread, - args=(event, function, pargs, run_once), + target=_simple_event_worker_process, + args=(self.running, event_queue, event, function, pargs, run_once), ) self._Processes.append(t) else: @@ -106,12 +166,13 @@ class ProcessManager: else: function(self.running, event_queue) 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(" + str(function) + "):" - + str(e), - debug.DebugLevel.ERROR, + + str(e) ) if run_once: break @@ -131,12 +192,13 @@ class ProcessManager: else: data = function(self.running) 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(" + str(function) + "):" - + str(e), - debug.DebugLevel.ERROR, + + str(e) ) self.env["runtime"]["EventManager"].put_to_event_queue(event, data) if run_once: diff --git a/src/fenrirscreenreader/fenrirVersion.py b/src/fenrirscreenreader/fenrirVersion.py index b0cf2bc9..34a97ffd 100644 --- a/src/fenrirscreenreader/fenrirVersion.py +++ b/src/fenrirscreenreader/fenrirVersion.py @@ -4,5 +4,5 @@ # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. -version = "2025.10.17" +version = "2025.11.23" code_name = "testing"