Program doesn't shut down cleanly

Very rarely, my wxPython-using program doesn’t shut down cleanly. All of the windows close, but the program itself continues to use up sockets, file handles, memory, etc. Thus if you rerun the program it won’t work correctly, because resources it needs are not available. You have to use Task Manager to find the program (shown under “pythonw.exe”) and kill it manually.

My best guess is that somehow the program is spawning a non-daemon thread which continues to execute even when WX is done. Newly-created threads inherit their parent threads’ daemon settings, of course. Now, I could track down every instance where I create a thread and manually set its daemon flag…but the only reason this is happening is because WX is creating non-daemon threads, from which I then create new threads, inheriting the daemon status). Is there some way I can tell WX to only create daemon threads and just avoid the whole mess in the first place? Frankly I don’t see why WX would need non-daemon threads anyway.

As an example, this program will hang when you try to close its window:
http://pastebin.com/Z05v2q9g

If you set foo.daemon to True, then when you close the window, it will exit properly.

-Chris

Platform and version? Does the MainLoop method exit and then it hangs at the end of the script, or is it stuck inside MainLoop?

If MainLoop is exiting then it likely is a thread that is stopping the exit, but not a wx thread because I don't think they participate at all in Python's deamon/non-daemon status. (wx does create a thread in a couple places as needed, but only for things like being able to receive messages from the system that need to be handled outside of the normal even stream.)

If the MainLoop method call is not exiting then the likely culprit is that there is a still a frame or dialog that hasn't been closed and/or destroyed. You can use wx.GetTopLevelWindows to see what TLWs still exist. You must also remove any wx.TaskBarIcons if your application has any as they will also keep the MainLoop alive.

···

On 12/6/12 12:09 PM, Chris Weisiger wrote:

Very rarely, my wxPython-using program doesn't shut down cleanly. All of
the windows close, but the program itself continues to use up sockets,
file handles, memory, etc. Thus if you rerun the program it won't work
correctly, because resources it needs are not available. You have to use
Task Manager to find the program (shown under "pythonw.exe") and kill it
manually.

My best guess is that somehow the program is spawning a non-daemon
thread which continues to execute even when WX is done. Newly-created
threads inherit their parent threads' daemon settings, of course. Now, I
could track down every instance where I create a thread and manually set
its daemon flag...but the only reason this is happening is because WX is
creating non-daemon threads, from which I then create new threads,
inheriting the daemon status). Is there some way I can tell WX to only
create daemon threads and just avoid the whole mess in the first place?
Frankly I don't see why WX would need non-daemon threads anyway.

As an example, this program will hang when you try to close its window:
import threadingimport wxdef waiter(): while True: time.slee - Pastebin.com

If you set foo.daemon to True, then when you close the window, it will
exit properly.

--
Robin Dunn
Software Craftsman

Platform and version? Does the MainLoop method exit and then it hangs at
the end of the script, or is it stuck inside MainLoop?

Python 2.7 / WX 2.9.x, Windows. MainLoop does exit and then it hangs, which
is why I suspected threads.

If MainLoop is exiting then it likely is a thread that is stopping the
exit, but not a wx thread because I don't think they participate at all in
Python's deamon/non-daemon status. (wx does create a thread in a couple
places as needed, but only for things like being able to receive messages
from the system that need to be handled outside of the normal even stream.)

Yeah, in hindsight I should have realized that WX was not creating threads.
I'd assumed it was creating one for each event handler it calls, but all
that is done in the main thread, and just as well since otherwise you
wouldn't be able to change the UI in response to events!

As a workaround while I track down which of my threads is failing to exit
cleanly, I put this in after the MainLoop() call:

os._exit(os.EX_OK)

Which forces the program to shut down all of its threads and shut down.
This is obviously a hack, but seeing as this unclean-shutdown problem only
happens about 1-2% of the time, and I'd have to be running in a debugger to
figure out which threads are still alive (since I have no console or other
way to check on program state), I think it's a decent safety check.

···

On Thu, Dec 6, 2012 at 4:21 PM, Robin Dunn <robin@alldunn.com> wrote:

--

Robin Dunn

-Chris