1
- use std:: cell:: { Cell , UnsafeCell } ;
1
+ use std:: cell:: Cell ;
2
2
use std:: sync:: { Arc , PoisonError , RwLock } ;
3
3
use std:: thread;
4
4
5
5
use crate :: Scope ;
6
6
use crate :: { scope:: Stack , Client , Hub } ;
7
7
8
+ use arc_swap:: ArcSwap ;
8
9
use once_cell:: sync:: Lazy ;
9
10
10
11
static PROCESS_HUB : Lazy < ( Arc < Hub > , thread:: ThreadId ) > = Lazy :: new ( || {
@@ -15,9 +16,51 @@ static PROCESS_HUB: Lazy<(Arc<Hub>, thread::ThreadId)> = Lazy::new(|| {
15
16
} ) ;
16
17
17
18
thread_local ! {
18
- static THREAD_HUB : UnsafeCell <Arc <Hub >> = UnsafeCell :: new(
19
- Arc :: new( Hub :: new_from_top( & PROCESS_HUB . 0 ) ) ) ;
20
- static USE_PROCESS_HUB : Cell <bool > = Cell :: new( PROCESS_HUB . 1 == thread:: current( ) . id( ) ) ;
19
+ static THREAD_HUB : ( ArcSwap <Hub >, Cell <bool >) = (
20
+ ArcSwap :: from_pointee( Hub :: new_from_top( & PROCESS_HUB . 0 ) ) ,
21
+ Cell :: new( PROCESS_HUB . 1 == thread:: current( ) . id( ) )
22
+ ) ;
23
+ }
24
+
25
+ pub ( crate ) struct SwitchGuard {
26
+ inner : Option < ( Arc < Hub > , bool ) > ,
27
+ }
28
+
29
+ impl SwitchGuard {
30
+ pub ( crate ) fn new ( hub : Arc < Hub > ) -> Self {
31
+ let inner = THREAD_HUB . with ( |( old_hub, is_process_hub) | {
32
+ {
33
+ let old_hub = old_hub. load ( ) ;
34
+ if std:: ptr:: eq ( old_hub. as_ref ( ) , hub. as_ref ( ) ) {
35
+ return None ;
36
+ }
37
+ }
38
+ let old_hub = old_hub. swap ( hub) ;
39
+ let was_process_hub = is_process_hub. replace ( false ) ;
40
+ Some ( ( old_hub, was_process_hub) )
41
+ } ) ;
42
+ SwitchGuard { inner }
43
+ }
44
+
45
+ fn swap ( & mut self ) -> Option < Arc < Hub > > {
46
+ if let Some ( ( old_hub, was_process_hub) ) = self . inner . take ( ) {
47
+ Some ( THREAD_HUB . with ( |( hub, is_process_hub) | {
48
+ let hub = hub. swap ( old_hub) ;
49
+ if was_process_hub {
50
+ is_process_hub. set ( true ) ;
51
+ }
52
+ hub
53
+ } ) )
54
+ } else {
55
+ None
56
+ }
57
+ }
58
+ }
59
+
60
+ impl Drop for SwitchGuard {
61
+ fn drop ( & mut self ) {
62
+ let _ = self . swap ( ) ;
63
+ }
21
64
}
22
65
23
66
#[ derive( Debug ) ]
@@ -103,17 +146,13 @@ impl Hub {
103
146
where
104
147
F : FnOnce ( & Arc < Hub > ) -> R ,
105
148
{
106
- if USE_PROCESS_HUB . with ( Cell :: get) {
107
- f ( & PROCESS_HUB . 0 )
108
- } else {
109
- // note on safety: this is safe because even though we change the Arc
110
- // by temporary binding we guarantee that the original Arc stays alive.
111
- // For more information see: run
112
- THREAD_HUB . with ( |stack| unsafe {
113
- let ptr = stack. get ( ) ;
114
- f ( & * ptr)
115
- } )
116
- }
149
+ THREAD_HUB . with ( |( hub, is_process_hub) | {
150
+ if is_process_hub. get ( ) {
151
+ f ( & PROCESS_HUB . 0 )
152
+ } else {
153
+ f ( & hub. load ( ) )
154
+ }
155
+ } )
117
156
}
118
157
119
158
/// Binds a hub to the current thread for the duration of the call.
@@ -125,47 +164,8 @@ impl Hub {
125
164
/// Once the function is finished executing, including after it
126
165
/// paniced, the original hub is re-installed if one was present.
127
166
pub fn run < F : FnOnce ( ) -> R , R > ( hub : Arc < Hub > , f : F ) -> R {
128
- let mut restore_process_hub = false ;
129
- let did_switch = THREAD_HUB . with ( |ctx| unsafe {
130
- let ptr = ctx. get ( ) ;
131
- if std:: ptr:: eq ( & * * ptr, & * hub) {
132
- None
133
- } else {
134
- USE_PROCESS_HUB . with ( |x| {
135
- if x. get ( ) {
136
- restore_process_hub = true ;
137
- x. set ( false ) ;
138
- }
139
- } ) ;
140
- let old = ( * ptr) . clone ( ) ;
141
- * ptr = hub. clone ( ) ;
142
- Some ( old)
143
- }
144
- } ) ;
145
-
146
- match did_switch {
147
- None => {
148
- // None means no switch happened. We can invoke the function
149
- // just like that, no changes necessary.
150
- f ( )
151
- }
152
- Some ( old_hub) => {
153
- use std:: panic;
154
-
155
- // this is for the case where we just switched the hub. This
156
- // means we need to catch the panic, restore the
157
- // old context and resume the panic if needed.
158
- let rv = panic:: catch_unwind ( panic:: AssertUnwindSafe ( f) ) ;
159
- THREAD_HUB . with ( |ctx| unsafe { * ctx. get ( ) = old_hub } ) ;
160
- if restore_process_hub {
161
- USE_PROCESS_HUB . with ( |x| x. set ( true ) ) ;
162
- }
163
- match rv {
164
- Err ( err) => panic:: resume_unwind ( err) ,
165
- Ok ( rv) => rv,
166
- }
167
- }
168
- }
167
+ let _guard = SwitchGuard :: new ( hub) ;
168
+ f ( )
169
169
}
170
170
171
171
/// Returns the currently bound client.
0 commit comments