-
-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Current Transport Provider architecture makes it hard to synchronise bootstrapping #213
Comments
Uff... this is a really big change. Creating the accepting connection is done by the actual transport (native/jnr/tcp). So the blocking will happen there. I may add the required changes and for 5.x. Just keep an eye on the 5.x-develop branch. |
Heh, yup understood, was expecting such a response :) It's something that's been bothering me for a while, I think I can wait. I hadn't noticed you'd started on a new major version, will definitely keep an eye on it. Most of the projects I use dbus-java in use Java 17 anyway, if not for source then at least at runtime. I look forward to it. |
I've just taken a look at the latest version 5 wrt to this change, and I think we are kind of half way there now? As I understand it, the |
The idea was to set Working with Someone has to call the Any suggestion? |
I still think it should be possible to separate the binding of the listening socket and the accepting of client socket from that listening socket, but maybe something else can be done in To recap, the architecture of my app is ..
The setup code for the embedded daemon looks something like. daemon = new EmbeddedDBusDaemon(listenBusAddress);
daemon.setSaslAuthMode(authMode);
daemon.startInBackground();
/* The first ugly hack - sleep some magic amount of time to guarantee there is something listening for connections */
Thread.sleep(1000);
/* Despite the above, we still sometimes saw the above taking more than one second on slow machines, so to reliably
get a connection we loop until it fails. And no, we can't just do this without the above sleep as well, as strange things
occasionally happen */
long expire = TimeUnit.SECONDS.toMillis(15) + System.currentTimeMillis();
while(System.currentTimeMillis() < expire) {
try {
conn = configureBuilder(DBusConnectionBuilder.forAddress(busAddress)).build();
log.info("Connected to embedded DBus {}", busAddress);
break;
} catch (DBusException dbe) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
/* Now we need to loop to request a bus name too. Without this, its possible we try to request the bus name
before the "Hello" message message has been sent. */
while(true) {
try {
((DBusConnection)conn).requestBusName("com.logonbox.vpn");
break;
}
catch(DBusException dbe) {
if(!(dbe.getCause() instanceof AccessDenied))
throw dbe;
Thread.sleep(500);
}
}
/* It's finally safe to actually start exporting services on this bus. But be aware, its possible that a client managed to
connect at this point, and sees the bus name, but doesn't yet see the exported services. So the client also needs to
retry if this happens */
exportServices(); So what I would like to see at this level, is something more like this. daemon = new EmbeddedDBusDaemon(listenBusAddress);
daemon.setSaslAuthMode(authMode);
// after "startInBackground()", I want guarantees i (or other clients) can immediately create a good connection
daemon.startInBackground();
// after this "build()", i want guarantees i can immediately use this connection with no errors
conn = configureBuilder(DBusConnectionBuilder.forAddress(busAddress)).build();
log.info("Connected to embedded DBus {}", busAddress);
// i want this bus name request to always succeed without retrying
((DBusConnection)conn).requestBusName("com.logonbox.vpn");
// And now export everything. As for other clients who happen to connect during the above, I
// can live with them having to retry if the expected service is not there, as long as the connection
// is *good*
exportServices(); As far as I can make, this would still not be possible with version 5. As you can see, the entry point for this problem is really kind of daemon = new EmbeddedDBusDaemon(listenBusAddress);
daemon.setSaslAuthMode(authMode);
daemon.startInForeground(() -> {
/* This callback is called when the daemon is completely ready to accept a connection */
// do all the other setup stuff
})); |
…callback after bind has been called (also usable in EmbeddedDBusDaemon)
I just pushed changes related to this. First of all, calling Speaking of This should eliminate the needs for random sleeps. I'm not sure if this fixes other problems you mentioned, but we'll see. |
Awesome. It works well. I don't seem to be seeing the problem with the request of the bus name happening before "Hello" either. I'll be working with this code over the coming weeks and it will get some solid testing, so am sure I will see it if it does still happen. Thanks as always for your work, this is a nice improvement to using the embedded daemon. |
5.0.0 is now released. |
While using dbus-java for simple access to an existing broker and exported services is very easy, I have found that some more complex uses require additional code and considerations that ideally could be better dealt with by dbus-java itself.
This has mostly turned up when using the embedded broker on non-Linux systems, where I bundle the broker along with a service. The service will attempt to start a broker, export some services on it, and then wait for a client to connect to the bus all in one JVM. All the while, a client will be attempting to connect to the bus from another JVM. If it happens to manage to make a physical connection to the bus, before the service has finished setting up (exporting services etc), then I may hit problems. The necessitates code (
Thread.sleep
s, polling code, excessive error checking) to deal with this.A similar problem exists when trying to use
DirectConnection
, either in the same JVM or split over two or more. This is demonstrated byTestPeer2Peer
. It only works as well as it does because there are a couple ofThread.sleeps()
in there.The crux of the problem is the blocking nature of the
DirectConnectionBuilder.build()
on the listening side. This will not return until the first client connection is received, so it is not possible to export services and have them ready for exactly when the client is "allowed in".While
withAutoConnect(false)
helps, in that at least you can get the connection instance without waiting for a client, you still cannot easily synchronise reliably.Going back to basics, this is a pattern I have used often for client/server sockets. It makes use of the natural blocking you get with a socket, and is totally reliable with no sleeps necessary for it to work (the Thread.sleep is just simulating work).
As it stands, this pattern is not possible with dbus-java due to it combining the binding and accepting of the sockets when dealing with listening sockets.
So in short, I think I am asking if you think this can decoupled, so it is possible to get a
DirectConnection
(orDBusConnection
) for a listening transport and have it's listening socket bound, but not yet accepting until instructed to do so.The text was updated successfully, but these errors were encountered: