-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
bevy_dynamic_plugin: Don't leak memory #6705
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how much cleaner this is. And avoiding leaking memory could be useful for modding.
However, I'm definitely nervous about the long list of non-local invariants that must be upheld to avoid UB.
That's largely the story of this whole crate though, so my official stance on this PR is "neutral".
This is definitely an issue - as The safety of the running an app with dynamic plugins relies on the safety calling The safety notes on The only more complete solution i can conceive involves obfuscating dynamically-defined types and providing dynamic plugins with a more carefully controlled app scope. I think for now I will change this PR to be leaky by default, with opt-in automatic dropping. |
b6a8662
to
80ab1b3
Compare
anyone know what this ci failure is about? |
Not your fault: there's duplicate dependencies in tree due to staggered updates. It won't block merging. |
c035dde
to
e1ddf8d
Compare
Hmm. This is getting a little over my head now. I don't fully understand all the safety implications here. How is this intended to be used exactly? If I'm understanding correctly, this PR adds a mechanism for unloading plugins. I cannot really imagine what that would look like in real user code. |
If this was used for modding, unloading plugins would be useful for disabling mods without a game restart. |
I understand the benefits of "unloading plugins" as a feature. I meant that I don't understand how this particular API is to be used, and the safety implications of it. |
as it stands i'm not very happy with the api/implementation of this PR. i'm taking another look at the moment to see if i can come up with a better design. |
0daa0e7
to
edc4488
Compare
an example might help: use bevy_dynamic_plugin::{DynamicPluginLibraries, DynamicPluginExt};
use bevy_app::App;
use bevy_ecs::system::ResMut;
const LIB_PATH: &str = "./libmy_dyn_plugin.so";
let mut app = App::new();
unsafe { app.load_plugin(LIB_PATH)
app.add_system(remove_library);
fn remove_library(mut libs: ResMut<DynamicPluginLibraries>) {
// use arbitrary logic to decide whether to unload the library:
if 1 != 2 {
// SAFETY: No resources or components that contain function pointers remain after the app is dropped
// and so code from the library cannot be called after the app is dropped.
// Additionally, we have checked './libmy_dyn_plugin.so' and ensured it does not cause UB when unloaded.
unsafe { libs.mark_for_unloading(LIB_PATH) };
// library is still loaded at this point.
}
}
app.run();
// library is no longer loaded. |
Objective
bevy_dynamic_plugin::dynamically_load_plugin
callsstd::mem::forget
on thelibloading::Library
instance. An escape hatch should be added to allow users to more easily handle the unloading of dynamic plugins.Solution
Add a
DynamicPlugin
struct which can be dropped manually.Add a new resource
DynamicPluginLibraries
which allows users to (unsafely) mark libraries for automatic unloading withDynamicPluginLibraries::mark_for_unloading
. The library will be dropped when the associatedDynamicPlugin
is or whenmark_for_unloading
is called, whichever happens last.Changelog
DynamicPlugin
.DynamicPluginLibraries
.dynamically_load_plugin
toDynamicPlugin
.bevy_ecs
andbevy_utils
dependencies forbevy_dynamic_plugin
.Migration Guide
dynamically_load_plugin
now returns aDynamicPlugin
instead of a tuple. CallDynamicPlugin::into_raw_parts
andArc::try_unwrap
on the resulting library if you need the old behavior.