Crash exiting MainLoop()

Hello,

I’ve got an application written in Python 3.7 using wxPython 4.0.7.post2 running on Windows 7 and 10. About 25% of the time I exit my program the Python interpreter crashes trying to leave MainLoop(). I usually get an error code like (0xC0000005). If I look in the Windows 10 event viewer I can see the program crash and it indicates that the module the fault happened in was wxbase30u_vc140_x64.dll.

My method that handles wx.EVT_CLOSE has:


print(‘Calling destroy’)
self.DestroyLater()
print(‘Called Destroy’)

I get both of the messages printed. But no further messages are printed at the point that MainLoop() should exit.

The application is too large to post here (~50k LOC). Is there a way to get more information about the crash? Debug into the DLL’s? Anything that could help me track this down?

Thank you!

What does the rest of the EVT_CLOSE handler look like? Does it call event.Skip()? If so, then it is probably due to the C++ part of the frame being destroyed twice.

There are .pdb files available at https://extras.wxpython.org/wxPython4/extras if you want to try to debug into the wrapper code.

Hi Robin,

The close handler does a bit of work to close active network connections, closes a MDI child window, then calls self.DestroyLater() . The event passed into the close handler is not accessed at all.

I’ve pulled down the pdb files and the appropriate source. The problem appears to be in Event.cpp, but I had the wrong source downloaded for that crash, so I don’t know where the real crash is exactly yet. Once I figure that out I’ll likely be back with more questions.

Thanks!

Using the debug files I’ve been able to tell that the crash is happening because a wx.Timer is trying to process an event. I’ve called Stop() and DeletePendingEvents() on all of the wx.Timers I create in my code. Is there any way to stop all wx.Timers() or destroy them in a safe way as part of shutdown? Is there a way to get a list of all wx.Timer objects?

Thanks!

If you use Python Timer the interpreter only stops after all timers have expired, so there will be no crash. The problem is only in the timer handler you have to query the existence of the C++ objects by inserting __nonzero__()

If a frame with a timer is destroyed while the timer is still running, then you do get a crash. That happens even if the event handler doesn’t reference any wrapped C++ objects.

An EVT_WINDOW_DESTROY handler on the frame that stops timers helps with that. Don’t forget event.Skip().

1 Like

Well, I was actually referring to a Python Timer (see docu of python): that is simply a thread in python, which happens here to be the wrapper of wx. You can destroy as many frames as you like as long as you don’t catch such events as you mentioned the wrapper will happily rap away, I hope :astonished:

Do you mean threading.Timer? How do you get that to play nice with wx, seeing as you can’t touch wx objects from an auxiliary thread?

Yes, I have just uploaded an example under this post

I’m sorry, but that’s not sound: In my_fast_select.py, you are calling wx.StatusBar.SetStatusText from a different thread, not the wx main thread, and that will get you in trouble.

Well, what you say is what the wx docu advises, though in less drastic words. This thread started off with a C++ part still running (the timer still firing) at shut down of the wrapper and, of course, posting more events correctly from a thread doesn’t alleviate the situation, not a bit.

So let’s be correct and wait… :sneezing_face:

and using ‘CallLater’ may cause hiccups: that object may be thread-safe, but it’s definitely not class-safe

problem.py (1.3 KB)

and it’s easy to flood the GUI, but a good Window Manager won’t crash (poorly written apps though may)

colour_pump.py (3.5 KB)

the larger the Frame the quicker the manager is puffed (a clear reminder that one is at a work station and not play station)

and if your app hangs up once in a while (or for initial calibration) just add a reset (here by choosing a delay)

colour_pump_reset.py (4.6 KB)

make it or fake it, the eye won’t detect it (although at 32 I do get some blank spots)

colour_pump_fake.py (4.7 KB)

The wx.Timer issue is still in 4.2.1. It have a constant timer in the wx.App, that did not cause any issues, but the main window really could try to run the wx.Timer after the App was destroyed by a different thread and tried to call wx.Timer and caused 0xC0000005.

You should stop wx.Timer before the owner window is destroyed. Otherwise, your app can crash.
Even if it looks fine, sometimes it can (perhaps for large apps).

See @AndersMunch’s post above. For example:

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timer = wx.Timer(self)
        self.timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self.OnTimer)

    def OnTimer(self, evt):
        if evt.Timer is self.timer:
            print(self.Title)

    def Destroy(self):
        try:
            ## self.timer.Stop() # Don't forget this. Otherwise ...
            print('toe')
        finally:
            return wx.Frame.Destroy(self)

if __name__ == "__main__":
    app = wx.App()
    frm = Frame(None, title='tic')
    frm2 = Frame(None, title='tac')
    frm2.Show()
    frm.Show()
    app.MainLoop()

To get the error, run this script as follows:

$ py -3 -Wd -Xdev -Xtracemalloc <this.py>
... (snip) ...
Windows fatal exception: access violation

Current thread 0x00002520 (most recent call first):
  File "C:\Python311\Lib\site-packages\wx\core.py", line 2262 in MainLoop
  File "C:\usr\home\lib\py-site\test-phoenix\issue_timer.py", line 32 in <module>

but when is the window destroyed (Python magic ?) :rofl:

from time import sleep
import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timer = wx.Timer(self)
        self.timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.Bind(wx.EVT_WINDOW_DESTROY, self.evt_destroy)

    def OnTimer(self, evt):
        if evt.Timer is self.timer:
            print(self.Title)

    def Destroy(self):
        try:
            ## self.timer.Stop() # Don't forget this. Otherwise ...
            print('toe')
        finally:
            return wx.Frame.Destroy(self)

    def evt_destroy(self, _):
        print(self.__nonzero__())
        sleep(4)
        self.timer.Stop()

if __name__ == "__main__":
    app = wx.App()
    frm = Frame(None, title='tic')
    frm2 = Frame(None, title='tac')
    frm2.Show()
    frm.Show()
    app.MainLoop()

I generally assumed killing the app would harmlessly kill the timers rather than having me play segfault Russian roulette. Yeah, my app’s a bit large and takes a while to shutdown.

but you haven’t got any async in it, have ye ? :cowboy_hat_face:

One, but only to be called if there is no gui. So it can’t really be responsible here. Basically it has an internal console with a bunch of commands and usually the GUI does the mainloop but sometimes you could have an async loop for input if you don’t launch with the GUI.

It does have an alternative scheduler thread that sometimes will be there to kill the gui app.

@tatarize
well, I tried a little bit with the wx.App but it looks as though you have to do clean housekeeping (and you are correct the docu does not explicitly say so, although as a subclass of object destroying would be natural) :sneezing_face:

P.S. I suppose the most economical would be a singleton for the destroy and a binding at each timer creation, like

import wx

def evt_destroy(timer):
    timer.Destroy()

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timer = wx.Timer(self)
        self.timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.Bind(wx.EVT_WINDOW_DESTROY,
                            lambda _: evt_destroy(self.timer))
        self.Show()

    def OnTimer(self, evt):
        if evt.Timer is self.timer:
            print(self.Title)

if __name__ == "__main__":
    app = wx.App()
    Frame(None, title='tic')
    Frame(None, title='tac')
    app.MainLoop()

or, to simplify the singleton (for those who like it :worried:)

import wx

class Frame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.timer = wx.Timer(self)
        self.timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        self.Bind(wx.EVT_WINDOW_DESTROY,
                            lambda _: wx.GetApp().evt_destroy(self.timer))
        self.Show()

    def OnTimer(self, evt):
        if evt.Timer is self.timer:
            print(self.Title)

class WxApp(wx.App):
    def __init__(self):
        super().__init__()
    def OnInit(self):
        Frame(None, title='tic')
        Frame(None, title='tac')
        self.MainLoop()
        return True
    def evt_destroy(self, timer):
        timer.Destroy()

if __name__ == "__main__":
    WxApp()