[Kde-perl] Memory fault when inserting QTableItem into QTable

Richard Dale Richard_Dale at tipitina.demon.co.uk
Mon Jul 12 07:25:27 CEST 2004


On Monday 12 July 2004 03:03, Ashley Winters wrote:
> --- Richard Dale <Richard_Dale at tipitina.demon.co.uk> wrote:
> [snip]
>
> > PerlQt overrides the takeItem() virtual method as part of its garbage
> >
> > collection tracking, and so when the perl version of takeItem() is
> > called the
> > recently deleted instance is found to not have a corresponding perl
> > instance
> > and another is created. Inside the perl takeItem() method the call
> > 'setAllocated( $_[0], 1 );' means that PerlQt owns this newly created
> >
> > instance owns the (already deleted) QTableItem and is responsible for
> >
> > deleting it.
> >
> > sub Qt::Table::takeItem
> > {
> >     package Qt::_internal;
> >     delete ${ this()->{"hidden children"} } { sv_to_ptr($_[0]) };
> >     delete $_[0]->{"has been hidden"};
> >     setAllocated( $_[0], 1 );
> >     no strict 'refs';
> >     $Qt::AutoLoad::AUTOLOAD = 'Qt::Table::takeItem';
> >     my $autoload = " Qt::Table::_UTOLOAD";
> >     dontRecurse();
> >     $autoload->( $_[0] );
> > }
>
> Hmm...
>
> > On garbage collection of the '$_[0]' passed to takeItem(), PerlQt
> > attempts to
> > delete an already deleted instance and there is a crash. Now I
> > understand
> > what's going on I can start to think of a fix..
>
> The takeItem() family of functions need to know who their caller is.
> When being called from C++, they should bypass all garbage-collection
> voodoo. It's either being called from C++ or from Perl, and whichever
> one is calling it beeds to take responsibility for deleting the object
> thereafter.
>
> When called from Perl, there is already a perfectly valid $item
> (otherwise, how did it get passed to takeItem()?), and we are obligated
> to call setAllocated(1) on the object, as well as remove any
> pre-existing 'hidden children' object remaining in the parent object.
> That way, Perl's garbage collection will properly destroy the object
> (just like the programmer was asking in the first place by calling
> takeItem()).
>
> When called from C++, the item being taken must be setAllocated(0), to
> allow the calling function to move or delete the item as it wishes.
> It's explicitly taking responsibility for the destruction of the object
> AWAY from Perl by calling takeItem(), and we need to honor it.
>
> So, the patch is a simple one-line fix:
>
> -     setAllocated( $_[0], 1 );
> +     setAllocated( $_[0], was_called_from_perl() );
>
> Of course, someone has to write was_called_from_perl(), which needs to
> pass the following test:
>
> package test1;
> use Qt::isa 'Qt::Table';
>
> # This does NOT qualify as being called from Perl if it is called from
> # C++
> sub takeItem { SUPER->takeItem(@_) }
>
> package test2;
> use Qt::isa 'Qt::Table';
>
> # How do you propose to pass this one when called from C++?
> sub takeItem { this->Qt::Table::takeItem(@_) }
>
> Okay, test2 counts as being called from Perl even when
> test2::takeItem() is called from C++. Use SUPER for proper behavior.
>
> Of course, all the takeItem() functions need the above one-line patch.
I think that rather than needing to determine how takeItem() has been been 
called, we need to be able to mark instances as being already deleted in 
QtSmokeBinding::deleted(). The attached patch adds a 'zombie' flag to the 
smokeperl_object struct like this:

struct smokeperl_object {
    bool allocated;
    bool zombie;
    Smoke *smoke;
    int classId;
    void *ptr;
};

So QtSmokeBinding::deleted() no longer unmaps the instance being deleted, it 
just marks it as a zombie. Later in handlers.cpp, smokeperl_free() always 
unmaps the instance, but only deletes it if it is both allocated and not a 
zombie:

int smokeperl_free(pTHX_ SV *sv, MAGIC *mg) {
    smokeperl_object *o = (smokeperl_object*)mg->mg_ptr;

    const char *className = o->smoke->classes[o->classId].className;
    unmapPointer(o, o->classId, 0);
    if(o->allocated && !o->zombie && o->ptr) {
        if(do_debug & qtdb_gc) fprintf(stderr, "Deleting (%s*)%p\n", 
className, o->ptr);
...

This way the perl takeItem()s don't need any change, because is zombie flag 
will always trump an allocated flag.

-- Richard
-------------- next part --------------
A non-text attachment was scrubbed...
Name: perlqtdispose_0.3.patch
Type: text/x-diff
Size: 3980 bytes
Desc: not available
Url : http://mail.kde.org/pipermail/kde-perl/attachments/20040712/35af4db2/perlqtdispose_0.3.bin


More information about the Kde-perl mailing list