[wxPython] Bleeding resources on Windoze

I'm currently developing and testing on Python 2.2, wxPython 2.2, Windows
ME.

My application is leaking resources despite taking every precaution I'm
aware of. Unfortunately, my problem is intermittent, and I have not been
able to duplicate the problem in a small sample app.

The troublesome window contains animation, and a paint happens 20 times per
second. I've left it running overnight with no problems. But once in a
while (and only when I'm doing stuff) the GDI resources dry up (the
resources are freed when I close my app).

Perhaps it has something to do with drawing lines after calling
SetClippingRegion. It doesn't seem to happen if I remove the
SetClippingRegion() or if I remove the DrawLine(). However, I can't seem to
reproduce the problem in a simple demo app. I tried various different
experiments like this, but the results seemed inconsistent, so I labeled the
problem "mostly intermittent sort of". :slight_smile:

This made me ask: Is wxPaintDC.__del__() responsible for any necessary
resource cleanup? If so, is it possible that the destructor is being
deferred slightly by the python garbage collector (since python doesn't
promise to delete the dc immediately after it gets dereferenced)?

I tried a nasty little experiment to test this theory: In a small test app,
I stored an extra reference to the paint dc, and then dereferenced it later
in an OnTimer message (thereby simulating delayed destruction of the paint
dc). The test app ran fine until I threw extra paints at it by resizing to
app. It didn't bleed resources in this case, but it did crash after a
while.

    def OnPaint(self, e):
        dc = wxPaintDC(self)
        self.grabby_paint_dc = dc # keep a nasty extra dc reference
         ... do some painting

    def OnTimer(self, e):
        if self.grabby_paint_dc:
            self.grabby_paint_dc = None # delayed removal of nasty extra dc
reference
        else:
            ... do some motion
            self.Refresh()

That was a kinda interesting result, so I added this hack:

    def OnPaint(self, e):
         ...
        dc.__del__()
        dc.__del__ = do_nothing

def do_nothing():
    pass

The test app worked fine with the hack, so I added it to my application.
The resource leakage seemed to go away! Unfortunately the leak came back
again after more testing (no changes to the code). So now I'm back to
square one.

BTW, what exactly does BeginDrawing and EndDrawing do?

Here's a chunk of my code:

    def OnPaint(self, e):
        dc = wxPaintDC(self)

        dx, dy = self.vu_off.GetWidth(), self.vu_off.GetHeight()
        ox, oy = self.origin

        temp_dc = wxMemoryDC()
        temp_dc.BeginDrawing()
        temp_dc.SelectObject(self.vu_temp)

        temp_dc.Blit(0,0, dx,dy, dc, 0,0, wxCOPY, 0)

        if self.active:
            if self.shattered:
                temp_dc.DrawBitmap(self.vu_shattered, 0,0, 1)
            else:
                temp_dc.DrawBitmap(self.vu_on, 0,0, 1)
        else:
            temp_dc.DrawBitmap(self.vu_off, 0,0, 1)

        temp_dc.SetClippingRegion(0,0,dx,49)

        for n in self.needles:
            x,y = n.endpoint()
            temp_dc.SetPen(n.pen)
            temp_dc.DrawLine(ox, oy, ox+x, oy-y)

        temp_dc.DestroyClippingRegion()

        if self.overload:
            temp_dc.DrawBitmap(self.vu_overload, 0,0, 1)
        else:
            temp_dc.DrawBitmap(self.vu_plate, 0,0, 1)

        temp_dc.EndDrawing ()

        dc.Blit(0,0, dx,dy, temp_dc, 0,0, wxCOPY, 0)

I'm currently developing and testing on Python 2.2, wxPython 2.2, Windows
ME.

My application is leaking resources despite taking every precaution I'm
aware of. Unfortunately, my problem is intermittent, and I have not been
able to duplicate the problem in a small sample app.

The troublesome window contains animation, and a paint happens 20 times

per

second. I've left it running overnight with no problems. But once in a
while (and only when I'm doing stuff) the GDI resources dry up (the
resources are freed when I close my app).

Have you tried wxPython 2.3.2.1 and/or a Hybrid version?

[...]

This made me ask: Is wxPaintDC.__del__() responsible for any necessary
resource cleanup?

Yes. DC's are a scarce resource on win9x/ME systems. It will also release
any pens, brushes or etc. that are selected into the DC.

If so, is it possible that the destructor is being
deferred slightly by the python garbage collector (since python doesn't
promise to delete the dc immediately after it gets dereferenced)?

It's been my experience that it happens immediately when the refcount
reaches zero. IIRC, the garbage collector only deals with cycles. I could
be wrong though as it's been a long time since I traced through Python at
that low of a level.

BTW, what exactly does BeginDrawing and EndDrawing do?

It depends on the type of DC and the platform. Basically it is a place
where the DC can do startup and cleanup code if needed, or for
optimizations. I think it is a hold over from win16 days.

Here's a chunk of my code:

I don't see anything obviously wrong, sorry.

···

--
Robin Dunn
Software Craftsman
robin@AllDunn.com Java give you jitters?
http://wxPython.org Relax with wxPython!

Robin Dunn wrote:

> I'm currently developing and testing on Python 2.2, wxPython
2.2, Windows
> ME.
>
> My application is leaking resources despite taking every precaution I'm
> aware of. Unfortunately, my problem is intermittent, and I
> have not been
> able to duplicate the problem in a small sample app.
>
> The troublesome window contains animation, and a paint happens 20 times
> per
> second. I've left it running overnight with no problems. But once in a
> while (and only when I'm doing stuff) the GDI resources dry up (the
> resources are freed when I close my app).

Have you tried wxPython 2.3.2.1 and/or a Hybrid version?

Oops. Actually I am using 2.3.2.1 Hybrid.

> This made me ask: Is wxPaintDC.__del__() responsible for any necessary
> resource cleanup?

Yes. DC's are a scarce resource on win9x/ME systems. It will
also release
any pens, brushes or etc. that are selected into the DC.

> If so, is it possible that the destructor is being
> deferred slightly by the python garbage collector (since python doesn't
> promise to delete the dc immediately after it gets dereferenced)?

It's been my experience that it happens immediately when the refcount
reaches zero. IIRC, the garbage collector only deals with
cycles. I could
be wrong though as it's been a long time since I traced through Python at
that low of a level.

I haven't seen any verified counterexamples to that assertion either.
A statistically anomalous series of leakages and non-leakages sent
me on a false lead looking for refcount issues. Intermittent bugs suck :frowning:

> BTW, what exactly does BeginDrawing and EndDrawing do?

It depends on the type of DC and the platform. Basically it is a place
where the DC can do startup and cleanup code if needed, or for
optimizations. I think it is a hold over from win16 days.

> Here's a chunk of my code:

I don't see anything obviously wrong, sorry.

Thanks anyway for taking a look.

- Ken