1- use std:: cell:: { Cell , UnsafeCell } ;
1+ use std:: cell:: { Cell , RefCell } ;
22use std:: marker:: PhantomData ;
33use std:: sync:: { Arc , LazyLock , MutexGuard , PoisonError , RwLock } ;
44use std:: thread;
@@ -14,8 +14,8 @@ static PROCESS_HUB: LazyLock<(Arc<Hub>, thread::ThreadId)> = LazyLock::new(|| {
1414} ) ;
1515
1616thread_local ! {
17- static THREAD_HUB : ( UnsafeCell <Arc <Hub >>, Cell <bool >) = (
18- UnsafeCell :: new( Arc :: new( Hub :: new_from_top( & PROCESS_HUB . 0 ) ) ) ,
17+ static THREAD_HUB : ( RefCell <Arc <Hub >>, Cell <bool >) = (
18+ RefCell :: new( Arc :: new( Hub :: new_from_top( & PROCESS_HUB . 0 ) ) ) ,
1919 Cell :: new( PROCESS_HUB . 1 == thread:: current( ) . id( ) )
2020 ) ;
2121}
@@ -50,13 +50,11 @@ impl SwitchGuard {
5050 /// to the previous one.
5151 pub fn new ( mut hub : Arc < Hub > ) -> Self {
5252 let inner = THREAD_HUB . with ( |( thread_hub, is_process_hub) | {
53- // SAFETY: `thread_hub` will always be a valid thread local hub,
54- // by definition not shared between threads.
55- let thread_hub = unsafe { & mut * thread_hub. get ( ) } ;
53+ let mut thread_hub = thread_hub. borrow_mut ( ) ;
5654 if std:: ptr:: eq ( thread_hub. as_ref ( ) , hub. as_ref ( ) ) {
5755 return None ;
5856 }
59- std:: mem:: swap ( thread_hub, & mut hub) ;
57+ std:: mem:: swap ( & mut * thread_hub, & mut hub) ;
6058 let was_process_hub = is_process_hub. replace ( false ) ;
6159 Some ( ( hub, was_process_hub) )
6260 } ) ;
@@ -69,8 +67,8 @@ impl SwitchGuard {
6967 fn swap ( & mut self ) -> Option < Arc < Hub > > {
7068 if let Some ( ( mut hub, was_process_hub) ) = self . inner . take ( ) {
7169 Some ( THREAD_HUB . with ( |( thread_hub, is_process_hub) | {
72- let thread_hub = unsafe { & mut * thread_hub. get ( ) } ;
73- std:: mem:: swap ( thread_hub, & mut hub) ;
70+ let mut thread_hub = thread_hub. borrow_mut ( ) ;
71+ std:: mem:: swap ( & mut * thread_hub, & mut hub) ;
7472 if was_process_hub {
7573 is_process_hub. set ( true ) ;
7674 }
@@ -171,7 +169,11 @@ impl Hub {
171169 if is_process_hub. get ( ) {
172170 f ( & PROCESS_HUB . 0 )
173171 } else {
174- f ( unsafe { & * hub. get ( ) } )
172+ // Bind `hub` as `hub.borrow().clone()`.
173+ // It is essential we drop `hub.borrow()` before the callback, otherwise we will
174+ // panic if the callback calls `Hub::run`, so we need the binding.
175+ let hub = hub. borrow ( ) . clone ( ) ;
176+ f ( & hub)
175177 }
176178 } )
177179 }
@@ -214,3 +216,21 @@ impl Hub {
214216 . with_mut ( |stack| f ( Arc :: make_mut ( & mut stack. top_mut ( ) . scope ) ) )
215217 }
216218}
219+
220+ #[ cfg( test) ]
221+ mod tests {
222+ use super :: * ;
223+
224+ /// Regression test for [`Hub::with`], ensuring that the `RefCell` borrow is not held during the callback.
225+ ///
226+ /// If we hold the `RefCell` borrow during the callback, this would panic.
227+ #[ test]
228+ fn hub_run_inside_with_scope ( ) {
229+ let outer_hub = Arc :: new ( Hub :: new ( None , Default :: default ( ) ) ) ;
230+ let inner_hub = Arc :: new ( Hub :: new ( None , Default :: default ( ) ) ) ;
231+
232+ Hub :: run ( outer_hub, || {
233+ Hub :: with ( |_| Hub :: run ( inner_hub, || { } ) ) ;
234+ } ) ;
235+ }
236+ }
0 commit comments