--- a/Beremiz_service.py Sat Aug 13 16:12:39 2022 +0200
+++ b/Beremiz_service.py Tue Aug 16 19:52:49 2022 +0200
@@ -435,8 +435,6 @@
reactor.registerWxApp(app)
-twisted_reactor_thread_id = None
wx_eval_lock = Semaphore(0)
@@ -451,24 +449,18 @@
obj.res = default_evaluator(tocall, *args, **kwargs)
+ main_thread_id = currentThread().ident def evaluator(tocall, *args, **kwargs):
- # To prevent deadlocks, check if current thread is not one of the UI
- # UI threads can be either the one from WX main loop or
- # worker thread from twisted "threadselect" reactor
+ # To prevent deadlocks, check if current thread is not one already main current_id = currentThread().ident
- if ui_thread is not None \
- and ui_thread.ident != current_id \
- and (not havetwisted or (
- twisted_reactor_thread_id is not None
- and twisted_reactor_thread_id != current_id)):
+ if main_thread_id != current_id: o = type('', (object,), dict(call=(tocall, args, kwargs), res=None))
wx.CallAfter(wx_evaluator, o)
- # avoid dead lock if called from the wx mainloop
+ # avoid dead lock if called from main : do job immediately return default_evaluator(tocall, *args, **kwargs)
evaluator = default_evaluator
@@ -555,13 +547,39 @@
LogMessageAndException(_("WAMP client startup failed. "))
+if havetwisted or havewx: + # reactor._installSignalHandlersAgain() + waker_func = reactor._runInMainThread + def ui_blocking_call(): + # FIXME: had to disable SignaHandlers install because + # signal not working in non-main thread + reactor.run(installSignalHandlers=False) + waker_func = wx.CallAfter + ui_blocking_call = app.MainLoop + def ui_launched_report(): + # IDE expects to see that string to stop waiting for runtime + # to be ready and connnect to it. + print("UI thread started successfully.") + # This orders ui loop to signal when ready on Stdout + reactor.callLater(0, ui_launched_report) + wx.CallAfter(ui_launched_report) RPC through pyro/wamp/UI may lead to delegation to Worker,
then this function ensures that Worker is already
- global pyro_thread, pyroserver, ui_thread, reactor, twisted_reactor_thread_id
+ global pyro_thread, pyroserver pyro_thread_started = Lock()
pyro_thread_started.acquire()
@@ -581,41 +599,19 @@
sys.stdout.write(_("Current working directory :") + WorkingDir + "\n")
- if not (havetwisted or havewx):
- ui_thread_started = Lock()
- ui_thread_started.acquire()
- # reactor._installSignalHandlersAgain()
- def ui_thread_target():
- # FIXME: had to disable SignaHandlers install because
- # signal not working in non-main thread
- reactor.run(installSignalHandlers=False)
- ui_thread_target = app.MainLoop
- ui_thread = Thread(target=ui_thread_target, name="UIThread")
- # This order ui loop to unblock main thread when ready.
- def signal_uithread_started():
- global twisted_reactor_thread_id
- twisted_reactor_thread_id = currentThread().ident
- ui_thread_started.release()
- reactor.callLater(0, signal_uithread_started)
- wx.CallAfter(ui_thread_started.release)
- # Wait for ui thread to be effective
- ui_thread_started.acquire()
- print("UI thread started successfully.")
runtime.GetPLCObjectSingleton().AutoLoad(autostart)
- runtime.MainWorker.runloop(FirstWorkerJob)
+ if havetwisted or havewx: + # worker that copes with wx and (wx)reactor + runtime.MainWorker.interleave(waker_func, FirstWorkerJob) + runtime.MainWorker.stop() + runtime.MainWorker.runloop(FirstWorkerJob) except KeyboardInterrupt:
@@ -631,9 +627,7 @@
--- a/runtime/Worker.py Sat Aug 13 16:12:39 2022 +0200
+++ b/runtime/Worker.py Tue Aug 16 19:52:49 2022 +0200
@@ -9,7 +9,8 @@
from __future__ import absolute_import
-from threading import Lock, Condition
+from threading import Lock, Condition, Thread from six.moves import _thread
@@ -86,6 +87,57 @@
+ def interleave(self, waker, *args, **kwargs): + as for twisted reactor's interleave, it passes all jobs to waker func + additionaly, it creates a new thread to wait for new job. + self.feed = Condition(self.mutex) + self._threadID = _thread.get_ident() + def wakerfeedingloop(): + _job = job(*args, **kwargs) + while not self._finish: + if self.job is not None: + self.own_thread = Thread(target = wakerfeedingloop).start() def call(self, *args, **kwargs):
creates a job, execute it in worker thread, and deliver result.