Description
The issue was originally posted in the discord thread and some initial testing was executed. Below I will share the original question and initiali findings. Issue is similiar to #597 as it invokes the same multithreading problem with Resource
handling of Godot, but contrary to the former, there is no custom ResourceFormatLoader
there, only custom Resource
(so it is saved as .tres
and .res
formats) - for this reason, I believe it is worthy to have this as a separate Issue here.
Example and initial data:
struct Test {}
#[gdextension]
unsafe impl ExtensionLibrary for Test {}
#[derive(GodotClass)]
#[class(base=Node)]
struct TestNode {
#[export]
nested_resources: Gd<ParentResource>,
}
#[godot_api]
impl INode for TestNode {
fn init(_base: Base<Node>) -> Self {
Self {
nested_resources: Gd::from_init_fn(ParentResource::init),
}
}
}
#[derive(GodotClass)]
#[class(base=Resource)]
struct ParentResource {
base: Base<Resource>,
#[export]
sub_resource: Gd<SubResource>,
}
#[godot_api]
impl IResource for ParentResource {
fn init(base: Base<Resource>) -> Self {
Self {
base,
sub_resource: Gd::from_init_fn(SubResource::init),
}
}
}
#[derive(GodotClass)]
#[class(base=Resource)]
struct SubResource {
#[export]
value: i32,
base: Base<Resource>,
}
#[godot_api]
impl IResource for SubResource {
fn init(base: Base<Resource>) -> Self {
Self { value: 50, base }
}
}
Above code panicked with stack trace:
<unnamed>' panicked at ~/.cargo/git/checkouts/gdext-76630c89719e160c/b4a91a6/godot-core/src/lib.rs:160:43:
no panic info available
stack backtrace:
0: rust_begin_unwind
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
1: core::panicking::panic_fmt
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
2: core::panicking::panic_display
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:196:5
3: core::panicking::panic_str
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:171:5
4: core::option::expect_failed
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/option.rs:1980:5
5: core::option::Option<T>::expect
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/option.rs:894:21
6: godot_core::private::handle_panic
at ~/.cargo/git/checkouts/gdext-76630c89719e160c/b4a91a6/godot-core/src/lib.rs:160:28
7: <rust::ParentResource as godot_core::obj::traits::cap::ImplementsGodotExports>::__register_exports::function
at ./rust/src/lib.rs:24:10
8: <unknown>
9: <unknown>
10: <unknown>
11: <unknown>
12: <unknown>
13: <unknown>
14: <unknown>
15: <unknown>
16: <unknown>
17: <unknown>
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
fatal runtime error: failed to initiate panic, error 5
With error message introduced in #581
assertion `left == right` failed: attempted to access binding from different thread than main thread; this is UB - use the "experimental-threads" feature.
left: ThreadId(1)
right: ThreadId(2)
Removing the #[export]
annotation from ParentResource::sub_resource
field stop the error from happening, but also removes the possibility of assigning SubResource
in the Godot Editor AND saving the ParentResource
with SubResource
data, either as Bundled or External Resource.
Initial hypothesis and findings
My initial hypothesis was that the user code was in some way callled by Godot Editor, similiar to mentioned ResourceFormatLoader
issue. To check it, the simple thread printing function was added to init
function of all structs:
pub(crate) fn print_thread(class: &str, method: &str) {
let thread_id = Os::singleton().get_thread_caller_id();
godot_print!("Thread: {thread_id}: {class}::{method}");
}
#[godot_api]
impl IResource for SubResource {
fn init(base: Base<Resource>) -> Self {
print_thread("SubResource", "init");
Self { value: 50, base }
}
}
The thread number that were always printed was 1
though, contrary to mentioned issue, so it seems that the secondary thread was used within no user defined code (at least not explicitly).
Additionally, the same nested export situation was tested with Node
-inheriting classes instead of Resource
. Panic wasn't happening, so it should be specific to Resources.