1
1
#![ allow( clippy:: tabs_in_doc_comments) ]
2
- #![ cfg_attr( feature = "unstable-thread-local" , feature( thread_local) ) ]
3
- #![ cfg_attr( all( not( test) , feature = "unstable-thread-local" ) , no_std) ]
2
+ #![ cfg_attr( not( test) , no_std) ]
4
3
4
+ extern crate alloc;
5
5
extern crate core;
6
6
7
+ use alloc:: sync:: { Arc , Weak } ;
7
8
use core:: {
8
9
cell:: Cell ,
9
10
future:: Future ,
10
11
marker:: PhantomData ,
11
12
pin:: Pin ,
12
- ptr ,
13
+ sync :: atomic :: { AtomicBool , Ordering } ,
13
14
task:: { Context , Poll }
14
15
} ;
15
16
@@ -19,91 +20,96 @@ use futures_core::stream::{FusedStream, Stream};
19
20
mod tests;
20
21
mod r#try;
21
22
22
- # [ cfg ( not ( feature = "unstable-thread-local" ) ) ]
23
- thread_local ! {
24
- static STORE : Cell <* mut ( ) > = const { Cell :: new ( ptr :: null_mut ( ) ) } ;
23
+ pub ( crate ) struct SharedStore < T > {
24
+ entered : AtomicBool ,
25
+ cell : Cell < Option < T > >
25
26
}
26
- #[ cfg( feature = "unstable-thread-local" ) ]
27
- #[ thread_local]
28
- static STORE : Cell < * mut ( ) > = Cell :: new ( ptr:: null_mut ( ) ) ;
29
27
30
- pub ( crate ) fn r#yield < T > ( value : T ) -> YieldFut < T > {
31
- YieldFut { value : Some ( value) }
28
+ impl < T > Default for SharedStore < T > {
29
+ fn default ( ) -> Self {
30
+ Self {
31
+ entered : AtomicBool :: new ( false ) ,
32
+ cell : Cell :: new ( None )
33
+ }
34
+ }
35
+ }
36
+
37
+ impl < T > SharedStore < T > {
38
+ pub fn has_value ( & self ) -> bool {
39
+ unsafe { & * self . cell . as_ptr ( ) } . is_some ( )
40
+ }
41
+ }
42
+
43
+ unsafe impl < T > Sync for SharedStore < T > { }
44
+
45
+ pub struct Yielder < T > {
46
+ pub ( crate ) store : Weak < SharedStore < T > >
47
+ }
48
+
49
+ impl < T > Yielder < T > {
50
+ pub fn r#yield ( & self , value : T ) -> YieldFut < ' _ , T > {
51
+ #[ cold]
52
+ fn invalid_usage ( ) -> ! {
53
+ panic ! ( "attempted to use async_stream_lite yielder outside of stream context or across threads" )
54
+ }
55
+
56
+ let Some ( store) = self . store . upgrade ( ) else {
57
+ invalid_usage ( ) ;
58
+ } ;
59
+ if !store. entered . load ( Ordering :: Relaxed ) {
60
+ invalid_usage ( ) ;
61
+ }
62
+
63
+ store. cell . replace ( Some ( value) ) ;
64
+
65
+ YieldFut { store, _p : PhantomData }
66
+ }
32
67
}
33
68
34
69
/// Future returned by an [`AsyncStream`]'s yield function.
35
70
///
36
71
/// This future must be `.await`ed inside the generator in order for the item to be yielded by the stream.
37
72
#[ must_use = "stream will not yield this item unless the future returned by yield is awaited" ]
38
- pub struct YieldFut < T > {
39
- value : Option < T >
73
+ pub struct YieldFut < ' y , T > {
74
+ store : Arc < SharedStore < T > > ,
75
+ _p : PhantomData < & ' y ( ) >
40
76
}
41
77
42
- impl < T > Unpin for YieldFut < T > { }
78
+ impl < T > Unpin for YieldFut < ' _ , T > { }
43
79
44
- impl < T > Future for YieldFut < T > {
80
+ impl < T > Future for YieldFut < ' _ , T > {
45
81
type Output = ( ) ;
46
82
47
- fn poll ( mut self : Pin < & mut Self > , _cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
48
- if self . value . is_none ( ) {
83
+ fn poll ( self : Pin < & mut Self > , _cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
84
+ if ! self . store . has_value ( ) {
49
85
return Poll :: Ready ( ( ) ) ;
50
86
}
51
87
52
- fn op < T > ( cell : & Cell < * mut ( ) > , value : & mut Option < T > ) {
53
- let ptr = cell. get ( ) . cast :: < Option < T > > ( ) ;
54
- let option_ref = unsafe { ptr. as_mut ( ) } . expect ( "attempted to use async_stream yielder outside of stream context or across threads" ) ;
55
- if option_ref. is_none ( ) {
56
- * option_ref = value. take ( ) ;
57
- }
58
- }
59
-
60
- #[ cfg( not( feature = "unstable-thread-local" ) ) ]
61
- return STORE . with ( |cell| {
62
- op ( cell, & mut self . value ) ;
63
- Poll :: Pending
64
- } ) ;
65
- #[ cfg( feature = "unstable-thread-local" ) ]
66
- {
67
- op ( & STORE , & mut self . value ) ;
68
- Poll :: Pending
69
- }
88
+ Poll :: Pending
70
89
}
71
90
}
72
91
73
- struct Enter < ' a , T > {
74
- _p : PhantomData < & ' a T > ,
75
- prev : * mut ( )
92
+ struct Enter < ' s , T > {
93
+ store : & ' s SharedStore < T >
76
94
}
77
95
78
- fn enter < T > ( dst : & mut Option < T > ) -> Enter < ' _ , T > {
79
- fn op < T > ( cell : & Cell < * mut ( ) > , dst : & mut Option < T > ) -> * mut ( ) {
80
- let prev = cell. get ( ) ;
81
- cell. set ( ( dst as * mut Option < T > ) . cast :: < ( ) > ( ) ) ;
82
- prev
83
- }
84
- #[ cfg( not( feature = "unstable-thread-local" ) ) ]
85
- let prev = STORE . with ( |cell| op ( cell, dst) ) ;
86
- #[ cfg( feature = "unstable-thread-local" ) ]
87
- let prev = op ( & STORE , dst) ;
88
- Enter { _p : PhantomData , prev }
96
+ fn enter < T > ( store : & SharedStore < T > ) -> Enter < ' _ , T > {
97
+ store. entered . store ( true , Ordering :: Relaxed ) ;
98
+ Enter { store }
89
99
}
90
100
91
101
impl < T > Drop for Enter < ' _ , T > {
92
102
fn drop ( & mut self ) {
93
- #[ cfg( not( feature = "unstable-thread-local" ) ) ]
94
- STORE . with ( |cell| cell. set ( self . prev ) ) ;
95
- #[ cfg( feature = "unstable-thread-local" ) ]
96
- STORE . set ( self . prev ) ;
103
+ self . store . entered . store ( false , Ordering :: Relaxed ) ;
97
104
}
98
105
}
99
106
100
107
pin_project_lite:: pin_project! {
101
108
/// A [`Stream`] created from an asynchronous generator-like function.
102
109
///
103
110
/// To create an [`AsyncStream`], use the [`async_stream`] function.
104
- #[ derive( Debug ) ]
105
111
pub struct AsyncStream <T , U > {
106
- _p : PhantomData < T >,
112
+ store : Arc < SharedStore < T > >,
107
113
done: bool ,
108
114
#[ pin]
109
115
generator: U
@@ -131,16 +137,15 @@ where
131
137
return Poll :: Ready ( None ) ;
132
138
}
133
139
134
- let mut dst = None ;
135
140
let res = {
136
- let _enter = enter ( & mut dst ) ;
141
+ let _enter = enter ( & me . store ) ;
137
142
me. generator . poll ( cx)
138
143
} ;
139
144
140
145
* me. done = res. is_ready ( ) ;
141
146
142
- if dst . is_some ( ) {
143
- return Poll :: Ready ( dst . take ( ) ) ;
147
+ if me . store . has_value ( ) {
148
+ return Poll :: Ready ( me . store . cell . take ( ) ) ;
144
149
}
145
150
146
151
if * me. done { Poll :: Ready ( None ) } else { Poll :: Pending }
@@ -153,16 +158,16 @@ where
153
158
154
159
/// Create an asynchronous [`Stream`] from an asynchronous generator function.
155
160
///
156
- /// The provided function will be given a "yielder" function , which, when called, causes the stream to yield an item:
161
+ /// The provided function will be given a [`Yielder`] , which, when called, causes the stream to yield an item:
157
162
/// ```
158
163
/// use async_stream_lite::async_stream;
159
164
/// use futures::{pin_mut, stream::StreamExt};
160
165
///
161
166
/// #[tokio::main]
162
167
/// async fn main() {
163
- /// let stream = async_stream(|r#yield | async move {
168
+ /// let stream = async_stream(|yielder | async move {
164
169
/// for i in 0..3 {
165
- /// r#yield(i).await;
170
+ /// yielder. r#yield(i).await;
166
171
/// }
167
172
/// });
168
173
/// pin_mut!(stream);
@@ -181,9 +186,9 @@ where
181
186
/// };
182
187
///
183
188
/// fn zero_to_three() -> impl Stream<Item = u32> {
184
- /// async_stream(|r#yield | async move {
189
+ /// async_stream(|yielder | async move {
185
190
/// for i in 0..3 {
186
- /// r#yield(i).await;
191
+ /// yielder. r#yield(i).await;
187
192
/// }
188
193
/// })
189
194
/// }
@@ -207,9 +212,9 @@ where
207
212
/// };
208
213
///
209
214
/// fn zero_to_three() -> BoxStream<'static, u32> {
210
- /// Box::pin(async_stream(|r#yield | async move {
215
+ /// Box::pin(async_stream(|yielder | async move {
211
216
/// for i in 0..3 {
212
- /// r#yield(i).await;
217
+ /// yielder. r#yield(i).await;
213
218
/// }
214
219
/// }))
215
220
/// }
@@ -232,18 +237,18 @@ where
232
237
/// };
233
238
///
234
239
/// fn zero_to_three() -> impl Stream<Item = u32> {
235
- /// async_stream(|r#yield | async move {
240
+ /// async_stream(|yielder | async move {
236
241
/// for i in 0..3 {
237
- /// r#yield(i).await;
242
+ /// yielder. r#yield(i).await;
238
243
/// }
239
244
/// })
240
245
/// }
241
246
///
242
247
/// fn double<S: Stream<Item = u32>>(input: S) -> impl Stream<Item = u32> {
243
- /// async_stream(|r#yield | async move {
248
+ /// async_stream(|yielder | async move {
244
249
/// pin_mut!(input);
245
250
/// while let Some(value) = input.next().await {
246
- /// r#yield(value * 2).await;
251
+ /// yielder. r#yield(value * 2).await;
247
252
/// }
248
253
/// })
249
254
/// }
@@ -261,15 +266,12 @@ where
261
266
/// See also [`try_async_stream`], a variant of [`async_stream`] which supports try notation (`?`).
262
267
pub fn async_stream < T , F , U > ( generator : F ) -> AsyncStream < T , U >
263
268
where
264
- F : FnOnce ( fn ( value : T ) -> YieldFut < T > ) -> U ,
269
+ F : FnOnce ( Yielder < T > ) -> U ,
265
270
U : Future < Output = ( ) >
266
271
{
267
- let generator = generator ( r#yield :: < T > ) ;
268
- AsyncStream {
269
- _p : PhantomData ,
270
- done : false ,
271
- generator
272
- }
272
+ let store = Arc :: new ( SharedStore :: default ( ) ) ;
273
+ let generator = generator ( Yielder { store : Arc :: downgrade ( & store) } ) ;
274
+ AsyncStream { store, done : false , generator }
273
275
}
274
276
275
277
pub use self :: r#try:: { TryAsyncStream , try_async_stream} ;
0 commit comments