Windows GUI app crash on exit (Successful WSASTARTUP not yet performed)
Closed this issue · 8 comments
Hello.
We create GUI applications using Pytango and PyQt/PySide and use events to receive data.
When the program is closed via X (close) button, Windows show popup with "unknown software exception" and "Assertion failed: Successful WSASTARTUP not yet performed (......\src\signaler.cpp:377)" error in the terminal, even with unsubscribing from events before accepting close signal.
Possible solution may be described here zeromq/czmq#1788
Some more details are here #367
Windows 10, Python 3.7, PyTango 9.3.1, PySide2 5.15.1, Tango 9.3.4
PyTango doesn't have a direct hook to the ZMQ context in the cppTango library. From what I can see, the singleton classes ApiUtil
(clients/device proxies) and Util
(servers) own the ZMQ contexts. The ApiUtil
has an event consumer, and the Util
has an event supplier.
If your application is doing client access to Tango devices, then it might help to call tango.ApiUtil.cleanup()
when your application is about to exit. That will free the ApiUtil
singleton, which should also release its ZMQ context (cppTango code here and here).
If you are seeing this problem with PyTango device servers, then you may also need to call: tango.Util.instance().server_cleanup()
. However, I'm not sure if that cleans up the ZMQ event supplier. From the cppTango code, it just mentions cleaning up the orb. I don't see a way to set the Util
singleton to NULL
.
If this works, please let me know. Maybe it will help to register an atexit call in the PyTango code on Windows?
Hi @ajoubertza,
Many thanks for these advices. We will certainly test it during the Jan21 (before end of January) release processes of Taurus and Sardana. These are the times we usually test on Windows. I will do my best to find some extra time earlier but I can not promise.
Hi @ajoubertza
I've tried to add tango.ApiUtil.cleanup()
to the PyQt closeEvent()
function in my client application and now it's just stuck on exit, without responding or showing error popup dialogs, like it was previously.
Thanks for trying it out, @Diego91RA. Maybe there is a different place to call it (fr example, using the Python atexit
module)? Or maybe you have to somehow cleanup all your PyTango clients before the doing the tango.ApiUtil.cleanup()
? You might even need to force a gc.collect()
step as part of the cleanup.
Other than that, you would need to attach a debugger to see what is happening.
Hi @ajoubertza
I think PyQt doesn't implement Python atexit
, it has its own on-close triggers, inherited from C++ Qt, such as closeEvent
or QApplication.aboutToQuit
.
I've tried different combinations of them and gc.collect()
worked fine (and has no effect), but tango.ApiUtil.cleanup()
was stuck anyway.
After that I've decided to comment line that switches my application to PUSH_CALLBACK model (self.api_util.set_asynch_cb_sub_model(tango.cb_sub_model.PUSH_CALLBACK)
) and there are no problems on exit.
But now I'm confused because it was necessary to switch event system model if I want the callback to be executed immediately and not to pull data from event queue by code.
This code:
for attr in self.attr_list: sub_id = self.dev.subscribe_event(attr, tango.EventType.PERIODIC_EVENT, self.recv_event)
works without model switch now, but default model is supposed to be PULL.
Are event system models deprecated for PyTango? And I can decide which one I want to use in the subscribe_event
constructor?
@Diego91RA I haven't used that ApiUtil method, but from the docs and code, I think the set_asynch_cb_sub_model
only applies to commands, not events.
cppTango details about commands: https://tango-controls.readthedocs.io/en/latest/development/client-api/cpp-client-programmers-guide.html#asynchronous-model
cppTango details about events: https://tango-controls.readthedocs.io/en/latest/development/client-api/cpp-client-programmers-guide.html#subscribing-to-events
We can see from those docs, that the parameters passed to the subscribe_event
method are what determines "push" or "pull" mode. This isn't so clearly visible in the PyTango docs, but I think it is as follows:
For push model:
subscribe_event(self, attr_name, event, callback, filters=[], stateless=False, extract_as=Numpy, green_mode=None) -> int
For pull model:
subscribe_event(self, attr_name, event, queuesize, filters=[], stateless=False, green_mode=None) -> int`
From that, it depends on the 3rd argument - if it is a callable, then we use push, if it is an integer then we use pull. In your example, you provide self.recv_event
, which is most likely a callable, so you are using the push model.
Hi @ajoubertza.
I've checked my old projects and found that I started using push/pull model switch in 2016, pytango 9.2.0 I think.
Maybe there was a bug, but that code followed me from one project to another since that.
Anyway, we've figured out tango.ApiUtil.cleanup()
on exit fix the crash problem in my case.
@reszelaz can you check it in your developments?
Also, can we do it in Pytango itself?