Re: BonoboUnknown & G_SIGNAL_TYPE_STATIC_SCOPE



hm, just to clarify a few things...

On 2 Jan 2002, Owen Taylor wrote:

> There is a contract in the way that GObject works in that if you
> provide a boxed type (or anything but a G_TYPE_POINTER) as an argument
> to a signal, it will persist until the emission is over unless you |
> in G_SIGNAL_TYPE_STATIC_SCOPE. If you stick the G_SIGNAL_TYPE_STATIC_SCOPE
> into BONOBO_TYPE_UNKNOWN you are violating this contract, not
> to mention g_assert() problems and the fact that you are forcing
> people to use get_type() in some cases.
> 
> People just have to learn to always use G_SIGNAL_TYPE_STATIC_SCOPE;
> doing it magically for one type doesn't help.

also, by convention, NAMESPACE_TYPE_OBJECT should reveal the _type id_ of an
object, ORing the type id with any numbers will not reveal a valid id
again.
the G_SIGNAL_TYPE_STATIC_SCOPE flag is supported only by the GSignal code as
its namespace indicates,
so a BONOBO_TYPE_UNKNOWN with G_SIGNAL_TYPE_STATIC_SCOPE is not a valid/
usable type id anywhere else than for g_signal_new.

> > > Sheesh, can't we add a bit of magic / evil C API so that Bonobo_Unknown_ref()
> > > only holds one reference to the remote object for all local references?
> > > This doesn't seem like the only place people are going to get bitten.
> > 
> > 	No - sadly not; the way the reference counting convention was
> > constructed does not allow us to distinguish when a Bonobo reference is
> > being handed, and when a 'weak' or CORBA only reference is being passed.
> > Thus it is not possible to reliably cache the Bonobo_Unknown reference
> > count - sad but true.
> 
> Hmm, yeah, forgot about that problem. 

we discussed this some with michael and stefan westerfeld last guadec, since
bonobo is by far not the only scenario that brings this up, basically the
only reliable ways are:

1) use pure proxy types remotely
2) keep a list of remote object references with the connections (sockets)
   as they come in. basically, this is making the incomming connection
   the entity owning remote references, so once it vanishes things get
   cleared up correctly.

for (1), C is unfortunately not good enough to make this conveniently
transparent, and to implement (2) orbit (CORBA) doesn't offer the required
hooks (as far as i'm told).

> This particular problem could still be helped with some Bonobo-internal
> magic ... since we can't transfer ownership of a boxed object to
> a different process we could keep locally a count of "non-transferrable"
> reference counts that proxy to a single remote reference count
> and use that for the copy/free functions for BONOBO_TYPE_OBJECT.

well, yes and no, one of the things i've got on my list for some time now
is writing a mechanism to mirror/sync types remotely and transfer instances
across process borders. this has many potential uses, such as
- generating automated GObject bindings from IDL systems that provide
  sufficient introspection mechanisms (CORBA, MCOP)
- doing object system manipulations (like GUI builders) remotely
- dispatching notification signals remotely or asyncronously (kinda like
  an event queue)
- make living objects persistent

the basic idea is that for the supported types (e.g. some BOXED
implementation) the "only" additional thing that is to be implemented
is a serializer/deserializer. plus of course a shit load of magic (but
generic) code to glue two type systems together ;)


> > 	Either way - I remain interested as to the possible merit of the
> > default behavior copying all the effectively 'const in' arguments to
> > these signals - for what is this useful ?. 
> 
> The idea is, is that without G_SIGNAL_TYPE_STATIC_SCOPE I can write:
> 
> gtk_style_set_font (GtkStyle *style, GdkFont *new_font)
> {
>   g_signal_emit_by_name (style, "about-to-set-font",
>                          style->old_font, new_font);
> 
>   style->font = g_object_ref (new_font);
>   g_object_unref (old_font);
>                             
> }
> 
> Which would be improper normally since if set_font() was
> called again, the old_font parameter would be come invalid.
> It's adding some automatic protection against reentrancy
> problem.
> 
> The idea is that before you add STATIC_SCOPE you need to
> analyze all your emit functions and make sure the parameter
> is not going be freed for the duration of the emission.
> 
> Yes, it's making things more complicated for the sake of making
> them "easier"; I fought this for a few months, but I had to
> go on to other things eventually.

this wasn't actually an issue we faught over, one of the earlier GSignal
implementations didn't do always-copy. but it worked only to some extend.
the main problem is that once you hit a reentrancy situation where
stuff from earlier in the call stack is destroeyed prematurely, it's
a) hard to debug for mere mortals, since the type and signal stuff is quite
   complex already, and since quite some reentrancy cases are triggered
   only rarely (especially if you get into the realms of remote callbacks
   coming in while you're calling out)
b) tedious to fix. basically every time someone reports a reentrancy
   bug for a signal you're emitting from your library, you get to make
   things emission safe on your own (add copyies/refs around the signal
   emission)
in the long run, this would have left us with a much less stable and
way harder to debug platform, and additionally with loads of problems
in the language binding area, which btw was one of the motivations for
kenelsons closure proposal.

for non-ref-but-copy types, the current system presents some overhead,
and in-place modifications don't work on them ether, which is basically
why we added
G_SIGNAL_TYPE_STATIC_SCOPE - flag signal argument types with this if you
                             can give a 100% guarantee that instances thereof
                             remain persistent across all signal emissions
                             they are used in.
                             this is _only_ usefull for non ref-counted,
                             value-copy types though, in fact, the GObject
                             value implementation blatanly ignores this
                             flag and still calls g_object_(un)ref() upon
                             signal emissions. (maybe it should actually
                             puke to point out to people that they're doing
                             something senseless)
(owen, i think our actuall matter of argument was the API portion of ORing the
type id with G_SIGNAL_TYPE_STATIC_SCOPE)

> > Similarly I'm intrigued by the amount of locking going on in
> > gsignal.c - as I'm given to understand that signal emission is not
> > thread safe - [ is this the case ? ].

signal emissions on different objects are definitely thread safe (provided
their methods don't modify unprotected non-instance data, such as
instance->class->foo), the signal system doesn't actually impose any
additional restrictions on object users.

it's just that, for the same reason you can't use the same object without
lock protection within different threads simultaneously (gtk_label_set_text(X)
called twice on X simultaneously will simply mess up your instance contents,
just like g_object_(un)ref() will mess up your ref count), you can't emit
signals on the same object simultaneously (the signal/closure system calls
g_object_(un)ref() and signal handlers usually call functions like
gtk_label_set_text()).

> No signal emission on a single object from multiple threads is not
> fully thread safe (though looking at gsignal.c it seems a lot closer
> than I thought), but:
> 
>   - GSignal stores the signal handlers in a global hash by
>     instance location so you can have signals on non-GObjects.
>     (Yep, though they have to have a destructor to call
>     g_signal_handlers_destroy(), so not GdkRectangle...)

the global handler table is protected, actually the following scenario should
work perfectly well (because "connecting" handlers doen't modify and instance):

threadA:
signal_connect (A, "foo", func1);
g_signal_emit (A, "foo");
// in emission
  func1();
threadB:
signal_connect (A, "foo", func2);
threadA:
// in emission, func1() terminates
  func2();
// emission done.

about signals on non-objects, the story isn't actually that easy.
the signal system demands an "instantiatable" type, that is a type
of which instances can be created and that have a common class
structure (i think G_TYPE_FLAG_INSTANTIATABLE is already sufficiently
documented), and as of today, there's just one fundamental instantiatable
type, which is G_TYPE_OBJECT.

>   - Just as we used to have problem with GType being an integer,
>     we have problems with signal ids being integers.

nope, for the type system, the basic idea is:
1) once upon a time, the type data was setup (written)
2) from then on, people only used (read) it happily ever after
i.e. the time spent in writing type data tends towards 0 with increasing
runtime, which is why using a read/write lock makes sense there.

the signal system is a whole different (alive!) beast, the majority of
data (handlers, closures, emissions, invocation hints) it uses/accesses
and manages is modifiable and changes frequently, which is why there's
one global mutex protecting it all.
going id->pointer for signal ids wouldn't really benefit us because the
functions that acquire the lock just to read out the (supposedly const)
signal nodes are infrequently used and pretty sparse (basically the
handfull of introspection functions we have).

> 
> Regards,
>                                         Owen
> 

---
ciaoTJ




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