Re: GObjectClass.dispose and bringing objects back to life

2011-12-06 Thread Stefan Sauer
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

2011-12-04 Thread Emmanuele Bassi
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

2011-12-04 Thread Morten Welinder
 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

2011-12-04 Thread David Nečas
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-04 Thread Tristan Van Berkom
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

2011-12-04 Thread Benjamin Otte
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

2011-12-04 Thread Morten Welinder
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

2011-12-02 Thread Dan Winship
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

2011-12-02 Thread Simon McVittie
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

2011-12-01 Thread Simon McVittie
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

2011-12-01 Thread Ryan Lortie
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

2011-12-01 Thread Tristan Van Berkom
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).