GDBus API Questions; was: GDBus/GVariant plans for next GLib release

View: New views
11 Messages — Rating Filter:   Alert me  

GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/15 David Zeuthen <david@...>:
>
> Hey,
> At the GNOME summit this weekend, Ryan Lortie, Matthias Clasen, Will
> Thompson and myself had a hallway conversation about how to get D-Bus
> functionality into the GLib stack. This mail is an attempt at
> summarizing what we talked about.

Thanks for this extensive write up, it is really appreciated!

I read the API docs on

>  http://people.freedesktop.org/~david/gdbus-20091014/

and it generally it looks really good, but I have some questions...

 * Can I register a GDBusInterfaceVTable without registering an
object? The use case I have in mind is something akin to dynamically
spawning objects on the server side when messages are send to objects
under a given path[1]. Fx. when messages are send to
/org/example/item/* I create the item matching * dynamically. Ideally
one would register the vtable for objects matching a regexp. This
would facilitate RESTful message passing on the bus.

* Properties and signals in GDBusProxy are namespaced?

* g_bus_unown_name() only takes the owner_id and not the connection to
unregister on? I see how this can be made to work, but there's
something about it that makes me a bit worried if I own the same bus
name on connections to different buses (this is a theoretical
possibility right?). Probably only an extremely little problem in
practice, but here goes the nitpicker bringing it up anyway ;-)

--
Cheers,
Mikkel

[1]: The real use case is spawning application-private logs in
Zeitgeist, but let's not get into the details about that :-)
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by David Zeuthen :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hey Mikkel,

On Thu, 2009-10-15 at 08:24 +0200, Mikkel Kamstrup Erlandsen wrote:
>  * Can I register a GDBusInterfaceVTable without registering an
> object? The use case I have in mind is something akin to dynamically
> spawning objects on the server side when messages are send to objects
> under a given path[1]. Fx. when messages are send to
> /org/example/item/* I create the item matching * dynamically. Ideally
> one would register the vtable for objects matching a regexp. This
> would facilitate RESTful message passing on the bus.

Yeah, there's a TODO in gdbusconnection.h to say we need something like
that - it would be similar dbus_connection_register_fallback() [1]. We
probably want a separate GDBusHierarchyVTable with functions to a) list
objects in the "directory"; and b) get introspection data for objects in
the "directory".

I haven't given this much thought yet - I guess playing around with it
helps - we also need to ensure that object mappings (e.g. generated
GDBusProxy subclasses - see below) plays nicely with it.

[1] : http://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html#g1aa656b16bda11e8042e5eb4dc8f69a8

> * Properties and signals in GDBusProxy are namespaced?

Yeah, this is because I envision that we're going to have code
generators (for those of us wanting that), like eggdbus-binding-tool,
spit out the follow things for every D-Bus interface

 - A GInterface FooBar with a VTable for methods and GObject
   properties and GObject signals.

 - A subclass of GDBusProxy, FooBarProxy, implementing FooBar. You'd
   use this subclass in g_bus_watch_proxy() by passing
   FOO_TYPE_BAR_PROXY for the @interface_type parameter.

   Now, if the properties/signals in GDBusProxy wasn't prefixed
   with g-dbus-proxy things wouldn't work if you had a property
   called e.g. 'flags' (or rather, a property for which the
   g-name would map to 'flags').

 - A static function to export a GObject implementing the
   FooBar GInterface

    gboolean
    foo_bar_register_object (GDBusConnection  *connection,
                             FooBar           *foo_bar,
                             const gchar      *object_path,
                             GError          **error);

   that just calls g_dbus_connection_register_object() under the
   hood (taking care of the other parameters). For hierarchies, I
   envision a similar function

    gboolean
    foo_bar_register_hierarchy (GDBusConnection  *connection,
                                FooBar           *foo_bar,
                                const gchar      *object_path,
                                GError          **error);

> * g_bus_unown_name() only takes the owner_id and not the connection to
> unregister on? I see how this can be made to work, but there's
> something about it that makes me a bit worried if I own the same bus
> name on connections to different buses (this is a theoretical
> possibility right?). Probably only an extremely little problem in
> practice, but here goes the nitpicker bringing it up anyway ;-)

All ids returned by GDBus are supposed to be unique so this shouldn't be
a problem.

Thanks,
David


_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/15 David Zeuthen <david@...>:

> Hey Mikkel,
>
> On Thu, 2009-10-15 at 08:24 +0200, Mikkel Kamstrup Erlandsen wrote:
>>  * Can I register a GDBusInterfaceVTable without registering an
>> object? The use case I have in mind is something akin to dynamically
>> spawning objects on the server side when messages are send to objects
>> under a given path[1]. Fx. when messages are send to
>> /org/example/item/* I create the item matching * dynamically. Ideally
>> one would register the vtable for objects matching a regexp. This
>> would facilitate RESTful message passing on the bus.
>
> Yeah, there's a TODO in gdbusconnection.h to say we need something like
> that - it would be similar dbus_connection_register_fallback() [1]. We
> probably want a separate GDBusHierarchyVTable with functions to a) list
> objects in the "directory"; and b) get introspection data for objects in
> the "directory".

I just looked over the newly introduced
g_dbus_connection_register_subtree() and related data structures, and
I think it will fit very nicely with what I am going to need. All in
all it looks really sweet, good work.

One thing though is that as I read it objects in a subtree must be
known before method calls are accepted to them? For my use case in
Zeitgeist I was hoping that I could completely get rid of a "Manager"
type of interface, and just implicitly create objects in the tree
whenever calls where made to them. This does not look possible as it
stands?

Maybe allowing '*' as a wildcard node name in the subtree enumeration function?

--
Cheers,
Mikkel
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/25 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:

> 2009/10/15 David Zeuthen <david@...>:
>> Hey Mikkel,
>>
>> On Thu, 2009-10-15 at 08:24 +0200, Mikkel Kamstrup Erlandsen wrote:
>>>  * Can I register a GDBusInterfaceVTable without registering an
>>> object? The use case I have in mind is something akin to dynamically
>>> spawning objects on the server side when messages are send to objects
>>> under a given path[1]. Fx. when messages are send to
>>> /org/example/item/* I create the item matching * dynamically. Ideally
>>> one would register the vtable for objects matching a regexp. This
>>> would facilitate RESTful message passing on the bus.
>>
>> Yeah, there's a TODO in gdbusconnection.h to say we need something like
>> that - it would be similar dbus_connection_register_fallback() [1]. We
>> probably want a separate GDBusHierarchyVTable with functions to a) list
>> objects in the "directory"; and b) get introspection data for objects in
>> the "directory".
>
> I just looked over the newly introduced
> g_dbus_connection_register_subtree() and related data structures, and
> I think it will fit very nicely with what I am going to need. All in
> all it looks really sweet, good work.
>
> One thing though is that as I read it objects in a subtree must be
> known before method calls are accepted to them? For my use case in
> Zeitgeist I was hoping that I could completely get rid of a "Manager"
> type of interface, and just implicitly create objects in the tree
> whenever calls where made to them. This does not look possible as it
> stands?
>
> Maybe allowing '*' as a wildcard node name in the subtree enumeration function?
I had a stab at this myself. The wildcard idea seemed like a bad one,
so I instead added another gboolean param to
g_dbus_connection_register_subtree(), @is_dynamic.

If is_dynamic is TRUE then objects need not be in the enumerated list
of objects in order to be introspected and dispatched. Pretty simple.

No matter the simplicity I still managed to screw up one of the unit
tests. I will fix it and add some specific tests for the dynamic case
if you give the "go" for this David.

--
Cheers,
Mikkel

PS: And thanks for nice readable and commented code David!

[dynamic-object-subtree-1.patch]

diff --git a/gdbus/example-subtree.c b/gdbus/example-subtree.c
index 041a8f4..f870589 100644
--- a/gdbus/example-subtree.c
+++ b/gdbus/example-subtree.c
@@ -332,6 +332,7 @@ on_name_acquired (GDBusConnection *connection,
   registration_id = g_dbus_connection_register_subtree (connection,
                                                        "/org/gtk/GDBus/TestSubtree/Devices",
                                                         &subtree_vtable,
+                                                        FALSE, /* is_dynamic */
                                                         NULL,  /* user_data */
                                                         NULL,  /* user_data_free_func */
                                                         NULL); /* GError** */
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index aa21213..d9099c0 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -3620,6 +3620,7 @@ struct ExportedSubtree
   gchar                    *object_path;
   GDBusConnection          *connection;
   const GDBusSubtreeVTable *vtable;
+  gboolean                  is_dynamic;
 
   GMainContext             *context;
   gpointer                  user_data;
@@ -3685,6 +3686,8 @@ handle_subtree_introspect (DBusConnection  *connection,
 
   //g_debug ("in handle_subtree_introspect for %s", requested_object_path);
 
+  /* Strictly we don't need the children in dynamic mode, but we avoid the
+   * conditionals to preserve code clarity */
   children = es->vtable->enumerate (es->connection,
                                     es->user_data,
                                     sender,
@@ -3693,8 +3696,9 @@ handle_subtree_introspect (DBusConnection  *connection,
   if (!is_root)
     {
       requested_node = strrchr (requested_object_path, '/') + 1;
-      /* skip if requested node is not part of children */
-      if (!_g_strv_has_string ((const gchar * const *) children, requested_node))
+
+      /* Assert existence of object if we are not dynamic */
+      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -3825,8 +3829,9 @@ handle_subtree_method_invocation (DBusConnection *connection,
   if (!is_root)
     {
       requested_node = strrchr (requested_object_path, '/') + 1;
-      /* skip if requested node is not part of children */
-      if (!_g_strv_has_string ((const gchar * const *) children, requested_node))
+
+      /* If not dynamic, skip if requested node is not part of children */
+      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -4048,6 +4053,9 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * @connection: A #GDBusConnection.
  * @object_path: The object path to register the subtree at.
  * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree.
+ * @is_dynamic: If %TRUE method calls to objects not in the enumerated range
+ *              will still be dispatched. This is useful if you want to
+ *              dynamically spawn objects in the subtree.
  * @user_data: Data to pass to functions in @vtable.
  * @user_data_free_func: Function to call when the subtree is unregistered.
  * @error: Return location for error or %NULL.
@@ -4059,8 +4067,9 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * by @object_path.
  *
  * When handling remote calls into any node in the subtree, first the
- * @enumerate and @introspection function is used to check if the node
- * exists and whether it supports the requested method. If so, the
+ * @enumerate function is used to check if the node exists. If the node exists
+ * or @is_dynamic is set to %TRUE the @introspection function is used to
+ * check if the node supports the requested method. If so, the
  * @dispatch function is used to determine where to dispatch the
  * call. The collected #GDBusInterfaceVTable and #gpointer will be
  * used to call into the interface vtable for processing the request.
@@ -4087,6 +4096,7 @@ guint
 g_dbus_connection_register_subtree (GDBusConnection            *connection,
                                     const gchar                *object_path,
                                     const GDBusSubtreeVTable   *vtable,
+                                    gboolean                    is_dynamic,
                                     gpointer                    user_data,
                                     GDestroyNotify              user_data_free_func,
                                     GError                    **error)
@@ -4099,6 +4109,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
   g_return_val_if_fail (!g_dbus_connection_get_is_disconnected (connection), 0);
   g_return_val_if_fail (object_path != NULL, 0);
   g_return_val_if_fail (vtable != NULL, 0);
+  g_return_val_if_fail (error == NULL || *error == NULL, 0);
 
   ret = 0;
 
@@ -4146,6 +4157,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
     }
 
   es->vtable = vtable;
+  es->is_dynamic = is_dynamic;
   es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */
   es->user_data = user_data;
   es->user_data_free_func = user_data_free_func;
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index da07e14..cd49a07 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -318,6 +318,7 @@ struct _GDBusSubtreeVTable
 guint            g_dbus_connection_register_subtree           (GDBusConnection            *connection,
                                                                const gchar                *object_path,
                                                                const GDBusSubtreeVTable   *vtable,
+                                                               gboolean                    is_dynamic,
                                                                gpointer                    user_data,
                                                                GDestroyNotify              user_data_free_func,
                                                                GError                    **error);
diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index 5a678e1..f868d54 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -649,6 +649,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
+                                                                FALSE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
@@ -658,6 +659,7 @@ test_object_registration (void)
   registration_id = g_dbus_connection_register_subtree (c,
                                                         "/foo/boss/executives",
                                                         &subtree_vtable,
+                                                        FALSE,
                                                         &data,
                                                         on_subtree_unregistered,
                                                         &error);
@@ -674,6 +676,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
+                                                                FALSE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);


_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by David Zeuthen :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hey Mikkel,

On Mon, 2009-10-26 at 23:52 +0100, Mikkel Kamstrup Erlandsen wrote:

> > I just looked over the newly introduced
> > g_dbus_connection_register_subtree() and related data structures, and
> > I think it will fit very nicely with what I am going to need. All in
> > all it looks really sweet, good work.
> >
> > One thing though is that as I read it objects in a subtree must be
> > known before method calls are accepted to them? For my use case in
> > Zeitgeist I was hoping that I could completely get rid of a "Manager"
> > type of interface, and just implicitly create objects in the tree
> > whenever calls where made to them. This does not look possible as it
> > stands?
> >
> > Maybe allowing '*' as a wildcard node name in the subtree enumeration function?

I'm actually a bit wary of introducing this kind of functionality -
mainly, I guess, because it screws with the notion that a D-Bus service
export a set of objects. In particular it makes it hard to
debug/introspect the service - for example, in extreme abuses of such a
feature (not the use-case you are suggesting though), you can't really
use e.g. d-feet to get an idea of what kind of objects are exported and
known by the service.

The subtree functionality is really just for performance hacks - the
intended use is to avoid creating a huge amount of objects. For example,
one use case is export the subtree /org/foo/Project/processes/<pid>
where <pid> is, say, a UNIX process id. With the subtree handler, no
object creation over is necessary.

Anyway, your original use case does seem sound and reasonable - it
reduces overhead insofar that the client saves a round-trip to a
hypothetical Manager.CreateObject() method that would be needed if we
didn't have this. It does make it less intuitive insofar that remote
object creation is this magical thing with automatically appearing
nodes... but I guess that's fine.

> I had a stab at this myself. The wildcard idea seemed like a bad one,
> so I instead added another gboolean param to
> g_dbus_connection_register_subtree(), @is_dynamic.
>
> If is_dynamic is TRUE then objects need not be in the enumerated list
> of objects in order to be introspected and dispatched. Pretty simple.
>
> No matter the simplicity I still managed to screw up one of the unit
> tests. I will fix it and add some specific tests for the dynamic case
> if you give the "go" for this David.

Sounds good to me. I'd prefer a GDBusSubtreeFlags flag enumeration with
a G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES member instead of
@is_dynamic. Just for future proofing and all. Maybe it would also be
nice to pass a "gboolean enumerated" to @introspect and @dispatch that
indicates whether the node was enumerated (or not).

> PS: And thanks for nice readable and commented code David!

Hey, thanks for trying it out and providing feedback!

Thanks,
David


_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/27 David Zeuthen <david@...>:

> Hey Mikkel,
>
> On Mon, 2009-10-26 at 23:52 +0100, Mikkel Kamstrup Erlandsen wrote:
>> > I just looked over the newly introduced
>> > g_dbus_connection_register_subtree() and related data structures, and
>> > I think it will fit very nicely with what I am going to need. All in
>> > all it looks really sweet, good work.
>> >
>> > One thing though is that as I read it objects in a subtree must be
>> > known before method calls are accepted to them? For my use case in
>> > Zeitgeist I was hoping that I could completely get rid of a "Manager"
>> > type of interface, and just implicitly create objects in the tree
>> > whenever calls where made to them. This does not look possible as it
>> > stands?
>> >
>> > Maybe allowing '*' as a wildcard node name in the subtree enumeration function?
>
> I'm actually a bit wary of introducing this kind of functionality -
> mainly, I guess, because it screws with the notion that a D-Bus service
> export a set of objects. In particular it makes it hard to
> debug/introspect the service - for example, in extreme abuses of such a
> feature (not the use-case you are suggesting though), you can't really
> use e.g. d-feet to get an idea of what kind of objects are exported and
> known by the service.
>
> The subtree functionality is really just for performance hacks - the
> intended use is to avoid creating a huge amount of objects. For example,
> one use case is export the subtree /org/foo/Project/processes/<pid>
> where <pid> is, say, a UNIX process id. With the subtree handler, no
> object creation over is necessary.
>
> Anyway, your original use case does seem sound and reasonable - it
> reduces overhead insofar that the client saves a round-trip to a
> hypothetical Manager.CreateObject() method that would be needed if we
> didn't have this. It does make it less intuitive insofar that remote
> object creation is this magical thing with automatically appearing
> nodes... but I guess that's fine.

Not to mention the built in race condition you have if the exported
objects can also be deleted via the manager. All client apps would
have to precede all calls to an object by a Create() message (and it
would still be racy). Anyway, enough about this :-) I think it is
probably a fragment of my REST/web background where such patterns are
more common.

>> I had a stab at this myself. The wildcard idea seemed like a bad one,
>> so I instead added another gboolean param to
>> g_dbus_connection_register_subtree(), @is_dynamic.
>>
>> If is_dynamic is TRUE then objects need not be in the enumerated list
>> of objects in order to be introspected and dispatched. Pretty simple.
>>
>> No matter the simplicity I still managed to screw up one of the unit
>> tests. I will fix it and add some specific tests for the dynamic case
>> if you give the "go" for this David.
>
> Sounds good to me. I'd prefer a GDBusSubtreeFlags flag enumeration with
> a G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES member instead of
> @is_dynamic. Just for future proofing and all. Maybe it would also be
> nice to pass a "gboolean enumerated" to @introspect and @dispatch that
> indicates whether the node was enumerated (or not).

That sounds like a better idea. I'll have another stab at this tonight
if the kids allow me :-)

--
Cheers,
Mikkel
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/27 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:

> 2009/10/27 David Zeuthen <david@...>:
>> Hey Mikkel,
>>
>> On Mon, 2009-10-26 at 23:52 +0100, Mikkel Kamstrup Erlandsen wrote:
>>> > I just looked over the newly introduced
>>> > g_dbus_connection_register_subtree() and related data structures, and
>>> > I think it will fit very nicely with what I am going to need. All in
>>> > all it looks really sweet, good work.
>>> >
>>> > One thing though is that as I read it objects in a subtree must be
>>> > known before method calls are accepted to them? For my use case in
>>> > Zeitgeist I was hoping that I could completely get rid of a "Manager"
>>> > type of interface, and just implicitly create objects in the tree
>>> > whenever calls where made to them. This does not look possible as it
>>> > stands?
>>> >
>>> > Maybe allowing '*' as a wildcard node name in the subtree enumeration function?
>>
>> I'm actually a bit wary of introducing this kind of functionality -
>> mainly, I guess, because it screws with the notion that a D-Bus service
>> export a set of objects. In particular it makes it hard to
>> debug/introspect the service - for example, in extreme abuses of such a
>> feature (not the use-case you are suggesting though), you can't really
>> use e.g. d-feet to get an idea of what kind of objects are exported and
>> known by the service.
>>
>> The subtree functionality is really just for performance hacks - the
>> intended use is to avoid creating a huge amount of objects. For example,
>> one use case is export the subtree /org/foo/Project/processes/<pid>
>> where <pid> is, say, a UNIX process id. With the subtree handler, no
>> object creation over is necessary.
>>
>> Anyway, your original use case does seem sound and reasonable - it
>> reduces overhead insofar that the client saves a round-trip to a
>> hypothetical Manager.CreateObject() method that would be needed if we
>> didn't have this. It does make it less intuitive insofar that remote
>> object creation is this magical thing with automatically appearing
>> nodes... but I guess that's fine.
>
> Not to mention the built in race condition you have if the exported
> objects can also be deleted via the manager. All client apps would
> have to precede all calls to an object by a Create() message (and it
> would still be racy). Anyway, enough about this :-) I think it is
> probably a fragment of my REST/web background where such patterns are
> more common.
>
>>> I had a stab at this myself. The wildcard idea seemed like a bad one,
>>> so I instead added another gboolean param to
>>> g_dbus_connection_register_subtree(), @is_dynamic.
>>>
>>> If is_dynamic is TRUE then objects need not be in the enumerated list
>>> of objects in order to be introspected and dispatched. Pretty simple.
>>>
>>> No matter the simplicity I still managed to screw up one of the unit
>>> tests. I will fix it and add some specific tests for the dynamic case
>>> if you give the "go" for this David.
>>
>> Sounds good to me. I'd prefer a GDBusSubtreeFlags flag enumeration with
>> a G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES member instead of
>> @is_dynamic. Just for future proofing and all. Maybe it would also be
>> nice to pass a "gboolean enumerated" to @introspect and @dispatch that
>> indicates whether the node was enumerated (or not).
>
> That sounds like a better idea. I'll have another stab at this tonight
> if the kids allow me :-)
They did :-)

Attached is a series of patches (0001 should be identical to my
previous) implementing what you describe, except adding the gboolean
enumerated arg to @introspect and @dispatch. I will get around to
that.

The failing unit tests I described was really just me b0rking up the
linking between some installed gdbus components and my devel version.
I have them running now[1] and I added some testing for the dynamic
objects as well (see 0003).

--
Cheers,
Mikkel

[1]: One caveat is that I see some errors about refcount on GVariants
from the /gdbus/method-calls-in-thread test every now and then, but it
runs mostly. Looks like a race, but it is unrelated to my hacks.
You've probably already seen it yourself.

[0001-Add-a-is_dynamic-gboolean-parameter-to-g_dbus_connec.patch]

From d569ee5b89b8865747a153fb3848764c52a4c865 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>
Date: Tue, 27 Oct 2009 20:45:34 +0100
Subject: [PATCH 1/3] Add a @is_dynamic gboolean parameter to g_dbus_connection_register_subtree(). This will be reworked to use an enumeration/flag instead for better future extensibility

---
 gdbus/example-subtree.c |    1 +
 gdbus/gdbusconnection.c |   24 ++++++++++++++++++------
 gdbus/gdbusconnection.h |    1 +
 gdbus/tests/export.c    |    3 +++
 4 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/gdbus/example-subtree.c b/gdbus/example-subtree.c
index 041a8f4..f870589 100644
--- a/gdbus/example-subtree.c
+++ b/gdbus/example-subtree.c
@@ -332,6 +332,7 @@ on_name_acquired (GDBusConnection *connection,
   registration_id = g_dbus_connection_register_subtree (connection,
                                                        "/org/gtk/GDBus/TestSubtree/Devices",
                                                         &subtree_vtable,
+                                                        FALSE, /* is_dynamic */
                                                         NULL,  /* user_data */
                                                         NULL,  /* user_data_free_func */
                                                         NULL); /* GError** */
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index aa21213..d9099c0 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -3620,6 +3620,7 @@ struct ExportedSubtree
   gchar                    *object_path;
   GDBusConnection          *connection;
   const GDBusSubtreeVTable *vtable;
+  gboolean                  is_dynamic;
 
   GMainContext             *context;
   gpointer                  user_data;
@@ -3685,6 +3686,8 @@ handle_subtree_introspect (DBusConnection  *connection,
 
   //g_debug ("in handle_subtree_introspect for %s", requested_object_path);
 
+  /* Strictly we don't need the children in dynamic mode, but we avoid the
+   * conditionals to preserve code clarity */
   children = es->vtable->enumerate (es->connection,
                                     es->user_data,
                                     sender,
@@ -3693,8 +3696,9 @@ handle_subtree_introspect (DBusConnection  *connection,
   if (!is_root)
     {
       requested_node = strrchr (requested_object_path, '/') + 1;
-      /* skip if requested node is not part of children */
-      if (!_g_strv_has_string ((const gchar * const *) children, requested_node))
+
+      /* Assert existence of object if we are not dynamic */
+      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -3825,8 +3829,9 @@ handle_subtree_method_invocation (DBusConnection *connection,
   if (!is_root)
     {
       requested_node = strrchr (requested_object_path, '/') + 1;
-      /* skip if requested node is not part of children */
-      if (!_g_strv_has_string ((const gchar * const *) children, requested_node))
+
+      /* If not dynamic, skip if requested node is not part of children */
+      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -4048,6 +4053,9 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * @connection: A #GDBusConnection.
  * @object_path: The object path to register the subtree at.
  * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree.
+ * @is_dynamic: If %TRUE method calls to objects not in the enumerated range
+ *              will still be dispatched. This is useful if you want to
+ *              dynamically spawn objects in the subtree.
  * @user_data: Data to pass to functions in @vtable.
  * @user_data_free_func: Function to call when the subtree is unregistered.
  * @error: Return location for error or %NULL.
@@ -4059,8 +4067,9 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * by @object_path.
  *
  * When handling remote calls into any node in the subtree, first the
- * @enumerate and @introspection function is used to check if the node
- * exists and whether it supports the requested method. If so, the
+ * @enumerate function is used to check if the node exists. If the node exists
+ * or @is_dynamic is set to %TRUE the @introspection function is used to
+ * check if the node supports the requested method. If so, the
  * @dispatch function is used to determine where to dispatch the
  * call. The collected #GDBusInterfaceVTable and #gpointer will be
  * used to call into the interface vtable for processing the request.
@@ -4087,6 +4096,7 @@ guint
 g_dbus_connection_register_subtree (GDBusConnection            *connection,
                                     const gchar                *object_path,
                                     const GDBusSubtreeVTable   *vtable,
+                                    gboolean                    is_dynamic,
                                     gpointer                    user_data,
                                     GDestroyNotify              user_data_free_func,
                                     GError                    **error)
@@ -4099,6 +4109,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
   g_return_val_if_fail (!g_dbus_connection_get_is_disconnected (connection), 0);
   g_return_val_if_fail (object_path != NULL, 0);
   g_return_val_if_fail (vtable != NULL, 0);
+  g_return_val_if_fail (error == NULL || *error == NULL, 0);
 
   ret = 0;
 
@@ -4146,6 +4157,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
     }
 
   es->vtable = vtable;
+  es->is_dynamic = is_dynamic;
   es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */
   es->user_data = user_data;
   es->user_data_free_func = user_data_free_func;
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index da07e14..cd49a07 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -318,6 +318,7 @@ struct _GDBusSubtreeVTable
 guint            g_dbus_connection_register_subtree           (GDBusConnection            *connection,
                                                                const gchar                *object_path,
                                                                const GDBusSubtreeVTable   *vtable,
+                                                               gboolean                    is_dynamic,
                                                                gpointer                    user_data,
                                                                GDestroyNotify              user_data_free_func,
                                                                GError                    **error);
diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index 5a678e1..f868d54 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -649,6 +649,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
+                                                                FALSE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
@@ -658,6 +659,7 @@ test_object_registration (void)
   registration_id = g_dbus_connection_register_subtree (c,
                                                         "/foo/boss/executives",
                                                         &subtree_vtable,
+                                                        FALSE,
                                                         &data,
                                                         on_subtree_unregistered,
                                                         &error);
@@ -674,6 +676,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
+                                                                FALSE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
--
1.6.3.3



[0002-implement-GDBusSubtreeFlags-enumeration.patch]

From 6ab58f1031fcde74c70ed60de5443ab851c174b1 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>
Date: Tue, 27 Oct 2009 21:48:31 +0100
Subject: [PATCH 2/3] implement GDBusSubtreeFlags enumeration

Use it for g_dbus_connection_register_subtree() arg in stead of is_dynamic.
Also add the new enumeration to the gtk-doc sections
---
 docs/reference/gdbus/gdbus-standalone-sections.txt |    1 +
 gdbus/gdbusconnection.c                            |   27 ++++++++++---------
 gdbus/gdbusconnection.h                            |    2 +-
 gdbus/gdbusenums.h                                 |   15 +++++++++++
 gdbus/tests/export.c                               |    6 ++--
 5 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-standalone-sections.txt b/docs/reference/gdbus/gdbus-standalone-sections.txt
index 442fee0..f763906 100644
--- a/docs/reference/gdbus/gdbus-standalone-sections.txt
+++ b/docs/reference/gdbus/gdbus-standalone-sections.txt
@@ -37,6 +37,7 @@ GDBusSubtreeVTable
 GDBusSubtreeEnumerateFunc
 GDBusSubtreeIntrospectFunc
 GDBusSubtreeDispatchFunc
+GDBusSubtreeFlags
 g_dbus_connection_register_subtree
 g_dbus_connection_unregister_subtree
 <SUBSECTION Standard>
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index d9099c0..8a175d6 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -3620,7 +3620,7 @@ struct ExportedSubtree
   gchar                    *object_path;
   GDBusConnection          *connection;
   const GDBusSubtreeVTable *vtable;
-  gboolean                  is_dynamic;
+  GDBusSubtreeFlags         flags;
 
   GMainContext             *context;
   gpointer                  user_data;
@@ -3698,7 +3698,8 @@ handle_subtree_introspect (DBusConnection  *connection,
       requested_node = strrchr (requested_object_path, '/') + 1;
 
       /* Assert existence of object if we are not dynamic */
-      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
+      if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) &&
+          !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -3831,7 +3832,8 @@ handle_subtree_method_invocation (DBusConnection *connection,
       requested_node = strrchr (requested_object_path, '/') + 1;
 
       /* If not dynamic, skip if requested node is not part of children */
-      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
+      if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) &&
+          !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -4053,9 +4055,7 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * @connection: A #GDBusConnection.
  * @object_path: The object path to register the subtree at.
  * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree.
- * @is_dynamic: If %TRUE method calls to objects not in the enumerated range
- *              will still be dispatched. This is useful if you want to
- *              dynamically spawn objects in the subtree.
+ * @flags: Flags used to fine tune the behavior of the subtree
  * @user_data: Data to pass to functions in @vtable.
  * @user_data_free_func: Function to call when the subtree is unregistered.
  * @error: Return location for error or %NULL.
@@ -4068,11 +4068,12 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  *
  * When handling remote calls into any node in the subtree, first the
  * @enumerate function is used to check if the node exists. If the node exists
- * or @is_dynamic is set to %TRUE the @introspection function is used to
- * check if the node supports the requested method. If so, the
- * @dispatch function is used to determine where to dispatch the
- * call. The collected #GDBusInterfaceVTable and #gpointer will be
- * used to call into the interface vtable for processing the request.
+ * or the #G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is set
+ * the @introspection function is used to check if the node supports the
+ * requested method. If so, the @dispatch function is used to determine
+ * where to dispatch the call. The collected #GDBusInterfaceVTable and
+ * #gpointer will be used to call into the interface vtable for processing
+ * the request.
  *
  * All calls into user-provided code will be invoked in the <link
  * linkend="g-main-context-push-thread-default">thread-default main
@@ -4096,7 +4097,7 @@ guint
 g_dbus_connection_register_subtree (GDBusConnection            *connection,
                                     const gchar                *object_path,
                                     const GDBusSubtreeVTable   *vtable,
-                                    gboolean                    is_dynamic,
+                                    GDBusSubtreeFlags           flags,
                                     gpointer                    user_data,
                                     GDestroyNotify              user_data_free_func,
                                     GError                    **error)
@@ -4157,7 +4158,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
     }
 
   es->vtable = vtable;
-  es->is_dynamic = is_dynamic;
+  es->flags = flags;
   es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */
   es->user_data = user_data;
   es->user_data_free_func = user_data_free_func;
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index cd49a07..4f517a3 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -318,7 +318,7 @@ struct _GDBusSubtreeVTable
 guint            g_dbus_connection_register_subtree           (GDBusConnection            *connection,
                                                                const gchar                *object_path,
                                                                const GDBusSubtreeVTable   *vtable,
-                                                               gboolean                    is_dynamic,
+                                                               GDBusSubtreeFlags           flags,
                                                                gpointer                    user_data,
                                                                GDestroyNotify              user_data_free_func,
                                                                GError                    **error);
diff --git a/gdbus/gdbusenums.h b/gdbus/gdbusenums.h
index ab37e16..de43ec0 100644
--- a/gdbus/gdbusenums.h
+++ b/gdbus/gdbusenums.h
@@ -257,6 +257,21 @@ typedef enum
   G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE = (1<<1),
 } GDBusPropertyInfoFlags;
 
+/**
+ * GDBusSubtreeFlags:
+ * @G_DBUS_SUBTREE_FLAGS_NONE: No flags set.
+ * @G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES: Method calls to objects not in the enumerated range
+ *                                                       will still be dispatched. This is useful if you want
+ *                                                       to dynamically spawn objects in the subtree.
+ *
+ * Flags passed to g_dbus_connection_register_subtree().
+ */
+typedef enum
+{
+  G_DBUS_SUBTREE_FLAGS_NONE = 0,                                /*< nick=none >*/
+  G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES = (1<<0), /*< nick=dispatch-to-unenumerated-nodes >*/
+} GDBusSubtreeFlags;
+
 G_END_DECLS
 
 #endif /* __G_DBUS_ENUMS_H__ */
diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index f868d54..e6e6d3e 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -649,7 +649,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
-                                                                FALSE,
+                                                                G_DBUS_SUBTREE_FLAGS_NONE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
@@ -659,7 +659,7 @@ test_object_registration (void)
   registration_id = g_dbus_connection_register_subtree (c,
                                                         "/foo/boss/executives",
                                                         &subtree_vtable,
-                                                        FALSE,
+                                                        G_DBUS_SUBTREE_FLAGS_NONE,
                                                         &data,
                                                         on_subtree_unregistered,
                                                         &error);
@@ -676,7 +676,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
-                                                                FALSE,
+                                                                G_DBUS_SUBTREE_FLAGS_NONE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
--
1.6.3.3



[0003-test-dynamic-objects-in-a-subtree.patch]

From ba058721ffe3f7395f0c0e65462ff1be125777e4 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>
Date: Wed, 28 Oct 2009 00:06:51 +0100
Subject: [PATCH 3/3] test dynamic objects in a subtree

Implement some test for dynamically spawning objects in a subtree
by setting the G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag
---
 gdbus/tests/export.c |  169 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 168 insertions(+), 1 deletions(-)

diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index e6e6d3e..be911e2 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -131,6 +131,73 @@ static const GDBusInterfaceInfo bar_interface_info =
   NULL,
 };
 
+/* -------------------- */
+
+static const GDBusMethodInfo dyna_method_info[] =
+{
+  {
+    "DynaCyber",
+    "", 0, NULL,
+    "", 0, NULL,
+    NULL
+  }
+};
+
+static const GDBusSignalInfo dyna_signal_info[] =
+{
+  
+};
+
+static const GDBusPropertyInfo dyna_property_info[] =
+{
+  
+};
+
+static const GDBusInterfaceInfo dyna_interface_info =
+{
+  "org.example.Dyna",
+  1,    /* 1 method*/
+  dyna_method_info,
+  0,    /* 0 signals */
+  NULL,
+  0,    /* 0 properties */
+  NULL,
+  NULL,
+};
+
+static void
+dyna_cyber (GDBusConnection *connection,
+            gpointer user_data,
+            const gchar *sender,
+            const gchar *object_path,
+            const gchar *interface_name,
+            const gchar *method_name,
+            GVariant *parameters,
+            GDBusMethodInvocation *invocation)
+{
+  GPtrArray *data = user_data;
+  char *node_name = strrchr (object_path, '/') + 1;
+  guint n;
+  
+  /* Add new node if it is not already known */  
+  for (n = 0; n < data->len ; n++)
+    {
+      if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0)
+        goto out;
+    }
+  g_ptr_array_add (data, g_strdup(node_name));
+
+  out:
+    g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static const GDBusInterfaceVTable dyna_interface_vtable =
+{
+  dyna_cyber,
+  NULL,
+  NULL
+};
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -381,6 +448,7 @@ subtree_enumerate (GDBusConnection       *connection,
   return nodes;
 }
 
+/* Only allows certain objects, and aborts on unknowns */
 static GPtrArray *
 subtree_introspect (GDBusConnection       *connection,
                     gpointer               user_data,
@@ -435,12 +503,71 @@ static const GDBusSubtreeVTable subtree_vtable =
 
 /* -------------------- */
 
+static gchar **
+dynamic_subtree_enumerate (GDBusConnection       *connection,
+                           gpointer               user_data,
+                           const gchar           *sender,
+                           const gchar           *object_path)
+{
+  GPtrArray *data = user_data;
+  gchar **nodes = g_new (gchar*, data->len + 1);
+  guint n;
+  
+  for (n = 0; n < data->len; n++)
+    {
+      nodes[n] = g_strdup (g_ptr_array_index (data, n));
+    }
+  nodes[data->len] = NULL;
+
+  return nodes;
+}
+
+/* Allow all objects to be introspected */
+static GPtrArray *
+dynamic_subtree_introspect (GDBusConnection       *connection,
+                            gpointer               user_data,
+                            const gchar           *sender,
+                            const gchar           *object_path,
+                            const gchar           *node)
+{
+  GPtrArray *interfaces;
+
+  /* All nodes (including the root node) implements the Dyna interface */
+  interfaces = g_ptr_array_new ();
+  g_ptr_array_add (interfaces, (gpointer) &dyna_interface_info);
+
+  return interfaces;
+}
+
+static const GDBusInterfaceVTable *
+dynamic_subtree_dispatch (GDBusConnection             *connection,
+                          gpointer                     user_data,
+                          const gchar                 *sender,
+                          const gchar                 *object_path,
+                          const gchar                 *interface_name,
+                          const gchar                 *node,
+                          gpointer                    *out_user_data)
+{
+  *out_user_data = user_data;
+  return &dyna_interface_vtable;
+}
+
+static const GDBusSubtreeVTable dynamic_subtree_vtable =
+{
+  dynamic_subtree_enumerate,
+  dynamic_subtree_introspect,
+  dynamic_subtree_dispatch
+};
+
+/* -------------------- */
+
 static void
 test_object_registration (void)
 {
   GDBusConnection *c;
   GError *error;
   ObjectRegistrationData data;
+  GPtrArray *dyna_data;
   gchar **nodes;
   guint boss_foo_reg_id;
   guint boss_bar_reg_id;
@@ -454,6 +581,7 @@ test_object_registration (void)
   guint subtree_registration_id;
   guint non_subtree_object_path_foo_reg_id;
   guint non_subtree_object_path_bar_reg_id;
+  guint dyna_subtree_registration_id;
   guint num_successful_registrations;
   DBusConnection *dc;
   DBusError dbus_error;
@@ -714,7 +842,41 @@ test_object_registration (void)
   non_subtree_object_path_foo_reg_id = registration_id;
   num_successful_registrations++;
 
+  /* now register a dynamic subtree, spawning objects as they are called */
+  dyna_data = g_ptr_array_new ();
+  dyna_subtree_registration_id = g_dbus_connection_register_subtree (c,
+                                                                     "/foo/dyna",
+                                                                     &dynamic_subtree_vtable,
+                                                                     G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+                                                                     dyna_data,
+                                                                     (GDestroyNotify)g_ptr_array_unref,
+                                                                     &error);
+  g_assert_no_error (error);
+  g_assert (dyna_subtree_registration_id > 0);
 
+  /* First assert that we have no nodes in the dynamic subtree */
+  nodes = get_nodes_at (c, "/foo/dyna");
+  g_assert (nodes != NULL);
+  g_assert_cmpint (g_strv_length (nodes), ==, 0);
+  g_strfreev (nodes);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4);
+
+  /* Install three nodes in the dynamic subtree via the backdoor and
+   * assert that they show up correctly in the introspection data */
+  g_ptr_array_add (dyna_data, "lol");
+  g_ptr_array_add (dyna_data, "cat");
+  g_ptr_array_add (dyna_data, "cheezburger");
+  nodes = get_nodes_at (c, "/foo/dyna");
+  g_assert (nodes != NULL);
+  g_assert_cmpint (g_strv_length (nodes), ==, 3);
+  g_assert_cmpstr (nodes[0], ==, "lol");
+  g_assert_cmpstr (nodes[1], ==, "cat");
+  g_assert_cmpstr (nodes[2], ==, "cheezburger");
+  g_strfreev (nodes);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4);
+  
   /* now check that the object hierarachy is properly generated... yes, it's a bit
    * perverse that we round-trip to the bus to introspect ourselves ;-)
    */
@@ -727,8 +889,9 @@ test_object_registration (void)
 
   nodes = get_nodes_at (c, "/foo");
   g_assert (nodes != NULL);
-  g_assert_cmpint (g_strv_length (nodes), ==, 1);
+  g_assert_cmpint (g_strv_length (nodes), ==, 2);
   g_assert_cmpstr (nodes[0], ==, "boss");
+  g_assert_cmpstr (nodes[1], ==, "dyna");
   g_strfreev (nodes);
   g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0);
 
@@ -842,6 +1005,10 @@ test_object_registration (void)
   g_strfreev (nodes);
 #endif
 
+  /* To prevent from exiting and attaching a DBus tool like D-Feet; uncomment: */
+  /*g_info ("Point D-feet or other tool at: %s", session_bus_get_temporary_address());
+  g_main_loop_run (loop);*/
+  
   g_object_unref (c);
 }
 
--
1.6.3.3



_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/28 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:

> 2009/10/27 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:
>> 2009/10/27 David Zeuthen <david@...>:
>>> Hey Mikkel,
>>>
>>> On Mon, 2009-10-26 at 23:52 +0100, Mikkel Kamstrup Erlandsen wrote:
>>>> > I just looked over the newly introduced
>>>> > g_dbus_connection_register_subtree() and related data structures, and
>>>> > I think it will fit very nicely with what I am going to need. All in
>>>> > all it looks really sweet, good work.
>>>> >
>>>> > One thing though is that as I read it objects in a subtree must be
>>>> > known before method calls are accepted to them? For my use case in
>>>> > Zeitgeist I was hoping that I could completely get rid of a "Manager"
>>>> > type of interface, and just implicitly create objects in the tree
>>>> > whenever calls where made to them. This does not look possible as it
>>>> > stands?
>>>> >
>>>> > Maybe allowing '*' as a wildcard node name in the subtree enumeration function?
>>>
>>> I'm actually a bit wary of introducing this kind of functionality -
>>> mainly, I guess, because it screws with the notion that a D-Bus service
>>> export a set of objects. In particular it makes it hard to
>>> debug/introspect the service - for example, in extreme abuses of such a
>>> feature (not the use-case you are suggesting though), you can't really
>>> use e.g. d-feet to get an idea of what kind of objects are exported and
>>> known by the service.
>>>
>>> The subtree functionality is really just for performance hacks - the
>>> intended use is to avoid creating a huge amount of objects. For example,
>>> one use case is export the subtree /org/foo/Project/processes/<pid>
>>> where <pid> is, say, a UNIX process id. With the subtree handler, no
>>> object creation over is necessary.
>>>
>>> Anyway, your original use case does seem sound and reasonable - it
>>> reduces overhead insofar that the client saves a round-trip to a
>>> hypothetical Manager.CreateObject() method that would be needed if we
>>> didn't have this. It does make it less intuitive insofar that remote
>>> object creation is this magical thing with automatically appearing
>>> nodes... but I guess that's fine.
>>
>> Not to mention the built in race condition you have if the exported
>> objects can also be deleted via the manager. All client apps would
>> have to precede all calls to an object by a Create() message (and it
>> would still be racy). Anyway, enough about this :-) I think it is
>> probably a fragment of my REST/web background where such patterns are
>> more common.
>>
>>>> I had a stab at this myself. The wildcard idea seemed like a bad one,
>>>> so I instead added another gboolean param to
>>>> g_dbus_connection_register_subtree(), @is_dynamic.
>>>>
>>>> If is_dynamic is TRUE then objects need not be in the enumerated list
>>>> of objects in order to be introspected and dispatched. Pretty simple.
>>>>
>>>> No matter the simplicity I still managed to screw up one of the unit
>>>> tests. I will fix it and add some specific tests for the dynamic case
>>>> if you give the "go" for this David.
>>>
>>> Sounds good to me. I'd prefer a GDBusSubtreeFlags flag enumeration with
>>> a G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES member instead of
>>> @is_dynamic. Just for future proofing and all. Maybe it would also be
>>> nice to pass a "gboolean enumerated" to @introspect and @dispatch that
>>> indicates whether the node was enumerated (or not).
>>
>> That sounds like a better idea. I'll have another stab at this tonight
>> if the kids allow me :-)
>
> They did :-)
>
> Attached is a series of patches (0001 should be identical to my
> previous) implementing what you describe, except adding the gboolean
> enumerated arg to @introspect and @dispatch. I will get around to
> that.
>
> The failing unit tests I described was really just me b0rking up the
> linking between some installed gdbus components and my devel version.
> I have them running now[1] and I added some testing for the dynamic
> objects as well (see 0003).

Some more tests to go with the other patches attached.

I mulled a bit over your idea about passing a gboolean to @introspect
and @dispatch. I am not sure I think it is such a good idea. What's
the motivation? It can potentially save one a two looksup in a hashmap
or some other data structure, but nothing really big. And I worry that
this might be racy in nature. The @introspect and @dispatch functions
would certainly have to be called from inside the same critical region
(enclosing the @enumerate call as well). Then we'd have a mutex grab
instead of two lookups...

Another thing is that the boolean arg also seems strangely out of
place in a way that I can't put my finger on... So unless you insist
on having it I wont do the extra arg right now.

--
Cheers,
Mikkel
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/28 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:

> 2009/10/28 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:
>> 2009/10/27 Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>:
>>> 2009/10/27 David Zeuthen <david@...>:
>>>> Hey Mikkel,
>>>>
>>>> On Mon, 2009-10-26 at 23:52 +0100, Mikkel Kamstrup Erlandsen wrote:
>>>>> > I just looked over the newly introduced
>>>>> > g_dbus_connection_register_subtree() and related data structures, and
>>>>> > I think it will fit very nicely with what I am going to need. All in
>>>>> > all it looks really sweet, good work.
>>>>> >
>>>>> > One thing though is that as I read it objects in a subtree must be
>>>>> > known before method calls are accepted to them? For my use case in
>>>>> > Zeitgeist I was hoping that I could completely get rid of a "Manager"
>>>>> > type of interface, and just implicitly create objects in the tree
>>>>> > whenever calls where made to them. This does not look possible as it
>>>>> > stands?
>>>>> >
>>>>> > Maybe allowing '*' as a wildcard node name in the subtree enumeration function?
>>>>
>>>> I'm actually a bit wary of introducing this kind of functionality -
>>>> mainly, I guess, because it screws with the notion that a D-Bus service
>>>> export a set of objects. In particular it makes it hard to
>>>> debug/introspect the service - for example, in extreme abuses of such a
>>>> feature (not the use-case you are suggesting though), you can't really
>>>> use e.g. d-feet to get an idea of what kind of objects are exported and
>>>> known by the service.
>>>>
>>>> The subtree functionality is really just for performance hacks - the
>>>> intended use is to avoid creating a huge amount of objects. For example,
>>>> one use case is export the subtree /org/foo/Project/processes/<pid>
>>>> where <pid> is, say, a UNIX process id. With the subtree handler, no
>>>> object creation over is necessary.
>>>>
>>>> Anyway, your original use case does seem sound and reasonable - it
>>>> reduces overhead insofar that the client saves a round-trip to a
>>>> hypothetical Manager.CreateObject() method that would be needed if we
>>>> didn't have this. It does make it less intuitive insofar that remote
>>>> object creation is this magical thing with automatically appearing
>>>> nodes... but I guess that's fine.
>>>
>>> Not to mention the built in race condition you have if the exported
>>> objects can also be deleted via the manager. All client apps would
>>> have to precede all calls to an object by a Create() message (and it
>>> would still be racy). Anyway, enough about this :-) I think it is
>>> probably a fragment of my REST/web background where such patterns are
>>> more common.
>>>
>>>>> I had a stab at this myself. The wildcard idea seemed like a bad one,
>>>>> so I instead added another gboolean param to
>>>>> g_dbus_connection_register_subtree(), @is_dynamic.
>>>>>
>>>>> If is_dynamic is TRUE then objects need not be in the enumerated list
>>>>> of objects in order to be introspected and dispatched. Pretty simple.
>>>>>
>>>>> No matter the simplicity I still managed to screw up one of the unit
>>>>> tests. I will fix it and add some specific tests for the dynamic case
>>>>> if you give the "go" for this David.
>>>>
>>>> Sounds good to me. I'd prefer a GDBusSubtreeFlags flag enumeration with
>>>> a G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES member instead of
>>>> @is_dynamic. Just for future proofing and all. Maybe it would also be
>>>> nice to pass a "gboolean enumerated" to @introspect and @dispatch that
>>>> indicates whether the node was enumerated (or not).
>>>
>>> That sounds like a better idea. I'll have another stab at this tonight
>>> if the kids allow me :-)
>>
>> They did :-)
>>
>> Attached is a series of patches (0001 should be identical to my
>> previous) implementing what you describe, except adding the gboolean
>> enumerated arg to @introspect and @dispatch. I will get around to
>> that.
>>
>> The failing unit tests I described was really just me b0rking up the
>> linking between some installed gdbus components and my devel version.
>> I have them running now[1] and I added some testing for the dynamic
>> objects as well (see 0003).
>
> Some more tests to go with the other patches attached.
And of course the attachment :-S

--
Cheers,
Mikkel

[0004-test-calls-to-non-existing-objects.patch]

From 9fe0126b9d74dd260e1d3ef3969afe94f8b92fa9 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@...>
Date: Wed, 28 Oct 2009 20:01:52 +0100
Subject: [PATCH 4/4] test calls to non-existing objects

Add a test for calls to a non-existing object which is then dynamically
spawned on the server
---
 gdbus/tests/export.c |   75 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 74 insertions(+), 1 deletions(-)

diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index be911e2..fabc0c3 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -375,6 +375,66 @@ count_interfaces (GDBusConnection *c,
   return ret;
 }
 
+static void
+dyna_create_callback (GDBusProxy   *proxy,
+                      GAsyncResult  *res,
+                      gpointer      user_data)
+{
+  GVariant *result;
+  GError *error;
+
+  error = NULL;
+  result = g_dbus_proxy_invoke_method_finish (proxy,
+                                              res,
+                                              &error);
+  g_assert_no_error (error);
+  g_assert (result != NULL);
+  g_variant_unref (result);
+
+  g_main_loop_quit (loop);
+}
+
+/* Dynamically create @object_name under /foo/dyna */
+static void
+dyna_create (GDBusConnection *c,
+             const gchar     *object_name)
+{
+  GError *error;
+  GDBusProxy *proxy;
+  gchar *object_path;
+
+  object_path = g_strconcat ("/foo/dyna/", object_name, NULL);
+  
+  error = NULL;
+  proxy = g_dbus_proxy_new_sync (c,
+                                 G_TYPE_DBUS_PROXY,
+                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                 g_dbus_connection_get_unique_name (c),
+                                 object_path,
+                                 "org.example.Dyna",
+                                 NULL,
+                                 &error);
+  g_assert_no_error (error);
+  g_assert (proxy != NULL);
+
+  /* do this async to avoid libdbus-1 deadlocks */
+  g_dbus_proxy_invoke_method (proxy,
+                              "DynaCyber",
+                              g_variant_new ("()"),
+                              -1,
+                              NULL,
+                              (GAsyncReadyCallback) dyna_create_callback,
+                              NULL);
+  g_main_loop_run (loop);
+
+  g_assert_no_error (error);
+
+  g_object_unref (proxy);
+  g_free (object_path);
+
+  return;
+}
 
 typedef struct
 {
@@ -861,7 +921,7 @@ test_object_registration (void)
   g_strfreev (nodes);
   g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4);
 
-  /* Install three nodes in the dynamic subtree via the backdoor and
+  /* Install three nodes in the dynamic subtree via the dyna_data backdoor and
    * assert that they show up correctly in the introspection data */
   g_ptr_array_add (dyna_data, "lol");
   g_ptr_array_add (dyna_data, "cat");
@@ -876,6 +936,19 @@ test_object_registration (void)
   g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4);
   g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4);
   g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4);
+
+  /* Call a non-existing object path and assert that it has been created */
+  dyna_create (c, "dynamicallycreated");
+  nodes = get_nodes_at (c, "/foo/dyna");
+  g_assert (nodes != NULL);
+  g_assert_cmpint (g_strv_length (nodes), ==, 4);
+  g_assert_cmpstr (nodes[0], ==, "lol");
+  g_assert_cmpstr (nodes[1], ==, "cat");
+  g_assert_cmpstr (nodes[2], ==, "cheezburger");
+  g_assert_cmpstr (nodes[3], ==, "dynamicallycreated");
+  g_strfreev (nodes);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/dynamicallycreated"), ==, 4);
+
   
   /* now check that the object hierarachy is properly generated... yes, it's a bit
    * perverse that we round-trip to the bus to introspect ourselves ;-)
--
1.6.3.3



_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by David Zeuthen :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hey Mikkel,

Sorry for not replying sooner!

On Wed, 2009-10-28 at 21:23 +0100, Mikkel Kamstrup Erlandsen wrote:
> >> Attached is a series of patches (0001 should be identical to my
> >> previous) implementing what you describe, except adding the gboolean
> >> enumerated arg to @introspect and @dispatch. I will get around to
> >> that.

Yeah, on second thought, it's probably best to skip that.

> >> The failing unit tests I described was really just me b0rking up the
> >> linking between some installed gdbus components and my devel version.
> >> I have them running now[1] and I added some testing for the dynamic
> >> objects as well (see 0003).
> >
> > Some more tests to go with the other patches attached.
>
> And of course the attachment :-S

This looks great, thanks for working on it and for writing unit tests
too. I've committed the patches with a small patch for white-space /
style fixes on top!

Thanks,
David


_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list

Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release

by Mikkel Kamstrup Erlandsen-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/11/9 David Zeuthen <david@...>:

> Hey Mikkel,
>
> Sorry for not replying sooner!
>
> On Wed, 2009-10-28 at 21:23 +0100, Mikkel Kamstrup Erlandsen wrote:
>> >> Attached is a series of patches (0001 should be identical to my
>> >> previous) implementing what you describe, except adding the gboolean
>> >> enumerated arg to @introspect and @dispatch. I will get around to
>> >> that.
>
> Yeah, on second thought, it's probably best to skip that.
>
>> >> The failing unit tests I described was really just me b0rking up the
>> >> linking between some installed gdbus components and my devel version.
>> >> I have them running now[1] and I added some testing for the dynamic
>> >> objects as well (see 0003).
>> >
>> > Some more tests to go with the other patches attached.
>>
>> And of course the attachment :-S
>
> This looks great, thanks for working on it and for writing unit tests
> too. I've committed the patches with a small patch for white-space /
> style fixes on top!

Cool. Thanks for helping out!

--
Cheers,
Mikkel
_______________________________________________
gtk-devel-list mailing list
gtk-devel-list@...
http://mail.gnome.org/mailman/listinfo/gtk-devel-list