Re: GObjectClass.dispose and bringing objects back to life



On Fri, 02 Dec 2011 at 15:55:18 +0900, Tristan Van Berkom wrote:
> Yes, real-world well-written GObjects *must* not crash after being disposed,
> code that crashes because apis are called after dispose time are bugs,
> and you should fix them as specially if you have possible circular object
> references.

(I'm not trying to justify the crash here; I'm trying to figure out which
of the other undesirable things that could happen instead of crashing
would be any better, so we can fix them.)

As with the documentation, it's easy to say that, but if dispose is meant to
drop all object references (which, AIUI, it is), many objects just can't work
after being disposed; so the only question is which way of not working
is least harmful. Which of the alternatives would you prefer? (This is a
question for all the GObject ninjas on this list, not just Tristan.)

For instance, a GDBusConnection (currently my pet example because I've spent
several weeks staring at it) has a GIOStream which it uses to send and receive
the actual messages[1]. After it's been disposed (even if it gets resurrected),
send_message() can either:

A. raise a GError which is purely an internal condition, so seems like an
   abuse of GError

B. g_warning() or g_critical() then fail to provide its documented behaviour
   (drop the message on the floor)

C. silently fail to provide its documented behaviour (drop the message on
   the floor)

D. abort or crash

"Send the message, as documented" isn't an option, because the ability to
do that has just been disposed of!

For methods that (under normal circumstances) can't fail, (A) becomes more
problematic, because these methods have no way to indicate an error.
Is the solution to have a GError on every method, even ones that "can't fail"?

(At the moment the GObject answer is generally "no, only on methods that
can be expected to fail at runtime" - which is useful, because it means you
don't have to waste time writing error-handling for things that can't
actually fail.)

For some methods that return a value and can't normally fail, (B) and (C)
also become problematic, because these methods have no documented
"error" return value, and returning an undocumented value might crash callers
(in particular, if documented to return a non-NULL pointer, and the only
possibility after dispose() is to return NULL). Is the solution to document
all methods of this sort as "can return NULL if the object has been disposed"
and require callers to watch out for it?

(Again, it's useful for as many things as possible to be "can't fail" so you
don't have to handle hypothetical errors that can't actually happen.)

>   o Most resources are normally freed in GObjectClass.finalize()
> 
>   o References to other GObjects are broken in dispose

As our libraries get more object-oriented, these look like incompatible rules?
Most of GDBusConnection's resources *are* GObjects.

> The bug Ryan is referring to in his reply seems to imply that people
> should be allowed to use 'weak references' for this... I dont see how
> that could be very safe... personally I would probably prefer using a
> "destroy" signal run from the dispose handler for such notifications.

For the "cached singleton" case that Ryan describes in the bug (which is
exactly what happened in GDBus), this wouldn't be enough: by the time
the dispose vfunc decided to send the destroy signal, it's too late for the
cache to not give someone a reference (it may have already done so), and
also too late for the dispose vfunc to stop disposing.

I suppose if the dispose vfunc was allowed to check the object's refcount
(which is officially private, at the moment), it could stop before chaining
up to its parent; this would be enough for GDBusConnection, but not for
any subclassable type, since the subclass' dispose has already been run and
you can't "take it back".

    S

[1] Actually it has a GDBusWorker which interacts with the stream in another
    thread, but the simplified version above is enough to get the general
    idea.


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