-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
bug: Fix stackoverflow on asset reload. #21619
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
base: main
Are you sure you want to change the base?
bug: Fix stackoverflow on asset reload. #21619
Conversation
|
This seems like a reasonable fix but I'd really like to add a test along with this PR. |
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.
This also seems sensible to me, but I would also like to block on a test for this.
|
Understood. Are there any asset-loader tests that I might emulate? |
|
Pretty much all the tests in bevy/crates/bevy_asset/src/lib.rs Line 1918 in 3dd5d70
|
|
Thank you for the test reference. They've been very helpful. Coming up with a test has been tricky but instructive. I finally figured out how to provoke the error. If I use an immediate load in an asset loader that tries to load its own asset path, it will cause a stack overflow, which is the behavior I originally saw. A asset loader with a self-path deferred load's does not provoke an error or overflow, but it does behave differently than a normal asset. It will emit asset I'll try to get these tests in with some better fixes tomorrow night. |
90a37d4 to
36c5537
Compare
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.
Seems good to me! Thank you!
|
Might need a rebase after #21626. |
|
@shanecelis let me know when CI is green and I'll do a proper review for you. |
|
Thanks. It looks better. In the tests I've tried to exercise asset loader that loads its own path in deferred, immediate, and unknown type, but I fear I've made it too restrictive now. If you exercise the tests with only the first commit, you'll see a stackoverflow error. I'm not certain it's the same stackoverflow I observed in the wild. I'm marking this as a draft for the moment because I'm running into an issue when I use it in Nano-9. I have an asset loader for a I think if a path is being loaded from a different Let me tinker on this a little longer. |
455d817 to
83df589
Compare
Test for deferred, immediate, and unknown type. test: Add self-load test for read_asset_bytes(). test: Use send_blocking. test: Clippy up.
Warn on deferred load of self asset path. feat: Permit read_asset_bytes for self dep. Don't record a loader_dependency for it, however. style: Reformat.
83df589 to
ab71ad1
Compare
|
@alice-i-cecile, this PR is now ready. I added a test to cover the I left an I'm not certain The deferred loaders will work with self-paths and be given the same handle. This PR merely emit a warning when this is detected:
I'm not entirely convinced we ought to emit this warning since it works and we're now avoiding the reload bug. Also the message could be clearer. What's it ignoring? (Adding itself as a dependent.) This depends mostly on whether and how strongly you want to discourage self-path loads. |
Rename error AssetDependentOnSelf to LoadSelfPath. Use debug! instead of warn! on deferred load which no longer induces an error.
|
I took my own suggestions from my last comments and applied them thusly:
I changed the error name from being about the negative effect of polluting dependencies that caused the stackoverflow, which are now avoided in this PR, to being about the behavior—a load of self-path—that causes the error. |
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.
One thing that I think is missing here is that it only fixes the "shallow" case. If instead we have something like a load chain like root -> intermediate -> root, suddenly we're back to the stack overflow right?
crates/bevy_asset/src/lib.rs
Outdated
| let (mut app, dir, _source_events) = create_app_with_source_event_sender(); | ||
| let asset_server = app.world().resource::<AssetServer>().clone(); | ||
|
|
||
| dir.insert_asset_text(Path::new("abc.cool.ron"), ""); |
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.
Can we use a different extension, just because in a lot of these asset tests cool.ron refers to CoolText assets. Whatever extension you want!
Repeat below.
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.
Sure thing.
| let test_assets = world.resource::<Assets<TestAssetUD>>(); | ||
| let asset = test_assets.get(&handle).unwrap(); | ||
| assert_eq!(handle.id(), asset.0.id().typed_unchecked::<TestAssetUD>()); | ||
| // This one fails. |
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 don't understand this. Why does this fail? The handle inside the asset should produce the same handle, so the asset type should be the same? Documenting that here would be useful.
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 tried going from my typed handle to untyped for the comparison.
assert_eq!(handle.clone().untyped(), asset.0);This fails with the following message:
thread 'tests::no_error_on_unknown_type_deferred_load_of_self_dependency' panick
ed at crates/bevy_asset/src/lib.rs:2322:17:
assertion `left == right` failed
left: StrongHandle{ type_id: TypeId(0x7872585c49bdc8219114f2b910448400), id: A
ssetIndex { generation: 0, index: 0 }, path: Some(abc.cool.ron) }
right: StrongHandle{ type_id: TypeId(0xccdca3e7c51e455a9fb8881ade004c9b), id: A
ssetIndex { generation: 0, index: 0 }, path: Some(--untyped://abc.cool.ron) }
It's the same AssetIndex but not the same type or path. I haven't done a lot of work with untyped assets, so maybe this is due to my inexperience more than anything.
|
Correct. |
|
I'm fine to merge this as an easy fix without dealing with the "deep" case, but can you file an issue to track the deep case and then add a TODO on the loading checks to indicate we need a more comprehensive fix? |
|
Will file an issue. I changed the extension in the tests to '.rsp' (Recursive Self Path). I changed the error name from
|
Objective
Fix the stackoverflow on asset reload when asset contains its own path as a dependency.
Problem
There is a way to create a circular dependency graph with an asset loader. I don't know how I managed to do it. I have tried to create a minimal example, but it has not exhibited the error yet. But I do have a means of exhibiting the error with my project Nano-9, reproduction details below.
Solution
This commit has two fixes: one at the insertion point (introducing self-reference), and one at the recursion point (following self-reference).
Insertion point
Issue warning when an asset wants to mark itself as a dependency and do not allow inserting itself as a dependent.
Recursion point
Check for self loops. Warn on self detection and do not loop.
It's likely that if you fix it at the insertion point, you don't need to worry about it at the recursion point. You will have stopped the cause of the issue. I left both in for transparency about where the issue lies so far as I could see.
Testing
I can reproduce this error with an example from my Nano-9 project. I wish it were a minimal example. It's not, but I have put it on a branch to isolate this issue. It uses my Bevy fork that is v0.16.1 plus a commit tagged v0.16.1b, which was required for it to build. This PR is a cherry pick of the fix commit against Bevy's main branch.
I can reproduce this error by doing the following:
Here is an excerpt of the crash report on macOS 15.6.1, M4 Max:
Exercising the fix
Alter Nano-9's Cargo file to use the fix branch:
Run the same command again, and you will see a warning:
2025-10-21T04:10:52.348726Z WARN bevy_asset::server::info: Asset 'BirdSprite.png' wants to treat itself as a dependencyAlternative Solution
This commit fixes the immediate stackoverflow issue; however, it would be even better if the user were prevented from making a circular dependency graph in the first place.