449 def wx_evaluator(obj, *args, **kwargs): |
447 def wx_evaluator(obj, *args, **kwargs): |
450 tocall, args, kwargs = obj.call |
448 tocall, args, kwargs = obj.call |
451 obj.res = default_evaluator(tocall, *args, **kwargs) |
449 obj.res = default_evaluator(tocall, *args, **kwargs) |
452 wx_eval_lock.release() |
450 wx_eval_lock.release() |
453 |
451 |
|
452 main_thread_id = currentThread().ident |
454 def evaluator(tocall, *args, **kwargs): |
453 def evaluator(tocall, *args, **kwargs): |
455 # To prevent deadlocks, check if current thread is not one of the UI |
454 # To prevent deadlocks, check if current thread is not one already main |
456 # UI threads can be either the one from WX main loop or |
|
457 # worker thread from twisted "threadselect" reactor |
|
458 current_id = currentThread().ident |
455 current_id = currentThread().ident |
459 |
456 |
460 if ui_thread is not None \ |
457 if main_thread_id != current_id: |
461 and ui_thread.ident != current_id \ |
|
462 and (not havetwisted or ( |
|
463 twisted_reactor_thread_id is not None |
|
464 and twisted_reactor_thread_id != current_id)): |
|
465 |
|
466 o = type('', (object,), dict(call=(tocall, args, kwargs), res=None)) |
458 o = type('', (object,), dict(call=(tocall, args, kwargs), res=None)) |
467 wx.CallAfter(wx_evaluator, o) |
459 wx.CallAfter(wx_evaluator, o) |
468 wx_eval_lock.acquire() |
460 wx_eval_lock.acquire() |
469 return o.res |
461 return o.res |
470 else: |
462 else: |
471 # avoid dead lock if called from the wx mainloop |
463 # avoid dead lock if called from main : do job immediately |
472 return default_evaluator(tocall, *args, **kwargs) |
464 return default_evaluator(tocall, *args, **kwargs) |
473 else: |
465 else: |
474 evaluator = default_evaluator |
466 evaluator = default_evaluator |
475 |
467 |
476 # Exception hooks |
468 # Exception hooks |
553 WC.RegisterWampClient(wampconf, PSKpath) |
545 WC.RegisterWampClient(wampconf, PSKpath) |
554 WC.RegisterWebSettings(NS) |
546 WC.RegisterWebSettings(NS) |
555 except Exception: |
547 except Exception: |
556 LogMessageAndException(_("WAMP client startup failed. ")) |
548 LogMessageAndException(_("WAMP client startup failed. ")) |
557 |
549 |
|
550 if havetwisted or havewx: |
|
551 if havetwisted: |
|
552 # reactor._installSignalHandlersAgain() |
|
553 waker_func = reactor._runInMainThread |
|
554 def ui_blocking_call(): |
|
555 # FIXME: had to disable SignaHandlers install because |
|
556 # signal not working in non-main thread |
|
557 reactor.run(installSignalHandlers=False) |
|
558 else: |
|
559 waker_func = wx.CallAfter |
|
560 ui_blocking_call = app.MainLoop |
|
561 |
|
562 def ui_launched_report(): |
|
563 # IDE expects to see that string to stop waiting for runtime |
|
564 # to be ready and connnect to it. |
|
565 print("UI thread started successfully.") |
|
566 |
|
567 # This orders ui loop to signal when ready on Stdout |
|
568 if havetwisted: |
|
569 reactor.callLater(0, ui_launched_report) |
|
570 else: |
|
571 wx.CallAfter(ui_launched_report) |
|
572 |
|
573 |
|
574 pyro_thread = None |
|
575 |
558 def FirstWorkerJob(): |
576 def FirstWorkerJob(): |
559 """ |
577 """ |
560 RPC through pyro/wamp/UI may lead to delegation to Worker, |
578 RPC through pyro/wamp/UI may lead to delegation to Worker, |
561 then this function ensures that Worker is already |
579 then this function ensures that Worker is already |
562 created when pyro starts |
580 created when pyro starts |
563 """ |
581 """ |
564 global pyro_thread, pyroserver, ui_thread, reactor, twisted_reactor_thread_id |
582 global pyro_thread, pyroserver |
565 |
583 |
566 pyro_thread_started = Lock() |
584 pyro_thread_started = Lock() |
567 pyro_thread_started.acquire() |
585 pyro_thread_started.acquire() |
568 pyro_thread = Thread(target=pyroserver.PyroLoop, |
586 pyro_thread = Thread(target=pyroserver.PyroLoop, |
569 kwargs=dict(when_ready=pyro_thread_started.release), |
587 kwargs=dict(when_ready=pyro_thread_started.release), |
579 # Beremiz IDE detects LOCAL:// runtime is ready by looking |
597 # Beremiz IDE detects LOCAL:// runtime is ready by looking |
580 # for self.workdir in the daemon's stdout. |
598 # for self.workdir in the daemon's stdout. |
581 sys.stdout.write(_("Current working directory :") + WorkingDir + "\n") |
599 sys.stdout.write(_("Current working directory :") + WorkingDir + "\n") |
582 sys.stdout.flush() |
600 sys.stdout.flush() |
583 |
601 |
584 if not (havetwisted or havewx): |
602 runtime.GetPLCObjectSingleton().AutoLoad(autostart) |
585 return |
603 |
586 |
604 try: |
587 ui_thread_started = Lock() |
605 if havetwisted or havewx: |
588 ui_thread_started.acquire() |
606 # worker that copes with wx and (wx)reactor |
589 if havetwisted: |
607 runtime.MainWorker.interleave(waker_func, FirstWorkerJob) |
590 # reactor._installSignalHandlersAgain() |
608 ui_blocking_call() |
591 def ui_thread_target(): |
609 runtime.MainWorker.stop() |
592 # FIXME: had to disable SignaHandlers install because |
610 |
593 # signal not working in non-main thread |
|
594 reactor.run(installSignalHandlers=False) |
|
595 else: |
611 else: |
596 ui_thread_target = app.MainLoop |
612 # blocking worker loop |
597 |
613 runtime.MainWorker.runloop(FirstWorkerJob) |
598 ui_thread = Thread(target=ui_thread_target, name="UIThread") |
614 |
599 ui_thread.start() |
|
600 |
|
601 # This order ui loop to unblock main thread when ready. |
|
602 if havetwisted: |
|
603 def signal_uithread_started(): |
|
604 global twisted_reactor_thread_id |
|
605 twisted_reactor_thread_id = currentThread().ident |
|
606 ui_thread_started.release() |
|
607 reactor.callLater(0, signal_uithread_started) |
|
608 else: |
|
609 wx.CallAfter(ui_thread_started.release) |
|
610 |
|
611 # Wait for ui thread to be effective |
|
612 ui_thread_started.acquire() |
|
613 print("UI thread started successfully.") |
|
614 |
|
615 runtime.GetPLCObjectSingleton().AutoLoad(autostart) |
|
616 |
|
617 try: |
|
618 runtime.MainWorker.runloop(FirstWorkerJob) |
|
619 except KeyboardInterrupt: |
615 except KeyboardInterrupt: |
620 pass |
616 pass |
621 |
617 |
622 pyroserver.Quit() |
618 pyroserver.Quit() |
623 pyro_thread.join() |
619 pyro_thread.join() |