Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cours
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Programmation sysytème avancée
cours
Commits
e698399c
Verified
Commit
e698399c
authored
6 months ago
by
orestis.malaspin
Browse files
Options
Downloads
Patches
Plain Diff
I think it's over
parent
48cdf2f3
No related branches found
No related tags found
No related merge requests found
Pipeline
#38470
passed
6 months ago
Stage: test
Changes
1
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
07_stackless_coroutines.md
+225
-1
225 additions, 1 deletion
07_stackless_coroutines.md
with
225 additions
and
1 deletion
07_stackless_coroutines.md
+
225
−
1
View file @
e698399c
...
...
@@ -295,9 +295,233 @@ impl Future for HttpGetFuture {
3.
`stream == Some() && read == Ok(0)`
=>
`Resolved`
*
Pas de modélisation explicite ici
## Exécution 1/
```
console
Program starting
First poll - start operation
Schedule other tasks
Schedule other tasks
Schedule other tasks
Schedule other tasks
Schedule other tasks
Schedule other tasks
Schedule other tasks
HTTP/1.1 200 OK
content-type: text/plain;
charset
=
utf-8
content-length: 11
connection: close
date: Mon, 17 Mar 2025 08:03:55 GMT
HelloWorld1
First poll - start operation
Schedule other tasks
Schedule other tasks
Schedule other tasks
Schedule other tasks
Schedule other tasks
HTTP/1.1 200 OK
content-type: text/plain;
charset
=
utf-8
content-length: 11
connection: close
date: Mon, 17 Mar 2025 08:03:55 GMT
HelloWorld2
```
## Exécution 2/
*
On a bien le bon nombre de
`poll()`
pour chaque tâche
*
On a bien les deux requêtes
`HelloWorld1`
et
`HelloWorld2`
*
Les deux tâches sont exécutées séquentiellement
## Problème?
*
C'est pas très simple à utiliser
*
Impossible à généraliser
*
Verbeux
*
En un mot: beurk!
## Généralisation
*
Il faut un support du compilateur
*
Nécessité d'avoir des mots clés:
`coroutine/wait`
*
Utilisation de l'outil
`corofy`
:
<https://github.com/PacktPublishing/Asynchronous-Programming-in-Rust/tree/main/ch07>
*
Outil très simplifié qui crée la machine d'état et les attentes
## Utilisation de corofy
```
console
cargo install --path .
```
*
Syntaxe
`coroutine/wait`
## Le nouveau `main`
```
rust
fn
get_path
(
i
:
usize
)
->
String
{
format!
(
"/{}/HelloWorld{i}"
,
i
*
1000
)
}
coroutine
fn
async_main
()
{
println!
(
"Program starting"
);
let
txt
=
Http
::
get
(
&
get_path
(
0
))
.wait
;
println!
(
"{txt}"
);
let
txt
=
Http
::
get
(
&
get_path
(
1
))
.wait
;
println!
(
"{txt}"
);
let
txt
=
Http
::
get
(
&
get_path
(
2
))
.wait
;
println!
(
"{txt}"
);
let
txt
=
Http
::
get
(
&
get_path
(
3
))
.wait
;
println!
(
"{txt}"
);
let
txt
=
Http
::
get
(
&
get_path
(
4
))
.wait
;
println!
(
"{txt}"
);
}
fn
main
()
{
let
start
=
Instant
::
now
();
let
mut
future
=
async_main
();
loop
{
match
future
.poll
()
{
PollState
::
NotReady
=>
(),
PollState
::
Ready
(
_
)
=>
break
,
}
}
println!
(
"Elapsed time: {}"
,
start
.elapsed
()
.as_millis
());
}
```
## Conversion 1/
```
rust
enum
State0
{
Start
,
Wait1
(
Box
<
dyn
Future
<
Output
=
String
>>
),
Wait2
(
Box
<
dyn
Future
<
Output
=
String
>>
),
Wait3
(
Box
<
dyn
Future
<
Output
=
String
>>
),
Wait4
(
Box
<
dyn
Future
<
Output
=
String
>>
),
Wait5
(
Box
<
dyn
Future
<
Output
=
String
>>
),
Resolved
,
}
```
## Conversion 2/
```
rust
fn
poll
(
&
mut
self
)
->
PollState
<
Self
::
Output
>
{
loop
{
match
self
.state
{
State0
::
Start
=>
{
println!
(
"Program starting"
);
let
fut1
=
Box
::
new
(
Http
::
get
(
&
get_path
(
0
)));
self
.state
=
State0
::
Wait1
(
fut1
);
}
State0
::
Wait1
(
ref
mut
f1
)
=>
{
match
f1
.poll
()
{
PollState
::
Ready
(
txt
)
=>
{
println!
(
"{txt}"
);
let
fut2
=
Box
::
new
(
Http
::
get
(
&
get_path
(
1
)));
self
.state
=
State0
::
Wait2
(
fut2
);
}
PollState
::
NotReady
=>
break
PollState
::
NotReady
,
}
}
// ...
}
}
}
```
*
La syntaxe
`async/await`
fait des choses similaires automatiquement (en beaucoup mieux)
## Coroutines et concurrence
*
On a besoin d'un
`join_all()`
des
`Future`
*
Idée: parcourir les
`Future`
et les
`poll()`
à tour de rôle jusqu'à ce que toutes soient
`Ready(_)`
*
On stocke les
`Future`
dans une liste, ainsi que leur état (terminé ou non)
```
rust
```
## Le `join_all` 1/
```
rust
pub
struct
JoinAll
<
F
:
Future
>
{
futures
:
Vec
<
(
bool
,
F
)
>
,
finished_count
:
usize
,
}
pub
fn
join_all
<
F
:
Future
>
(
futures
:
Vec
<
F
>
)
->
JoinAll
<
F
>
{
let
futures
=
futures
.into_iter
()
.map
(|
f
|
(
false
,
f
))
.collect
();
JoinAll
{
futures
,
finished_count
:
0
,
}
}
```
*
`futures`
contient les
`Future`
et si chacune est terminée ou pas
*
On veut connaître combien sont terminées ou non
*
On transforme la liste de
`Future`
en tuple et on y va!
## Le `join_all` 2/
```
rust
coroutine
fn
async_main
()
{
println!
(
"Program starting"
);
let
mut
futures
=
vec!
[];
for
i
in
0
..
5
{
futures
.push
(
request
(
i
));
}
future
::
join_all
(
futures
)
.wait
;
}
fn
main
()
{
let
start
=
Instant
::
now
();
let
mut
future
=
async_main
();
loop
{
match
future
.poll
()
{
PollState
::
NotReady
=>
(),
PollState
::
Ready
(
_
)
=>
break
,
}
}
println!
(
"Elapsed time: {}"
,
start
.elapsed
()
.as_millis
());
}
```
## Le `join_all()` 3/
```
rust
impl
<
F
:
Future
>
Future
for
JoinAll
<
F
>
{
type
Output
=
String
;
fn
poll
(
&
mut
self
)
->
PollState
<
Self
::
Output
>
{
for
(
finished
,
future
)
in
self
.futures
.iter_mut
()
{
if
*
finished
{
continue
;
}
match
future
.poll
()
{
PollState
::
Ready
(
_
)
=>
{
*
finished
=
true
;
self
.finished_count
+=
1
;
}
PollState
::
NotReady
=>
continue
,
}
}
if
self
.finished_count
==
self
.futures
.len
()
{
PollState
::
Ready
(
String
::
new
())
}
else
{
PollState
::
NotReady
}
}
}
```
*
Si
`finished`
=> on continue
*
Sinon: on
`poll()`
chaque
`Future`
:
*
Si
`Ready`
=> on marque le
`Future`
comme terminé
*
Sinon on
`continue`
*
Quand tous sont terminés le
`JoinAll`
est
`Ready`
, sinon
`NotReady`
## Conclusion
*
Les coroutines sans pile, ne sont pas pré-emptables (on peut pas suspendre leur exécution à l'import quel moment)
*
En l'absence d'une pile, on a pas les informations nécessaire pour arrêter le processus n'importe où
*
On peut suspendre l'exécution qu'aux points
`wait`
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
sign in
to comment