GNOME Bugzilla – Bug 617446
Allow custom code to be executed when dispatching a NotifyQueue
Last modified: 2013-08-19 11:43:48 UTC
currently, the ::notify signal is emitted per-property. for complex objects changing different properties at the same time it's possible to compress the notification queue using the freeze...notify+...thaw mechanism. this, however, keeps the ::notify signal emission per-property, to maintain invariants and API guarantees. it is not possible though, from within the ::notify signal emission, to know how many properties are going to be notified, or how many are left. for instance, given a GObjectController class following the design at: http://live.gnome.org/EmmanueleBassi/GController which would allow emitting a ::changed signal with a reference to all the properties that have been changed between a freeze_notify() and a thaw_notify(), there is no way to actually implement bulk notification without sub-classing GObject and change the dispatch_properties_changed virtual function inside the GObjectClass vtable. this obviously lacks generality, as it would only work with a specific sub-type of GObject. rationale: this is a slightly more complex way of dealing with the notification of changes on object properties by it allows: • better performance in case of bulk changes (a single notification function instead of multiple ones); • a reusable MVC design for creating UI elements binding to objects; • a structured property change notification that is detached from the GObject class and allows further changes without requiring changes to the GObject class itself. unfortunately, I have no well defined proposal on how to make this work within the GObject class implementation; creating a ::dispatch-properties-changed signal would overlap the ::notify signal and would confuse the overall GObject design. adding a callback mechanism similar to the weak/toggle references would in theory work: void g_object_add_property_dispatch (GObject *gobject, GPropertyDispatchCallback callback, gpointer user_data, GDestroyNotify notify); though I feel it's a bit on the clunky side of the API. another possible angle of attack would be the ability to retrieve the GObjectNotifyContext from an object and add a callback for the dispatcher to allow third party code to run before/after the GObject dispatcher. finally, a fourth option would be to allow querying a GObject during the ::notify signal emission how many properties are left, so that third party code can connect to the ::notify signal and do something like: static void on_notify (GObject *gobject, GParamSpec *pspec, GController *controller) { static GControllerReference *ref; static guint counter = 0; guint n_props = g_object_get_notify_count (gobject); /* first emission */ if (ref == NULL) ref = g_controller_create_reference (controller, ...); g_controller_reference_add_index (ref, counter, pspec->name); /* last property: emit the ::changed signal */ if (n_props == 0) { g_controller_emit_changed (controller, ref); g_object_unref (ref); /* reset for the next notification emission */ ref = NULL counter = 0; } else counter += 1; } which is slightly more clunky but still feasible.
What about a 'thaw-notify' signal that gets emitted at the top of g_object_notify_queue_thaw or 'notify-thawed' at the bottom of that function? It would be very similar to your 'dispatch-properties-changed' idea but be specific to freezing/thawing batched updates instead of getting emitted for every change. This way it would sort of fit along-side 'notify' in the API instead of rendering it redundant.
I like the idea of a ::thaw-notify signal. a lot, actually.
Created attachment 162539 [details] [review] object: Add ::thaw-notify signal The ::thaw-notify signal is emitted then the NotifyQueue for an object is fully thawed, and the property changes are dispatched. The signal allows arbitrary code to get a bulk notification in case multiple properties changed in the same queue, to avoid multiple invocations of the GObject::notify signal.
given the high overhead of a signal, and given the critical path of property notification, I was thinking of having a GDispatchFunc callback registered on a per-object basis. aside from GController, this could would be useful for animation frameworks based on properties; it would allow compressing all animation changes done during the frame advancement to cause a single state update, even for interdependent properties - e.g. change the transformation matrix once, after the decomposed transformations (rotate, scale, translate) have been updated; or update the boundaries of a scene graph element once, after the positional and dimensional properties have been changed. the API would look like: g_object_add_dispatch_notify (GObject *, GDispatchFunc, gpointer, GDestroyNotify) and GDispatchFunc would have the following signature: void (* GDispatchFunc) (GObject *, GParamSpec **pspec, guint n_pspecs) something cute would be dispatching the property notification in the same main context as the g_object_add_dispatch_notify() func was called.