Hi, I've been looking into the details of how GObjects are destroyed, hoping to solve <https://bugzilla.gnome.org/show_bug.cgi?id=665211>, in which disposing a global singleton GDBusConnection in one thread races with reffing and using it in another thread, causing resurrection and a crash if both happen. (Advice from GObject gurus on that bug would be very much appreciated.)
While doing so I noticed these bits of documentation: http://developer.gnome.org/gobject/stable/howto-gobject-destruction.html > It is possible that object methods might be invoked after dispose is > run and before finalize runs. GObject does not consider this to be a > program error: you must gracefully detect this and neither crash nor > warn the user. http://developer.gnome.org/gobject/stable/gobject-memory.html#gobject-destruction-table > The object is also expected to be able to answer client method invocations > (with possibly an error code but no memory violation) until finalize > is executed. http://developer.gnome.org/gobject/stable/gobject-memory.html#gobject-memory-cycles > Object methods should be able to run without program error (that is, > without segfault :) in-between the two phases. I'm pretty sure typical real-world code doesn't follow these (mine certainly doesn't); in general it isn't possible to do anything useful in an object after its state and the objects it uses have been discarded, however much someone might want to ref it halfway through dispose. When this documentation says "object method" or "client method", does that refer to semi-internal GObject virtual functions like get_property() and dispose(), or to methods defined by the object like g_io_stream_close(), or both? I did a quick survey of GLib (in recent master) to get an idea of how widely the rules I quoted are followed. Here are the cases I spotted where they aren't: * GDBusConnection * drops its reference to the GDBusWorker * start_message_processing will assert * flush_sync will assert * close will assert * send_message_unlocked will segfault * dispatching incoming messages ceases to happen * GFileEnumerator * drops its ref to contained * get_container will return NULL (which it can't normally), potentially crashing its users * GFilterOutputStream * drops its reference to the base_stream * get_base_stream will return NULL, which it can't normally * write, flush, close will critical (or segfault if checks are disabled) * GInetSocketAddress * dispose() isn't idempotent (I'll open a bug) * drops its ref to address * get_address will return NULL, which it can't normally * get_family, get_native_size, to_native will critical or segfault GDBusProxy appears to keep working correctly if resurrected from dispose(), which surprised me, but that's only because it doesn't unref its connection until finalize() (which is wrong, strictly speaking). GIOStream assumes its sub-streams still exist in *its* dispose, which breaks the "chain up last" pattern unless you skip closing the sub-streams until finalize(). If a method is invoked after an object is disposed, which of these is it meant to do? * silently do nothing, return a dummy value if it returns a value * potentially crashes callers that were expecting, e.g., a non-NULL return * silent failure to do what was asked seems non-ideal * warn/critical and behave as above * as above, but at least it's visible; on the other hand it contradicts the documentation * raise a GError if possible * not always possible * not really recoverable - what's the user going to do about it? * none of the above, it Can't Happen * contradicts the documentation, but is by far the easiest to explain! Any thoughts? S _______________________________________________ gtk-devel-list mailing list [email protected] http://mail.gnome.org/mailman/listinfo/gtk-devel-list
