Crash possibly due to destroyed frame not releasing GDI handles

Hello, I’m currently developing the GUI part of an application using wxPython. As part of this development I’m writing test to check that values are correctly set, retrieved and so on. I’m using the python unittest framework, where I create the main frame in setUp() and delete it in tearDown(). As the number of tests grew I suddenly got a problem were the program would crash due to a failure to create a new wxPython object.

After searching around on the net on this mail list and elsewere I realised it was probably due to my program not releasing its allocated GDI handles after each test. A look in task manager confirmed this as the GDI numer would rise until it hit 10.000 and the program crashed. In order to localize the problem I have written a small test program where I provoke the error to happen. The test program will run for about 10.000 iterations before I get the error. This is what it looks like:

0
1000
2000
3000
4000
5000
6000
7000
8000
9000
F.

test_gui_example.py (756 Bytes)

···

======================================================================
FAIL: test_1 (main.GuiTest)

Traceback (most recent call last):
File “C:\workspace\test\test_gui_example.py”, line 20, in test_1
self.set_up()
File “C:\workspace\test\test_gui_example.py”, line 7, in set_up
self.frame = GuiFrame(None)
File “C:\workspace\test\test_gui_example.py”, line 31, in init
self.button = wx.Button(self.panel)
File “C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx_controls.py”, line 87, in init
controls.Button_swiginit(self,controls.new_Button(*args, **kwargs))
PyAssertionError: C++ assertion “wxAssertFailure” failed at …\src\msw\control.cpp(159) in wxControl::MSWCreateControl(): CreateWindowEx(“BUTTON”, flags=56010000, ex=00000000) failed


Ran 2 tests in 20.101s

FAILED (failures=1)

If I comment out the line self.frame.DestroyChildren the program will only run for about 3000 iterations. In my real application I get a crash after 27 tests. It seems to me that the GDI handle of the frame is not properly released. Could someone help me with this problem?

I’m using Python 2.7.3, with wx 2.8 in Eclipse SDK 3.7.2

greetings

Mads Nilsson

Mads Nilsson wrote:

Hello, I'm currently developing the GUI part of an application using
wxPython. As part of this development I'm writing test to check that
values are correctly set, retrieved and so on. I'm using the python
unittest framework, where I create the main frame in setUp() and delete
it in tearDown(). As the number of tests grew I suddenly got a problem
were the program would crash due to a failure to create a new wxPython
object.
After searching around on the net on this mail list and elsewere I
realised it was probably due to my program not releasing its allocated
GDI handles after each test. A look in task manager confirmed this as
the GDI numer would rise until it hit 10.000 and the program crashed. In
order to localize the problem I have written a small test program where
I provoke the error to happen. The test program will run for about
10.000 iterations before I get the error.

The Destroy() method of top-level windows does not actually destroy them, but puts them in a queue of objects to be deleted later. That usually happens when there are no longer any pending events, in other words, in idle time. Since your tests are never running MainLoop then there are no events being processed and so there is never any idle time notifications, and so those frames and their contents are never destroyed.

You may want to try something like what I did for Phoenix's unit tests, see: wxTrac has been migrated to GitHub Issues - wxWidgets The WidgetTestCase is the base class used for the test cases which test UI objects. Some of what is done there in the various helper methods probably won't work with Classic, but the important bit for you to look at is the tearDown method.

···

--
Robin Dunn
Software Craftsman

kl. 22:35:50 UTC+2 onsdag 4. september 2013 skrev Robin Dunn følgende:

The Destroy() method of top-level windows does not actually destroy
them, but puts them in a queue of objects to be deleted later. That
usually happens when there are no longer any pending events, in other
words, in idle time. Since your tests are never running MainLoop then
there are no events being processed and so there is never any idle time
notifications, and so those frames and their contents are never destroyed.

You may want to try something like what I did for Phoenix’s unit tests,
see:
http://trac.wxwidgets.org/browser/wxPython/Phoenix/trunk/unittests/wtc.py

The WidgetTestCase is the base class used for the test cases which test
UI objects. Some of what is done there in the various helper methods
probably won’t work with Classic, but the important bit for you to look
at is the tearDown method.


Robin Dunn

Software Craftsman

http://wxPython.org

Hi Robin, thanks for the quick reply. I tried your solution and it worked like a charm on my example program, however in my real program I still have a leak of GDI handles. After trying different things I finally found that it was due to my auiNotebook.

I am working on expanding an existing gui where the notebook that has been used is wx.lib.agw.aui.AuiNotebook. I have modified my example to include a notebook of this type, and now it crashes after 200 iterations:

0
100
200
F.

test_gui_example.py (929 Bytes)

···

======================================================================
FAIL: test_1 (main.GuiTest)

Traceback (most recent call last):
File “C:\workspace\test\test_gui_example.py”, line 24, in test_1
self.set_up()
File “C:\workspace\test\test_gui_example.py”, line 8, in set_up
self.frame = GuiFrame(None)
File “C:\workspace\test\test_gui_example.py”, line 34, in init
self.notebook = aui.AuiNotebook(self)
File “C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\agw\aui\auibook.py”, line 2691, in init
self.InitNotebook(agwStyle)
File “C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\agw\aui\auibook.py”, line 2721, in InitNotebook
self.SetArtProvider(TA.AuiDefaultTabArt())
File “C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\agw\aui\tabart.py”, line 124, in init
self._active_close_bmp = BitmapFromBits(nb_close_bits, 16, 16, wx.BLACK)
File “C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx\lib\agw\aui\aui_utilities.py”, line 141, in BitmapFromBits
img = wx.BitmapFromBits(bits, w, h).ConvertToImage()
File “C:\Python27\lib\site-packages\wx-2.8-msw-unicode\wx_gdi.py”, line 618, in ConvertToImage
return gdi.Bitmap_ConvertToImage(*args, **kwargs)
PyAssertionError: C++ assertion “bmp.Ok()” failed at …..\src\msw\dib.cpp(148) in wxDIB::Create(): wxDIB::Create(): invalid bitmap


Ran 2 tests in 3.372s

FAILED (failures=1)

A look in task manager confirms that the number of GDI objects is rising sharply. If I use the standard wx.aui.AuiNotebook there is no such problem. Is there a leak of GDI handles in the agw auinotebook, or am I using it wrong?

greetings

Mads Nilsson

Mads Nilsson wrote:

Hi Robin, thanks for the quick reply. I tried your solution and it
worked like a charm on my example program, however in my real program I
still have a leak of GDI handles. After trying different things I
finally found that it was due to my auiNotebook.
I am working on expanding an existing gui where the notebook that has
been used is wx.lib.agw.aui.AuiNotebook. I have modified my example to
include a notebook of this type, and now it crashes after 200 iterations:
0
100
200
F.

A look in task manager confirms that the number of GDI objects is rising
sharply. If I use the standard wx.aui.AuiNotebook there is no such
problem. Is there a leak of GDI handles in the agw auinotebook, or am I
using it wrong?

The test looks okay. You should look in wx.lib.agw.aui to see if you can spot any cycles or other issues that might cause something to not be released.

···

--
Robin Dunn
Software Craftsman

kl. 06:36:56 UTC+2 fredag 6. september 2013 skrev Robin Dunn følgende:

The test looks okay. You should look in wx.lib.agw.aui to see if you
can spot any cycles or other issues that might cause something to not be
released.

Are you saying that you can’t reproduce the crash? I looked through the code, doesn’t seem like anything has been changed.

Mads Nilsson

Mads Nilsson wrote:

kl. 06:36:56 UTC+2 fredag 6. september 2013 skrev Robin Dunn f�lgende:

    The test looks okay. You should look in wx.lib.agw.aui to see if you
    can spot any cycles or other issues that might cause something to
    not be
    released.

Are you saying that you can't reproduce the crash? I looked through the
code, doesn't seem like anything has been changed.

No, I meant that your test code looks to me like it is using the aui classes correctly, which is what you asked.

The problem is not necessarily due to something that has changed recently, there may be a problem that has not manifested until now. I suggested that you look for the cause of the problem because this is Open Source and you seem to be motivated to find a fix.

···

--
Robin Dunn
Software Craftsman