1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! # Coroutines (Async)
//!
//! `cu` is designed to have flexible coroutine handling. For example, consider these program styles:
//! - everything being `async` - typically involving both CPU-bound
//! work and IO work interwined. Can take advantage of multiple background threads.
//! - Some IO heavy work that doesn't really involve CPU - for example,
//! spawning compiler processes and wait for them, or spawning network requests.
//! Usually won't have significant performance benefit from having multiple background threads.
//! - Heavy CPU work that only has a little IO. Using `async` usually has very little
//! benefit. (Would probably use something like `rayon` to get parallelism).
//!
//! You pick the style you want.
//!
//! The async runtime being used under the hood is [`tokio`](https://docs.rs/tokio).
//! There are 2 feature flags you can choose from: `coroutine` and `coroutine-heavy`.
//! `coroutine` uses one foreground (current-thread) tokio runtime and one background thread to
//! drive IO tasks. `coroutine-heavy` does not have a current-thread runtime - everything
//! is done on the multi-threaded, background runtime.
//!
//! # Async entry point
//!
//! To make your entire program async with [`cli`](module@crate::cli),
//! simply make the `main` function `async.
//!
//! ```rust
//! use std::time::Duration;
//! # use pistonite_cu as cu;
//! #[cu::cli]
//! async fn main(_: cu::cli::Flags) -> cu::Result<()> {
//! cu::info!("doing some work");
//! tokio::time::sleep(Duration::from_millis(100)).await;
//! cu::info!("done");
//! Ok(())
//! }
//! ```
//!
//! When using `coroutine`, the main future will be spawned onto the current-thread
//! runtime (so the main thread is still driving it). When using `coroutine-heavy`,
//! the main future is spawned onto the background runtime, and the main thread
//! waits until the future is completed.
//!
//! # Coroutines used internally and `co_*` APIs
//! Some `cu` functions use coroutines internally behind "synchronous" APIs,
//! allowing seamless integration from a synchronous context.
//!
//! For example, when spawning child processes, an async task processes
//! IO from the child and streams results to the main thread. This allows
//! for a clean API to for example, read child's output line-by-line.
//!
//! However, there is an important catch - it is crucial that we never block
//! an async runtime. This means to wait for a future:
//! - If we are not in an async runtime, we can enter the async runtime by
//! calling an entry point to the runtime (a.k.a `block`), to block
//! the current thread while letting the runtime run until the future is finished.
//! - If we are already in an async runtime, we must call `.await` instead
//! of block. Otherwise, either the entire runtime will block and may deadlock,
//! or tokio will detect it and panic.
//!
//! This is why the APIs that use `coroutine` under the hood will have
//! another version with a `co_*` prefix. For example, `spawn` and `co_spawn`.
//! You MUST use `.spawn()?` if not in an async runtime, and `.co_spawn().await?`
//! in an async runtime. Note that calling `.co_spawn()` while not in an async runtime
//! is also not allowed, because tokio will assume a runtime is active and panic
//! if not.
//!
//! # Advanced Usage
//! If additional functionality from `tokio` is needed (not already provided by re-exports),
//! then you can add `tokio` to `Cargo.toml`:
//! ```toml
//! [dependencies]
//! tokio = "1"
//! ```
// re-exports
pub use ;
pub use block;
pub use spawn_blocking;
pub use ;
pub use ;
pub use ;