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}