Skip to content

Add fallback interfaces to the object server - #2113

Merged
zeenix merged 3 commits into
z-galaxy:mainfrom
zeenix:ai/exciting-hawking-pxg3u0
Nov 5, 2026
Merged

zeenix merged 3 commits into
z-galaxy:mainfrom
zeenix:ai/exciting-hawking-pxg3u0

Conversation

@zeenix

@zeenix zeenix commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

ObjectServer only dispatches a call to an interface registered at the exact path of the call, and replies with UnknownObject for any other path. So a service cannot serve objects whose paths are not known in advance, such as one per ID, unit or session (systemd's unit templates, for example). Replying to such calls outside of the object server is not an option either, as the object server replies to every method call once it runs.

ObjectServer::at_fallback and connection::Builder::serve_at_fallback register an interface that serves its path and every path below it, unless that path has an interface of the same name of its own, or a closer ancestor has a fallback of that name. This is what libdbus' dbus_connection_register_fallback and sd-bus' sd_bus_add_fallback_vtable provide. A path that only fallbacks serve is an object too: Properties, Introspectable and Peer work on it. ObjectServer::interface gets the interface that serves a path, fallbacks included, with a signal emitter for that path. The one instance learns the path of a method call from its header, and that of a property access from its signal emitter. The service chapter of the book mentions the feature, with a link to the API docs.

The objects registered below a fallback have it as well: their introspection data lists it, and an ObjectManager reports it for them, with its properties read at their paths, and emits InterfacesAdded and InterfacesRemoved as registrations change what serves them.

Left out on purpose: an instance per path (as with the find callback of sd-bus), and enumerating the paths that only fallbacks serve.

The last commit is unrelated, and came up while testing this. Connection::object_server starts listening for method calls in the background, and calls that arrive before then go unanswered. Services that request their name after registering their interfaces, or that use Builder::serve_at, are not affected, but a caller of the unique name, or a peer-to-peer peer, can be. The commit documents that and points to the builder. I can split it out if you prefer.

Fixes #2112.

Generated by Claude Opus 5.5.

@zeenix
zeenix force-pushed the ai/exciting-hawking-pxg3u0 branch from 252318b to 85ac9ae Compare November 5, 2026 14:59

@zeenix zeenix left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested.

Review by Codex on behalf of @zeenix.

The shared-instance fallback API fits the reported use case. One issue remains: ObjectManager omits inherited interfaces from children already registered in the object tree (inline finding).

Comment thread zbus/tests/object_server_fallback.rs Resolved
@zeenix
zeenix force-pushed the ai/exciting-hawking-pxg3u0 branch from 85ac9ae to 94a063d Compare November 5, 2026 17:32

zeenix commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

CodSpeed Macro Runners is red for a reason outside this PR. It fails instantly, before any benchmark runs, and its details link to CodSpeed's notice of an outage of their ARM64 macro runners (https://codspeed.io/changelog/2026-11-05-arm64-macro-runners-outage), which Run benchmarks (walltime) stays queued for. CodSpeed Performance Analysis, the simulation benchmarks and the rest of CI pass. There is nothing to fix here: it should clear on a re-run once those runners are back.

Generated by Claude Opus 5.5.


Generated by Claude Code

@zeenix zeenix left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested.

Review by Codex on behalf of @zeenix.

A mutable fallback handler can deadlock when it registers a managed child. There is also a small layout issue in the new test file; details inline.

Comment thread zbus/src/object_server/mod.rs Resolved
Comment thread zbus/tests/object_server_fallback.rs Resolved
`ObjectServer` only dispatched a call to an interface registered at the
exact path of the call, and replied with `UnknownObject` for any other
path. A service thus had to register each of its objects up front, which
it cannot do for objects whose paths are not known in advance, such as
one object per ID, unit or session, and which is wasteful for objects
only worth creating on demand. Replying to such calls outside of the
object server is not an option either, as the object server replies to
every method call once it runs.

`ObjectServer::at_fallback` and `connection::Builder::serve_at_fallback`
register an interface that serves its path and every path below it,
unless that path has an interface of the same name of its own, or a
closer ancestor has one registered as a fallback. This is what
`dbus_connection_register_fallback` of libdbus and
`sd_bus_add_fallback_vtable` of sd-bus provide. A path that only
fallbacks serve is an object too, with the standard interfaces.
`ObjectServer::interface` gets the interface that serves a path, be it
a fallback.

The objects registered below a fallback have it as well: their
introspection data lists it, and an `ObjectManager` reports it for them,
with its properties read at their paths, and emits `InterfacesAdded` and
`InterfacesRemoved` as registrations change what serves them. The paths
that only fallbacks serve are not enumerated, though.

The same instance serves all those paths, so it learns the path of a
method call from the header of the call, and that of a property access
from the path of its signal emitter.

Fixes z-galaxy#2112.

Assisted-by: Claude Opus 5.5 (claude-opus-5-5)
Point to `ObjectServer::at_fallback` from the service chapter, for
objects whose paths are not known in advance.

Assisted-by: Claude Opus 5.5 (claude-opus-5-5)
`Connection::object_server` starts the task that listens for method
calls in the background, and the connection drops the calls it reads
before that task listens, so their callers never get a reply. A service
that requests its well-known name after registering its interfaces is
not affected, as nobody can call it through that name before then, and
neither is one whose interfaces are registered through
`connection::Builder::serve_at`, which has the object server listen
before the connection reads any message. The peer of a peer-to-peer
connection, or a caller of the unique name of the connection, can call
in that window though. Document this, and point to the builder.

Assisted-by: Claude Opus 5.5 (claude-opus-5-5)
@zeenix
zeenix force-pushed the ai/exciting-hawking-pxg3u0 branch from 94a063d to 0cd03fa Compare November 5, 2026 20:50

@zeenix zeenix left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No actionable findings.

Review by Codex on behalf of @zeenix.

Reviewed the updated code and commit series at 0cd03fad.

@zeenix
zeenix merged commit 9fe1c1e into z-galaxy:main Nov 5, 2026
38 of 39 checks passed
@zeenix
zeenix deleted the ai/exciting-hawking-pxg3u0 branch November 5, 2026 23:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Support path fallbacks in ObjectServer (equivalent to dbus_connection_register_fallback)

1 participant