Mailing List Archive

wierdness with pythonwin dialogs, threads & refcounts
I have found strange behaviour (strange to me at least) when creating
a pythonwin dialog on a separate thread created using the standard
python thread module. The dialog object is never deleted since the
thread is keeping references to it.

The following example uses dlgtest.py that comes with the pythonwin
distribution:

#------------- 8< ----------------------------
C:\dev\PYTHON\Pythonwin\pywin\DEMOS>python
Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> from dlgtest import TestDialog
>>> import sys, thread
>>> def test():
... t = TestDialog()
... print "refs:", sys.getrefcount(t)
... t = None
...
>>> test()
Edit text changed!
refs: 2
>>> thread.start_new_thread( test, () )
>>> refs: 6
#------------- 8< ----------------------------

As you can see from the above, creating the dialog in the thread gives
it an extra 4 references compared with the non-threaded invocation. Is
this a bug, or is the thread module incompatible with pythonwin? If so
how would I do the equivalent using pythonwin threads? I have read
the available docs, but it is still pretty opaque to me.

Any help would be greatly appreciated.

Dave K
wierdness with pythonwin dialogs, threads & refcounts [ In reply to ]
Dave Kirby writes:

> I have found strange behaviour (strange to me at least) when
> creating a pythonwin dialog on a separate thread created using the
> standard python thread module. The dialog object is never deleted
> since the thread is keeping references to it.
>
> The following example uses dlgtest.py that comes with the pythonwin
> distribution:
>
> #------------- 8< ----------------------------
> C:\dev\PYTHON\Pythonwin\pywin\DEMOS>python
> Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on
> win32 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
> >>> from dlgtest import TestDialog
> >>> import sys, thread
> >>> def test():
> ... t = TestDialog()
> ... print "refs:", sys.getrefcount(t)
> ... t = None
> ...
> >>> test()
> Edit text changed!
> refs: 2
> >>> thread.start_new_thread( test, () )
> >>> refs: 6
> #------------- 8< ----------------------------
>
> As you can see from the above, creating the dialog in the thread
> gives it an extra 4 references compared with the non-threaded
> invocation. Is this a bug, or is the thread module incompatible with
> pythonwin? If so how would I do the equivalent using pythonwin
> threads? I have read the available docs, but it is still pretty
> opaque to me.

First, there's no incompatibility with the thread module in
Pythonwin.

Second, you should use threading.py, not the low level thread module
(which is very, very primitive). Or you can use
win32process.beginthreadex which is a wrapper around MFC threads.
This lets you manipulate priorities. (As of a couple years ago, there
was a small memory leak involved with using MFC threads, however).

As for the source of your questions - without doing things in a debug
build of python / Pythonwin, it's almost impossible to tell what's
going on, or even if there really is a problem.

Using sys.getrefcount increments the refcount. Printing something in
the interactive interp binds the thing to a var named "_" (to
optimize looking at it again). Printing to stdout in Pythonwin
involves all kinds of hidden manipulations.

If you want to know whether you're leaking TestDialogs you could code
a loop that creates threading.Thread's that show a TestDialog and
then joins the Thread. Run it for as many iterations as you can
stand, and watch the process in TaskManager.

hope-you're-not-prone-to-Carpal-Tunnel-Syndrome-ly y'rs



- Gordon
wierdness with pythonwin dialogs, threads & refcounts [ In reply to ]
On Thu, 17 Jun 1999 09:21:51 -0500, "Gordon McMillan"
<gmcm@hypernet.com> wrote:

>Dave Kirby writes:
>
>> I have found strange behaviour (strange to me at least) when
>> creating a pythonwin dialog on a separate thread created using the
>> standard python thread module. The dialog object is never deleted
>> since the thread is keeping references to it.
>> [example snipped]
>
>First, there's no incompatibility with the thread module in
>Pythonwin.
>
>Second, you should use threading.py, not the low level thread module
>(which is very, very primitive). Or you can use
>win32process.beginthreadex which is a wrapper around MFC threads.
>This lets you manipulate priorities. (As of a couple years ago, there
>was a small memory leak involved with using MFC threads, however).

I have just tried both the threading.py and beginthreadex with the
same results - vis:

[carrying on from the previous example]
>>> import win32process
>>> test()
Edit text changed!
refs: 2
>>> x = win32process.beginthreadex( None, 10000, test, (), 0 )
>>> refs: 6
>>> import threading
>>> thread = threading.Thread(target = test)
>>> thread.start()
>>> refs: 6

Again the refcount is 6 when test is run from a separate thread, but 2
when run in the main thread.


>
>As for the source of your questions - without doing things in a debug
>build of python / Pythonwin, it's almost impossible to tell what's
>going on, or even if there really is a problem.
>
>Using sys.getrefcount increments the refcount. Printing something in
>the interactive interp binds the thing to a var named "_" (to
>optimize looking at it again). Printing to stdout in Pythonwin
>involves all kinds of hidden manipulations.
>
>If you want to know whether you're leaking TestDialogs you could code
>a loop that creates threading.Thread's that show a TestDialog and
>then joins the Thread. Run it for as many iterations as you can
>stand, and watch the process in TaskManager.

I am pretty sure there is a real problem here. Whatever the hidden
manipulations are it shouldnt make any difference which thread it is
running in. I discovered this problem because the __del__ method was
not being called for the dialog class in the program I am writing.
When I moved it to the main thread it was destroyed ok.

I will play around with this some more and try to narrow it down
further, but I am not intimate with either the internals of pythonwin
or Windows threads, so don't hold your breath. One thought that did
occur to me is that the python thread/threading modules and
win32process.beginthreadex are all creating worker threads so there is
no message processing being done to clean up the dialog when it is
closed. I will test this hypothesis as soon as I have figured out how
to create GUI threads in pythonwin.

Regards,

Dave K
wierdness with pythonwin dialogs, threads & refcounts [ In reply to ]
There could be a number of leaks in Pythonwin, but I dont think dialogs do.

The ref count is likely to be 6, as the pywin.mfc.Dialog base class hooks a
number of commands and messages. Each of these hooks is a circular
reference, as a bound method is saved away. These references are deleted
when a window recieves the WM_DESTROY message.

So in a nutshell, there is nothing obvious as to why the dialog is leaking.
The very first step would be to ensure it does not leak without threads.

Otherwise, it may come down to me tracking this down, as there are some
complicated interactions with MFC going on...

Finally, there is a "threadedGui.py" (or something) that should demonstrate
how to create "native Pythonwin threads". However, now that we have the
threading module, there really isnt any real advantage to using Pythonwin
threads over threading.

Mark.

Dave Kirby wrote in message <3768b75e.5469224@NNRP.UK.INSNET.NET>...
>
>I have found strange behaviour (strange to me at least) when creating
>a pythonwin dialog on a separate thread created using the standard
wierdness with pythonwin dialogs, threads & refcounts [ In reply to ]
Mark Hammond writes:

> There could be a number of leaks in Pythonwin, but I dont think
> dialogs do.
>
> The ref count is likely to be 6, as the pywin.mfc.Dialog base class
> hooks a number of commands and messages. Each of these hooks is a
> circular reference, as a bound method is saved away. These
> references are deleted when a window recieves the WM_DESTROY
> message.
>
> So in a nutshell, there is nothing obvious as to why the dialog is
> leaking. The very first step would be to ensure it does not leak
> without threads.
>
> Otherwise, it may come down to me tracking this down, as there are
> some complicated interactions with MFC going on...

I played with this enough to find that he's onto something. Without
any threading, the dialog gets a WM_DESTROY and goes thru __del__.
When executed from a threading.Thread, it never goes thru OnDestroy.
I tried doing DoModal myself, then un-hooking the messages myself.
No luck. Perhaps because there's no parent window, and the trhead
does not have a Windows message queue?

> Dave Kirby wrote in message <3768b75e.5469224@NNRP.UK.INSNET.NET>...
> >
> >I have found strange behaviour (strange to me at least) when creating
> >a pythonwin dialog on a separate thread created using the standard
>
>
>
>
> --
> http://www.python.org/mailman/listinfo/python-list

- Gordon
wierdness with pythonwin dialogs, threads & refcounts [ In reply to ]
On Thu, 17 Jun 1999 19:15:42 -0500, "Gordon McMillan" >
>I played with this enough to find that he's onto something. Without
>any threading, the dialog gets a WM_DESTROY and goes thru __del__.
>When executed from a threading.Thread, it never goes thru OnDestroy.
>I tried doing DoModal myself, then un-hooking the messages myself.
>No luck. Perhaps because there's no parent window, and the trhead
>does not have a Windows message queue?

Yep, I have got it to work correctly by replacing the
thread.start_new_thread function with my own version using the
WinThread class:

#------- 8< -----------------------------
from pywin.mfc.thread import WinThread

class MyThread(WinThread):
def __init__(self, fn, args ):
WinThread.__init__(self)
self.fn = fn
self.args = args

def InitInstance(self):
apply( self.fn, self.args )
self.PumpIdle()
self.close()

def start_win_thread( fn, args ):
thread = MyThread( fn, args )
thread.CreateThread()
#------- 8< -----------------------------

I am still climbing the learning cliff of Win32 threads so I dont know
if it is the approved way of doing it, but it seems to work well
enough for my purposes.

Dave K
wierdness with pythonwin dialogs, threads & refcounts [ In reply to ]
Dave Kirby wrote in message <376e53a0.24292290@nnrp.uk.insnet.net>...

>I am still climbing the learning cliff of Win32 threads so I dont know
>if it is the approved way of doing it, but it seems to work well
>enough for my purposes.

Well, seeing as that is the only way that works, I would say it is
"approved" :-)

The MFC threading stuff is almost identical to a Python thread created with
the thread or threading modules. Indeed, to Python, they are identical.
The only difference I can see if that we have a real MFC thread backing this
thread.

At some stage I will try and sort this out so standard Python threads also
work correctly...

Mark.