Re: GObjectClass.dispose and bringing objects back to life
On 12/04/2011 05:03 PM, David Nečas wrote: On Sun, Dec 04, 2011 at 01:40:58PM +, Emmanuele Bassi wrote: this is, of course, not true: GNOME is full of badly written GObject code, mostly because it has been written at various stages of the learning process of various people. plus, the documentation is not entirely clear in a lot of places, thus the learning process can (and effectively is) broken. ... objects should obviously not be in a fully functional state after dispose - but they should be in a stable state, so that other objects referencing them from outside of the boundaries of their code space can still hold a valid reference to them, until such time that every reference can be released. So, who thinks this idea is not actually inherently inconsistent please please specify in the documentation, based on your rationale, how are objects supposed to behave in the disposed state. Namely with respect to (not) keeping impossible invariants, returning nontrivial no-longer-available values, methods that normally create or acquire something before returning it and other practical issues. Or say that there can be certain things the object owners may not do with disposed objects. Or anything. This 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. has as, a practical guide, approximately the same value as ‘people should live together in peace’. As it has been said before dispose and finalize get called when an object is getting destroyed. Dispose might get called a few times to break ref-cycles. Finalize is getting called a single time. I'd say most objects can't be revived in a meaning ful way when finalize is hit. I'd say this is also almost true for dispose. One would need to revive them on the first dispose cycle and unreffing anything. Stefan Thank you in advance, Yeti ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
hi; On 4 December 2011 13:23, Benjamin Otte o...@gnome.org wrote: Tristan Van Berkom tvb at gnome.org writes: 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. This is of course not true. Because if it were true, the whole GNOME stack would not be real-world well-written GObject code and I think we all agree that a lot of our code is pretty damn good. this is, of course, not true: GNOME is full of badly written GObject code, mostly because it has been written at various stages of the learning process of various people. plus, the documentation is not entirely clear in a lot of places, thus the learning process can (and effectively is) broken. So why does GObject even have dispose? Why does it ship this obviously wrong documentation? And why do some people believe that objects must survive after a dispose? objects should obviously not be in a fully functional state after dispose - but they should be in a stable state, so that other objects referencing them from outside of the boundaries of their code space can still hold a valid reference to them, until such time that every reference can be released. What we probably also should do is deprecate one of the two virtual functions, so people use the same one to clean up everywhere. this would be insane and wrong on so many levels that it's not even funny to contemplate them. the double-step in the object lifecycle end is necessary in a refcounted system with reference cycles. you cannot just release references to an object on finalize() if there is even the slightest chance of a cycle; any attempt at doing so introduces instabilities, segfaults, or leaks. ciao, Emmanuele. -- W: http://www.emmanuelebassi.name B: http://blogs.gnome.org/ebassi/ ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
What we probably also should do is deprecate one of the two virtual functions, so people use the same one to clean up everywhere. That would be a _really_ bad idea. _finalize gets rid of the last fragments of the object. Since random code could have obtained refs to the object, the object designer can generally not control when this happens. _dispose has two functions: (1) break reference cycles by getting rid of as many objects as it can, and (2) getting rid of externally- visible parts of the object and subobjects it owns. (2) tells you why you can't merge the two methods. Widgets, for example, really need to go away from the screen when you tell them, not whenever something else decides to release a ref. Objects that have open files really need to close them at dispose time, not next Wednesday. Gtk1 didn't have dispose in name, but it had the destroy method instead. Same thing. [...] we [...] know that trying to support objects functioning after dispose is like trying to make your code handle NULL returns I don't think we know that, not do I think it's true. Making _dispose work really comes down to following one simple rule: after _dispose, the object should be as well defined as it was after _init. I.e., anything you free you set to NULL and you don't free anything that was allocated in _init[*]. M. [*] Unless you allocate a new dummy object to put in its place. That wouldn't make much sense unless you have big trouble with circular references. ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
On Sun, Dec 04, 2011 at 01:40:58PM +, Emmanuele Bassi wrote: this is, of course, not true: GNOME is full of badly written GObject code, mostly because it has been written at various stages of the learning process of various people. plus, the documentation is not entirely clear in a lot of places, thus the learning process can (and effectively is) broken. ... objects should obviously not be in a fully functional state after dispose - but they should be in a stable state, so that other objects referencing them from outside of the boundaries of their code space can still hold a valid reference to them, until such time that every reference can be released. So, who thinks this idea is not actually inherently inconsistent please please specify in the documentation, based on your rationale, how are objects supposed to behave in the disposed state. Namely with respect to (not) keeping impossible invariants, returning nontrivial no-longer-available values, methods that normally create or acquire something before returning it and other practical issues. Or say that there can be certain things the object owners may not do with disposed objects. Or anything. This 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. has as, a practical guide, approximately the same value as ‘people should live together in peace’. Thank you in advance, Yeti ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
2011/12/5 David Nečas y...@physics.muni.cz: On Sun, Dec 04, 2011 at 01:40:58PM +, Emmanuele Bassi wrote: this is, of course, not true: GNOME is full of badly written GObject code, mostly because it has been written at various stages of the learning process of various people. plus, the documentation is not entirely clear in a lot of places, thus the learning process can (and effectively is) broken. ... objects should obviously not be in a fully functional state after dispose - but they should be in a stable state, so that other objects referencing them from outside of the boundaries of their code space can still hold a valid reference to them, until such time that every reference can be released. So, who thinks this idea is not actually inherently inconsistent please please specify in the documentation, based on your rationale, how are objects supposed to behave in the disposed state. Namely with respect to (not) keeping impossible invariants, returning nontrivial no-longer-available values, methods that normally create or acquire something before returning it and other practical issues. Or say that there can be certain things the object owners may not do with disposed objects. Or anything. This 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. has as, a practical guide, approximately the same value as ‘people should live together in peace’. It's a gray area that has not needed real clarification with real-world code thus far, and I doubt it really absolutely needs clarification. 'Let us live in peace' is actually pretty much the gist of it afaics. Why is this a non-issue ? Because during the dispose cycle of objects, sane people don't call apis which would ever fail at dispose time. People might however call functions/macros like: - g_object_get_qdata(), i.e. I have some explicitly attached data I want to inspect once while disposing, and for whatever reason I chose not to call g_object_set_data_full() - g_type_name_from_instance() (i.e., sometimes I like to print the type names and reference counts of object hierarchies being destroyed, just to debug my own code) Probably much existing code will fire run-time assertions when asked to actually perform a task at dispose time after an important resource is already discarded. I think the policy right now, and has always been, don't crash because your api has been accessed at dispose time. Do we need a more explicit policy ? Do we need to define what code should do when asked to do something useful after dispose time ? Considering that any sane code does not ever ask an object to do anything useful after dispose time, no... it would just be an exercise of planning for what to do in code that is anyway, not meant to be reached. That said, policy about locking and sharing of objects in MT environments (related more to GIO than pure GObject) _might_ need clarification. Implementing MT-safe singleton objects (as was I think the case in the referred bug report) is probably just damn complex to do, definitely needs to be done with some manual locking around the access to the singleton static pointer and probably just a really bad idea to use in conjunction with GObject weak references. That is anyway, IMO, a separate discussion than the one we are having about policy at dispose time. Cheers, -Tristan ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
I would be somewhat tempted to listen to all the stuff you're saying below. But then I looked at the code you maintain[1], and I realized it doesn't do anything of that. So I'm inclined to think that what you're talking about is more about an ideal world that you wish we all aspired to, but is not in any way related to how people write code in the real world. Benjamin 1: http://git.gnome.org/browse/gnumeric/tree/gnumeric.doap On Sun, Dec 4, 2011 at 3:15 PM, Morten Welinder mort...@gnome.org wrote: What we probably also should do is deprecate one of the two virtual functions, so people use the same one to clean up everywhere. That would be a _really_ bad idea. _finalize gets rid of the last fragments of the object. Since random code could have obtained refs to the object, the object designer can generally not control when this happens. _dispose has two functions: (1) break reference cycles by getting rid of as many objects as it can, and (2) getting rid of externally- visible parts of the object and subobjects it owns. (2) tells you why you can't merge the two methods. Widgets, for example, really need to go away from the screen when you tell them, not whenever something else decides to release a ref. Objects that have open files really need to close them at dispose time, not next Wednesday. Gtk1 didn't have dispose in name, but it had the destroy method instead. Same thing. [...] we [...] know that trying to support objects functioning after dispose is like trying to make your code handle NULL returns I don't think we know that, not do I think it's true. Making _dispose work really comes down to following one simple rule: after _dispose, the object should be as well defined as it was after _init. I.e., anything you free you set to NULL and you don't free anything that was allocated in _init[*]. M. [*] Unless you allocate a new dummy object to put in its place. That wouldn't make much sense unless you have big trouble with circular references. ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
Benjamin Otte o...@gnome.org wrote: But then I looked at [gnumeric] and I realized it doesn't do anything of that. So I'm inclined to think that what you're talking about is more about an ideal world that you wish we all aspired to, but is not in any way related to how people write code in the real world. We take bug reports. We even fix bugs. I cannot really use pointing 400k lines of code for anything, so throw me a bone -- a class name, for example -- and I can actually do something. (And if you just meant to throw a few insults my way, well I hope you feel better.) M. (not quite an angel himself) ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
On 12/02/2011 07:55 AM, Tristan Van Berkom wrote: o References to other GObjects are broken in dispose, and since dispose can run multiple times, care must be taken, i.e.: if (priv-reffed_object != NULL) { g_object_unref (priv-reffed_object); priv-reffed_object = NULL; } or, if you depend on glib = 2.28, g_clear_object (priv-reffed_object); -- Dan ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
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. ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
GObjectClass.dispose and bringing objects back to life
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 gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
On Thu, 2011-12-01 at 19:26 +, Simon McVittie wrote: 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.) See https://bugzilla.gnome.org/show_bug.cgi?id=548954 for pretty much the same issue. Cheers ___ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Re: GObjectClass.dispose and bringing objects back to life
On Fri, Dec 2, 2011 at 4:26 AM, Simon McVittie simon.mcvit...@collabora.co.uk wrote: 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. 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. For instance, in many cases an object might fire a signal during it's dispose cycle, that dispose cycle might run a few times and listeners to a signal fired from dispose might query the disposing object's state. This should absolutely not cause a crash, if it does, you need to make sure that: o Most resources are normally freed in GObjectClass.finalize() o References to other GObjects are broken in dispose, and since dispose can run multiple times, care must be taken, i.e.: if (priv-reffed_object != NULL) { g_object_unref (priv-reffed_object); priv-reffed_object = NULL; } 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 GIO is still pretty young and generally, the age-old rule 'if it's not broken, don't fix it' applies. In other words, I don't think people are hitting these assertions, because (as I mention below...), revived objects are not really revived in the full sense of the word, they are just temporarily available so that everyone who has a reference to them can finish up... If you really have 2 threads which refer to the same object, then naturally they will both hold a reference to it and the object wont die until the last thread is finished using it. If for some odd reason you need a second thread to be notified of the forced destruction of a said object, then it's probably best to use a GtkWidget::destroy paradigm on that object... i.e. both objects do hold a reference and either one releases their reference at destroy signal emission time. 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. * 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).