Re: Weak references for GObject



Owen Taylor writes:
>[We don't want to have to do this:]
> object = g_weak_ref_deref (blah->weak);
> [ do something with object ]
> g_object_unref (object);

Okay, and I totally agree, and I realize that I made a mistake about my
original formulation of how softrefs (or weakrefs) might work in GTK,
without that particular efficiency problem. Let me attempt to correct
my mistake:

>[I]n GTK+ / C terms, a weak reference looks like:
>  
> A ===========> B
> <- notifier -
>
>So, if you do post-morten notification of weak references, you have a
>time period when A has a invalid pointer but hasn't been notified of
>it.

So, the hallmark of the sort of weak reference system I've been outlining
is that code never gets a chance to even see a pointer to an object that
has been finalized. In order for that to be the case, the (de)allocator
needs to actually know the addresses that hold soft references, so it can
atomically null them out at the same time that it queues up the finalizers
for call at a later point in time.

The prototypes would actually end up looking something like this:

    void g_soft_ref   (GtkObject        **object,
                       GtkDestroyNotify  notify,
                       gpointer          data);
    void g_soft_unref (GtkObject        **object,
                       GtkDestroyNotify  notify,
                       gpointer          data);

(I.e., "GtkObject **object" and not "GtkObject *") That is, you could only
make a soft reference out of an object reference that actually lives in
memory (and it better not be registered on the stack past the end of a
particular function call).

To use a soft reference, you'd need to do a g_object_ref()-like operation
on it, to guarantee that it couldn't get deallocated in the middle of an
operation. However, you'd also have to be prepared for the possibility that
the object had been nulled out and you just haven't yet been notified. If
you allow multiple threads to be running GTK code simultaneously, then this
check *has* to happen inside the GTK internals, since there is a possible
race in this code:

    if (soft != null) g_object_ref (soft);

where the test passes but right at that point some other thread goes and
frees the object pointed to by soft.

(Note that Java has an analogous situation, where it's possible to directly
"notice" a nulled-out weak ref before the notifier-equivalent gets a chance
to run. In the case of Java, the notifier-equivalent is a thread that's
servicing a ReferenceQueue.)

In that case, there still would have to be a special soft-dereference
operation, something like:

    GtkObject *g_soft_deref (GtkObject *object);

which would be defined to atomically check the object for non-null-ness
and increment its refcount if so, or otherwise return null. You could use
normal gtk_object_unref() when you're done using the object:

    strong = g_soft_deref (soft);
    if (strong) { 
        ... operate on it ... 
        g_object_unref(strong);
    }
    else { 
        ... deal with it having been deallocated ... 
    }

But you'd need to explicitly inform the system when you no longer were keeping
a particular soft reference, by calling g_soft_unref():

    blah->soft = strong;
    g_soft_ref(&blah->soft, ...);
    ... do stuff ...
    g_soft_unref(&blah->soft, ...);

I'm not sure if this counts as too inefficient in your book. I think the
main issues are (1) the space needed to store pointers to all soft
reference pointers, (2) the extra space needed to keep make the
notification queue live longer than the objects being freed, and (3) the
time needed in the (de)allocator to null out the pointers and do the
queueing (as opposed to just calling the finalizers, as is currently done).

Okay, one question before I close. Back to this:

> object = g_weak_ref_deref (blah->weak);
> [ do something with object ]
> g_object_unref (object);

If this isn't what's currently done, how do you guarantee that a
weakly-pointed object doesn't get freed out from under you in the middle of
an operation? Given multiple threads, it's easy to imagine dangerous
scenarios, but even in the single-threaded case, isn't there the danger of
an inner call freeing a weakly-pointed object unbeknownst to the caller? Do
you have to write code like this?:

    if (weak != null) {
        ... stuff that isn't function calls ...
        some_call_that_can_affect_weak();
        if (weak != null) {
            ... etc ...
        }
    }

Cheers,

-dan

PS: I just upgraded to 1.4, and I'm pleased as punch so far. Thanks to all
of you; you guys rock!




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]