diff --git a/exercices/06_coroutines/correction/Cargo.lock b/exercices/06_coroutines/correction/Cargo.lock new file mode 100644 index 0000000000000000000000000000000000000000..76e0abc1a4a2e90a4658586216bcbf48cbc48e4b --- /dev/null +++ b/exercices/06_coroutines/correction/Cargo.lock @@ -0,0 +1,113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "coroutines" +version = "0.1.0" +dependencies = [ + "mio", +] + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/exercices/06_coroutines/correction/Cargo.toml b/exercices/06_coroutines/correction/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..36d20d352000db623ef0e5f8fcc0c9029d1eef39 --- /dev/null +++ b/exercices/06_coroutines/correction/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "coroutines" +version = "0.1.0" +edition = "2021" + +[dependencies] +mio = { version = "1.0.3", features = ["net", "os-poll"] } diff --git a/exercices/06_coroutines/correction/src/future.rs b/exercices/06_coroutines/correction/src/future.rs new file mode 100644 index 0000000000000000000000000000000000000000..92f98f344ee50014f22c54a5fbcdcd56c426dbf4 --- /dev/null +++ b/exercices/06_coroutines/correction/src/future.rs @@ -0,0 +1,60 @@ +pub trait Future { + type Output; + + fn poll(&mut self) -> PollState<Self::Output>; +} + +pub enum PollState<T> { + Ready(T), + NotReady, +} + +pub struct JoinThree<F: Future> { + future1: (bool, F), + future2: (bool, F), + future3: (bool, F), +} + +impl<F: Future> Future for JoinThree<F> { + type Output = (); + fn poll(&mut self) -> PollState<Self::Output> { + if !self.future1.0 { + match self.future1.1.poll() { + PollState::Ready(_) => { + self.future1.0 = true; + } + PollState::NotReady => {} + } + } + if !self.future2.0 { + match self.future2.1.poll() { + PollState::Ready(_) => { + self.future2.0 = true; + } + PollState::NotReady => {} + } + } + + if !self.future3.0 { + match self.future3.1.poll() { + PollState::Ready(_) => { + self.future3.0 = true; + } + PollState::NotReady => {} + } + } + if self.future1.0 && self.future2.0 && self.future3.0 { + PollState::Ready(()) + } else { + PollState::NotReady + } + } +} + +pub fn join_three<F: Future>(future1: F, future2: F, future3: F) -> JoinThree<F> { + JoinThree { + future1: (false, future1), + future2: (false, future2), + future3: (false, future3), + } +} diff --git a/exercices/06_coroutines/correction/src/http.rs b/exercices/06_coroutines/correction/src/http.rs new file mode 100644 index 0000000000000000000000000000000000000000..575153b9cc0e86ef81868c1dcd0193dc6d61a7b3 --- /dev/null +++ b/exercices/06_coroutines/correction/src/http.rs @@ -0,0 +1,73 @@ +use crate::future::{Future, PollState}; +use std::io::{ErrorKind, Read, Write}; + +fn get_req(path: &str) -> String { + format!( + "GET {path} HTTP/1.1\r\n\ + Host: localhost\r\n\ + Connection: close\r\n\ + \r\n" + ) +} + +pub struct Http; + +impl Http { + pub fn get(path: &str) -> impl Future<Output = String> { + HttpGetFuture::new(path) + } +} + +struct HttpGetFuture { + stream: Option<mio::net::TcpStream>, + buffer: Vec<u8>, + path: String, +} + +impl HttpGetFuture { + fn new(path: &str) -> Self { + Self { + stream: None, + buffer: vec![], + path: String::from(path), + } + } + fn write_request(&mut self) { + let stream = std::net::TcpStream::connect("localhost:8080").unwrap(); + stream.set_nonblocking(true).unwrap(); + let mut stream = mio::net::TcpStream::from_std(stream); + stream.write_all(get_req(&self.path).as_bytes()).unwrap(); + self.stream = Some(stream); + } +} + +impl Future for HttpGetFuture { + type Output = String; + fn poll(&mut self) -> PollState<Self::Output> { + if self.stream.is_none() { + println!("First poll - start operation"); + self.write_request(); + return PollState::NotReady; + } + let mut buf = vec![0u8; 4096]; + loop { + match self.stream.as_mut().unwrap().read(&mut buf) { + Ok(0) => { + let s = String::from_utf8_lossy(&self.buffer); + break PollState::Ready(String::from(s)); + } + Ok(n) => { + self.buffer.extend(&buf[0..n]); + continue; + } + Err(e) if e.kind() == ErrorKind::WouldBlock => { + break PollState::NotReady; + } + Err(e) if e.kind() == ErrorKind::Interrupted => { + continue; + } + Err(e) => panic!("{e:?}"), + } + } + } +} diff --git a/exercices/06_coroutines/correction/src/lib.rs b/exercices/06_coroutines/correction/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..5dfe1656cdcbf8b2b4bc1b31b8625d4879416947 --- /dev/null +++ b/exercices/06_coroutines/correction/src/lib.rs @@ -0,0 +1,2 @@ +pub mod future; +pub mod http; diff --git a/exercices/06_coroutines/correction/src/main.rs b/exercices/06_coroutines/correction/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..0268320b5d858a7edc97f6bd3cb2a70403001014 --- /dev/null +++ b/exercices/06_coroutines/correction/src/main.rs @@ -0,0 +1,62 @@ +use std::time::Instant; + +use coroutines::future::{join_three, Future, PollState}; +use coroutines::http::Http; + +struct Coroutine { + state: CoroutineState, +} + +impl Coroutine { + fn new(i: usize) -> Self { + Self { + state: CoroutineState::Start(i), + } + } +} + +impl Future for Coroutine { + type Output = (); // We return nothing from the Coroutine Future + + fn poll(&mut self) -> PollState<Self::Output> { + loop { + match self.state { + CoroutineState::Start(i) => { + println!("Program starting"); + let path = format!("/{}/HelloWorld{i}", i * 1000); + self.state = CoroutineState::Wait(Box::new(Http::get(&path))); + } + CoroutineState::Wait(ref mut future) => match future.poll() { + PollState::Ready(txt) => { + println!("{txt}"); + self.state = CoroutineState::Resolved; + break PollState::Ready(()); + } + PollState::NotReady => break PollState::NotReady, + }, + CoroutineState::Resolved => panic!("Polled a resolved Future which is illegal!"), + } + } + } +} + +enum CoroutineState { + Start(usize), + Wait(Box<dyn Future<Output = String>>), + Resolved, +} + +fn async_main() -> impl Future<Output = ()> { + let future1 = Coroutine::new(1); + let future2 = Coroutine::new(2); + let future3 = Coroutine::new(3); + + join_three(future1, future2, future3) +} + +fn main() { + let start = Instant::now(); + let mut future = async_main(); + while let PollState::NotReady = future.poll() {} + println!("Elapsed time: {}", start.elapsed().as_millis()); +}