Skip to content

Commit e48b7ae

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

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

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

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

27128
* **R1: Async API look and feel** \* The new `async` API shall provide the same
@@ -201,3 +302,83 @@ guarantee any of above.
201302
**Results:**
202303

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

0 commit comments

Comments
 (0)