Skip to content

Commit 2d51086

Browse files
committed
iox-1002: Extend async documentation
1 parent ce027cb commit 2d51086

1 file changed

Lines changed: 175 additions & 0 deletions

File tree

doc/design-documents/drafts/async_api/async_api.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,101 @@ A high-level pitch of the feature:
2222
* The `async` usage in Rust is already well established technique that is
2323
adopted by many crates, including those with highest usage
2424

25+
26+
### Introduction to async in context of iceoryx2
27+
28+
The Rust `async` APIs are build with the help of usage of compiler. At the end
29+
`async` and `await` is just a syntactic sugar that is removed during `HIR`. In
30+
practice it means:
31+
* `async fn abc() -> bool ` signature is turned into
32+
`fn abc() -> impl core::future::Future<Output=bool>`. This basically means
33+
each async fn is a anonymous type that implements `core::future::Future`
34+
* `await` is turned be compiler into generated state machine that simply
35+
decompose to all states (continues and returns) in function using `Future`
36+
trait API. To have small example, below code:
37+
```rust
38+
39+
async fn some_other_async_fn() {
40+
println!("Test");
41+
}
42+
43+
async fn abc() -> bool {
44+
45+
some_other_async_fn().await
46+
47+
true
48+
}
49+
```
50+
51+
Will be turned into (pseudocode):
52+
```rust
53+
54+
fn some_other_async_fn() -> impl core::future::Future<Output=()> {
55+
println!("Test");
56+
core::task::Poll::Ready(())
57+
}
58+
59+
fn abc() -> impl core::future::Future<Output=bool> {
60+
61+
let future = some_other_async_fn();
62+
let mut _hidden_satte = 0;
63+
64+
if _hidden_satte == 0 {
65+
match future.poll(...) {
66+
core::task::Poll::Pending => return Pending
67+
core::task::Poll::Ready(_) => {
68+
///
69+
_hidden_satte = 1;
70+
}
71+
}
72+
}
73+
74+
/// And for next .awaits, continue the idea
75+
/// Compiler will ofc generate something totally different but thats the idea behind
76+
77+
true
78+
}
79+
80+
```
81+
82+
At the end, Rust SIG has decided to provide interface (traits) and compiler
83+
syntactic sugar to support `async` but left the `runtime` being external needed
84+
component that is not part of language.
85+
86+
#### Brief simplification how does Futures works
87+
Once creating own `Future`implementation using `core::future::Future` trait You
88+
are provided the `Context` that holds the `Waker`. The implementer
89+
responsibility for future is to return either `core::task::Poll::Pending` once
90+
Your Future is not ready yet or `core::task::Poll::Ready(_)` once Future is
91+
done wih work. In the first case, once You detected you are not ready, Your
92+
obligation is to:
93+
* take `Waker` and store it somewhere
94+
* return `core::task::Poll::Pending` (at this moment runtime will remove You
95+
from processing queue and will not process until woken)
96+
* call `Waker::wake*` API once You know your Future can progress
97+
98+
After telling runtime via `Waker::wake*` that you are ready, `runtime` will
99+
bring You back to some of its workers and again will execute `Future::poll` to
100+
check Your progress.
101+
102+
### Connection to iceoryx2 messaging patterns
103+
104+
#### Events
105+
Nothing to add, blocking API needs async versions
106+
107+
#### Publisher Subscriber
108+
Here in async world (and even in non async really) as a user expectation would
109+
be that I can react once a new `sample` is produced so that I don't have to poll
110+
for it, instead simply `await` it.
111+
112+
#### Request Response
113+
Here in async world (and even in non async really) as a user expectation would
114+
be that there is one entity that is a "Server" (the one that hosts method and
115+
produces responses) and there are clients that do requests. Due to this,
116+
`request-response` would need `async` API to only act once there is request and
117+
once there is reply for it.
118+
119+
25120
## Requirements
26121

27122
* **R1: Async API look and feel** \* The new `async` API shall provide the same
@@ -201,3 +296,83 @@ guarantee any of above.
201296
**Results:**
202297

203298
* PubSub API will have `async` API available
299+
300+
301+
302+
## Extended examples
303+
304+
### Pub Sub pseudo code example
305+
306+
> NOTE: `async main` is the extension provided by runtimes, it simply wraps
307+
> regular main into creation of runtime and put this into execution
308+
309+
**Process 1**
310+
```rust
311+
async fn main() {
312+
let node = NodeBuilder::new().create::<ipc_threadsafe::Service>().unwrap();
313+
314+
let event = node
315+
.service_builder(&"MyEventName".try_into().unwrap())
316+
.event()
317+
.open_or_create()
318+
.unwrap();
319+
320+
let listener = event.listener_builder().create_async().unwrap();
321+
let listener2 = event.listener_builder().create_async().unwrap();
322+
323+
let task1_handle = spawn(async move {
324+
println!("Awaiting for Iceoryx event in batches while doing something else ...");
325+
loop {
326+
327+
listener
328+
.wait_all(&mut |event_id| {
329+
print!("Received Iceoryx event: {:?}\n", event_id);
330+
})
331+
.await // During this not being ready, worker can do any other work
332+
.unwrap();
333+
}
334+
});
335+
336+
337+
spawn(async move {
338+
339+
// Some logic
340+
// ..
341+
342+
// now I need sample in this place due to my logic, so I simply 'await' it and once
343+
// I have a sample, this code will continue executing further
344+
let sample_res = listener2.wait().await; // During this not being ready, worker can do any other work
345+
346+
// Process sample
347+
348+
// ..
349+
350+
});
351+
352+
353+
354+
// Optionally You may wait until task finishes
355+
task1_handle.await.unwrap(); // During this not being ready, worker can do any other work
356+
}
357+
```
358+
359+
**Process 2**
360+
```rust
361+
async fn main() {
362+
let node = NodeBuilder::new().create::<ipc_threadsafe::Service>().unwrap();
363+
364+
let event = node
365+
.service_builder(&"MyEventName".try_into().unwrap())
366+
.event()
367+
.open_or_create()
368+
.unwrap();
369+
370+
let notifier = event.notifier_builder().create_async().unwrap();
371+
println!("Awaiting for Iceoryx event in batches while doing something else ...");
372+
373+
loop {
374+
notifier.notify();
375+
sleep(100).await; // During that sleep, worker can be doing any other work ;)
376+
}
377+
}
378+
```

0 commit comments

Comments
 (0)