--- GObject.xs~ 2005-10-11 13:27:10.000000000 -0400 +++ GObject.xs 2005-10-11 17:34:36.000000000 -0400 @@ -433,6 +433,12 @@ return 0; /* not reached */ } +/* Manipulate a pointer to indicate that an SV is undead. Relies on + * pointers being word-aligned, as most (all?) are */ +#define IS_UNDEAD(x) (GPOINTER_TO_UINT(x) & 1) +#define MAKE_UNDEAD(x) GUINT_TO_POINTER(GPOINTER_TO_UINT(x) | 1) +#define REVIVE_UNDEAD(x) GUINT_TO_POINTER(GPOINTER_TO_UINT(x) & ~1) + /* * this function is called whenever the gobject gets destroyed. this only * happens if the perl object is no longer referenced anywhere else, so @@ -445,14 +451,25 @@ return; #ifdef NOISY - warn ("gobject_destroy_wrapper (%p)[%d]", obj, SvREFCNT (obj)); + warn ("gobject_destroy_wrapper (%p)[%d]", obj, + SvREFCNT ((SV*)REVIVE_UNDEAD(obj))); #endif + obj = REVIVE_UNDEAD(obj); sv_unmagic (obj, PERL_MAGIC_ext); /* we might want to optimize away the call to DESTROY here for non-perl classes. */ SvREFCNT_dec (obj); } +static void +update_wrapper (GObject *object, gpointer obj) { + /* printf("update_wrapper [%p] (%p)\n", object, obj); */ + g_object_steal_qdata (object, wrapper_quark); + g_object_set_qdata_full (object, + wrapper_quark, + obj, + (GDestroyNotify)gobject_destroy_wrapper); +} =item SV * gperl_new_object (GObject * object, gboolean own) @@ -552,10 +569,9 @@ /* attach magic */ sv_magic (obj, 0, PERL_MAGIC_ext, (const char *)object, 0); - /* this is the one refcount that represents all non-zero perl - * refcounts. it is just temporarily given to the gobject, - * DESTROY takes it back again. this effectively increases - * the combined refcount by one. */ + /* The SV has a ref to the C object. If we are to own this + * object, then any other references will be taken care of + * below in take_ownership */ g_object_ref (object); /* create the wrapper to return, the _noinc decreases the @@ -566,18 +582,15 @@ sv_bless (sv, stash); /* attach it to the gobject */ - g_object_set_qdata_full (object, - wrapper_quark, - (gpointer)obj, - (GDestroyNotify)gobject_destroy_wrapper); - - /* the noinc above is actually the trick, as it leaves the - * attached object's refcount artificially one too low, - * so DESTROY gets called when all handed-out refs are gone - * and we still have the object attached. DESTROY will - * then borrow the ref added by g_object_ref back, and - * thus will eventually trigger gobject destruction, which - * in turn will trigger perl wrapper destruction. */ + update_wrapper (object, obj); + /* printf("creating new wrapper for [%p] (%p)\n", object, obj); */ + + /* the noinc is so that the SV (initially) exists only as long + * as the perl code needs it. When the DESTROY gets called, we + * check and see if the SV is the only referer to the C object, + * and if so remove both. Otherwise, the SV will become + * "undead," to be either revived or destroyed with the C + * object */ #ifdef NOISY warn ("gperl_new_object%d %s(%p)[%d] => %s (%p) (NEW)", own, @@ -588,20 +601,18 @@ } else { /* create the wrapper to return, increases the combined * refcount by one. */ - sv = newRV_inc (obj); - /* Now we need to handle the case of a gobject that has - * been DESTROYed but gets "revived" later. This operation - * does not alter the refcount of the combined object. - * This can only happen if the call with own is not - * the first call. Unfortunately, this is the common case - * for gobjectclasses implemented in perl. - */ - if (object->ref_count == 1 && own) { - g_object_ref (object); - SvREFCNT_dec (obj); + /* if the SV is undead, revive it */ + if (IS_UNDEAD(obj)) { + g_object_ref (object); + obj = REVIVE_UNDEAD(obj); + update_wrapper (object, obj); + sv = newRV_noinc (obj); + /* printf("reviving undead wrapper for [%p] (%p)\n", object, obj); */ + } else { + /* printf("reusing previous wrapper for %p\n", obj); */ + sv = newRV_inc (obj); } - } #ifdef NOISY @@ -860,6 +871,12 @@ g_object_steal_qdata (object, wrapper_quark); } else { SvREFCNT_inc (SvRV (sv)); + if (object->ref_count > 1) { + /* become undead */ + SV *obj = SvRV(sv); + update_wrapper (object, MAKE_UNDEAD(obj)); + /* printf("zombies! [%p] (%p)\n", object, obj);*/ + } } #if GPERL_THREAD_SAFE if(perl_gobject_tracking)