genawaiter/sync/mod.rs
1/*!
2This module implements a generator which can be shared between threads.
3
4You can create a basic generator with [`gen!`] and [`yield_!`].
5
6[`gen!`]: macro.gen.html
7
8```rust
9# #[cfg(feature = "proc_macro")]
10# fn feature_gate() {
11# use genawaiter::{sync::gen, yield_};
12#
13let mut my_generator = gen!({
14 yield_!(10);
15});
16# my_generator.resume();
17# }
18```
19
20If you need to reuse logic between multiple generators, you can define the logic with
21[`sync_producer!`] and [`yield_!`], and instantiate generators with [`Gen::new`].
22
23```rust
24# #[cfg(feature = "proc_macro")]
25# fn feature_gate() {
26# use genawaiter::{sync::Gen, sync_producer as producer, yield_};
27#
28let my_producer = producer!({
29 yield_!(10);
30});
31let mut my_generator = Gen::new(my_producer);
32# my_generator.resume();
33# }
34```
35
36If you don't like macros, you can use the low-level API directly.
37
38```rust
39# use genawaiter::sync::{Co, Gen};
40#
41async fn my_producer(co: Co<u8>) {
42 co.yield_(10).await;
43}
44let mut my_generator = Gen::new(my_producer);
45# my_generator.resume();
46```
47
48# Storing a generator in a `static`
49
50In Rust, the type of static variables must be nameable, but the type of an `async fn` is
51not nameable – `async fn`s always return `impl Future`. So, in order to store a
52generator in a static, you'll need `dyn Future`, plus a layer of indirection. This crate
53provides the [`GenBoxed`](type.GenBoxed.html) type alias with the
54[`Gen::new_boxed`](type.GenBoxed.html#method.new_boxed) function to make this easier
55(and to smooth out a rough corner in the type inference).
56
57Additionally, as usual when dealing with statics in Rust, you'll need some form of
58synchronization. Here is one possible pattern, using the [`once_cell`] crate.
59
60[`once_cell`]: https://crates.io/crates/once_cell
61
62```
63# #[cfg(feature = "proc_macro")]
64# fn feature_gate() {
65use genawaiter::{sync::{Gen, GenBoxed}, sync_producer as producer, yield_};
66use once_cell::sync::Lazy;
67use std::sync::Mutex;
68
69static INEFFICIENT_COUNTER: Lazy<Mutex<GenBoxed<i32>>> =
70 Lazy::new(|| Mutex::new(Gen::new_boxed(producer!({
71 let mut n = 0;
72 loop {
73 n += 1;
74 yield_!(n);
75 }
76 }))));
77# }
78```
79
80# Examples
81
82## Using `Iterator`
83
84Generators implement `Iterator`, so you can use them in a for loop:
85
86```rust
87# #[cfg(feature = "proc_macro")]
88# fn feature_gate() {
89use genawaiter::{sync::gen, yield_};
90
91let odds_under_ten = gen!({
92 let mut n = 1;
93 while n < 10 {
94 yield_!(n);
95 n += 2;
96 }
97});
98
99# let mut test = Vec::new();
100for num in odds_under_ten {
101 println!("{}", num);
102 # test.push(num);
103}
104# assert_eq!(test, [1, 3, 5, 7, 9]);
105# }
106```
107
108## Collecting into a `Vec`
109
110```rust
111# #[cfg(feature = "proc_macro")]
112# fn feature_gate() {
113# use genawaiter::{sync::gen, yield_};
114#
115# let odds_under_ten = gen!({
116# for n in (1..).step_by(2).take_while(|&n| n < 10) { yield_!(n); }
117# });
118#
119let xs: Vec<_> = odds_under_ten.into_iter().collect();
120assert_eq!(xs, [1, 3, 5, 7, 9]);
121# }
122```
123
124## A generator is a closure
125
126Like any closure, you can capture values from outer scopes.
127
128```rust
129# #[cfg(feature = "proc_macro")]
130# fn feature_gate() {
131# use genawaiter::{sync::gen, yield_, GeneratorState};
132#
133let two = 2;
134let mut multiply = gen!({
135 yield_!(10 * two);
136});
137assert_eq!(multiply.resume(), GeneratorState::Yielded(20));
138# }
139```
140
141## Using `resume()`
142
143```rust
144# #[cfg(feature = "proc_macro")]
145# fn feature_gate() {
146# use genawaiter::{sync::gen, yield_, GeneratorState};
147#
148# let mut odds_under_ten = gen!({
149# for n in (1..).step_by(2).take_while(|&n| n < 10) { yield_!(n); }
150# });
151#
152assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(1));
153assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(3));
154assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(5));
155assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(7));
156assert_eq!(odds_under_ten.resume(), GeneratorState::Yielded(9));
157assert_eq!(odds_under_ten.resume(), GeneratorState::Complete(()));
158# }
159```
160
161## Passing resume arguments
162
163You can pass values into the generator.
164
165Note that the first resume argument will be lost. This is because at the time the first
166value is sent, there is no future being awaited inside the generator, so there is no
167place the value could go where the generator could observe it.
168
169```rust
170# #[cfg(feature = "proc_macro")]
171# fn feature_gate() {
172# use genawaiter::{sync::gen, yield_};
173#
174let mut check_numbers = gen!({
175 let num = yield_!(());
176 assert_eq!(num, 1);
177
178 let num = yield_!(());
179 assert_eq!(num, 2);
180});
181
182check_numbers.resume_with(0);
183check_numbers.resume_with(1);
184check_numbers.resume_with(2);
185# }
186```
187
188## Returning a completion value
189
190You can return a completion value with a different type than the values that are
191yielded.
192
193```rust
194# #[cfg(feature = "proc_macro")]
195# fn feature_gate() {
196# use genawaiter::{sync::gen, yield_, GeneratorState};
197#
198let mut numbers_then_string = gen!({
199 yield_!(10);
200 yield_!(20);
201 "done!"
202});
203
204assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(10));
205assert_eq!(numbers_then_string.resume(), GeneratorState::Yielded(20));
206assert_eq!(numbers_then_string.resume(), GeneratorState::Complete("done!"));
207# }
208```
209
210## Defining a reusable producer function
211
212```rust
213# #[cfg(feature = "proc_macro")]
214# fn feature_gate() {
215# use genawaiter::{sync::{producer_fn, Gen}, yield_, GeneratorState};
216#
217#[producer_fn(u8)]
218async fn produce() {
219 yield_!(10);
220}
221
222let mut gen = Gen::new(produce);
223assert_eq!(gen.resume(), GeneratorState::Yielded(10));
224# }
225```
226
227## Defining a reusable producer closure
228
229```rust
230# #[cfg(feature = "proc_macro")]
231# fn feature_gate() {
232# use genawaiter::{sync::Gen, yield_, GeneratorState};
233use genawaiter::sync_producer as producer;
234
235let produce = producer!({
236 yield_!(10);
237});
238
239let mut gen = Gen::new(produce);
240assert_eq!(gen.resume(), GeneratorState::Yielded(10));
241# }
242```
243
244## Using the low-level API
245
246You can define an `async fn` directly, instead of relying on the `gen!` or `producer!`
247macros.
248
249```rust
250use genawaiter::sync::{Co, Gen};
251
252async fn producer(co: Co<i32>) {
253 let mut n = 1;
254 while n < 10 {
255 co.yield_(n).await;
256 n += 2;
257 }
258}
259
260let odds_under_ten = Gen::new(producer);
261let result: Vec<_> = odds_under_ten.into_iter().collect();
262assert_eq!(result, [1, 3, 5, 7, 9]);
263```
264
265## Using the low-level API with an async closure (nightly Rust only)
266
267```ignore
268# use genawaiter::{sync::Gen, GeneratorState};
269#
270let gen = Gen::new(async move |co| {
271 co.yield_(10).await;
272 co.yield_(20).await;
273});
274assert_eq!(gen.resume(), GeneratorState::Yielded(10));
275assert_eq!(gen.resume(), GeneratorState::Yielded(20));
276assert_eq!(gen.resume(), GeneratorState::Complete(()));
277```
278
279## Using the low-level API with an async <del>closure</del> faux·sure (for stable Rust)
280
281```
282# use genawaiter::{sync::Gen, GeneratorState};
283#
284let mut gen = Gen::new(|co| async move {
285 co.yield_(10).await;
286 co.yield_(20).await;
287});
288assert_eq!(gen.resume(), GeneratorState::Yielded(10));
289assert_eq!(gen.resume(), GeneratorState::Yielded(20));
290assert_eq!(gen.resume(), GeneratorState::Complete(()));
291```
292
293## Using the low-level API with function arguments
294
295This is just ordinary Rust, nothing special.
296
297```rust
298# use genawaiter::{sync::{Co, Gen}, GeneratorState};
299#
300async fn multiples_of(num: i32, co: Co<i32>) {
301 let mut cur = num;
302 loop {
303 co.yield_(cur).await;
304 cur += num;
305 }
306}
307
308let mut gen = Gen::new(|co| multiples_of(10, co));
309assert_eq!(gen.resume(), GeneratorState::Yielded(10));
310assert_eq!(gen.resume(), GeneratorState::Yielded(20));
311assert_eq!(gen.resume(), GeneratorState::Yielded(30));
312```
313*/
314
315pub use crate::sync::{boxed::GenBoxed, engine::Co, generator::Gen};
316
317/// Creates a generator.
318///
319/// This macro takes one argument, which is the body of the generator. It should
320/// contain one or more calls to the [`yield_!`] macro.
321///
322/// # Examples
323///
324/// [_See the module-level docs for examples._](.)
325#[cfg(feature = "proc_macro")]
326pub use genawaiter_macro::sync_gen as gen;
327
328/// Turns a function into a producer, which can then be used to create a
329/// generator.
330///
331/// The body of the function should contain one or more [`yield_!`] expressions.
332///
333/// # Examples
334///
335/// [_See the module-level docs for examples._](.)
336#[cfg(feature = "proc_macro")]
337pub use genawaiter_proc_macro::sync_producer_fn as producer_fn;
338
339mod boxed;
340mod engine;
341mod generator;
342mod iterator;
343#[cfg(feature = "futures03")]
344mod stream;
345
346#[cfg(feature = "nightly")]
347#[cfg(test)]
348mod nightly_tests;
349
350#[cfg(test)]
351mod tests {
352 use crate::{
353 sync::{Co, Gen},
354 testing::{DummyFuture, SlowFuture},
355 GeneratorState,
356 };
357 use futures::executor::block_on;
358 use std::{
359 cell::{Cell, RefCell},
360 future::Future,
361 };
362
363 async fn simple_producer(co: Co<i32>) -> &'static str {
364 co.yield_(10).await;
365 "done"
366 }
367
368 #[test]
369 fn function() {
370 let mut gen = Gen::new(simple_producer);
371 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
372 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
373 }
374
375 #[test]
376 fn simple_closure() {
377 async fn gen(i: i32, co: Co<i32>) -> &'static str {
378 co.yield_(i * 2).await;
379 "done"
380 }
381
382 let mut gen = Gen::new(|co| gen(5, co));
383 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
384 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
385 }
386
387 #[test]
388 fn resume_args() {
389 async fn gen(resumes: &RefCell<Vec<&str>>, co: Co<i32, &'static str>) {
390 let resume_arg = co.yield_(10).await;
391 resumes.borrow_mut().push(resume_arg);
392 let resume_arg = co.yield_(20).await;
393 resumes.borrow_mut().push(resume_arg);
394 }
395
396 let resumes = RefCell::new(Vec::new());
397 let mut gen = Gen::new(|co| gen(&resumes, co));
398 assert_eq!(*resumes.borrow(), &[] as &[&str]);
399
400 assert_eq!(gen.resume_with("ignored"), GeneratorState::Yielded(10));
401 assert_eq!(*resumes.borrow(), &[] as &[&str]);
402
403 assert_eq!(gen.resume_with("abc"), GeneratorState::Yielded(20));
404 assert_eq!(*resumes.borrow(), &["abc"]);
405
406 assert_eq!(gen.resume_with("def"), GeneratorState::Complete(()));
407 assert_eq!(*resumes.borrow(), &["abc", "def"]);
408 }
409
410 #[test]
411 fn async_resume() {
412 async fn produce(co: Co<i32>) {
413 SlowFuture::new().await;
414 co.yield_(10).await;
415 SlowFuture::new().await;
416 co.yield_(20).await;
417 }
418
419 async fn run_test() {
420 let mut gen = Gen::new(produce);
421
422 let x = gen.async_resume().await;
423 assert_eq!(x, GeneratorState::Yielded(10));
424
425 let x = gen.async_resume().await;
426 assert_eq!(x, GeneratorState::Yielded(20));
427
428 let x = gen.async_resume().await;
429 assert_eq!(x, GeneratorState::Complete(()));
430 }
431
432 block_on(run_test());
433 }
434
435 #[test]
436 #[should_panic(expected = "non-async method")]
437 fn forbidden_await_helpful_message() {
438 async fn wrong(_: Co<i32>) {
439 DummyFuture.await;
440 }
441
442 let mut gen = Gen::new(wrong);
443 gen.resume();
444 }
445
446 #[test]
447 #[should_panic(expected = "Co::yield_")]
448 fn multiple_yield_helpful_message() {
449 async fn wrong(co: Co<i32>) {
450 let _ = co.yield_(10);
451 let _ = co.yield_(20);
452 }
453
454 let mut gen = Gen::new(wrong);
455 gen.resume();
456 }
457
458 #[test]
459 #[should_panic = "should have been dropped by now"]
460 fn escaped_co_helpful_message() {
461 async fn shenanigans(co: Co<i32>) -> Co<i32> {
462 co
463 }
464
465 let mut gen = Gen::new(shenanigans);
466 let escaped_co = match gen.resume() {
467 GeneratorState::Yielded(_) => panic!(),
468 GeneratorState::Complete(co) => co,
469 };
470 let _ = escaped_co.yield_(10);
471 }
472
473 /// This tests in a roundabout way that the `Gen` object can be moved. This
474 /// should happen without moving the allocations inside so we don't
475 /// segfault.
476 #[test]
477 fn gen_is_movable() {
478 #[inline(never)]
479 async fn produce(addrs: &mut Vec<*const i32>, co: Co<i32>) -> &'static str {
480 let sentinel: Cell<i32> = Cell::new(0x8001);
481 // If the future state moved, this reference would become invalid, and
482 // hilarity would ensue.
483 let sentinel_ref: &Cell<i32> = &sentinel;
484
485 // Test a few times that `sentinel` and `sentinel_ref` point to the same
486 // data.
487
488 assert_eq!(sentinel.get(), 0x8001);
489 sentinel_ref.set(0x8002);
490 assert_eq!(sentinel.get(), 0x8002);
491 addrs.push(sentinel.as_ptr());
492
493 co.yield_(10).await;
494
495 assert_eq!(sentinel.get(), 0x8002);
496 sentinel_ref.set(0x8003);
497 assert_eq!(sentinel.get(), 0x8003);
498 addrs.push(sentinel.as_ptr());
499
500 co.yield_(20).await;
501
502 assert_eq!(sentinel.get(), 0x8003);
503 sentinel_ref.set(0x8004);
504 assert_eq!(sentinel.get(), 0x8004);
505 addrs.push(sentinel.as_ptr());
506
507 "done"
508 }
509
510 /// Create a generator, resume it once (so `sentinel_ref` gets
511 /// initialized), and then move it out of the function.
512 fn create_generator(
513 addrs: &mut Vec<*const i32>,
514 ) -> Gen<i32, (), impl Future<Output = &'static str> + '_> {
515 let mut gen = Gen::new(move |co| produce(addrs, co));
516 assert_eq!(gen.resume(), GeneratorState::Yielded(10));
517 gen
518 }
519
520 let mut addrs = Vec::new();
521 let mut gen = create_generator(&mut addrs);
522
523 assert_eq!(gen.resume(), GeneratorState::Yielded(20));
524 assert_eq!(gen.resume(), GeneratorState::Complete("done"));
525 drop(gen);
526
527 assert!(addrs.iter().all(|&p| p == addrs[0]));
528 }
529}