kayrx_karx/lib.rs
1#![warn(
2 rust_2018_idioms,
3 unreachable_pub,
4 // missing_debug_implementations,
5 // missing_docs,
6)]
7#![allow(
8 warnings,
9 missing_docs,
10 type_alias_bounds,
11 clippy::type_complexity,
12 clippy::borrow_interior_mutable_const,
13 clippy::needless_doctest_main,
14 clippy::too_many_arguments,
15 clippy::new_without_default
16)]
17
18//! Karx Async Execute Engine
19//!
20//! # Spawning
21//!
22//! To spawn a future onto an executor, we first need to allocate it on the heap and keep some
23//! state alongside it. The state indicates whether the future is ready for polling, waiting to be
24//! woken up, or completed. Such a future is called a *task*.
25//!
26//! All executors have some kind of queue that holds runnable tasks:
27//!
28//! ```
29//! let (sender, receiver) = crossbeam::channel::unbounded();
30//! #
31//! # // A future that will get spawned.
32//! # let future = async { 1 + 2 };
33//! #
34//! # // A function that schedules the task when it gets woken up.
35//! # let schedule = move |task| sender.send(task).unwrap();
36//! #
37//! # // Construct a task.
38//! # let (task, handle) = kayrx_karx::spawn(future, schedule, ());
39//! ```
40//!
41//! A task is constructed using either [`spawn`] or [`spawn_local`]:
42//!
43//! ```
44//! # let (sender, receiver) = crossbeam::channel::unbounded();
45//! #
46//! // A future that will be spawned.
47//! let future = async { 1 + 2 };
48//!
49//! // A function that schedules the task when it gets woken up.
50//! let schedule = move |task| sender.send(task).unwrap();
51//!
52//! // Construct a task.
53//! let (task, handle) = kayrx_karx::spawn(future, schedule, ());
54//!
55//! // Push the task into the queue by invoking its schedule function.
56//! task.schedule();
57//! ```
58//!
59//! The last argument to the [`spawn`] function is a *tag*, an arbitrary piece of data associated
60//! with the task. In most executors, this is typically a task identifier or task-local storage.
61//!
62//! The function returns a runnable [`Task`] and a [`JoinHandle`] that can await the result.
63//!
64//! # Execution
65//!
66//! Task executors have some kind of main loop that drives tasks to completion. That means taking
67//! runnable tasks out of the queue and running each one in order:
68//!
69//! ```no_run
70//! # let (sender, receiver) = crossbeam::channel::unbounded();
71//! #
72//! # // A future that will get spawned.
73//! # let future = async { 1 + 2 };
74//! #
75//! # // A function that schedules the task when it gets woken up.
76//! # let schedule = move |task| sender.send(task).unwrap();
77//! #
78//! # // Construct a task.
79//! # let (task, handle) = kayrx_karx::spawn(future, schedule, ());
80//! #
81//! # // Push the task into the queue by invoking its schedule function.
82//! # task.schedule();
83//! #
84//! for task in receiver {
85//! task.run();
86//! }
87//! ```
88//!
89//! When a task is run, its future gets polled. If polling does not complete the task, that means
90//! it's waiting for another future and needs to go to sleep. When woken up, its schedule function
91//! will be invoked, pushing it back into the queue so that it can be run again.
92//!
93//! # Cancelation
94//!
95//! Both [`Task`] and [`JoinHandle`] have methods that cancel the task. When canceled, the task's
96//! future will not be polled again and will get dropped instead.
97//!
98//! If canceled by the [`Task`] instance, the task is destroyed immediately. If canceled by the
99//! [`JoinHandle`] instance, it will be scheduled one more time and the next attempt to run it will
100//! simply destroy it.
101//!
102//! The `JoinHandle` future will then evaluate to `None`, but only after the task's future is
103//! dropped.
104//!
105//! # Performance
106//!
107//! Task construction incurs a single allocation that holds its state, the schedule function, and
108//! the future or the result of the future if completed.
109//!
110//! The layout of a task is equivalent to 4 `usize`s followed by the schedule function, and then by
111//! a union of the future and its output.
112//!
113//! # Waking
114//!
115//! The handy [`waker_fn`] constructor converts any function into a [`Waker`]. Every time it is
116//! woken, the function gets called:
117//!
118//! ```
119//! let waker = kayrx_karx::waker_fn(|| println!("Wake!"));
120//!
121//! // Prints "Wake!" twice.
122//! waker.wake_by_ref();
123//! waker.wake_by_ref();
124//! ```
125//!
126//! This is useful for implementing single-future executors like `block_on`.
127//!
128//! Dynamic task internal. Inspired by golang runtime.
129//!
130//! It is okay to do blocking inside a task, the internal will
131//! detect this, and scale the thread pool.
132//!
133//! ```rust
134//! use std::thread;
135//! use std::time::Duration;
136//!
137//! use futures_timer::Delay;
138//!
139//! fn main() {
140//! kayrx_karx::exec(async {
141//! for _ in 0..10 {
142//! Delay::new(Duration::from_secs(1)).await;
143//! println!("Non-blocking Hello World");
144//! }
145//! });
146//!
147//! kayrx_karx::exec(async {
148//! for _ in 0..10 {
149//! thread::sleep(Duration::from_secs(1));
150//! println!("Blocking Hello World");
151//! }
152//! });
153//!
154//! thread::sleep(Duration::from_secs(11));
155//! }
156//! ```
157//! [`spawn`]: fn.spawn.html
158//! [`spawn_local`]: fn.spawn_local.html
159//! [`waker_fn`]: fn.waker_fn.html
160//! [`Task`]: struct.Task.html
161//! [`JoinHandle`]: struct.JoinHandle.html
162//! [`Waker`]: https://doc.rust-lang.org/std/task/struct.Waker.html
163
164extern crate alloc;
165
166pub mod futures;
167mod executor;
168mod header;
169mod join_handle;
170mod raw;
171mod state;
172mod task;
173mod utils;
174mod waker_fn;
175
176pub use self::executor::spawn as exec;
177pub use self::join_handle::JoinHandle;
178pub use self::task::{spawn, spawn_local, Task};
179pub use self::waker_fn::waker_fn;
180
181