Describe the bug:
The issue here is that we are using an AtomicRefCell to control access to the ActorRegistry via a shared reference. When we try to read or write it when we are already writing it (i.e. registering an actor inside another actor register function), it panics.
There are a few things to solve here. First, the actor dependency graph we have at the moment is convoluted. The PR (peding) for fixing the inspector after navigation improves things, but it doesn't fix all cases. There are loops in actor dependencies for registration and tricky cases.
The second part is that ActorRegistry::register both creates an actor name, adds the actor to the registry, and returns the name. All of this must be done while holding a mutable lock. This is an issue for dependencies, since sometimes we are passing the current actor name to other new ones during registration, and then saving the other ones in the current actor. So using names as a handle makes this problematic situation easy to do.
Finally, we should probably move to something like RwLock to avoid panics, but only after we are sure that the registry is structured in a way that doesn't allow deadlocks.
I have been working on a series of patches to fix this issue, I'll be sending them incrementally. They are not complex patches, but because they change all the actors, the line count can get big quickly.
To Reproduce:
Difficult to reliably reproduce, as it is a racing condition. I have triggered this a few times last weeks for different places (mainly NodeActor and NetworkEventActor. It usually happens when loading very big pages with many actors.
Platform:
All platforms.
Stack trace:
[2026-05-21T10:41:11Z DEBUG devtools::actor] registering NodeActor223 (90f38d9fe8fd4042bbcf2e438a32281a)
already mutably borrowed (thread Devtools, at /home/eri/Code/igalia/servo/home/.local/share/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic_refcell-0.1.14/src/lib.rs:158)
0: servoshell::backtrace::print
at /home/eri/Code/igalia/servo/servo-main/ports/servoshell/backtrace.rs:18:5
1: servoshell::panic_hook::panic_hook
at /home/eri/Code/igalia/servo/servo-main/ports/servoshell/panic_hook.rs:40:17
2: core::ops::function::Fn::call
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:79:5
3: <alloc::boxed::Box<dyn for<'a, 'b> core::ops::function::Fn<(&'a std::panic::PanicHookInfo<'b>,), Output = ()> + core::marker::Sync + core::marker::Send> as core::ops::function::Fn<(&std::panic::PanicHookInfo,)>>::call
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/alloc/src/boxed.rs:2254:9
std::panicking::panic_with_hook
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/panicking.rs:833:13
4: std::panicking::panic_handler::{closure#0}
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/panicking.rs:698:13
5: std::sys::backtrace::__rust_end_short_backtrace::<std::panicking::panic_handler::{closure#0}, !>
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/sys/backtrace.rs:182:18
6: __rustc::rust_begin_unwind
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/panicking.rs:689:5
7: core::panicking::panic_fmt
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/core/src/panicking.rs:80:14
8: core::panicking::panic_display
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panicking.rs:259:5
9: atomic_refcell::AtomicRefCell<T>::borrow_mut
at /home/eri/Code/igalia/servo/home/.local/share/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/atomic_refcell-0.1.14/src/lib.rs:158:23
10: devtools::actor::ActorRegistry::register
at /home/eri/Code/igalia/servo/servo-main/components/devtools/actor.rs:178:14
11: devtools::actors::network_event::NetworkEventActor::register
at /home/eri/Code/igalia/servo/servo-main/components/devtools/actors/network_event.rs:546:18
12: devtools::DevtoolsInstance::create_network_event_actor
at /home/eri/Code/igalia/servo/servo-main/components/devtools/lib.rs:736:13
13: devtools::DevtoolsInstance::handle_network_event
at /home/eri/Code/igalia/servo/servo-main/components/devtools/lib.rs:719:26
14: devtools::DevtoolsInstance::run
at /home/eri/Code/igalia/servo/servo-main/components/devtools/lib.rs:405:26
15: devtools::start_server::{{closure}}::{{closure}}
at /home/eri/Code/igalia/servo/servo-main/components/devtools/lib.rs:136:38
16: profile_traits::mem::ProfilerChan::run_with_memory_reporting
at /home/eri/Code/igalia/servo/servo-main/components/shared/profile/mem.rs:143:9
17: devtools::start_server::{{closure}}
at /home/eri/Code/igalia/servo/servo-main/components/devtools/lib.rs:132:35
18: std::sys::backtrace::__rust_begin_short_backtrace
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:166:18
19: std::thread::lifecycle::spawn_unchecked::{{closure}}::{{closure}}
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/lifecycle.rs:91:13
20: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:274:9
21: std::panicking::catch_unwind::do_call
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:581:40
22: __rust_try
23: std::panicking::catch_unwind
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:544:19
std::panic::catch_unwind
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:359:14
std::thread::lifecycle::spawn_unchecked::{{closure}}
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/lifecycle.rs:89:26
24: core::ops::function::FnOnce::call_once{{vtable.shim}}
at /home/eri/Code/igalia/servo/home/.local/share/rustup/toolchains/1.95.0-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
25: <alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output = ()> + core::marker::Send> as core::ops::function::FnOnce<()>>::call_once
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/alloc/src/boxed.rs:2240:9
<std::sys::thread::unix::Thread>::new::thread_start
at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/sys/thread/unix.rs:118:17
26: <unknown>
27: <unknown>
Describe the bug:
The issue here is that we are using an
AtomicRefCellto control access to theActorRegistryvia a shared reference. When we try to read or write it when we are already writing it (i.e. registering an actor inside another actor register function), it panics.There are a few things to solve here. First, the actor dependency graph we have at the moment is convoluted. The PR (peding) for fixing the inspector after navigation improves things, but it doesn't fix all cases. There are loops in actor dependencies for registration and tricky cases.
The second part is that
ActorRegistry::registerboth creates an actor name, adds the actor to the registry, and returns the name. All of this must be done while holding a mutable lock. This is an issue for dependencies, since sometimes we are passing the current actor name to other new ones during registration, and then saving the other ones in the current actor. So using names as a handle makes this problematic situation easy to do.Finally, we should probably move to something like
RwLockto avoid panics, but only after we are sure that the registry is structured in a way that doesn't allow deadlocks.I have been working on a series of patches to fix this issue, I'll be sending them incrementally. They are not complex patches, but because they change all the actors, the line count can get big quickly.
To Reproduce:
Difficult to reliably reproduce, as it is a racing condition. I have triggered this a few times last weeks for different places (mainly
NodeActorandNetworkEventActor. It usually happens when loading very big pages with many actors.Platform:
All platforms.
Stack trace: