-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Support for dynamic linking #6927
Comments
Hi Alice, Thanks for your quick response! That RFC (#6780) is interesting for dynamic library deployment in general but I'm not sure it's relevant to this problem, unless I'm missing something! Just to reiterate, my test:
As I mentioned, this is a specific problem with (I think) some kind of static data in the multi-threaded runtime, not one with dynamic linking in general. Thanks again! Paul (* actually it's not really Foreign at all, since it's calling a dylib, not a cdylib) |
I understand that the RFC solves a broader problem than your problem. But it would solve your problem. As for the single-threaded runtime, I'm pretty sure that you're just getting lucky. Both runtimes use a thread-local variable for the context, and the thread-local does not work if you use dylibs in a way that cause the application to contain several copies of Tokio. |
OK, thanks - I'll have to hold out hope that that gets adopted then! :-) I had just found this (runtime/context.rs:77) which seems the key to it, as you say:
What I don't understand, though, is why when I'm calling the This was interesting, though: "if you use dylibs in a way that cause the application to contain several copies of Tokio". Does that mean there's a way using using dylibs where Tokio is shared? |
The root cause is that you end up with several copies of the thread-local, and only one of them gets updated. I don't know if it's possible to compile the dylibs without getting multiple copies of Tokio. |
Hello @Darksonn and @sandtreader. Good morning/afternoon/evening. I came across the same problem (exactly the same actually, making a plugin management system) and while looking into it, I stumbled upon this issue. Just wanted to say that yes, this is possible, and it's actually a very simple solution, that shouldn't cause any breaking changes (I think). Bear with me for a bit, I'll mention this again at the end. In According to the reference:
For more details, if you haven't already, I really recommend taking a minute to read more in-depth how the compiler works this out. It's actually pretty smart about it and very cool! [lib]
crate-type = ["rlib", "dylib"] You can then proceed by normally linking it in your crate as a dependency, just like any other crate, while setting the appropriate flag for compilation, as mentioned above. Now then, for the possible drawback I hinted at when I said "I think". From what I read in the specs and tested locally, it really doesn't seem like I'd like to hear what you think, and thank you @Darksonn for maintaining this, Tokio is amazing! |
Wow, that's really interesting, thanks @oestradiol! That does sound like a get-out-of-jail-free card (although it leaves the issue of ABI, that's a different can of worms). It's a shame we have to fork Alternatively, @Darksonn what are the chances of making a dylib build standard? Pretty please :-) |
Well, with a little prompting, ChatGPT suggests a solution using a build.rs: https://chatgpt.com/share/672baa62-b284-800b-91d0-2f48aff57336 Basically do a local fork, either from Github or from cargo's source cache, and then hack it. I will play with this when I have time! |
I don't think this is a big issue tbh, if the ABI changes (it's unstable after all), all you'll need to do is recompile. And once you recompile, all the dependencies get recompiled too which should fix whatever issues you would have otherwise.
you don't need to force others to fork
I also want this. Would be great if possible! |
To clarify, the suggestion is to add this to our [lib]
crate-type = ["rlib", "dylib"] I will have to investigate what the implications of that are before I can say anything. |
While the thread local variable is the specific thing that you're seeing broken, what's happening here is a broader case of pointer incompatibility. What you've got today is a linkage that looks like:
You then call Switching to a dynamically linked tokio reduces the chances of this, because you get a pattern like this:
If Examples of things that can change the identity of these libraries include:
If you're doing it with a hot-reloading system, this is going to cause spooky bugs. For example:
Adding Finally - because |
@maurer Yeah, I was indeed kind of assuming without noticing that all of those conditions would be fulfilled. I see what you're talking about, and honestly, that's a big issue in this plugin mechanism that I'm not very sure how to solve hahah Thanks for warning me, I really appreciate it! |
Version
Platform
Debian 12
Linux j 6.1.0-17-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.69-1 (2023-12-30) x86_64 GNU/Linux
Description
Put simply, using Tokio multi-threaded runtime with dynamic libraries doesn't work, even if you explicitly pass it through to the library and call it directly. Single-threaded works fine.
I've created a test environment for this: https://github.com/sandtreader/rust-tokio-dylib with annotated code and a full explanation.
My guess is that the multi-threaded runtime is using some static data for its thread-pooling which is not encompassed by the runtime, and the dynamic library has a different copy of it. I've tried to remove the issue of the thread-local variables by passing the runtime directly to the library and calling it directly rather than implicitly with
tokio::spawn()
.I know this has been raised before (both here: #1964, and in async-ffi: oxalica/async-ffi#14) but I think it's important - a lot of larger systems use dynamic plugin mechanisms for flexibility and shorter build times, and it's a shame that Tokio (at least in multi-threaded form) can't be used in these. I'm certainly prepared to put effort into resolving it, although I know little about Tokio internals at the moment!
Just one more thing - yes I know the whole area of dynamic libraries in Rust has major issues around the unstable ABI. In my particular use case this isn't a major problem because it's mostly there for compositional flexibility and build times within a single workspace, but there are also possible solutions like
abi_stable
or reducing the interface to C-only. This static data problem (if that's what it is) is an orthgonal issue.Many thanks!
Paul
The text was updated successfully, but these errors were encountered: