2
2
3
3
use super :: * ;
4
4
5
- use std:: cell:: RefCell ;
6
5
use std:: marker:: PhantomData ;
7
6
use std:: sync:: atomic:: AtomicU32 ;
8
7
@@ -175,6 +174,13 @@ macro_rules! define_client_side {
175
174
}
176
175
with_api ! ( self , self , define_client_side) ;
177
176
177
+ enum BridgeState < ' a , ' bridge > {
178
+ NotConnected ,
179
+ Connected ( & ' a mut Bridge < ' bridge > ) ,
180
+ InUse ,
181
+ }
182
+
183
+ #[ repr( align( 2 ) ) ]
178
184
struct Bridge < ' a > {
179
185
/// Reusable buffer (only `clear`-ed, never shrunk), primarily
180
186
/// used for making requests.
@@ -192,59 +198,70 @@ impl<'a> !Sync for Bridge<'a> {}
192
198
193
199
#[ allow( unsafe_code) ]
194
200
mod state {
195
- use super :: Bridge ;
196
- use std:: cell:: { Cell , RefCell } ;
201
+ use super :: { Bridge , BridgeState } ;
202
+ use std:: cell:: Cell ;
197
203
use std:: ptr;
198
204
199
- thread_local ! {
200
- static BRIDGE_STATE : Cell <* const ( ) > = const { Cell :: new( ptr:: null( ) ) } ;
201
- }
205
+ struct RestoreOnDrop ( * mut ( ) ) ;
202
206
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 ) ;
209
210
}
211
+ }
210
212
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 ( ) ;
212
219
let outer = BRIDGE_STATE . replace ( inner) ;
213
220
let _restore = RestoreOnDrop ( outer) ;
214
221
215
222
f ( )
216
223
}
217
224
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
+
230
245
f ( bridge)
231
246
}
232
247
}
233
248
234
249
impl Bridge < ' _ > {
235
250
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) ,
242
259
} )
243
260
}
244
261
}
245
262
246
263
pub ( crate ) fn is_available ( ) -> bool {
247
- state:: with ( |s| s . is_some ( ) )
264
+ state:: with ( |s| matches ! ( s , BridgeState :: Connected ( _ ) | BridgeState :: InUse ) )
248
265
}
249
266
250
267
/// 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<()>>(
309
326
let ( globals, input) = <( ExpnGlobals < Span > , A ) >:: decode ( reader, & mut ( ) ) ;
310
327
311
328
// 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 } ;
313
330
314
- let output = state:: set ( & state , || f ( input) ) ;
331
+ let output = state:: connect ( & mut bridge , || f ( input) ) ;
315
332
316
333
// Take the `cached_buffer` back out, for the output value.
317
- buf = RefCell :: into_inner ( state ) . cached_buffer ;
334
+ buf = bridge . cached_buffer ;
318
335
319
336
// HACK(eddyb) Separate encoding a success value (`Ok(output)`)
320
337
// from encoding a panic (`Err(e: PanicMessage)`) to avoid
0 commit comments