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.