22
33use super :: * ;
44
5- use std:: cell:: RefCell ;
65use std:: marker:: PhantomData ;
76use std:: sync:: atomic:: AtomicU32 ;
87
@@ -175,6 +174,13 @@ macro_rules! define_client_side {
175174}
176175with_api ! ( self , self , define_client_side) ;
177176
177+ enum BridgeState < ' a , ' bridge > {
178+ NotConnected ,
179+ Connected ( & ' a mut Bridge < ' bridge > ) ,
180+ InUse ,
181+ }
182+
183+ #[ repr( align( 2 ) ) ]
178184struct Bridge < ' a > {
179185 /// Reusable buffer (only `clear`-ed, never shrunk), primarily
180186 /// used for making requests.
@@ -192,59 +198,70 @@ impl<'a> !Sync for Bridge<'a> {}
192198
193199#[ allow( unsafe_code) ]
194200mod state {
195- use super :: Bridge ;
196- use std:: cell:: { Cell , RefCell } ;
201+ use super :: { Bridge , BridgeState } ;
202+ use std:: cell:: Cell ;
197203 use std:: ptr;
198204
199- thread_local ! {
200- static BRIDGE_STATE : Cell <* const ( ) > = const { Cell :: new( ptr:: null( ) ) } ;
201- }
205+ struct RestoreOnDrop ( * mut ( ) ) ;
202206
203- pub ( super ) fn set < ' bridge , R > ( state : & RefCell < Bridge < ' bridge > > , f : impl FnOnce ( ) -> R ) -> R {
204- struct RestoreOnDrop ( * const ( ) ) ;
205- impl Drop for RestoreOnDrop {
206- fn drop ( & mut self ) {
207- BRIDGE_STATE . set ( self . 0 ) ;
208- }
207+ impl Drop for RestoreOnDrop {
208+ fn drop ( & mut self ) {
209+ BRIDGE_STATE . set ( self . 0 ) ;
209210 }
211+ }
210212
211- let inner = ptr:: from_ref ( state) . cast ( ) ;
213+ thread_local ! {
214+ static BRIDGE_STATE : Cell <* mut ( ) > = const { Cell :: new( ptr:: null_mut( ) ) } ;
215+ }
216+
217+ pub ( super ) fn connect < ' bridge , R > ( bridge : & mut Bridge < ' bridge > , f : impl FnOnce ( ) -> R ) -> R {
218+ let inner = ptr:: from_mut ( bridge) . cast ( ) ;
212219 let outer = BRIDGE_STATE . replace ( inner) ;
213220 let _restore = RestoreOnDrop ( outer) ;
214221
215222 f ( )
216223 }
217224
218- pub ( super ) fn with < R > (
219- f : impl for < ' bridge > FnOnce ( Option < & RefCell < Bridge < ' bridge > > > ) -> R ,
220- ) -> R {
221- let state = BRIDGE_STATE . get ( ) ;
222- // SAFETY: the only place where the pointer is set is in `set`. It puts
223- // back the previous value after the inner call has returned, so we know
224- // that as long as the pointer is not null, it came from a reference to
225- // a `RefCell<Bridge>` that outlasts the call to this function. Since `f`
226- // works the same for any lifetime of the bridge, including the actual
227- // one, we can lie here and say that the lifetime is `'static` without
228- // anyone noticing.
229- let bridge = unsafe { state. cast :: < RefCell < Bridge < ' static > > > ( ) . as_ref ( ) } ;
225+ pub ( super ) fn with < R > ( f : impl for < ' bridge > FnOnce ( BridgeState < ' _ , ' bridge > ) -> R ) -> R {
226+ // As `Bridge` has an alignment of 2, this cannot be a valid pointer.
227+ // Use it to indicate that the bridge is in use.
228+ let state = BRIDGE_STATE . replace ( ptr:: without_provenance_mut ( 1 ) ) ;
229+ let _restore = RestoreOnDrop ( state) ;
230+ let bridge = if state. is_null ( ) {
231+ BridgeState :: NotConnected
232+ } else if state. addr ( ) == 1 {
233+ BridgeState :: InUse
234+ } else {
235+ // SAFETY: the only place where the pointer is set is in `connect`.
236+ // It puts back the previous value after the inner call has returned,
237+ // so we know that as long as the pointer is valid, it came from a
238+ // mutable reference to a `Bridge` that outlasts the call to this
239+ // function. Since `f` must work for any lifetime of the bridge,
240+ // including the actual one, we can lie here and say that the
241+ // lifetime is `'static` without anyone noticing.
242+ BridgeState :: Connected ( unsafe { & mut * ( state as * mut Bridge < ' static > ) } )
243+ } ;
244+
230245 f ( bridge)
231246 }
232247}
233248
234249impl Bridge < ' _ > {
235250 fn with < R > ( f : impl FnOnce ( & mut Bridge < ' _ > ) -> R ) -> R {
236- state:: with ( |state| {
237- let bridge = state. expect ( "procedural macro API is used outside of a procedural macro" ) ;
238- let mut bridge = bridge
239- . try_borrow_mut ( )
240- . expect ( "procedural macro API is used while it's already in use" ) ;
241- f ( & mut bridge)
251+ state:: with ( |state| match state {
252+ BridgeState :: NotConnected => {
253+ panic ! ( "procedural macro API is used outside of a procedural macro" ) ;
254+ }
255+ BridgeState :: InUse => {
256+ panic ! ( "procedural macro API is used while it's already in use" ) ;
257+ }
258+ BridgeState :: Connected ( bridge) => f ( bridge) ,
242259 } )
243260 }
244261}
245262
246263pub ( crate ) fn is_available ( ) -> bool {
247- state:: with ( |s| s . is_some ( ) )
264+ state:: with ( |s| matches ! ( s , BridgeState :: Connected ( _ ) | BridgeState :: InUse ) )
248265}
249266
250267/// A client-side RPC entry-point, which may be using a different `proc_macro`
@@ -309,12 +326,12 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
309326 let ( globals, input) = <( ExpnGlobals < Span > , A ) >:: decode ( reader, & mut ( ) ) ;
310327
311328 // Put the buffer we used for input back in the `Bridge` for requests.
312- let state = RefCell :: new ( Bridge { cached_buffer : buf. take ( ) , dispatch, globals } ) ;
329+ let mut bridge = Bridge { cached_buffer : buf. take ( ) , dispatch, globals } ;
313330
314- let output = state:: set ( & state , || f ( input) ) ;
331+ let output = state:: connect ( & mut bridge , || f ( input) ) ;
315332
316333 // Take the `cached_buffer` back out, for the output value.
317- buf = RefCell :: into_inner ( state ) . cached_buffer ;
334+ buf = bridge . cached_buffer ;
318335
319336 // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
320337 // from encoding a panic (`Err(e: PanicMessage)`) to avoid
0 commit comments