1
- use std:: cell:: Cell ;
1
+ //! This module provides a way to deal with self-referential data.
2
+ //!
3
+ //! The main idea is to allocate such data in a generator frame and then
4
+ //! give access to it by executing user-provided closures inside that generator.
5
+ //! The module provides a safe abstraction for the latter task.
6
+ //!
7
+ //! The interface consists of two exported macros meant to be used together:
8
+ //! * `declare_box_region_type` wraps a generator inside a struct with `access`
9
+ //! method which accepts closures.
10
+ //! * `box_region_allow_access` is a helper which should be called inside
11
+ //! a generator to actually execute those closures.
12
+
2
13
use std:: marker:: PhantomData ;
3
14
use std:: ops:: { Generator , GeneratorState } ;
4
15
use std:: pin:: Pin ;
@@ -14,24 +25,23 @@ impl AccessAction {
14
25
15
26
#[ derive( Copy , Clone ) ]
16
27
pub enum Action {
28
+ Initial ,
17
29
Access ( AccessAction ) ,
18
30
Complete ,
19
31
}
20
32
21
- thread_local ! ( pub static BOX_REGION_ARG : Cell <Action > = Cell :: new( Action :: Complete ) ) ;
22
-
23
33
pub struct PinnedGenerator < I , A , R > {
24
- generator : Pin < Box < dyn Generator < Yield = YieldType < I , A > , Return = R > > > ,
34
+ generator : Pin < Box < dyn Generator < Action , Yield = YieldType < I , A > , Return = R > > > ,
25
35
}
26
36
27
37
impl < I , A , R > PinnedGenerator < I , A , R > {
28
- pub fn new < T : Generator < Yield = YieldType < I , A > , Return = R > + ' static > (
38
+ pub fn new < T : Generator < Action , Yield = YieldType < I , A > , Return = R > + ' static > (
29
39
generator : T ,
30
40
) -> ( I , Self ) {
31
41
let mut result = PinnedGenerator { generator : Box :: pin ( generator) } ;
32
42
33
43
// Run it to the first yield to set it up
34
- let init = match Pin :: new ( & mut result. generator ) . resume ( ( ) ) {
44
+ let init = match Pin :: new ( & mut result. generator ) . resume ( Action :: Initial ) {
35
45
GeneratorState :: Yielded ( YieldType :: Initial ( y) ) => y,
36
46
_ => panic ! ( ) ,
37
47
} ;
@@ -40,21 +50,17 @@ impl<I, A, R> PinnedGenerator<I, A, R> {
40
50
}
41
51
42
52
pub unsafe fn access ( & mut self , closure : * mut dyn FnMut ( ) ) {
43
- BOX_REGION_ARG . with ( |i| {
44
- i. set ( Action :: Access ( AccessAction ( closure) ) ) ;
45
- } ) ;
46
-
47
- // Call the generator, which in turn will call the closure in BOX_REGION_ARG
48
- if let GeneratorState :: Complete ( _) = Pin :: new ( & mut self . generator ) . resume ( ( ) ) {
53
+ // Call the generator, which in turn will call the closure
54
+ if let GeneratorState :: Complete ( _) =
55
+ Pin :: new ( & mut self . generator ) . resume ( Action :: Access ( AccessAction ( closure) ) )
56
+ {
49
57
panic ! ( )
50
58
}
51
59
}
52
60
53
61
pub fn complete ( & mut self ) -> R {
54
62
// Tell the generator we want it to complete, consuming it and yielding a result
55
- BOX_REGION_ARG . with ( |i| i. set ( Action :: Complete ) ) ;
56
-
57
- let result = Pin :: new ( & mut self . generator ) . resume ( ( ) ) ;
63
+ let result = Pin :: new ( & mut self . generator ) . resume ( Action :: Complete ) ;
58
64
if let GeneratorState :: Complete ( r) = result { r } else { panic ! ( ) }
59
65
}
60
66
}
@@ -89,7 +95,7 @@ macro_rules! declare_box_region_type {
89
95
>) ;
90
96
91
97
impl $name {
92
- fn new<T : :: std:: ops:: Generator <Yield = $yield_type, Return = $retc> + ' static >(
98
+ fn new<T : :: std:: ops:: Generator <$crate :: box_region :: Action , Yield = $yield_type, Return = $retc> + ' static >(
93
99
generator: T
94
100
) -> ( $reti, Self ) {
95
101
let ( initial, pinned) = $crate:: box_region:: PinnedGenerator :: new( generator) ;
@@ -98,7 +104,7 @@ macro_rules! declare_box_region_type {
98
104
99
105
$v fn access<F : for <$( $lifetimes) * > FnOnce ( $( $args, ) * ) -> R , R >( & mut self , f: F ) -> R {
100
106
// Turn the FnOnce closure into *mut dyn FnMut()
101
- // so we can pass it in to the generator using the BOX_REGION_ARG thread local
107
+ // so we can pass it in to the generator
102
108
let mut r = None ;
103
109
let mut f = Some ( f) ;
104
110
let mut_f: & mut dyn for <$( $lifetimes) * > FnMut ( ( $( $args, ) * ) ) =
@@ -140,9 +146,9 @@ macro_rules! declare_box_region_type {
140
146
#[ macro_export]
141
147
#[ allow_internal_unstable( fn_traits) ]
142
148
macro_rules! box_region_allow_access {
143
- ( for ( $( $lifetimes: tt) * ) , ( $( $args: ty) ,* ) , ( $( $exprs: expr) ,* ) ) => {
149
+ ( for ( $( $lifetimes: tt) * ) , ( $( $args: ty) ,* ) , ( $( $exprs: expr) ,* ) , $action : ident ) => {
144
150
loop {
145
- match $crate :: box_region :: BOX_REGION_ARG . with ( |i| i . get ( ) ) {
151
+ match $action {
146
152
$crate:: box_region:: Action :: Access ( accessor) => {
147
153
let accessor: & mut dyn for <$( $lifetimes) * > FnMut ( $( $args) ,* ) = unsafe {
148
154
:: std:: mem:: transmute( accessor. get( ) )
@@ -152,10 +158,11 @@ macro_rules! box_region_allow_access {
152
158
let marker = $crate:: box_region:: Marker :: <
153
159
for <$( $lifetimes) * > fn ( ( $( $args, ) * ) )
154
160
>:: new( ) ;
155
- yield $crate:: box_region:: YieldType :: Accessor ( marker)
161
+ $action = yield $crate:: box_region:: YieldType :: Accessor ( marker) ;
156
162
} ;
157
163
}
158
164
$crate:: box_region:: Action :: Complete => break ,
165
+ $crate:: box_region:: Action :: Initial => panic!( "unexpected box_region action: Initial" ) ,
159
166
}
160
167
}
161
168
}
0 commit comments