Last Night I committed a couple of bug fixes:

- GUI.h change order of instructions in PERL_FREE macro to prevent crashes (Trackers 1243378 and 1248578)

I hope this fix will eliminate most of the crashes/warnings hat happen on program termination. If you have any example code that still crashes or warns when your script ends, then I be interested in seeing it. Details of the exact problem below.

- GUI.xs change logic in all message loops (Dialog, DoEvents, DoModal) to prevent memory leak (Tracker: 1201190)

All 3 message loops were leaking memory whenever one of the windows functions TranslateMDISysAccel, TranslateAccelerator or IsDialogMessage processed and dispatched a message.

- Listbox.xs add documentation to differentiate between SetCurSel and SetSel (Tracker 1177898)



Crashes on Exit - explanation of the problem and the (simple) fix
------------------------------------------------------------------

Tracker 1243378 gave me some code that reliably crashed on exit in my environment (AS Perl 5.8.7, Win98, MSVC 6)

The code was approximately:

[1] my $mw = Win32::GUI::Window->new( ... );
[2] my $re = $mw->AddRichEdit( ... );

[3] $re->Change( -onMouseMove => sub {my $text = $re->Text() } );

Some notation:
- The Win32::GUI::Window object will be referred to as WO
- The Win32::GUI::RichEdit object will be referred to as RO
- The anonymous sub in line [3] will be referred to as AS

Here's what was happening:
Line [1] creates TWO, with ref count 1 (from $mw)
Line [2] creates RO, with ref count 2 (from $re and WO)
Line [3] creates AS, with ref count 1 (from storing in hvEvents hash in perlud for RO)
Line [3] increases ref count of RO to 3 (from closure on $re)

Now, when the script finishes the following happens:
$mw and $re go out of scope.
WO ref count goes to 0, RO ref count goes to 2

DESTROY gets called on WO, as it's ref count is 0:
- all child object refs are removed from WO, leading to RO ref count going to 1 (from the closure referenced in its own hvEvents hash) - DestroyWindow() is called on WO window handle. DestroyWindow() sends WM_DESTROY messages to each of the top level window's children, before sending WM_DESTROY to the top-level window itself. - WM_DESTROY received by RO, calls PERLUD_FREE, which among other things clears the hvEvents hash. This reduces the ref count of RO to 0, as AS ref count goes to 0, resulting in RO's DESTROY method being called before PERLUD_FREE finishes - RO's destroy method removes any child window references from RO (none in this case), then calls DestroyWindow() on RO window handle, resulting in RO receiving WM_DESTROY again, and calling PERUD_FREE a second time. PERLUD_FREE gets a pointer to perud, and tries to free stuff that may already have been freed by the first call, and frees stuff that the first call has not yet freed, but will try to once control returns there.

The fix was simply to make PERUD_FREE set the pointer to perud to NULL before freeing the memory used, so that the second PERUD_FREE sees that perud has already been tidied-up and does nothing.

Regards,
Rob.

Reply via email to