more on object/method change

Havoc Pennington hp@redhat.com
Fri, 4 Jul 2003 21:17:35 -0400


Hi,

Here is a bunch of thinking out loud/note-to-self type rambling, so I
don't forget and other people can comment if they want...

===

Started playing with additional changes toward an object/method kind
of API.  For some initial thoughts on what this would look like from
GLib:
 https://listman.redhat.com/archives/message-bus-list/2003-May/msg00014.html
The DCOP API should give some indication what it would look like from
Qt.

I started thinking with the one-to-one case with no message bus, to
keep things simple (no services involved). So the way it works now is
that two apps simply send and receive named messages:

 - Message org.freedesktop.Prefs.GetValue sent to peer
 - peer dispatches to the handler for that name, and returns a reply

To emulate DCOP, and to provide the kind of API suggested for GLib, we
need to introduce the concept of object instances. That is, more than
one object inside the peer may be able to handle the
org.freedesktop.Prefs.GetValue message.

"Signals" are not currently different from any other kind of message,
they just have a name.

===

I believe the way DCOP works is that object instances register under
well-known names:

 MyDCOPObject *obj1 = new MyDCOPObject("object1");

So then you can invoke methods on that object by name:
 
  if (!client->call(app.name(), "object1", "aFunction(QString,int)",
                    data, replyType, reply))

If you look at KWrite in kdcop for example, you can see things like:
 BlockSelectionInterface#1
 ClipboardInterface#1
also: 
 ViewCursorInterface#1-1

I'm not sure if the #1 is added by kdcop or whether it's already in
the object name provided by kwrite. Also I'm not sure what the
difference between #1 and #1-1 is. If someone wants to helpfully
explain, that will save me code-diving. ;-)

===

I find the DCOP setup somewhat confusing, as interface names and
object instance names are treated similarly. What would make sense to
me for dbus might be the following:

 - we have object instance IDs (object references), which 
   can be simply integers, or could be integers-as-strings

 - we have interface names, naming a set of methods/signals to be
   implemented

So if I ask for the list of objects in another process, I would get
back a list of instance IDs. For each instance ID, I can then ask what
interfaces it supports. Also, I could ask for all instances that
implement a given interface.

So right now a message contains:

 name = org.freedesktop.Prefs.GetValue

now it would contain:

 destination object = 0x3491
 interface = org.freedesktop.Prefs
 method = GetValue

The problem here is that it creates a bunch of round trips; before I
can invoke GetValue, I have to ask for a list of objects that
implement the Prefs interface. While with DCOP I have a well-known
object name such as "PrefsInterface" that I can just send a message to
up front. i.e. the interface name implies an object reference.

A simple solution to that is to introduce magic; if you omit the
object ID, then we magically route the message to the first instance
that implements that interface, which I believe is the same semantic
found in DCOP. That means messages could contain simply:

 interface = org.freedesktop.Prefs
 method = GetValue

meaning "invoke GetValue on the primary preferences object."
If desired, the D-BUS library could have code to let apps control
which implementor of org.freedesktop.Prefs was primary.

===

OK, so let's get the message bus involved. The additional issue here
is which process to send the message to, with routing via service
name. The obvious thing is something like:

 service = org.freedesktop.PrefsDaemon
 interface = org.freedesktop.Prefs
 method = GetValue

I wonder if it's too much magic if we allow the interface to be
omitted, in which case the destination interface would be taken
to be the same as the service name:

 service = org.freedesktop.Prefs
 method = GetValue

implying interface = org.freedesktop.Prefs as well.  This is mostly
just a bandwidth-saving hack I guess.

===

Another question is how do signals work. To "emit" a signal we want to
invoke the connected method on the receiving objects.

Say I send a signal with this information:

 emitting object = 0x3491
 emitting interface = org.freedesktop.Prefs
 signal = ValueChanged

Here the "interface" field is the interface that emits, not the
interface the message is destined for.

Without the message bus involved, the peer application would keep 
an in-process list of object instances interested in the above 
signal; would that list map from the entire (instance, interface,
signal) triplet to (instance, interface, method) triplets?

Or is it possible to connect only to an (interface, signal) pair? If
it is, does that mean you've connected to the signal on any object
instance, or does it mean you've connected to the signal on the same
"primary" instance you'd get when sending a message without an
explicit destination object ID?

When a signal is emitted, does it always go over the wire, or does the
signal have to be explicitly requested before the peer will send it?
I'd say it has to be requested but DBusConnection backend should track
which have been requested, and only request one time even if there are
multiple in-process objects listening. In other words the request is
simply to emit the signal or not, the signal emitter process does not
track which object instances are listening in the other process, and
the emitted message does not contain a list of destination objects.

Once the message bus is involved, we might want the message bus to
manage the "whether to emit ValueChanged" flag by keeping track of
which clients wanted to see it emitted, and asking the emitting
connection to emit it or not depending on whether anyone cares.

===

Do we need to know whether the object instance ID 42 inside the
service com.Foo.Bar is the same as object instance ID 42 inside
service com.Baz.Foo? Should the object instance ID perhaps consist of
some bits provided by the client, and some bits added in by the bus
daemon, such that object instance IDs become globally unique across
the message bus? Perhaps instance IDs are 64 bits or an array of two
32-bit ints for this purpose?

If that's the case, can you send a message to an object instance ID
only, omitting the service name, even when using the message bus?

If this is the case, perhaps the reply to org.freedesktop.Hello would
include the connection ID to form the upper bits of instance IDs.

Does this reintroduce the client ID concept that we had previously
avoided? (Well, does it reintroduce the problems we had with it?)

===

Are there well-known object instance IDs, e.g. the one for the 
bus driver org.freedesktop.DBus service owner? Or is this pointless 
because you can just use an interface name instead?

Havoc