├── .gitignore ├── README.md └── async_rust ├── chapter_01 ├── communicating_with_processes │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── fib_blocking │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── improving_http_request_performance_with_async │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── interacting_with_processes │ ├── .gitignore │ ├── Cargo.toml │ ├── child_process │ └── src │ │ └── main.rs ├── introduction_to_processes │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── introduction_to_processes_multithreading │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── process_per_connection │ ├── connection │ │ ├── Cargo.toml │ │ ├── connection │ │ └── src │ │ │ └── main.rs │ ├── connection_bin │ ├── process_spawner │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── scripts │ │ ├── prep.sh │ │ └── run.sh │ ├── server │ │ ├── Cargo.toml │ │ ├── server │ │ └── src │ │ │ └── main.rs │ └── server_bin ├── requests_blocking │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── threads_and_condvar │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs └── what_are_threads │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_1 ├── build_own_runtime │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── configuring_our_runtime │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── creating_our_own_join_marco │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── increasing_workers_and_queues │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── passing_tasks_to_different_queues │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── refactoring_our_spawn_task_function │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs └── task_stealing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_10 ├── README.md ├── building-our-async-client │ ├── Cargo.toml │ ├── async_runtime │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── executor.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── reciever.rs │ │ │ ├── sender.rs │ │ │ ├── sleep.rs │ │ │ └── waker.rs │ ├── client │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── data_layer │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── data.rs │ │ │ └── lib.rs │ └── server │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── building-our-server │ ├── Cargo.toml │ ├── async_runtime │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── executor.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── reciever.rs │ │ │ ├── sender.rs │ │ │ ├── sleep.rs │ │ │ └── waker.rs │ ├── client │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── data_layer │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── data.rs │ │ │ └── lib.rs │ └── server │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── building-our-std-async-runtime │ ├── Cargo.toml │ ├── async_runtime │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── executor.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── reciever.rs │ │ │ ├── sender.rs │ │ │ ├── sleep.rs │ │ │ └── waker.rs │ ├── client │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── data_layer │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── data.rs │ │ │ └── lib.rs │ └── server │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs ├── order.txt └── setting-up-the-basics │ ├── Cargo.toml │ ├── async_runtime │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── client │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── data_layer │ ├── Cargo.toml │ └── src │ │ ├── data.rs │ │ └── lib.rs │ └── server │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_11 ├── README.md ├── basic_async_testing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── basic_testing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── channel_testing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── data_race_testing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── deadlock_testing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── network_testing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs └── putting_it_all_together │ ├── .gitignore │ ├── Cargo.toml │ ├── input.txt │ ├── output.txt │ └── src │ └── main.rs ├── chapter_2 ├── context-in-futures │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── futures │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── pinning_futures │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── putting-it-all-together │ ├── Cargo.toml │ ├── login.txt │ ├── logout.txt │ └── src │ │ └── main.rs ├── sharing-data-between-futures │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── understanding_tasks │ ├── Cargo.toml │ └── src │ │ └── main.rs └── waking-futures-remotely │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_3 ├── building-our-own-async-queue │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── configuring-our-runtime │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── creating-out-own-join-macro │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── increasing-workers-and-queues │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── refactoring-our-spawn-task-function │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── running-background-processes │ ├── Cargo.toml │ └── src │ │ └── main.rs └── task-stealing │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_4 ├── building_an_executor_for_hyper │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── runtime.rs └── listening_to_sockets_with_mio │ ├── .gitignore │ ├── Cargo.toml │ └── src │ ├── hyper_client.rs │ ├── main.rs │ └── runtime.rs ├── chapter_5 ├── calling_a_coroutine_from_a_coroutine │ ├── Cargo.toml │ ├── numbers.txt │ ├── output.txt │ └── src │ │ └── main.rs ├── controlling_coroutines │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── implementing_a_simple_generator_in_rust │ ├── Cargo.toml │ ├── data.txt │ └── src │ │ └── main.rs ├── mimicking_async_behavour_with_coroutines │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── stacking_coroutines │ ├── Cargo.toml │ ├── numbers.txt │ ├── output.txt │ └── src │ │ └── main.rs ├── testing_coroutines │ ├── Cargo.toml │ └── src │ │ └── main.rs └── why_use_coroutines │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_6 ├── defining-our-subjects │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── enabling-broadcasting-with-an-event-bus │ ├── Cargo.toml │ └── src │ │ └── main.rs └── getting-user-input-by-callbacks │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_7 ├── README.md ├── localset │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── localstate │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── metrics │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── runtime_setup │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── different_runtimes.rs │ │ └── main.rs └── shutdown │ ├── Cargo.toml │ └── src │ └── main.rs ├── chapter_8 ├── actors-vs-mutexes │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── basic_actors │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── building-a-basic-actor │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── routing │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── state_recovery │ ├── .gitignore │ ├── Cargo.toml │ ├── data.json │ └── src │ │ └── main.rs └── supervision │ ├── .gitignore │ ├── Cargo.toml │ ├── data.json │ └── src │ └── main.rs └── chapter_9 ├── async_decorator_pattern ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── circuit_breaker ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── decorator_pattern ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── modular_design ├── .gitignore ├── Cargo.toml └── src │ ├── async_mod.rs │ └── main.rs ├── retry_patterns ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── state_machine ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── waterfall ├── .gitignore ├── Cargo.toml └── src │ └── main.rs └── waterfall_logic_flow ├── .gitignore ├── Cargo.toml └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | .DS_Store 17 | .idea 18 | async_rust/chapter_5/why_use_coroutines/numbers.txt 19 | async_rust/chapter_5/why_use_coroutines/numbers.txt 20 | 21 | numbers.txt 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-rust-oreilly 2 | The code for the async rust book for O'Reilly 3 | -------------------------------------------------------------------------------- /async_rust/chapter_01/communicating_with_processes/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/communicating_with_processes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "communicating_with_processes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tempfile = "3.2.0" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_01/communicating_with_processes/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, BufRead}; 2 | use std::process; 3 | 4 | 5 | fn main() { 6 | let pid = process::id(); 7 | println!("process ID: {}", pid); 8 | 9 | 10 | let stdin = io::stdin(); 11 | let mut lines = stdin.lock().lines(); 12 | 13 | 14 | loop { 15 | let line = match lines.next() { 16 | Some(Ok(line)) => line, 17 | _ => { 18 | eprintln!("Failed to read from stdin"); 19 | break; 20 | } 21 | }; 22 | println!("Received: {}", line); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /async_rust/chapter_01/fib_blocking/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/fib_blocking/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fib_blocking" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /async_rust/chapter_01/fib_blocking/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | 4 | fn fibonacci(n: u64) -> u64 { 5 | if n == 0 || n == 1 { 6 | return n; 7 | } 8 | fibonacci(n-1) + fibonacci(n-2) 9 | } 10 | 11 | 12 | fn main() { 13 | let mut threads = Vec::new(); 14 | 15 | 16 | for i in 0..8 { 17 | let handle = thread::spawn(move || { 18 | let result = fibonacci(4000); 19 | println!("Thread {} result: {}", i, result); 20 | }); 21 | threads.push(handle); 22 | } 23 | for handle in threads { 24 | handle.join().unwrap(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /async_rust/chapter_01/improving_http_request_performance_with_async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "improving_http_request_performance_with_async" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1", features = ["full"] } 8 | reqwest = { version = "0.11", features = ["json"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | serde_json = "1.0" 11 | -------------------------------------------------------------------------------- /async_rust/chapter_01/improving_http_request_performance_with_async/src/main.rs: -------------------------------------------------------------------------------- 1 | use reqwest::Error; 2 | use serde::Deserialize; 3 | use tokio::time::sleep; 4 | use std::time::Duration; 5 | use std::time::Instant; 6 | use serde_json; 7 | 8 | 9 | 10 | #[derive(Deserialize, Debug)] 11 | struct Response { 12 | url: String, 13 | args: serde_json::Value, 14 | } 15 | 16 | async fn fetch_data(seconds: u64) -> Result { 17 | let request_url = format!("https://httpbin.org/delay/{}", seconds); 18 | let response = reqwest::get(&request_url).await?; 19 | let delayed_response: Response = response.json().await?; 20 | Ok(delayed_response) 21 | } 22 | 23 | 24 | async fn calculate_last_login() { 25 | sleep(Duration::from_secs(1)).await; 26 | println!("Logged in 2 days ago"); 27 | } 28 | 29 | #[tokio::main] 30 | async fn main() -> Result<(), Error> { 31 | let start_time = Instant::now(); 32 | let data = fetch_data(5); 33 | let time_since = calculate_last_login(); 34 | let (posts, _) = tokio::join!( 35 | data, time_since 36 | ); 37 | let duration = start_time.elapsed(); 38 | println!("Fetched {:?}", posts); 39 | println!("Time taken: {:?}", duration); 40 | Ok(()) 41 | } -------------------------------------------------------------------------------- /async_rust/chapter_01/interacting_with_processes/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/interacting_with_processes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interacting_with_processes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tempfile = "3.2.0" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_01/interacting_with_processes/child_process: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_01/interacting_with_processes/child_process -------------------------------------------------------------------------------- /async_rust/chapter_01/interacting_with_processes/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Write; 3 | use std::env; 4 | use std::process::{Command, Stdio}; 5 | 6 | 7 | fn main() { 8 | let child_process_code = r#" 9 | use std::env; 10 | use std::process; 11 | use std::thread; 12 | use std::time::Duration; 13 | 14 | 15 | fn main() { 16 | loop { 17 | println!("This is the child process speaking!"); 18 | thread::sleep(Duration::from_secs(4)); 19 | let pid = process::id(); 20 | println!("Child process ID: {}", pid); 21 | } 22 | } 23 | "#; 24 | 25 | 26 | // Create a temporary file 27 | let mut temp_dir = env::temp_dir(); 28 | temp_dir.push("child_process_code.rs"); 29 | let mut file = File::create(&temp_dir).expect("Failed to create temporary file"); 30 | 31 | 32 | // Write the Rust code to the temporary file 33 | file.write_all(child_process_code.as_bytes()) 34 | .expect("Failed to write child process code to temporary file"); 35 | 36 | // Compile the child process code 37 | let compile_output = Command::new("rustc") 38 | .arg("-o") 39 | .arg("child_process") 40 | .arg(&temp_dir) 41 | .output() 42 | .expect("Failed to compile child process code"); 43 | 44 | 45 | if !compile_output.status.success() { 46 | eprintln!( 47 | "Error during compilation:\n{}", 48 | String::from_utf8_lossy(&compile_output.stderr) 49 | ); 50 | return; 51 | } 52 | 53 | // Spawn the child process 54 | let mut child = Command::new("./child_process") 55 | .stdout(Stdio::inherit()) 56 | .spawn() 57 | .expect("Failed to spawn child process"); 58 | 59 | 60 | println!("Child process spawned with PID: {}", child.id()); 61 | 62 | 63 | // Wait for the child process to finish 64 | let status = child.wait().expect("Failed to wait for child process"); 65 | 66 | 67 | println!("Child process terminated with status: {:?}", status); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /async_rust/chapter_01/introduction_to_processes/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/introduction_to_processes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "introduction_to_processes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /async_rust/chapter_01/introduction_to_processes/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::thread::sleep; 2 | use std::time::{Duration, Instant}; 3 | 4 | 5 | fn task() { 6 | println!("Running task..."); 7 | sleep(Duration::from_secs(1)); 8 | } 9 | 10 | 11 | fn main() { 12 | let start = Instant::now(); 13 | 14 | for _ in 0..10 { 15 | task(); 16 | } 17 | let elapsed = start.elapsed(); 18 | println!("The whole program took: {:?}", elapsed); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /async_rust/chapter_01/introduction_to_processes_multithreading/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/introduction_to_processes_multithreading/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "introduction_to_processes_multithreading" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /async_rust/chapter_01/introduction_to_processes_multithreading/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process::{Command, exit}; 3 | use std::time::{Duration, Instant}; 4 | use std::thread::sleep; 5 | 6 | 7 | fn run_processes() { 8 | let mut process1 = Command::new(env::current_exe().unwrap()) 9 | .arg("task") 10 | .arg("1") 11 | .spawn() 12 | .expect("Failed to start process1"); 13 | 14 | 15 | let mut process2 = Command::new(env::current_exe().unwrap()) 16 | .arg("task") 17 | .arg("6") 18 | .spawn() 19 | .expect("Failed to start process2"); 20 | 21 | 22 | process1.wait().expect("Failed to wait for process1"); 23 | process2.wait().expect("Failed to wait for process2"); 24 | 25 | 26 | println!("Both processes have completed."); 27 | } 28 | 29 | fn task(start_task_number: usize) { 30 | for i in start_task_number..start_task_number + 5 { 31 | sleep(Duration::from_secs(1)); 32 | println!("Task {} completed in process: {}", i, std::process::id()); 33 | } 34 | exit(0); 35 | } 36 | 37 | 38 | fn main() { 39 | let args: Vec = env::args().collect(); 40 | 41 | 42 | let start = Instant::now(); 43 | 44 | 45 | if args.len() > 2 && args[1] == "task" { 46 | let start_task_number = args[2].parse::().unwrap(); 47 | task(start_task_number); 48 | } else { 49 | run_processes(); 50 | } 51 | 52 | 53 | if args.len() <= 1 { 54 | let elapsed = start.elapsed(); 55 | println!("The whole program took: {:?}", elapsed); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/connection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "connection_bin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1", features = ["full"] } 8 | reqwest = { version = "0.11", features = ["json"] } 9 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/connection/connection: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_01/process_per_connection/connection/connection -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/connection/src/main.rs: -------------------------------------------------------------------------------- 1 | use reqwest::Error; 2 | 3 | 4 | #[tokio::main] 5 | async fn main() -> Result<(), Error> { 6 | let url = "https://jsonplaceholder.typicode.com/posts/1"; 7 | let response = reqwest::get(url).await?; 8 | 9 | if response.status().is_success() { 10 | let body = response.text().await?; 11 | println!("{}", body); 12 | } else { 13 | println!("Failed to get a valid response. Status: {}", response.status()); 14 | } 15 | 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/connection_bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_01/process_per_connection/connection_bin -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/process_spawner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "process_spawner" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/process_spawner/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio::process::Command; 2 | use tokio::join; 3 | 4 | #[tokio::main] 5 | async fn main() -> std::io::Result<()> { 6 | let mut handles = vec![]; 7 | 8 | // Define the number of processes you want to run 9 | let num_processes = 4; 10 | 11 | // Spawn the processes in a loop 12 | for _ in 0..num_processes { 13 | let handle = tokio::spawn(async { 14 | let output = Command::new("../connection_bin") 15 | .output() 16 | .await; 17 | 18 | match output { 19 | Ok(output) => { 20 | println!("Process completed with output: {}", String::from_utf8_lossy(&output.stdout)); 21 | Ok(output.status.code().unwrap_or(-1)) // Return the exit code 22 | } 23 | Err(e) => { 24 | eprintln!("Failed to run process: {}", e); 25 | Err(e) 26 | } 27 | } 28 | }); 29 | handles.push(handle); 30 | } 31 | 32 | let mut results = Vec::with_capacity(handles.len()); 33 | for handle in handles { 34 | results.push(handle.await.unwrap()); 35 | } 36 | 37 | // Handle the results 38 | for (i, result) in results.into_iter().enumerate() { 39 | match result { 40 | Ok(exit_code) => println!("Process {} exited with code {}", i + 1, exit_code), 41 | Err(e) => eprintln!("Process {} failed: {}", i + 1, e), 42 | } 43 | } 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/scripts/prep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # navigate to directory 4 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 5 | cd $SCRIPTPATH 6 | 7 | cd .. 8 | cd connection && cargo build --release && cd .. 9 | cd server && cargo build --release && cd .. 10 | 11 | 12 | cp connection/target/release/connection_bin ./ 13 | cp server/target/release/server_bin ./ -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # navigate to directory 4 | SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" 5 | cd "$SCRIPTPATH" 6 | 7 | cd .. 8 | 9 | # Function to get current time in milliseconds 10 | current_time_millis() { 11 | echo $(($(date +%s) * 1000 + $(date +%3N))) 12 | } 13 | 14 | # Start a timer in milliseconds 15 | # start_time=$(current_time_millis) 16 | # echo "Start time: $start_time milliseconds" 17 | 18 | # Run the background tasks 19 | ./server_bin & 20 | pid1=$! 21 | 22 | ./server_bin & 23 | pid2=$! 24 | 25 | ./server_bin & 26 | pid3=$! 27 | 28 | ./server_bin & 29 | pid4=$! 30 | 31 | # Wait for all background tasks to complete and capture their exit codes 32 | wait $pid1 33 | exit_code1=$? 34 | 35 | wait $pid2 36 | exit_code2=$? 37 | 38 | wait $pid3 39 | exit_code3=$? 40 | 41 | wait $pid4 42 | exit_code4=$? 43 | 44 | # Calculate the elapsed time in milliseconds 45 | # end_time=$(current_time_millis) 46 | # elapsed_time=$((end_time - start_time)) 47 | 48 | # echo "End time: $end_time milliseconds" 49 | 50 | # Print the outcomes and timing information 51 | echo "Task 1 (PID $pid1) exited with code $exit_code1" 52 | echo "Task 2 (PID $pid2) exited with code $exit_code2" 53 | echo "Task 3 (PID $pid3) exited with code $exit_code3" 54 | echo "Task 4 (PID $pid4) exited with code $exit_code4" 55 | 56 | # echo "Total time elapsed: $elapsed_time milliseconds" 57 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server_bin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/server/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_01/process_per_connection/server/server -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/server/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::process::{Command, Output}; 2 | 3 | 4 | fn main() { 5 | // Replace `./path_to_your_binary` with the actual path to your compiled binary 6 | let output: Output = Command::new("./connection_bin") 7 | .output() 8 | .expect("Failed to execute command"); 9 | 10 | if output.status.success() { 11 | // Capture the standard output and print it 12 | let stdout = String::from_utf8_lossy(&output.stdout); 13 | println!("Output: {}", stdout); 14 | } else { 15 | // Capture the standard error and print it if there's an error 16 | let stderr = String::from_utf8_lossy(&output.stderr); 17 | eprintln!("Error: {}", stderr); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /async_rust/chapter_01/process_per_connection/server_bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_01/process_per_connection/server_bin -------------------------------------------------------------------------------- /async_rust/chapter_01/requests_blocking/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/requests_blocking/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "requests_blocking" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | reqwest = "0.11.14" 10 | tokio = { version = "1.26.0", features = ["full"] } 11 | -------------------------------------------------------------------------------- /async_rust/chapter_01/requests_blocking/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use reqwest::Error; 3 | 4 | 5 | #[tokio::main] 6 | async fn main() -> Result<(), Error> { 7 | let url = "https://jsonplaceholder.typicode.com/posts/1"; 8 | 9 | 10 | let start_time = Instant::now(); 11 | 12 | 13 | let _ = reqwest::get(url).await?; 14 | let _ = reqwest::get(url).await?; 15 | let _ = reqwest::get(url).await?; 16 | let _ = reqwest::get(url).await?; 17 | 18 | 19 | 20 | let elapsed_time = start_time.elapsed(); 21 | println!("Request took {} ms", elapsed_time.as_millis()); 22 | 23 | let start_time = Instant::now(); 24 | 25 | let (_, _, _, _) = tokio::join!( 26 | reqwest::get(url), 27 | reqwest::get(url), 28 | reqwest::get(url), 29 | reqwest::get(url), 30 | ); 31 | let elapsed_time = start_time.elapsed(); 32 | println!("Request took {} ms", elapsed_time.as_millis()); 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /async_rust/chapter_01/threads_and_condvar/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/threads_and_condvar/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "threads_and_condvar" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /async_rust/chapter_01/threads_and_condvar/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Condvar, Mutex}; 2 | use std::thread; 3 | use std::time::Duration; 4 | use std::sync::atomic::AtomicBool; 5 | use std::sync::atomic::Ordering::Relaxed; 6 | 7 | 8 | fn main() { 9 | let shared_data = Arc::new((Mutex::new(false), Condvar::new())); 10 | let shared_data_clone = Arc::clone(&shared_data); 11 | 12 | let STOP = Arc::new(AtomicBool::new(false)); 13 | let STOP_CLONE = Arc::clone(&STOP); 14 | 15 | let background_thread = thread::spawn(move || { 16 | let (lock, cvar) = &*shared_data_clone; 17 | let mut received_value = lock.lock().unwrap(); 18 | while !STOP.load(Relaxed) { 19 | received_value = cvar.wait(received_value).unwrap(); 20 | println!("Received value: {}", *received_value); 21 | } 22 | }); 23 | 24 | let updater_thread = thread::spawn(move || { 25 | let (lock, cvar) = &*shared_data; 26 | let values = [false, true, false, true]; 27 | 28 | 29 | for i in 0..4 { 30 | let update_value = values[i as usize]; 31 | println!("Updating value to {}...", update_value); 32 | *lock.lock().unwrap() = update_value; 33 | cvar.notify_one(); 34 | thread::sleep(Duration::from_secs(4)); 35 | } 36 | STOP_CLONE.store(true, Relaxed); 37 | println!("STOP has been updated"); 38 | cvar.notify_one(); 39 | }); 40 | updater_thread.join().unwrap(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /async_rust/chapter_01/what_are_threads/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_01/what_are_threads/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "what_are_threads" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /async_rust/chapter_01/what_are_threads/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use std::thread; 3 | 4 | 5 | fn fibonacci(n: u64) -> u64 { 6 | if n == 0 || n == 1 { 7 | return n; 8 | } 9 | fibonacci(n-1) + fibonacci(n-2) 10 | } 11 | 12 | 13 | fn main() { 14 | let start = Instant::now(); 15 | let mut handles = vec![]; 16 | for _ in 0..4 { 17 | let handle = thread::spawn(|| { 18 | fibonacci(50) 19 | }); 20 | handles.push(handle); 21 | } 22 | let duration = start.elapsed(); 23 | println!("fibonacci(50) in {:?}", duration); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /async_rust/chapter_1/build_own_runtime/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/build_own_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "build_own_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_1/build_own_runtime/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, panic::catch_unwind, thread}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | 6 | use async_task::{Runnable, Task}; 7 | use futures_lite::future; 8 | use once_cell::sync::Lazy; 9 | 10 | 11 | fn spawn_task(future: F) -> Task 12 | where 13 | F: Future + Send + 'static, 14 | T: Send + 'static, 15 | { 16 | static QUEUE: Lazy> = Lazy::new(|| { 17 | let (tx, rx) = flume::unbounded::(); 18 | thread::spawn(move || { 19 | while let Ok(runnable) = rx.recv() { 20 | println!("runnable accepted"); 21 | let _ = catch_unwind(|| runnable.run()); 22 | } 23 | }); 24 | tx 25 | }); 26 | let schedule = |runnable| QUEUE.send(runnable).unwrap(); 27 | let (runnable, task) = async_task::spawn(future, schedule); 28 | runnable.schedule(); 29 | println!("Here is the queue count: {:?}", QUEUE.len()); 30 | return task 31 | 32 | } 33 | 34 | 35 | struct CounterFuture { 36 | count: u32, 37 | } 38 | impl Future for CounterFuture { 39 | type Output = u32; 40 | 41 | 42 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 43 | -> Poll { 44 | self.count += 1; 45 | println!("polling with result: {}", self.count); 46 | std::thread::sleep(Duration::from_secs(1)); 47 | 48 | 49 | if self.count < 3 { 50 | cx.waker().wake_by_ref(); 51 | Poll::Pending 52 | } else { 53 | Poll::Ready(self.count) 54 | } 55 | } 56 | } 57 | 58 | async fn async_fn() { 59 | std::thread::sleep(Duration::from_secs(1)); 60 | println!("async fn"); 61 | } 62 | 63 | 64 | fn main() { 65 | let one = CounterFuture { count: 0 }; 66 | let two = CounterFuture { count: 0 }; 67 | 68 | 69 | let t_one = spawn_task(one); 70 | let t_two = spawn_task(two); 71 | let t_three = spawn_task(async { 72 | async_fn().await; 73 | async_fn().await; 74 | async_fn().await; 75 | async_fn().await; 76 | }); 77 | std::thread::sleep(Duration::from_secs(5)); 78 | println!("before the block"); 79 | future::block_on(t_one); 80 | future::block_on(t_two); 81 | future::block_on(t_three); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /async_rust/chapter_1/configuring_our_runtime/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/configuring_our_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "configuring_our_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_1/creating_our_own_join_marco/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/creating_our_own_join_marco/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "creating_our_own_join_marco" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_1/increasing_workers_and_queues/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/increasing_workers_and_queues/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "increasing_workers_and_queues" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_1/increasing_workers_and_queues/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, panic::catch_unwind, thread}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | 6 | use async_task::{Runnable, Task}; 7 | use futures_lite::future; 8 | use once_cell::sync::Lazy; 9 | 10 | 11 | fn spawn_task(future: F) -> Task 12 | where 13 | F: Future + Send + 'static, 14 | T: Send + 'static, 15 | { 16 | static QUEUE: Lazy> = Lazy::new(|| { 17 | let (tx, rx) = flume::unbounded::(); 18 | 19 | for _ in 0..3 { 20 | let receiver = rx.clone(); 21 | thread::spawn(move || { 22 | while let Ok(runnable) = receiver.recv() { 23 | let _ = catch_unwind(|| runnable.run()); 24 | } 25 | }); 26 | } 27 | 28 | tx 29 | }); 30 | let schedule = |runnable| QUEUE.send(runnable).unwrap(); 31 | let (runnable, task) = async_task::spawn(future, schedule); 32 | runnable.schedule(); 33 | println!("Here is the queue count: {:?}", QUEUE.len()); 34 | return task 35 | 36 | } 37 | 38 | 39 | struct CounterFuture { 40 | count: u32, 41 | } 42 | impl Future for CounterFuture { 43 | type Output = u32; 44 | 45 | 46 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 47 | -> Poll { 48 | self.count += 1; 49 | println!("polling with result: {}", self.count); 50 | std::thread::sleep(Duration::from_secs(1)); 51 | 52 | 53 | if self.count < 3 { 54 | cx.waker().wake_by_ref(); 55 | Poll::Pending 56 | } else { 57 | Poll::Ready(self.count) 58 | } 59 | } 60 | } 61 | 62 | async fn async_fn() { 63 | std::thread::sleep(Duration::from_secs(1)); 64 | println!("async fn"); 65 | } 66 | 67 | 68 | fn main() { 69 | let one = CounterFuture { count: 0 }; 70 | let two = CounterFuture { count: 0 }; 71 | 72 | 73 | let t_one = spawn_task(one); 74 | let t_two = spawn_task(two); 75 | let t_three = spawn_task(async { 76 | async_fn().await; 77 | async_fn().await; 78 | async_fn().await; 79 | async_fn().await; 80 | }); 81 | std::thread::sleep(Duration::from_secs(5)); 82 | println!("before the block"); 83 | future::block_on(t_one); 84 | future::block_on(t_two); 85 | future::block_on(t_three); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /async_rust/chapter_1/passing_tasks_to_different_queues/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/passing_tasks_to_different_queues/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "passing_tasks_to_different_queues" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_1/passing_tasks_to_different_queues/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, panic::catch_unwind, thread}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | 6 | use async_task::{Runnable, Task}; 7 | use futures_lite::future; 8 | use once_cell::sync::Lazy; 9 | 10 | 11 | fn spawn_task(future: F) -> Task 12 | where 13 | F: Future + Send + 'static + FutureOrderLabel, 14 | T: Send + 'static, 15 | { 16 | static HIGH_QUEUE: Lazy> = Lazy::new(|| { 17 | let (tx, rx) = flume::unbounded::(); 18 | for _ in 0..2 { 19 | let receiver = rx.clone(); 20 | thread::spawn(move || { 21 | while let Ok(runnable) = receiver.recv() { 22 | let _ = catch_unwind(|| runnable.run()); 23 | } 24 | }); 25 | } 26 | tx 27 | }); 28 | static LOW_QUEUE: Lazy> = Lazy::new(|| { 29 | let (tx, rx) = flume::unbounded::(); 30 | for _ in 0..1 { 31 | let receiver = rx.clone(); 32 | thread::spawn(move || { 33 | while let Ok(runnable) = receiver.recv() { 34 | let _ = catch_unwind(|| runnable.run()); 35 | } 36 | }); 37 | } 38 | tx 39 | }); 40 | 41 | let schedule_high = |runnable| HIGH_QUEUE.send(runnable).unwrap(); 42 | let schedule_low = |runnable| LOW_QUEUE.send(runnable).unwrap(); 43 | 44 | let schedule = match future.get_order() { 45 | FutureType::High => schedule_high, 46 | FutureType::Low => schedule_low 47 | }; 48 | let (runnable, task) = async_task::spawn(future, schedule); 49 | runnable.schedule(); 50 | return task 51 | } 52 | 53 | 54 | struct CounterFuture { 55 | count: u32, 56 | order: FutureType 57 | } 58 | 59 | impl Future for CounterFuture { 60 | type Output = u32; 61 | 62 | 63 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 64 | -> Poll { 65 | self.count += 1; 66 | println!("polling with result: {}", self.count); 67 | std::thread::sleep(Duration::from_secs(1)); 68 | 69 | 70 | if self.count < 3 { 71 | cx.waker().wake_by_ref(); 72 | Poll::Pending 73 | } else { 74 | Poll::Ready(self.count) 75 | } 76 | } 77 | } 78 | 79 | impl FutureOrderLabel for CounterFuture { 80 | fn get_order(&self) -> FutureType { 81 | self.order 82 | } 83 | } 84 | 85 | 86 | async fn async_fn() { 87 | std::thread::sleep(Duration::from_secs(1)); 88 | println!("async fn"); 89 | } 90 | 91 | 92 | #[derive(Debug, Clone, Copy)] 93 | enum FutureType { 94 | High, 95 | Low 96 | } 97 | 98 | trait FutureOrderLabel: Future { 99 | fn get_order(&self) -> FutureType; 100 | } 101 | 102 | 103 | 104 | 105 | fn main() { 106 | let one = CounterFuture { count: 0, order: FutureType::High}; 107 | let two = CounterFuture { count: 0, order: FutureType::Low }; 108 | 109 | 110 | let t_one = spawn_task(one); 111 | let t_two = spawn_task(two); 112 | // let t_three = spawn_task(async { 113 | // async_fn().await; 114 | // async_fn().await; 115 | // async_fn().await; 116 | // async_fn().await; 117 | // }); 118 | std::thread::sleep(Duration::from_secs(5)); 119 | println!("before the block"); 120 | future::block_on(t_one); 121 | future::block_on(t_two); 122 | // future::block_on(t_three); 123 | } 124 | -------------------------------------------------------------------------------- /async_rust/chapter_1/refactoring_our_spawn_task_function/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/refactoring_our_spawn_task_function/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "refactoring_our_spawn_task_function" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_1/task_stealing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_1/task_stealing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "task_stealing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | once_cell = "1.17.1" 12 | flume = "0.10.14" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_10/README.md: -------------------------------------------------------------------------------- 1 | # std TCP Server 2 | 3 | This is the part of the book where we build an async TCP server just using the standard library. Below is the order: 4 | 5 | 1. setting-up-the-basics 6 | 2. building-our-std-async-runtime 7 | 3. building-our-server 8 | 4. building-our-async-client 9 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "client", 5 | "server", 6 | "data_layer", 7 | "async_runtime" 8 | ] -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | sync::{Arc, mpsc}, 4 | task::{Context, Poll, Waker}, 5 | pin::Pin, 6 | collections::VecDeque 7 | }; 8 | use crate::waker::create_raw_waker; 9 | 10 | 11 | pub struct Task { 12 | future: Pin + Send>>, 13 | waker: Arc, 14 | } 15 | 16 | 17 | pub struct Executor { 18 | pub polling: VecDeque, 19 | } 20 | impl Executor { 21 | 22 | pub fn new() -> Self { 23 | Executor { 24 | polling: VecDeque::new(), 25 | } 26 | } 27 | 28 | pub fn spawn(&mut self, future: F) -> mpsc::Receiver 29 | where 30 | F: Future + 'static + Send, 31 | T: Send + 'static, 32 | { 33 | let (tx, rx) = mpsc::channel(); 34 | let future: Pin + Send>> = Box::pin( 35 | async move { 36 | let result = future.await; 37 | let _ = tx.send(result); 38 | }); 39 | let task = Task { 40 | future, 41 | waker: self.create_waker(), 42 | }; 43 | self.polling.push_back(task); 44 | rx 45 | } 46 | 47 | pub fn poll(&mut self) { 48 | let mut task = match self.polling.pop_front() { 49 | Some(task) => task, 50 | None => return, 51 | }; 52 | let waker = task.waker.clone(); 53 | let context = &mut Context::from_waker(&waker); 54 | match task.future.as_mut().poll(context) { 55 | Poll::Ready(()) => {} 56 | Poll::Pending => { 57 | self.polling.push_back(task); 58 | } 59 | } 60 | } 61 | 62 | pub fn create_waker(&self) -> Arc { 63 | Arc::new(unsafe{Waker::from_raw(create_raw_waker())}) 64 | } 65 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod executor; 2 | pub mod waker; 3 | pub mod reciever; 4 | pub mod sleep; 5 | pub mod sender; 6 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin 5 | }; 6 | mod executor; 7 | mod waker; 8 | 9 | 10 | pub struct CountingFuture { 11 | pub count: i32, 12 | } 13 | impl Future for CountingFuture { 14 | type Output = i32; 15 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 16 | -> Poll { 17 | self.count += 1; 18 | if self.count == 4 { 19 | println!("CountingFuture is done!"); 20 | Poll::Ready(self.count) 21 | } else { 22 | cx.waker().wake_by_ref(); 23 | println!( 24 | "CountingFuture is not done yet! {}", 25 | self.count 26 | ); 27 | Poll::Pending 28 | } 29 | } 30 | } 31 | 32 | fn main() { 33 | let counter = CountingFuture { count: 0 }; 34 | let counter_two = CountingFuture { count: 0 }; 35 | let mut executor = executor::Executor::new(); 36 | let handle = executor.spawn(counter); 37 | let _handle_two = executor.spawn(counter_two); 38 | std::thread::spawn(move || { 39 | loop { 40 | executor.poll(); 41 | } 42 | }); 43 | let result = handle.recv().unwrap(); 44 | println!("Result: {}", result); 45 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/reciever.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin, 5 | net::TcpStream, 6 | io::{self, Read}, 7 | sync::{Arc, Mutex} 8 | }; 9 | 10 | 11 | pub struct TcpReceiver { 12 | pub stream: Arc>, 13 | pub buffer: Vec 14 | } 15 | impl Future for TcpReceiver { 16 | 17 | type Output = io::Result>; 18 | 19 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 20 | -> Poll { 21 | let mut stream = match self.stream.try_lock() { 22 | Ok(stream) => stream, 23 | Err(_) => { 24 | cx.waker().wake_by_ref(); 25 | return Poll::Pending; 26 | } 27 | }; 28 | stream.set_nonblocking(true)?; 29 | let mut local_buf = [0; 1024]; 30 | 31 | match stream.read(&mut local_buf) { 32 | Ok(0) => { 33 | Poll::Ready(Ok(self.buffer.to_vec())) 34 | }, 35 | Ok(n) => { 36 | std::mem::drop(stream); 37 | self.buffer.extend_from_slice(&local_buf[..n]); 38 | cx.waker().wake_by_ref(); 39 | Poll::Pending 40 | }, 41 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 42 | cx.waker().wake_by_ref(); 43 | Poll::Pending 44 | }, 45 | Err(e) => Poll::Ready(Err(e)) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/sender.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin, 5 | net::TcpStream, 6 | io::{self, Write}, 7 | sync::{Arc, Mutex} 8 | }; 9 | 10 | 11 | pub struct TcpSender { 12 | pub stream: Arc>, 13 | pub buffer: Vec 14 | } 15 | impl Future for TcpSender { 16 | 17 | type Output = io::Result<()>; 18 | 19 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 20 | -> Poll { 21 | let mut stream = match self.stream.try_lock() { 22 | Ok(stream) => stream, 23 | Err(_) => { 24 | cx.waker().wake_by_ref(); 25 | return Poll::Pending; 26 | } 27 | }; 28 | stream.set_nonblocking(true)?; 29 | match stream.write_all(&self.buffer) { 30 | Ok(_) => { 31 | Poll::Ready(Ok(())) 32 | }, 33 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { 34 | cx.waker().wake_by_ref(); 35 | Poll::Pending 36 | }, 37 | Err(e) => Poll::Ready(Err(e)) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/sleep.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | time::{Duration, Instant}, 6 | }; 7 | 8 | 9 | pub struct Sleep { 10 | when: Instant, 11 | } 12 | impl Sleep { 13 | pub fn new(duration: Duration) -> Self { 14 | Sleep { 15 | when: Instant::now() + duration, 16 | } 17 | } 18 | } 19 | 20 | 21 | impl Future for Sleep { 22 | type Output = (); 23 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 24 | -> Poll { 25 | let now = Instant::now(); 26 | if now >= self.when { 27 | Poll::Ready(()) 28 | } else { 29 | cx.waker().wake_by_ref(); 30 | Poll::Pending 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/async_runtime/src/waker.rs: -------------------------------------------------------------------------------- 1 | use std::task::{RawWaker, RawWakerVTable}; 2 | 3 | 4 | static VTABLE: RawWakerVTable = RawWakerVTable::new( 5 | my_clone, 6 | my_wake, 7 | my_wake_by_ref, 8 | my_drop, 9 | ); 10 | 11 | unsafe fn my_clone(raw_waker: *const ()) -> RawWaker { 12 | RawWaker::new(raw_waker, &VTABLE) 13 | } 14 | 15 | unsafe fn my_wake(raw_waker: *const ()) { 16 | drop(Box::from_raw(raw_waker as *mut u32)); 17 | } 18 | 19 | unsafe fn my_wake_by_ref(_raw_waker: *const ()) { 20 | } 21 | 22 | unsafe fn my_drop(raw_waker: *const ()) { 23 | drop(Box::from_raw(raw_waker as *mut u32)); 24 | } 25 | 26 | pub fn create_raw_waker() -> RawWaker { 27 | let data = Box::into_raw(Box::new(42u32)); 28 | RawWaker::new(data as *const (), &VTABLE) 29 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | data_layer = { path = "../data_layer" } 8 | async_runtime = { path = "../async_runtime" } 9 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/client/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io, 3 | sync::{Arc, Mutex}, 4 | net::TcpStream, 5 | time::Instant 6 | }; 7 | use data_layer::data::Data; 8 | use async_runtime::{ 9 | executor::Executor, 10 | reciever::TcpReceiver, 11 | sender::TcpSender, 12 | }; 13 | 14 | 15 | async fn send_data(field1: u32, field2: u16, field3: String) 16 | -> io::Result { 17 | let stream = Arc::new(Mutex::new(TcpStream::connect( 18 | "127.0.0.1:7878")? 19 | )); 20 | let message = Data {field1, field2, field3}; 21 | TcpSender { 22 | stream: stream.clone(), 23 | buffer: message.serialize()? 24 | }.await?; 25 | let receiver = TcpReceiver { 26 | stream: stream.clone(), 27 | buffer: Vec::new() 28 | }; 29 | String::from_utf8(receiver.await?).map_err(|_| 30 | io::Error::new(io::ErrorKind::InvalidData, "Invalid UTF-8") 31 | ) 32 | } 33 | 34 | 35 | fn main() -> io::Result<()> { 36 | let mut executor = Executor::new(); 37 | let mut handles = Vec::new(); 38 | let start = Instant::now(); 39 | for i in 0..4000 { 40 | let handle = executor.spawn(send_data( 41 | i, i as u16, format!("Hello, server! {}", i) 42 | )); 43 | handles.push(handle); 44 | } 45 | std::thread::spawn(move || { 46 | loop { 47 | executor.poll(); 48 | } 49 | }); 50 | println!("Waiting for result..."); 51 | for handle in handles { 52 | match handle.recv().unwrap() { 53 | Ok(result) => println!("Result: {}", result), 54 | Err(e) => println!("Error: {}", e) 55 | }; 56 | } 57 | let duration = start.elapsed(); 58 | println!("Time elapsed in expensive_function() is: {:?}", duration); 59 | Ok(()) 60 | } 61 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/data_layer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_layer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/data_layer/src/data.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Cursor, Read, Write}; 2 | 3 | #[derive(Debug)] 4 | pub struct Data { 5 | pub field1: u32, 6 | pub field2: u16, 7 | pub field3: String, 8 | } 9 | impl Data { 10 | pub fn serialize(&self) -> io::Result> { 11 | let mut bytes = Vec::new(); 12 | bytes.write(&self.field1.to_ne_bytes())?; 13 | bytes.write(&self.field2.to_ne_bytes())?; 14 | let field3_len = self.field3.len() as u32; 15 | bytes.write(&field3_len.to_ne_bytes())?; 16 | bytes.extend_from_slice(self.field3.as_bytes()); 17 | Ok(bytes) 18 | } 19 | pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result { 20 | // Initialize buffers for the fields, using arrays of the appropriate size 21 | let mut field1_bytes = [0u8; 4]; 22 | let mut field2_bytes = [0u8; 2]; 23 | 24 | // Read the first field (4 bytes) from the cursor into the buffer. Do the same for second field. 25 | cursor.read_exact(&mut field1_bytes)?; 26 | cursor.read_exact(&mut field2_bytes)?; 27 | 28 | // Convert the byte arrays into the appropriate data types (u32 and u16) 29 | let field1 = u32::from_ne_bytes(field1_bytes); 30 | let field2 = u16::from_ne_bytes(field2_bytes); 31 | 32 | // Initialize a buffer to read the length of the third field , which is 4 bytes long 33 | let mut len_bytes = [0u8; 4]; 34 | 35 | // Read the length from the cursor into the buffer 36 | cursor.read_exact(&mut len_bytes)?; 37 | 38 | // Convert the length bytes into a usize 39 | let len = u32::from_ne_bytes(len_bytes) as usize; 40 | 41 | // Initialize a buffer with the specified length to hold the third field's data 42 | let mut field3_bytes = vec![0u8; len]; 43 | 44 | // Read the third field's data from the cursor into the buffer 45 | cursor.read_exact(&mut field3_bytes)?; 46 | 47 | // Convert the third field's bytes into a UTF-8 string, or return an error if this cannot be done. 48 | let field3 = String::from_utf8(field3_bytes) 49 | .map_err(|_| io::Error::new( 50 | io::ErrorKind::InvalidData, "Invalid UTF-8" 51 | ))?; 52 | 53 | // Return the structured data 54 | Ok(Data { field1, field2, field3 }) 55 | } 56 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/data_layer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | data_layer = { path = "../data_layer" } 8 | async_runtime = { path = "../async_runtime" } 9 | 10 | [profile.release] 11 | opt-level = 'z' 12 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-async-client/server/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | thread, 3 | sync::{mpsc::channel, atomic::{AtomicBool, Ordering}}, 4 | io::{self, Read, Write, ErrorKind, Cursor}, 5 | net::{TcpListener, TcpStream} 6 | }; 7 | use data_layer::data::Data; 8 | use async_runtime::{ 9 | executor::Executor, 10 | sleep::Sleep 11 | }; 12 | 13 | 14 | static FLAGS: [AtomicBool; 3] = [ 15 | AtomicBool::new(false), 16 | AtomicBool::new(false), 17 | AtomicBool::new(false), 18 | ]; 19 | 20 | 21 | macro_rules! spawn_worker { 22 | ($name:expr, $rx:expr, $flag:expr) => { 23 | thread::spawn(move || { 24 | let mut executor = Executor::new(); 25 | loop { 26 | if let Ok(stream) = $rx.try_recv() { 27 | println!( 28 | "{} Received connection: {}", 29 | $name, 30 | stream.peer_addr().unwrap() 31 | ); 32 | executor.spawn(handle_client(stream)); 33 | } else { 34 | if executor.polling.len() == 0 { 35 | println!("{} is sleeping", $name); 36 | $flag.store(true, Ordering::SeqCst); 37 | thread::park(); 38 | } 39 | } 40 | executor.poll(); 41 | } 42 | }) 43 | }; 44 | } 45 | 46 | 47 | async fn handle_client(mut stream: TcpStream) -> std::io::Result<()> { 48 | stream.set_nonblocking(true)?; 49 | let mut buffer = Vec::new(); 50 | let mut local_buf = [0; 1024]; 51 | loop { 52 | match stream.read(&mut local_buf) { 53 | Ok(0) => { 54 | break; 55 | }, 56 | Ok(len) => { 57 | buffer.extend_from_slice(&local_buf[..len]); 58 | }, 59 | Err(ref e) if e.kind() == ErrorKind::WouldBlock => { 60 | if buffer.len() > 0 { 61 | break; 62 | } 63 | Sleep::new(std::time::Duration::from_millis(10)).await; 64 | continue; 65 | }, 66 | Err(e) => { 67 | println!("Failed to read from connection: {}", e); 68 | } 69 | } 70 | } 71 | match Data::deserialize(&mut Cursor::new(buffer.as_slice())) { 72 | Ok(message) => { 73 | println!("Received message: {:?}", message); 74 | }, 75 | Err(e) => { 76 | println!("Failed to decode message: {}", e); 77 | } 78 | } 79 | Sleep::new(std::time::Duration::from_secs(1)).await; 80 | stream.write_all(b"Hello, client!")?; 81 | Ok(()) 82 | } 83 | 84 | 85 | 86 | fn main() -> io::Result<()> { 87 | let listener = TcpListener::bind("127.0.0.1:7878")?; 88 | println!("Server listening on port 7878"); 89 | 90 | let (one_tx, one_rx) = channel::(); 91 | let (two_tx, two_rx) = channel::(); 92 | let (three_tx, three_rx) = channel::(); 93 | 94 | let one = spawn_worker!("One", one_rx, &FLAGS[0]); 95 | let two = spawn_worker!("Two", two_rx, &FLAGS[1]); 96 | let three = spawn_worker!("Three", three_rx, &FLAGS[2]); 97 | 98 | let router = [one_tx, two_tx, three_tx]; 99 | let threads = [one, two, three]; 100 | let mut index = 0; 101 | 102 | for stream in listener.incoming() { 103 | match stream { 104 | Ok(stream) => { 105 | let _ = router[index].send(stream); 106 | if FLAGS[index].load(Ordering::SeqCst) { 107 | FLAGS[index].store(false, Ordering::SeqCst); 108 | threads[index].thread().unpark(); 109 | } 110 | index += 1; // cycle through the index of threads 111 | if index == 3 { 112 | index = 0; 113 | } 114 | } 115 | Err(e) => { 116 | println!("Connection failed: {}", e); 117 | } 118 | } 119 | } 120 | Ok(()) 121 | } 122 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "client", 5 | "server", 6 | "data_layer", 7 | "async_runtime" 8 | ] -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | sync::{Arc, mpsc}, 4 | task::{Context, Poll, Waker}, 5 | pin::Pin, 6 | collections::VecDeque 7 | }; 8 | use crate::waker::create_raw_waker; 9 | 10 | 11 | pub struct Task { 12 | future: Pin + Send>>, 13 | waker: Arc, 14 | } 15 | 16 | 17 | pub struct Executor { 18 | pub polling: VecDeque, 19 | } 20 | impl Executor { 21 | 22 | pub fn new() -> Self { 23 | Executor { 24 | polling: VecDeque::new(), 25 | } 26 | } 27 | 28 | pub fn spawn(&mut self, future: F) -> mpsc::Receiver 29 | where 30 | F: Future + 'static + Send, 31 | T: Send + 'static, 32 | { 33 | let (tx, rx) = mpsc::channel(); 34 | let future: Pin + Send>> = Box::pin( 35 | async move { 36 | let result = future.await; 37 | let _ = tx.send(result); 38 | }); 39 | let task = Task { 40 | future, 41 | waker: self.create_waker(), 42 | }; 43 | self.polling.push_back(task); 44 | rx 45 | } 46 | 47 | pub fn poll(&mut self) { 48 | let mut task = match self.polling.pop_front() { 49 | Some(task) => task, 50 | None => return, 51 | }; 52 | let waker = task.waker.clone(); 53 | let context = &mut Context::from_waker(&waker); 54 | match task.future.as_mut().poll(context) { 55 | Poll::Ready(()) => {} 56 | Poll::Pending => { 57 | self.polling.push_back(task); 58 | } 59 | } 60 | } 61 | 62 | pub fn create_waker(&self) -> Arc { 63 | Arc::new(unsafe{Waker::from_raw(create_raw_waker())}) 64 | } 65 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod executor; 2 | pub mod waker; 3 | pub mod reciever; 4 | pub mod sleep; 5 | pub mod sender; 6 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin 5 | }; 6 | mod executor; 7 | mod waker; 8 | 9 | 10 | pub struct CountingFuture { 11 | pub count: i32, 12 | } 13 | impl Future for CountingFuture { 14 | type Output = i32; 15 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 16 | -> Poll { 17 | self.count += 1; 18 | if self.count == 4 { 19 | println!("CountingFuture is done!"); 20 | Poll::Ready(self.count) 21 | } else { 22 | cx.waker().wake_by_ref(); 23 | println!( 24 | "CountingFuture is not done yet! {}", 25 | self.count 26 | ); 27 | Poll::Pending 28 | } 29 | } 30 | } 31 | 32 | fn main() { 33 | let counter = CountingFuture { count: 0 }; 34 | let counter_two = CountingFuture { count: 0 }; 35 | let mut executor = executor::Executor::new(); 36 | let handle = executor.spawn(counter); 37 | let _handle_two = executor.spawn(counter_two); 38 | std::thread::spawn(move || { 39 | loop { 40 | executor.poll(); 41 | } 42 | }); 43 | let result = handle.recv().unwrap(); 44 | println!("Result: {}", result); 45 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/reciever.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin, 5 | net::TcpStream, 6 | io::{self, Read}, 7 | sync::{Arc, Mutex} 8 | }; 9 | 10 | 11 | pub struct TcpReceiver { 12 | pub stream: Arc>, 13 | pub buffer: Vec 14 | } 15 | impl Future for TcpReceiver { 16 | 17 | type Output = io::Result>; 18 | 19 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 20 | -> Poll { 21 | let mut stream = match self.stream.try_lock() { 22 | Ok(stream) => stream, 23 | Err(_) => { 24 | cx.waker().wake_by_ref(); 25 | return Poll::Pending; 26 | } 27 | }; 28 | stream.set_nonblocking(true)?; 29 | let mut local_buf = [0; 1024]; 30 | 31 | match stream.read(&mut local_buf) { 32 | Ok(0) => { 33 | Poll::Ready(Ok(self.buffer.to_vec())) 34 | }, 35 | Ok(n) => { 36 | std::mem::drop(stream); 37 | self.buffer.extend_from_slice(&local_buf[..n]); 38 | cx.waker().wake_by_ref(); 39 | Poll::Pending 40 | }, 41 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 42 | cx.waker().wake_by_ref(); 43 | Poll::Pending 44 | }, 45 | Err(e) => Poll::Ready(Err(e)) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/sender.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin, 5 | net::TcpStream, 6 | io::{self, Write}, 7 | sync::{Arc, Mutex} 8 | }; 9 | 10 | 11 | pub struct TcpSender { 12 | pub stream: Arc>, 13 | pub buffer: Vec 14 | } 15 | impl Future for TcpSender { 16 | 17 | type Output = io::Result<()>; 18 | 19 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 20 | -> Poll { 21 | let mut stream = match self.stream.try_lock() { 22 | Ok(stream) => stream, 23 | Err(_) => { 24 | cx.waker().wake_by_ref(); 25 | return Poll::Pending; 26 | } 27 | }; 28 | stream.set_nonblocking(true)?; 29 | match stream.write_all(&self.buffer) { 30 | Ok(_) => { 31 | Poll::Ready(Ok(())) 32 | }, 33 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { 34 | cx.waker().wake_by_ref(); 35 | Poll::Pending 36 | }, 37 | Err(e) => Poll::Ready(Err(e)) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/sleep.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | time::{Duration, Instant}, 6 | }; 7 | 8 | 9 | pub struct Sleep { 10 | when: Instant, 11 | } 12 | impl Sleep { 13 | pub fn new(duration: Duration) -> Self { 14 | Sleep { 15 | when: Instant::now() + duration, 16 | } 17 | } 18 | } 19 | 20 | 21 | impl Future for Sleep { 22 | type Output = (); 23 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 24 | -> Poll { 25 | let now = Instant::now(); 26 | if now >= self.when { 27 | Poll::Ready(()) 28 | } else { 29 | cx.waker().wake_by_ref(); 30 | Poll::Pending 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/async_runtime/src/waker.rs: -------------------------------------------------------------------------------- 1 | use std::task::{RawWaker, RawWakerVTable}; 2 | 3 | 4 | static VTABLE: RawWakerVTable = RawWakerVTable::new( 5 | my_clone, 6 | my_wake, 7 | my_wake_by_ref, 8 | my_drop, 9 | ); 10 | 11 | unsafe fn my_clone(raw_waker: *const ()) -> RawWaker { 12 | RawWaker::new(raw_waker, &VTABLE) 13 | } 14 | 15 | unsafe fn my_wake(raw_waker: *const ()) { 16 | drop(Box::from_raw(raw_waker as *mut u32)); 17 | } 18 | 19 | unsafe fn my_wake_by_ref(_raw_waker: *const ()) { 20 | } 21 | 22 | unsafe fn my_drop(raw_waker: *const ()) { 23 | drop(Box::from_raw(raw_waker as *mut u32)); 24 | } 25 | 26 | pub fn create_raw_waker() -> RawWaker { 27 | let data = Box::into_raw(Box::new(42u32)); 28 | RawWaker::new(data as *const (), &VTABLE) 29 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/client/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/data_layer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_layer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/data_layer/src/data.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Cursor, Read, Write}; 2 | 3 | #[derive(Debug)] 4 | pub struct Data { 5 | pub field1: u32, 6 | pub field2: u16, 7 | pub field3: String, 8 | } 9 | impl Data { 10 | pub fn serialize(&self) -> io::Result> { 11 | let mut bytes = Vec::new(); 12 | bytes.write(&self.field1.to_ne_bytes())?; 13 | bytes.write(&self.field2.to_ne_bytes())?; 14 | let field3_len = self.field3.len() as u32; 15 | bytes.write(&field3_len.to_ne_bytes())?; 16 | bytes.extend_from_slice(self.field3.as_bytes()); 17 | Ok(bytes) 18 | } 19 | pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result { 20 | // Initialize buffers for the fields, using arrays of the appropriate size 21 | let mut field1_bytes = [0u8; 4]; 22 | let mut field2_bytes = [0u8; 2]; 23 | 24 | // Read the first field (4 bytes) from the cursor into the buffer. Do the same for second field. 25 | cursor.read_exact(&mut field1_bytes)?; 26 | cursor.read_exact(&mut field2_bytes)?; 27 | 28 | // Convert the byte arrays into the appropriate data types (u32 and u16) 29 | let field1 = u32::from_ne_bytes(field1_bytes); 30 | let field2 = u16::from_ne_bytes(field2_bytes); 31 | 32 | // Initialize a buffer to read the length of the third field , which is 4 bytes long 33 | let mut len_bytes = [0u8; 4]; 34 | 35 | // Read the length from the cursor into the buffer 36 | cursor.read_exact(&mut len_bytes)?; 37 | 38 | // Convert the length bytes into a usize 39 | let len = u32::from_ne_bytes(len_bytes) as usize; 40 | 41 | // Initialize a buffer with the specified length to hold the third field's data 42 | let mut field3_bytes = vec![0u8; len]; 43 | 44 | // Read the third field's data from the cursor into the buffer 45 | cursor.read_exact(&mut field3_bytes)?; 46 | 47 | // Convert the third field's bytes into a UTF-8 string, or return an error if this cannot be done. 48 | let field3 = String::from_utf8(field3_bytes) 49 | .map_err(|_| io::Error::new( 50 | io::ErrorKind::InvalidData, "Invalid UTF-8" 51 | ))?; 52 | 53 | // Return the structured data 54 | Ok(Data { field1, field2, field3 }) 55 | } 56 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/data_layer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | data_layer = { path = "../data_layer" } 8 | async_runtime = { path = "../async_runtime" } 9 | 10 | [profile.release] 11 | opt-level = 'z' 12 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-server/server/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | thread, 3 | sync::{mpsc::channel, atomic::{AtomicBool, Ordering}}, 4 | io::{self, Read, Write, ErrorKind, Cursor}, 5 | net::{TcpListener, TcpStream} 6 | }; 7 | use data_layer::data::Data; 8 | use async_runtime::{ 9 | executor::Executor, 10 | sleep::Sleep 11 | }; 12 | 13 | 14 | static FLAGS: [AtomicBool; 3] = [ 15 | AtomicBool::new(false), 16 | AtomicBool::new(false), 17 | AtomicBool::new(false), 18 | ]; 19 | 20 | 21 | macro_rules! spawn_worker { 22 | ($name:expr, $rx:expr, $flag:expr) => { 23 | thread::spawn(move || { 24 | let mut executor = Executor::new(); 25 | loop { 26 | if let Ok(stream) = $rx.try_recv() { 27 | println!( 28 | "{} Received connection: {}", 29 | $name, 30 | stream.peer_addr().unwrap() 31 | ); 32 | executor.spawn(handle_client(stream)); 33 | } else { 34 | if executor.polling.len() == 0 { 35 | println!("{} is sleeping", $name); 36 | $flag.store(true, Ordering::SeqCst); 37 | thread::park(); 38 | } 39 | } 40 | executor.poll(); 41 | } 42 | }) 43 | }; 44 | } 45 | 46 | 47 | async fn handle_client(mut stream: TcpStream) -> std::io::Result<()> { 48 | stream.set_nonblocking(true)?; 49 | let mut buffer = Vec::new(); 50 | let mut local_buf = [0; 1024]; 51 | loop { 52 | match stream.read(&mut local_buf) { 53 | Ok(0) => { 54 | break; 55 | }, 56 | Ok(len) => { 57 | buffer.extend_from_slice(&local_buf[..len]); 58 | }, 59 | Err(ref e) if e.kind() == ErrorKind::WouldBlock => { 60 | if buffer.len() > 0 { 61 | break; 62 | } 63 | println!("starting to sleep"); 64 | Sleep::new(std::time::Duration::from_millis(10)).await; 65 | println!("finished sleeping"); 66 | continue; 67 | }, 68 | Err(e) => { 69 | println!("Failed to read from connection: {}", e); 70 | } 71 | } 72 | } 73 | match Data::deserialize(&mut Cursor::new(buffer.as_slice())) { 74 | Ok(message) => { 75 | println!("Received message: {:?}", message); 76 | }, 77 | Err(e) => { 78 | println!("Failed to decode message: {}", e); 79 | } 80 | } 81 | Sleep::new(std::time::Duration::from_secs(1)).await; 82 | stream.write_all(b"Hello, client!")?; 83 | Ok(()) 84 | } 85 | 86 | 87 | 88 | fn main() -> io::Result<()> { 89 | let (one_tx, one_rx) = channel::(); 90 | let (two_tx, two_rx) = channel::(); 91 | let (three_tx, three_rx) = channel::(); 92 | 93 | let one = spawn_worker!("One", one_rx, &FLAGS[0]); 94 | let two = spawn_worker!("Two", two_rx, &FLAGS[1]); 95 | let three = spawn_worker!("Three", three_rx, &FLAGS[2]); 96 | 97 | let router = [one_tx, two_tx, three_tx]; 98 | let threads = [one, two, three]; 99 | let mut index = 0; 100 | 101 | let listener = TcpListener::bind("127.0.0.1:7878")?; 102 | println!("Server listening on port 7878"); 103 | 104 | for stream in listener.incoming() { 105 | match stream { 106 | Ok(stream) => { 107 | let _ = router[index].send(stream); 108 | if FLAGS[index].load(Ordering::SeqCst) { 109 | FLAGS[index].store(false, Ordering::SeqCst); 110 | threads[index].thread().unpark(); 111 | } 112 | index += 1; // cycle through the index of threads 113 | if index == 3 { 114 | index = 0; 115 | } 116 | } 117 | Err(e) => { 118 | println!("Connection failed: {}", e); 119 | } 120 | } 121 | } 122 | Ok(()) 123 | } 124 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "client", 5 | "server", 6 | "data_layer", 7 | "async_runtime" 8 | ] -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | sync::{Arc, mpsc}, 4 | task::{Context, Poll, Waker}, 5 | pin::Pin, 6 | collections::VecDeque 7 | }; 8 | use crate::waker::create_raw_waker; 9 | 10 | 11 | pub struct Task { 12 | future: Pin + Send>>, 13 | waker: Arc, 14 | } 15 | 16 | 17 | pub struct Executor { 18 | pub polling: VecDeque, 19 | } 20 | impl Executor { 21 | 22 | pub fn new() -> Self { 23 | Executor { 24 | polling: VecDeque::new(), 25 | } 26 | } 27 | 28 | pub fn spawn(&mut self, future: F) -> mpsc::Receiver 29 | where 30 | F: Future + 'static + Send, 31 | T: Send + 'static, 32 | { 33 | let (tx, rx) = mpsc::channel(); 34 | let future: Pin + Send>> = Box::pin( 35 | async move { 36 | let result = future.await; 37 | let _ = tx.send(result); 38 | }); 39 | let task = Task { 40 | future, 41 | waker: self.create_waker(), 42 | }; 43 | self.polling.push_back(task); 44 | rx 45 | } 46 | 47 | pub fn poll(&mut self) { 48 | let mut task = match self.polling.pop_front() { 49 | Some(task) => task, 50 | None => return, 51 | }; 52 | let waker = task.waker.clone(); 53 | let context = &mut Context::from_waker(&waker); 54 | match task.future.as_mut().poll(context) { 55 | Poll::Ready(()) => {} 56 | Poll::Pending => { 57 | self.polling.push_back(task); 58 | } 59 | } 60 | } 61 | 62 | pub fn create_waker(&self) -> Arc { 63 | Arc::new(unsafe{Waker::from_raw(create_raw_waker())}) 64 | } 65 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod executor; 2 | pub mod waker; 3 | pub mod reciever; 4 | pub mod sleep; 5 | pub mod sender; 6 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin 5 | }; 6 | mod executor; 7 | mod waker; 8 | 9 | 10 | pub struct CountingFuture { 11 | pub count: i32, 12 | } 13 | impl Future for CountingFuture { 14 | type Output = i32; 15 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 16 | -> Poll { 17 | self.count += 1; 18 | if self.count == 4 { 19 | println!("CountingFuture is done!"); 20 | Poll::Ready(self.count) 21 | } else { 22 | cx.waker().wake_by_ref(); 23 | println!( 24 | "CountingFuture is not done yet! {}", 25 | self.count 26 | ); 27 | Poll::Pending 28 | } 29 | } 30 | } 31 | 32 | fn main() { 33 | let counter = CountingFuture { count: 0 }; 34 | let counter_two = CountingFuture { count: 0 }; 35 | let mut executor = executor::Executor::new(); 36 | let handle = executor.spawn(counter); 37 | let _handle_two = executor.spawn(counter_two); 38 | std::thread::spawn(move || { 39 | loop { 40 | executor.poll(); 41 | } 42 | }); 43 | let result = handle.recv().unwrap(); 44 | println!("Result: {}", result); 45 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/reciever.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin, 5 | net::TcpStream, 6 | io::{self, Read}, 7 | sync::{Arc, Mutex} 8 | }; 9 | 10 | 11 | pub struct TcpReceiver { 12 | pub stream: Arc>, 13 | pub buffer: Vec 14 | } 15 | impl Future for TcpReceiver { 16 | 17 | type Output = io::Result>; 18 | 19 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 20 | -> Poll { 21 | let mut stream = match self.stream.try_lock() { 22 | Ok(stream) => stream, 23 | Err(_) => { 24 | cx.waker().wake_by_ref(); 25 | return Poll::Pending; 26 | } 27 | }; 28 | stream.set_nonblocking(true)?; 29 | let mut local_buf = [0; 1024]; 30 | 31 | match stream.read(&mut local_buf) { 32 | Ok(0) => { 33 | Poll::Ready(Ok(self.buffer.to_vec())) 34 | }, 35 | Ok(n) => { 36 | std::mem::drop(stream); 37 | self.buffer.extend_from_slice(&local_buf[..n]); 38 | cx.waker().wake_by_ref(); 39 | Poll::Pending 40 | }, 41 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 42 | cx.waker().wake_by_ref(); 43 | Poll::Pending 44 | }, 45 | Err(e) => Poll::Ready(Err(e)) 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/sender.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | task::{Context, Poll}, 4 | pin::Pin, 5 | net::TcpStream, 6 | io::{self, Write}, 7 | sync::{Arc, Mutex} 8 | }; 9 | 10 | 11 | pub struct TcpSender { 12 | pub stream: Arc>, 13 | pub buffer: Vec 14 | } 15 | impl Future for TcpSender { 16 | 17 | type Output = io::Result<()>; 18 | 19 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 20 | -> Poll { 21 | let mut stream = match self.stream.try_lock() { 22 | Ok(stream) => stream, 23 | Err(_) => { 24 | cx.waker().wake_by_ref(); 25 | return Poll::Pending; 26 | } 27 | }; 28 | stream.set_nonblocking(true)?; 29 | match stream.write_all(&self.buffer) { 30 | Ok(_) => { 31 | Poll::Ready(Ok(())) 32 | }, 33 | Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { 34 | cx.waker().wake_by_ref(); 35 | Poll::Pending 36 | }, 37 | Err(e) => Poll::Ready(Err(e)) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/sleep.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll}, 5 | time::{Duration, Instant}, 6 | }; 7 | 8 | 9 | pub struct Sleep { 10 | when: Instant, 11 | } 12 | impl Sleep { 13 | pub fn new(duration: Duration) -> Self { 14 | Sleep { 15 | when: Instant::now() + duration, 16 | } 17 | } 18 | } 19 | 20 | 21 | impl Future for Sleep { 22 | type Output = (); 23 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 24 | -> Poll { 25 | let now = Instant::now(); 26 | if now >= self.when { 27 | Poll::Ready(()) 28 | } else { 29 | cx.waker().wake_by_ref(); 30 | Poll::Pending 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/async_runtime/src/waker.rs: -------------------------------------------------------------------------------- 1 | use std::task::{RawWaker, RawWakerVTable}; 2 | 3 | 4 | static VTABLE: RawWakerVTable = RawWakerVTable::new( 5 | my_clone, 6 | my_wake, 7 | my_wake_by_ref, 8 | my_drop, 9 | ); 10 | 11 | unsafe fn my_clone(raw_waker: *const ()) -> RawWaker { 12 | RawWaker::new(raw_waker, &VTABLE) 13 | } 14 | 15 | unsafe fn my_wake(raw_waker: *const ()) { 16 | drop(Box::from_raw(raw_waker as *mut u32)); 17 | } 18 | 19 | unsafe fn my_wake_by_ref(_raw_waker: *const ()) { 20 | } 21 | 22 | unsafe fn my_drop(raw_waker: *const ()) { 23 | drop(Box::from_raw(raw_waker as *mut u32)); 24 | } 25 | 26 | pub fn create_raw_waker() -> RawWaker { 27 | let data = Box::into_raw(Box::new(42u32)); 28 | RawWaker::new(data as *const (), &VTABLE) 29 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/client/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/data_layer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_layer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/data_layer/src/data.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Cursor, Read, Write}; 2 | 3 | #[derive(Debug)] 4 | pub struct Data { 5 | pub field1: u32, 6 | pub field2: u16, 7 | pub field3: String, 8 | } 9 | impl Data { 10 | pub fn serialize(&self) -> io::Result> { 11 | let mut bytes = Vec::new(); 12 | bytes.write(&self.field1.to_ne_bytes())?; 13 | bytes.write(&self.field2.to_ne_bytes())?; 14 | let field3_len = self.field3.len() as u32; 15 | bytes.write(&field3_len.to_ne_bytes())?; 16 | bytes.extend_from_slice(self.field3.as_bytes()); 17 | Ok(bytes) 18 | } 19 | pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result { 20 | // Initialize buffers for the fields, using arrays of the appropriate size 21 | let mut field1_bytes = [0u8; 4]; 22 | let mut field2_bytes = [0u8; 2]; 23 | 24 | // Read the first field (4 bytes) from the cursor into the buffer. Do the same for second field. 25 | cursor.read_exact(&mut field1_bytes)?; 26 | cursor.read_exact(&mut field2_bytes)?; 27 | 28 | // Convert the byte arrays into the appropriate data types (u32 and u16) 29 | let field1 = u32::from_ne_bytes(field1_bytes); 30 | let field2 = u16::from_ne_bytes(field2_bytes); 31 | 32 | // Initialize a buffer to read the length of the third field , which is 4 bytes long 33 | let mut len_bytes = [0u8; 4]; 34 | 35 | // Read the length from the cursor into the buffer 36 | cursor.read_exact(&mut len_bytes)?; 37 | 38 | // Convert the length bytes into a usize 39 | let len = u32::from_ne_bytes(len_bytes) as usize; 40 | 41 | // Initialize a buffer with the specified length to hold the third field's data 42 | let mut field3_bytes = vec![0u8; len]; 43 | 44 | // Read the third field's data from the cursor into the buffer 45 | cursor.read_exact(&mut field3_bytes)?; 46 | 47 | // Convert the third field's bytes into a UTF-8 string, or return an error if this cannot be done. 48 | let field3 = String::from_utf8(field3_bytes) 49 | .map_err(|_| io::Error::new( 50 | io::ErrorKind::InvalidData, "Invalid UTF-8" 51 | ))?; 52 | 53 | // Return the structured data 54 | Ok(Data { field1, field2, field3 }) 55 | } 56 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/data_layer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/building-our-std-async-runtime/server/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /async_rust/chapter_10/order.txt: -------------------------------------------------------------------------------- 1 | basic_server 2 | pooled_server 3 | parked_thread_server 4 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "client", 5 | "server", 6 | "data_layer", 7 | "async_runtime" 8 | ] -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/async_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/async_runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: u64, right: u64) -> u64 { 2 | left + right 3 | } 4 | 5 | #[cfg(test)] 6 | mod tests { 7 | use super::*; 8 | 9 | #[test] 10 | fn it_works() { 11 | let result = add(2, 2); 12 | assert_eq!(result, 4); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/client/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/data_layer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "data_layer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/data_layer/src/data.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Cursor, Read, Write}; 2 | 3 | #[derive(Debug)] 4 | pub struct Data { 5 | pub field1: u32, 6 | pub field2: u16, 7 | pub field3: String, 8 | } 9 | impl Data { 10 | pub fn serialize(&self) -> io::Result> { 11 | let mut bytes = Vec::new(); 12 | bytes.write(&self.field1.to_ne_bytes())?; 13 | bytes.write(&self.field2.to_ne_bytes())?; 14 | let field3_len = self.field3.len() as u32; 15 | bytes.write(&field3_len.to_ne_bytes())?; 16 | bytes.extend_from_slice(self.field3.as_bytes()); 17 | Ok(bytes) 18 | } 19 | pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result { 20 | // Initialize buffers for the fields, using arrays of the appropriate size 21 | let mut field1_bytes = [0u8; 4]; 22 | let mut field2_bytes = [0u8; 2]; 23 | 24 | // Read the first field (4 bytes) from the cursor into the buffer. Do the same for second field. 25 | cursor.read_exact(&mut field1_bytes)?; 26 | cursor.read_exact(&mut field2_bytes)?; 27 | 28 | // Convert the byte arrays into the appropriate data types (u32 and u16) 29 | let field1 = u32::from_ne_bytes(field1_bytes); 30 | let field2 = u16::from_ne_bytes(field2_bytes); 31 | 32 | // Initialize a buffer to read the length of the third field , which is 4 bytes long 33 | let mut len_bytes = [0u8; 4]; 34 | 35 | // Read the length from the cursor into the buffer 36 | cursor.read_exact(&mut len_bytes)?; 37 | 38 | // Convert the length bytes into a usize 39 | let len = u32::from_ne_bytes(len_bytes) as usize; 40 | 41 | // Initialize a buffer with the specified length to hold the third field's data 42 | let mut field3_bytes = vec![0u8; len]; 43 | 44 | // Read the third field's data from the cursor into the buffer 45 | cursor.read_exact(&mut field3_bytes)?; 46 | 47 | // Convert the third field's bytes into a UTF-8 string, or return an error if this cannot be done. 48 | let field3 = String::from_utf8(field3_bytes) 49 | .map_err(|_| io::Error::new( 50 | io::ErrorKind::InvalidData, "Invalid UTF-8" 51 | ))?; 52 | 53 | // Return the structured data 54 | Ok(Data { field1, field2, field3 }) 55 | } 56 | } -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/data_layer/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_10/setting-up-the-basics/server/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /async_rust/chapter_11/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 11 - Testing 2 | 3 | Order of projects: 4 | 5 | 1. basic_testing 6 | 2. basic_async_testing 7 | 3. deadlock_testing 8 | 4. data_race_testing 9 | 5. channel_testing 10 | 6. network_testing 11 | 7. putting_it_all_together -------------------------------------------------------------------------------- /async_rust/chapter_11/basic_async_testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/basic_async_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.34.0", features = ["full"] } 10 | 11 | [dev-dependencies] 12 | mockall = "0.11.4" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_11/basic_async_testing/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | 3 | 4 | pub trait AsyncProcess { 5 | 6 | fn get_result(&self, key: X) -> impl Future< 7 | Output = Result> + Send + 'static; 8 | 9 | } 10 | 11 | 12 | async fn do_something(async_handle: T, input: i32) -> Result 13 | where T: AsyncProcess + Send + Sync + 'static 14 | { 15 | println!("something is happening"); 16 | let result: i32 = async_handle.get_result(input).await?; 17 | if result > 10 { 18 | return Err("result is too big".to_string()); 19 | } 20 | if result == 8 { 21 | return Ok(result * 2) 22 | } 23 | Ok(result * 3) 24 | } 25 | 26 | 27 | fn main() { 28 | println!("Hello, world!"); 29 | } 30 | 31 | 32 | #[cfg(test)] 33 | mod get_team_processes_tests { 34 | 35 | use super::*; 36 | use mockall::predicate::*; 37 | use mockall::mock; 38 | use std::boxed::Box; 39 | 40 | mock! { 41 | DatabaseHandler {} 42 | 43 | impl AsyncProcess for DatabaseHandler { 44 | fn get_result(&self, key: i32) -> impl Future< 45 | Output = Result> + Send + 'static; 46 | } 47 | } 48 | 49 | #[test] 50 | fn do_something_fail() { 51 | // Arrange 52 | let mut handle = MockDatabaseHandler::new(); 53 | 54 | handle.expect_get_result() 55 | .with(eq(4)) 56 | .returning( 57 | |_|{ 58 | Box::pin(async move { Ok(11) }) 59 | } 60 | ); 61 | 62 | let runtime = tokio::runtime::Builder::new_current_thread().enable_all() 63 | .build() 64 | .unwrap(); 65 | // Act 66 | let outcome = runtime.block_on(do_something(handle, 4)); 67 | 68 | // Assert 69 | assert_eq!(outcome, Err("result is too big".to_string())); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /async_rust/chapter_11/basic_testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/basic_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | 11 | [dev-dependencies] 12 | mockall = "0.11.4" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_11/basic_testing/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pub trait AsyncProcess { 5 | 6 | fn spawn(&self, input: X) -> Result; 7 | 8 | fn get_result(&self, key: Y) -> Result; 9 | 10 | } 11 | 12 | 13 | fn do_something(async_handle: T, input: i32) -> Result 14 | where T: AsyncProcess 15 | { 16 | let key = async_handle.spawn(input)?; 17 | println!("something is happening"); 18 | let result = async_handle.get_result(key)?; 19 | if result > 10 { 20 | return Err("result is too big".to_string()); 21 | } 22 | if result == 8 { 23 | return Ok(result * 2) 24 | } 25 | Ok(result * 3) 26 | } 27 | 28 | 29 | 30 | fn main() { 31 | println!("Hello, world!"); 32 | } 33 | 34 | 35 | #[cfg(test)] 36 | mod get_team_processes_tests { 37 | 38 | use super::*; 39 | use mockall::predicate::*; 40 | use mockall::mock; 41 | 42 | 43 | mock! { 44 | DatabaseHandler {} 45 | impl AsyncProcess for DatabaseHandler { 46 | fn spawn(&self, input: i32) -> Result; 47 | 48 | fn get_result(&self, key: String) -> Result; 49 | } 50 | } 51 | 52 | #[test] 53 | fn do_something_fail() { 54 | 55 | // Arrange 56 | let mut handle = MockDatabaseHandler::new(); 57 | 58 | handle.expect_spawn() 59 | .with(eq(4)) 60 | .returning(|_|{Ok("test_key".to_string())}); 61 | 62 | handle.expect_get_result() 63 | .with(eq("test_key".to_string())) 64 | .returning(|_|{Ok(11)}); 65 | 66 | // Act 67 | let outcome = do_something(handle, 4); 68 | 69 | // Assert 70 | assert_eq!(outcome, Err("result is too big".to_string())); 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /async_rust/chapter_11/channel_testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/channel_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-trait = "0.1.74" 10 | tokio = { version = "1.34.0", features = ["full"] } 11 | 12 | [dev-dependencies] 13 | mockall = "0.11.4" 14 | -------------------------------------------------------------------------------- /async_rust/chapter_11/channel_testing/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | println!("Hello, world!"); 4 | } 5 | 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use tokio::sync::mpsc; 10 | use tokio::time::{Duration, timeout}; 11 | use tokio::runtime::Builder; 12 | 13 | #[test] 14 | fn test_channel_capacity() { 15 | let runtime = Builder::new_current_thread().enable_all().build().unwrap(); 16 | let (sender, mut receiver) = mpsc::channel::(5); 17 | 18 | let receiver = runtime.spawn(async move { 19 | let mut i = 0; 20 | while let Some(msg) = receiver.recv().await { 21 | assert_eq!(msg, i); 22 | i += 1; 23 | println!("Got message: {}", msg); 24 | } 25 | }); 26 | let sender = runtime.spawn(async move { 27 | for i in 0..10 { 28 | sender.send(i).await.expect("Failed to send message"); 29 | } 30 | }); 31 | 32 | let result = runtime.block_on(async { 33 | timeout(Duration::from_secs(5), async { 34 | // reciever.await.unwrap(); 35 | sender.await.unwrap(); 36 | }).await 37 | }); 38 | assert!(result.is_ok(), "A potential filled channel is not handled correctly"); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /async_rust/chapter_11/data_race_testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/data_race_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-trait = "0.1.74" 10 | tokio = { version = "1.34.0", features = ["full"] } 11 | 12 | [dev-dependencies] 13 | mockall = "0.11.4" 14 | -------------------------------------------------------------------------------- /async_rust/chapter_11/data_race_testing/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | println!("Hello, world!"); 4 | } 5 | 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | 10 | use std::sync::atomic::{AtomicUsize, Ordering}; 11 | use tokio::runtime::Builder; 12 | use tokio::time::{sleep, Duration}; 13 | 14 | static COUNTER: AtomicUsize = AtomicUsize::new(0); 15 | 16 | async fn unsafe_add() { 17 | let value = COUNTER.load(Ordering::SeqCst); 18 | sleep(Duration::from_secs(1)).await; 19 | COUNTER.store(value + 1, Ordering::SeqCst); 20 | } 21 | 22 | #[test] 23 | fn test_data_race() { 24 | let runtime = tokio::runtime::Runtime::new().unwrap(); 25 | let mut handles = vec![]; 26 | let total = 100000; 27 | 28 | for _ in 0..total { 29 | let handle = runtime.spawn(unsafe_add()); 30 | handles.push(handle); 31 | } 32 | 33 | for handle in handles { 34 | runtime.block_on(handle).unwrap(); 35 | } 36 | 37 | assert_eq!(COUNTER.load(Ordering::SeqCst), total, "Data race occurred!"); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /async_rust/chapter_11/deadlock_testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/deadlock_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-trait = "0.1.74" 10 | tokio = { version = "1.34.0", features = ["full"] } 11 | 12 | [dev-dependencies] 13 | mockall = "0.11.4" 14 | -------------------------------------------------------------------------------- /async_rust/chapter_11/deadlock_testing/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | println!("Hello, world!"); 4 | } 5 | 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | use tokio::sync::Mutex; 10 | use std::sync::Arc; 11 | use tokio::time::{sleep, Duration, timeout}; 12 | 13 | #[tokio::test] 14 | async fn test_deadlock_detection() { 15 | let resource1 = Arc::new(Mutex::new(0)); 16 | let resource2 = Arc::new(Mutex::new(0)); 17 | 18 | let resource1_clone = Arc::clone(&resource1); 19 | let resource2_clone = Arc::clone(&resource2); 20 | 21 | let handle1 = tokio::spawn(async move { 22 | let _lock1 = resource1.lock().await; 23 | sleep(Duration::from_millis(100)).await; 24 | let _lock2 = resource2.lock().await; 25 | }); 26 | 27 | let handle2 = tokio::spawn(async move { 28 | let _lock2 = resource2_clone.lock().await; 29 | sleep(Duration::from_millis(100)).await; 30 | let _lock1 = resource1_clone.lock().await; 31 | }); 32 | 33 | let result = timeout(Duration::from_secs(5), async { 34 | let _ = handle1.await; 35 | let _ = handle2.await; 36 | }).await; 37 | assert!(result.is_ok(), "A potential deadlock detected!"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /async_rust/chapter_11/network_testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/network_testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.34.0", features = ["full"] } 10 | reqwest = { version = "0.11.22", features = ["json"] } 11 | 12 | [dev-dependencies] 13 | mockito = "1.2.0" 14 | -------------------------------------------------------------------------------- /async_rust/chapter_11/network_testing/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | println!("Hello, world!"); 4 | } 5 | 6 | 7 | #[cfg(test)] 8 | mod tests { 9 | 10 | use tokio::runtime::Builder; 11 | use mockito::Matcher; 12 | use reqwest; 13 | 14 | #[test] 15 | fn test_networking() { 16 | 17 | let mut server = mockito::Server::new(); 18 | let url = server.url(); 19 | 20 | // Create a mock 21 | let mock = server.mock("GET", "/my-endpoint") 22 | .match_query(Matcher::AllOf(vec![ 23 | Matcher::UrlEncoded("param1".into(), "value1".into()), 24 | Matcher::UrlEncoded("param2".into(), "value2".into()), 25 | ])) 26 | .with_status(201) 27 | .with_body("world") 28 | .expect(5) 29 | .create(); 30 | 31 | 32 | let runtime = Builder::new_current_thread() 33 | .enable_io() 34 | .enable_time() 35 | .build() 36 | .unwrap(); 37 | let mut handles = vec![]; 38 | 39 | for _ in 0..5 { 40 | let url_clone = url.clone(); 41 | handles.push(runtime.spawn(async move { 42 | let client = reqwest::Client::new(); 43 | client.get(&format!("{}/my-endpoint?param1=value1¶m2=value2", url_clone)) 44 | .send() 45 | .await 46 | .unwrap() 47 | })); 48 | } 49 | 50 | for handle in handles { 51 | runtime.block_on(handle).unwrap(); 52 | } 53 | mock.assert(); 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /async_rust/chapter_11/putting_it_all_together/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_11/putting_it_all_together/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.34.0", features = ["full"] } 10 | 11 | [dev-dependencies] 12 | tokio-test = "0.4.3" 13 | -------------------------------------------------------------------------------- /async_rust/chapter_11/putting_it_all_together/input.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_11/putting_it_all_together/input.txt -------------------------------------------------------------------------------- /async_rust/chapter_11/putting_it_all_together/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxwellflitton/async-rust-oreilly/fe523fa6ac288ed69a09d864eaab022a9781db4a/async_rust/chapter_11/putting_it_all_together/output.txt -------------------------------------------------------------------------------- /async_rust/chapter_11/putting_it_all_together/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #[tokio::main] 5 | async fn main() { 6 | println!("Hello, world!"); 7 | } 8 | 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | 13 | use tokio::sync::Mutex; 14 | use tokio::time::{sleep, Duration}; 15 | use tokio_test::{task::spawn, assert_pending}; 16 | use std::sync::Arc; 17 | use std::task::Poll; 18 | 19 | async fn async_mutex_locker(mutex: Arc>) -> () { 20 | let mut lock = mutex.lock().await; 21 | *lock += 1; 22 | sleep(Duration::from_millis(1)).await; 23 | } 24 | 25 | #[tokio::test] 26 | async fn test_monitor_file_metadata() { 27 | let mutex = Arc::new(Mutex::new(0)); 28 | let mutex_clone1 = mutex.clone(); 29 | let mutex_clone2 = mutex.clone(); 30 | 31 | let mut future1 = spawn(async_mutex_locker(mutex_clone1)); 32 | let mut future2 = spawn(async_mutex_locker(mutex_clone2)); 33 | 34 | assert_pending!(future1.poll()); 35 | assert_pending!(future2.poll()); 36 | 37 | for _ in 0..10 { 38 | assert_pending!(future2.poll()); 39 | sleep(Duration::from_millis(1)).await; 40 | } 41 | 42 | assert_eq!(future1.poll(), Poll::Ready(())); 43 | sleep(Duration::from_millis(3)).await; 44 | assert_pending!(future2.poll()); 45 | 46 | drop(future1); 47 | sleep(Duration::from_millis(1)).await; 48 | assert_eq!(future2.poll(), Poll::Ready(())); 49 | 50 | let lock = mutex.lock().await; 51 | assert_eq!(*lock, 2); 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /async_rust/chapter_2/context-in-futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "context-in-futures" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.36.0", features = ["full"] } -------------------------------------------------------------------------------- /async_rust/chapter_2/context-in-futures/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use tokio::time::timeout; 3 | 4 | async fn slow_task() -> &'static str { 5 | tokio::time::sleep(Duration::from_secs(10)).await; 6 | "Slow Task Completed" 7 | } 8 | 9 | 10 | #[tokio::main] 11 | async fn main() { 12 | let duration = Duration::from_secs(3); 13 | let result = timeout(duration, slow_task()).await; 14 | 15 | match result { 16 | Ok(value) => println!("Task completed successfully: {}", value), 17 | Err(_) => println!("Task timed out"), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /async_rust/chapter_2/futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "futures" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.39.3", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_2/futures/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | use tokio::task::JoinHandle; 6 | 7 | 8 | struct CounterFuture { 9 | count: u32, 10 | } 11 | 12 | impl Future for CounterFuture { 13 | type Output = u32; 14 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 15 | self.count += 1; 16 | println!("polling with result: {}", self.count); 17 | std::thread::sleep(Duration::from_secs(1)); 18 | if self.count < 3 { 19 | cx.waker().wake_by_ref(); 20 | Poll::Pending 21 | } else { 22 | Poll::Ready(self.count) 23 | } 24 | } 25 | } 26 | 27 | 28 | #[tokio::main] 29 | async fn main() { 30 | let counter_one = CounterFuture { count: 0 }; 31 | let counter_two = CounterFuture { count: 0 }; 32 | let handle_one: JoinHandle = tokio::task::spawn(async move { 33 | counter_one.await 34 | }); 35 | let handle_two: JoinHandle = tokio::task::spawn(async move { 36 | counter_two.await 37 | }); 38 | tokio::join!(handle_one, handle_two); 39 | } 40 | -------------------------------------------------------------------------------- /async_rust/chapter_2/pinning_futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pinning_futures" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.39.0", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_2/pinning_futures/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | use std::future::Future; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | 6 | 7 | struct SelfReferential { 8 | data: String, 9 | self_pointer: *const String, 10 | } 11 | 12 | 13 | impl SelfReferential { 14 | fn new(data: String) -> SelfReferential { 15 | let mut sr = SelfReferential { 16 | data, 17 | self_pointer: ptr::null(), 18 | }; 19 | sr.self_pointer = &sr.data as *const String; 20 | sr 21 | } 22 | 23 | fn print(&self) { 24 | unsafe { 25 | println!("{}", *self.self_pointer); 26 | } 27 | } 28 | } 29 | 30 | 31 | struct SimpleFuture { 32 | count: u32, 33 | } 34 | 35 | impl Future for SimpleFuture { 36 | type Output = u32; 37 | 38 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 39 | if self.count < 3 { 40 | self.count += 1; 41 | cx.waker().wake_by_ref(); 42 | Poll::Pending 43 | } else { 44 | Poll::Ready(self.count) 45 | } 46 | } 47 | } 48 | 49 | 50 | fn main() { 51 | let first = SelfReferential::new("first".to_string()); 52 | let moved_first = first; // Move the struct 53 | // The original `first` is no longer valid; this might invalidate pointers 54 | //if pinning isn't used 55 | moved_first.print(); 56 | } -------------------------------------------------------------------------------- /async_rust/chapter_2/putting-it-all-together/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "putting-it-all-together" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.39.0", features = ["full"] } 8 | futures-util = "0.3" 9 | -------------------------------------------------------------------------------- /async_rust/chapter_2/putting-it-all-together/login.txt: -------------------------------------------------------------------------------- 1 | one 2 | three 3 | five 4 | two 5 | four 6 | six 7 | -------------------------------------------------------------------------------- /async_rust/chapter_2/putting-it-all-together/logout.txt: -------------------------------------------------------------------------------- 1 | one 2 | three 3 | five 4 | four 5 | two 6 | six 7 | -------------------------------------------------------------------------------- /async_rust/chapter_2/putting-it-all-together/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{File, OpenOptions}; 2 | use std::io::prelude::*; 3 | use std::sync::{Arc, Mutex}; 4 | use std::future::Future; 5 | use std::pin::Pin; 6 | use std::task::{Context, Poll}; 7 | use tokio::task::JoinHandle; 8 | use futures_util::future::join_all; 9 | 10 | type AsyncFileHandle = Arc>; 11 | type FileJoinHandle = JoinHandle>; 12 | 13 | 14 | fn get_handle(file_path: &dyn ToString) -> AsyncFileHandle { 15 | match OpenOptions::new().append(true).open(file_path.to_string()) { 16 | Ok(opened_file) => { 17 | Arc::new(Mutex::new(opened_file)) 18 | }, 19 | Err(_) => { 20 | Arc::new(Mutex::new(File::create(file_path.to_string()).unwrap())) 21 | } 22 | } 23 | } 24 | 25 | 26 | struct AsyncWriteFuture { 27 | pub handle: AsyncFileHandle, 28 | pub entry: String 29 | } 30 | 31 | 32 | impl Future for AsyncWriteFuture { 33 | 34 | type Output = Result; 35 | 36 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 37 | let mut guard = match self.handle.try_lock() { 38 | Ok(guard) => guard, 39 | Err(error) => { 40 | println!("error for {} : {}", self.entry, error); 41 | cx.waker().wake_by_ref(); 42 | return Poll::Pending 43 | } 44 | }; 45 | let lined_entry = format!("{}\n", self.entry); 46 | match guard.write_all(lined_entry.as_bytes()) { 47 | Ok(_) => println!("written for: {}", self.entry), 48 | Err(e) => println!("{}", e) 49 | }; 50 | Poll::Ready(Ok(true)) 51 | } 52 | } 53 | 54 | 55 | fn write_log(file_handle: AsyncFileHandle, line: String) -> FileJoinHandle { 56 | let future = AsyncWriteFuture{ 57 | handle: file_handle, 58 | entry: line 59 | }; 60 | tokio::task::spawn(async move { 61 | future.await 62 | }) 63 | } 64 | 65 | 66 | #[tokio::main] 67 | async fn main() { 68 | let login_handle = get_handle(&"login.txt"); 69 | let logout_handle = get_handle(&"logout.txt"); 70 | 71 | 72 | let names = ["one", "two", "three", "four", "five", "six"]; 73 | let mut handles = Vec::new(); 74 | 75 | 76 | for name in names { 77 | let file_handle = login_handle.clone(); 78 | let file_handle_two = logout_handle.clone(); 79 | let handle = write_log(file_handle, name.to_string()); 80 | let handle_two = write_log(file_handle_two, name.to_string()); 81 | handles.push(handle); 82 | handles.push(handle_two); 83 | } 84 | let _ = join_all(handles).await; 85 | } 86 | -------------------------------------------------------------------------------- /async_rust/chapter_2/sharing-data-between-futures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sharing-data-between-futures" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.39.0", features = ["full"] } -------------------------------------------------------------------------------- /async_rust/chapter_2/sharing-data-between-futures/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use tokio::task::JoinHandle; 3 | use core::task::Poll; 4 | use tokio::time::Duration; 5 | use std::task::Context; 6 | use std::pin::Pin; 7 | use std::future::Future; 8 | 9 | 10 | #[derive(Debug)] 11 | enum CounterType { 12 | Increment, 13 | Decrement 14 | } 15 | 16 | struct SharedData { 17 | counter: i32, 18 | } 19 | 20 | impl SharedData { 21 | fn increment(&mut self) { 22 | self.counter += 1; 23 | } 24 | fn decrement(&mut self) { 25 | self.counter -= 1; 26 | } 27 | } 28 | 29 | 30 | struct CounterFuture { 31 | counter_type: CounterType, 32 | data_reference: Arc>, 33 | count: u32 34 | } 35 | 36 | impl Future for CounterFuture { 37 | type Output = u32; 38 | 39 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 40 | std::thread::sleep(Duration::from_secs(1)); 41 | let mut guard = match self.data_reference.try_lock() { 42 | Ok(guard) => guard, 43 | Err(error) => { 44 | println!( 45 | "error for {:?}: {}", 46 | self.counter_type, error 47 | ); 48 | cx.waker().wake_by_ref(); 49 | return Poll::Pending 50 | } 51 | }; 52 | let value = &mut *guard; 53 | match self.counter_type { 54 | CounterType::Increment => { 55 | value.increment(); 56 | println!("after increment: {}", value.counter); 57 | }, 58 | CounterType::Decrement => { 59 | value.decrement(); 60 | println!("after decrement: {}", value.counter); 61 | } 62 | } 63 | std::mem::drop(guard); 64 | self.count += 1; 65 | if self.count < 3 { 66 | cx.waker().wake_by_ref(); 67 | return Poll::Pending 68 | } else { 69 | return Poll::Ready(self.count) 70 | } 71 | } 72 | } 73 | 74 | 75 | async fn count(count: u32, data: Arc>, 76 | counter_type: CounterType) -> u32 { 77 | for _ in 0..count { 78 | let mut data = data.lock().await; 79 | match counter_type { 80 | CounterType::Increment => { 81 | data.increment(); 82 | println!("after increment: {}", data.counter); 83 | }, 84 | CounterType::Decrement => { 85 | data.decrement(); 86 | println!("after decrement: {}", data.counter); 87 | } 88 | } 89 | std::mem::drop(data); 90 | std::thread::sleep(Duration::from_secs(1)); 91 | } 92 | return count 93 | } 94 | 95 | #[tokio::main] 96 | async fn main() { 97 | let shared_data = Arc::new(Mutex::new(SharedData{counter: 0})); 98 | let counter_one = CounterFuture { 99 | counter_type: CounterType::Increment, 100 | data_reference: shared_data.clone(), 101 | count: 0 102 | }; 103 | let counter_two = CounterFuture { 104 | counter_type: CounterType::Decrement, 105 | data_reference: shared_data.clone(), 106 | count: 0 107 | }; 108 | let handle_one: JoinHandle = tokio::task::spawn(async move { 109 | counter_one.await 110 | }); 111 | let handle_two: JoinHandle = tokio::task::spawn(async move { 112 | counter_two.await 113 | }); 114 | tokio::join!(handle_one, handle_two); 115 | } -------------------------------------------------------------------------------- /async_rust/chapter_2/understanding_tasks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "understanding_tasks" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.39.3", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_2/understanding_tasks/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use tokio::time::sleep; 3 | use std::thread; 4 | use std::time::Instant; 5 | 6 | 7 | async fn prep_coffee_mug() { 8 | sleep(Duration::from_millis(100)).await; 9 | println!("Pouring milk..."); 10 | thread::sleep(Duration::from_secs(3)); 11 | println!("Milk poured."); 12 | println!("Putting instant coffee..."); 13 | thread::sleep(Duration::from_secs(3)); 14 | println!("Instant coffee put."); 15 | } 16 | 17 | 18 | async fn make_coffee() { 19 | println!("boiling kettle..."); 20 | sleep(Duration::from_secs(10)).await; 21 | println!("kettle boiled."); 22 | println!("pouring boiled water..."); 23 | thread::sleep(Duration::from_secs(3)); 24 | println!("boiled water poured."); 25 | } 26 | 27 | 28 | async fn make_toast() { 29 | println!("putting bread in toaster..."); 30 | sleep(Duration::from_secs(10)).await; 31 | println!("bread toasted."); 32 | println!("buttering toasted bread..."); 33 | thread::sleep(Duration::from_secs(5)); 34 | println!("toasted bread buttered."); 35 | } 36 | 37 | 38 | #[tokio::main] 39 | async fn main() { 40 | let start_time = Instant::now(); 41 | let coffee_mug_step = prep_coffee_mug(); 42 | let coffee_step = make_coffee(); 43 | let toast_step = make_toast(); 44 | tokio::join!(coffee_mug_step, coffee_step, toast_step); 45 | let elapsed_time = start_time.elapsed(); 46 | println!("It took: {} seconds", elapsed_time.as_secs()); 47 | 48 | let person_one = tokio::task::spawn(async { 49 | prep_coffee_mug().await; 50 | make_coffee().await; 51 | make_toast().await; 52 | }); 53 | person_one.await.unwrap(); 54 | 55 | let person_two = tokio::task::spawn(async { 56 | let coffee_mug_step = prep_coffee_mug(); 57 | let coffee_step = make_coffee(); 58 | let toast_step = make_toast(); 59 | tokio::join!(coffee_mug_step, coffee_step, toast_step); 60 | }); 61 | person_two.await.unwrap(); 62 | } 63 | -------------------------------------------------------------------------------- /async_rust/chapter_2/waking-futures-remotely/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "waking-futures-remotely" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.39.3", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_2/waking-futures-remotely/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::pin::Pin; 2 | use std::task::{Context, Poll, Waker}; 3 | use std::sync::{Arc, Mutex}; 4 | use std::future::Future; 5 | use tokio::sync::mpsc; 6 | use tokio::task; 7 | 8 | struct MyFuture { 9 | state: Arc>, 10 | } 11 | 12 | struct MyFutureState { 13 | data: Option>, 14 | waker: Option, 15 | } 16 | 17 | impl MyFuture { 18 | fn new() -> (Self, Arc>) { 19 | let state = Arc::new(Mutex::new(MyFutureState { 20 | data: None, 21 | waker: None, 22 | })); 23 | ( 24 | MyFuture { 25 | state: state.clone(), 26 | }, 27 | state, 28 | ) 29 | } 30 | } 31 | 32 | impl Future for MyFuture { 33 | type Output = String; 34 | 35 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) 36 | -> Poll { 37 | println!("Polling the future"); 38 | let mut state = self.state.lock().unwrap(); 39 | 40 | if state.data.is_some() { 41 | let data = state.data.take().unwrap(); 42 | Poll::Ready(String::from_utf8(data).unwrap()) 43 | } else { 44 | state.waker = Some(cx.waker().clone()); 45 | Poll::Pending 46 | } 47 | } 48 | } 49 | 50 | #[tokio::main] 51 | async fn main() { 52 | let (my_future, state) = MyFuture::new(); 53 | let (tx, mut rx) = mpsc::channel::<()>(1); 54 | let task_handle = task::spawn(async { 55 | my_future.await 56 | }); 57 | tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; 58 | println!("spawning trigger task"); 59 | 60 | let trigger_task = task::spawn(async move { 61 | rx.recv().await; 62 | let mut state = state.lock().unwrap(); 63 | state.data = Some(b"Hello from the outside".to_vec()); 64 | loop { 65 | if let Some(waker) = state.waker.take() { 66 | waker.wake(); 67 | break; 68 | } 69 | } 70 | }); 71 | tx.send(()).await.unwrap(); 72 | 73 | let outome = task_handle.await.unwrap(); 74 | println!("Task completed with outcome: {}", outome); 75 | trigger_task.await.unwrap(); 76 | } 77 | -------------------------------------------------------------------------------- /async_rust/chapter_3/building-our-own-async-queue/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_3/building-our-own-async-queue/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, panic::catch_unwind, thread}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | use std::sync::LazyLock; 6 | 7 | use async_task::{Runnable, Task}; 8 | use futures_lite::future; 9 | 10 | 11 | fn spawn_task(future: F) -> Task 12 | where 13 | F: Future + Send + 'static, 14 | T: Send + 'static, 15 | { 16 | static QUEUE: LazyLock> = LazyLock::new(|| { 17 | let (tx, rx) = flume::unbounded::(); 18 | thread::spawn(move || { 19 | while let Ok(runnable) = rx.recv() { 20 | println!("runnable accepted"); 21 | let _ = catch_unwind(|| runnable.run()); 22 | } 23 | }); 24 | tx 25 | }); 26 | let schedule = |runnable| QUEUE.send(runnable).unwrap(); 27 | let (runnable, task) = async_task::spawn(future, schedule); 28 | runnable.schedule(); 29 | println!("Here is the queue count: {:?}", QUEUE.len()); 30 | return task 31 | } 32 | 33 | 34 | struct CounterFuture { 35 | count: u32, 36 | } 37 | impl Future for CounterFuture { 38 | type Output = u32; 39 | 40 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 41 | -> Poll { 42 | self.count += 1; 43 | println!("polling with result: {}", self.count); 44 | std::thread::sleep(Duration::from_secs(1)); 45 | if self.count < 3 { 46 | cx.waker().wake_by_ref(); 47 | Poll::Pending 48 | } else { 49 | Poll::Ready(self.count) 50 | } 51 | } 52 | } 53 | 54 | async fn async_fn() { 55 | std::thread::sleep(Duration::from_secs(1)); 56 | println!("async fn"); 57 | } 58 | 59 | fn main() { 60 | let one = CounterFuture { count: 0 }; 61 | let two = CounterFuture { count: 0 }; 62 | let t_one = spawn_task(one); 63 | let t_two = spawn_task(two); 64 | let t_three = spawn_task(async { 65 | async_fn().await; 66 | async_fn().await; 67 | async_fn().await; 68 | async_fn().await; 69 | }); 70 | std::thread::sleep(Duration::from_secs(5)); 71 | println!("before the block"); 72 | future::block_on(t_one); 73 | future::block_on(t_two); 74 | future::block_on(t_three); 75 | } 76 | -------------------------------------------------------------------------------- /async_rust/chapter_3/configuring-our-runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_3/creating-out-own-join-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_3/increasing-workers-and-queues/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_3/increasing-workers-and-queues/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, panic::catch_unwind, thread}; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | use std::time::Duration; 5 | use std::sync::LazyLock; 6 | 7 | use async_task::{Runnable, Task}; 8 | use futures_lite::future; 9 | 10 | 11 | #[derive(Debug, Clone, Copy)] 12 | enum FutureType { 13 | High, 14 | Low 15 | } 16 | 17 | 18 | trait FutureOrderLabel: Future { 19 | fn get_order(&self) -> FutureType; 20 | } 21 | 22 | 23 | fn spawn_task(future: F) -> Task 24 | where 25 | F: Future + Send + 'static + FutureOrderLabel, 26 | T: Send + 'static, 27 | { 28 | static HIGH_QUEUE: LazyLock> = LazyLock::new(|| { 29 | let (tx, rx) = flume::unbounded::(); 30 | for _ in 0..2 { 31 | let receiver = rx.clone(); 32 | thread::spawn(move || { 33 | while let Ok(runnable) = receiver.recv() { 34 | let _ = catch_unwind(|| runnable.run()); 35 | } 36 | }); 37 | } 38 | tx 39 | }); 40 | static LOW_QUEUE: LazyLock> = LazyLock::new(|| { 41 | let (tx, rx) = flume::unbounded::(); 42 | for _ in 0..1 { 43 | let receiver = rx.clone(); 44 | thread::spawn(move || { 45 | while let Ok(runnable) = receiver.recv() { 46 | let _ = catch_unwind(|| runnable.run()); 47 | } 48 | }); 49 | } 50 | tx 51 | }); 52 | let schedule_high = |runnable| HIGH_QUEUE.send(runnable).unwrap(); 53 | let schedule_low = |runnable| LOW_QUEUE.send(runnable).unwrap(); 54 | 55 | let schedule = match future.get_order() { 56 | FutureType::High => schedule_high, 57 | FutureType::Low => schedule_low 58 | }; 59 | let (runnable, task) = async_task::spawn(future, schedule); 60 | runnable.schedule(); 61 | return task 62 | } 63 | 64 | 65 | struct CounterFuture { 66 | count: u32, 67 | order: FutureType 68 | } 69 | 70 | impl FutureOrderLabel for CounterFuture { 71 | fn get_order(&self) -> FutureType { 72 | self.order 73 | } 74 | } 75 | impl Future for CounterFuture { 76 | type Output = u32; 77 | 78 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 79 | -> Poll { 80 | self.count += 1; 81 | println!("polling with result: {}", self.count); 82 | std::thread::sleep(Duration::from_secs(1)); 83 | if self.count < 3 { 84 | cx.waker().wake_by_ref(); 85 | Poll::Pending 86 | } else { 87 | Poll::Ready(self.count) 88 | } 89 | } 90 | } 91 | 92 | async fn async_fn() { 93 | std::thread::sleep(Duration::from_secs(1)); 94 | println!("async fn"); 95 | } 96 | 97 | fn main() { 98 | let one = CounterFuture { count: 0 , order: FutureType::High}; 99 | let t_one = spawn_task(one); 100 | future::block_on(t_one); 101 | // let one = CounterFuture { count: 0 }; 102 | // let two = CounterFuture { count: 0 }; 103 | // let t_one = spawn_task(one); 104 | // let t_two = spawn_task(two); 105 | // let t_three = spawn_task(async { 106 | // async_fn().await; 107 | // async_fn().await; 108 | // async_fn().await; 109 | // async_fn().await; 110 | // }); 111 | // std::thread::sleep(Duration::from_secs(5)); 112 | // println!("before the block"); 113 | // future::block_on(t_one); 114 | // future::block_on(t_two); 115 | // future::block_on(t_three); 116 | } 117 | -------------------------------------------------------------------------------- /async_rust/chapter_3/refactoring-our-spawn-task-function/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_3/running-background-processes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_3/task-stealing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-our-own-async-queue" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-task = "4.4.0" 8 | futures-lite = "1.12.0" 9 | flume = "0.10.14" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_4/building_an_executor_for_hyper/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_4/building_an_executor_for_hyper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building_out_own_async_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | flume = "0.11.0" 12 | 13 | # dependencies below are for integrating our runtime with hyper 14 | hyper = { version = "0.14.26", features = ["http1", "http2", "client", "runtime"] } 15 | smol = "1.3.0" 16 | anyhow = "1.0.70" 17 | async-native-tls = "0.5.0" 18 | http = "0.2.9" 19 | tokio = "1.14.0" 20 | -------------------------------------------------------------------------------- /async_rust/chapter_4/listening_to_sockets_with_mio/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_4/listening_to_sockets_with_mio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building_out_own_async_runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | async-task = "4.4.0" 10 | futures-lite = "1.12.0" 11 | flume = "0.11.0" 12 | 13 | # dependencies below are for integrating our runtime with hyper 14 | hyper = { version = "0.14.26", features = ["http1", "http2", "client", "runtime"] } 15 | smol = "1.3.0" 16 | anyhow = "1.0.70" 17 | async-native-tls = "0.5.0" 18 | http = "0.2.9" 19 | tokio = "1.14.0" 20 | mio = {version = "1.0.2", features = ["net", "os-poll"]} 21 | -------------------------------------------------------------------------------- /async_rust/chapter_4/listening_to_sockets_with_mio/src/main.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | #[macro_use] 3 | mod runtime; 4 | 5 | use crate::runtime::{FutureType, spawn_task_function}; 6 | #[allow(dead_code)] 7 | mod hyper_client; 8 | 9 | use runtime::Runtime; 10 | 11 | use futures_lite::future; 12 | 13 | use mio::net::{TcpListener, TcpStream}; 14 | use mio::{Events, Interest, Poll as MioPoll, Token}; 15 | use std::io::{Read, Write}; 16 | use std::time::Duration; 17 | use std::error::Error; 18 | 19 | use std::future::Future; 20 | use std::pin::Pin; 21 | use std::task::{Context, Poll}; 22 | 23 | use std::collections::VecDeque; 24 | use std::sync::{Condvar, Mutex}; 25 | 26 | const SERVER: Token = Token(0); 27 | const CLIENT: Token = Token(1); 28 | 29 | 30 | struct ServerFuture { 31 | server: TcpListener, 32 | poll: MioPoll, 33 | } 34 | 35 | 36 | impl Future for ServerFuture { 37 | 38 | type Output = String; 39 | 40 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 41 | -> Poll { 42 | 43 | let mut events = Events::with_capacity(1); 44 | 45 | let _ = self.poll.poll( 46 | &mut events, 47 | Some(Duration::from_millis(200)) 48 | ).unwrap(); 49 | 50 | for event in events.iter() { 51 | if event.token() == SERVER && event.is_readable() { 52 | let (mut stream, _) = self.server.accept().unwrap(); 53 | 54 | let mut buffer = [0u8; 1024]; 55 | 56 | let mut received_data = Vec::new(); 57 | 58 | loop { 59 | match stream.read(&mut buffer) { 60 | Ok(n) if n > 0 => { 61 | received_data.extend_from_slice(&buffer[..n]); 62 | } 63 | Ok(_) => { 64 | break; 65 | } 66 | Err(e) => { 67 | eprintln!("Error reading from stream: {}", e); 68 | break; 69 | } 70 | } 71 | } 72 | if !received_data.is_empty() { 73 | let received_str = String::from_utf8_lossy(&received_data); 74 | return Poll::Ready(received_str.to_string()) 75 | } 76 | cx.waker().wake_by_ref(); 77 | return Poll::Pending 78 | } 79 | } 80 | cx.waker().wake_by_ref(); 81 | return Poll::Pending 82 | } 83 | } 84 | 85 | 86 | pub struct Channel { 87 | queue: Mutex>, 88 | item_ready: Condvar, 89 | } 90 | 91 | impl Channel { 92 | pub fn new() -> Self { 93 | Self { 94 | queue: Mutex::new(VecDeque::new()), 95 | item_ready: Condvar::new(), 96 | } 97 | } 98 | 99 | pub fn send(&self, message: T) { 100 | self.queue.lock().unwrap().push_back(message); 101 | self.item_ready.notify_one(); 102 | } 103 | 104 | pub fn receive(&self) -> T { 105 | let mut b = self.queue.lock().unwrap(); 106 | loop { 107 | if let Some(message) = b.pop_front() { 108 | return message; 109 | } 110 | b = self.item_ready.wait(b).unwrap(); 111 | } 112 | } 113 | } 114 | 115 | 116 | fn main() -> Result<(), Box> { 117 | Runtime::new().with_low_num(2).with_high_num(4).run(); 118 | 119 | let addr = "127.0.0.1:13265".parse()?; 120 | let mut server = TcpListener::bind(addr)?; 121 | let mut stream = TcpStream::connect(server.local_addr()?)?; 122 | 123 | let poll: MioPoll = MioPoll::new()?; 124 | poll.registry() 125 | .register(&mut server, SERVER, Interest::READABLE)?; 126 | 127 | let server_worker = ServerFuture{ 128 | server, 129 | poll, 130 | }; 131 | let test = spawn_task!(server_worker); 132 | 133 | let mut client_poll: MioPoll = MioPoll::new()?; 134 | client_poll.registry() 135 | .register(&mut stream, CLIENT, Interest::WRITABLE)?; 136 | 137 | let mut events = Events::with_capacity(128); 138 | 139 | let _ = client_poll.poll( 140 | &mut events, 141 | None 142 | ).unwrap(); 143 | 144 | for event in events.iter() { 145 | if event.token() == CLIENT && event.is_writable() { 146 | let message = "that's so dingo!\n"; 147 | let _ = stream.write_all(message.as_bytes()); 148 | } 149 | } 150 | 151 | let outcome = future::block_on(test); 152 | println!("outcome: {}", outcome); 153 | 154 | Ok(()) 155 | 156 | } 157 | -------------------------------------------------------------------------------- /async_rust/chapter_5/calling_a_coroutine_from_a_coroutine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "calling_a_coroutine_from_a_coroutine" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_5/calling_a_coroutine_from_a_coroutine/numbers.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 -------------------------------------------------------------------------------- /async_rust/chapter_5/calling_a_coroutine_from_a_coroutine/output.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 1 7 | 2 8 | 3 9 | 4 10 | 5 11 | 1 12 | 2 13 | 3 14 | 4 15 | 5 16 | 1 17 | 2 18 | 3 19 | 4 20 | 5 21 | -------------------------------------------------------------------------------- /async_rust/chapter_5/calling_a_coroutine_from_a_coroutine/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines)] 2 | #![feature(coroutine_trait)] 3 | use std::fs::File; 4 | use std::io::{self, BufRead, BufReader}; 5 | use std::ops::{Coroutine, CoroutineState}; 6 | use std::pin::Pin; 7 | use std::fs::OpenOptions; 8 | use std::io::Write; 9 | 10 | trait SymmetricCoroutine { 11 | type Input; 12 | type Output; 13 | 14 | fn resume_with_input( 15 | self: Pin<&mut Self>, input: Self::Input 16 | ) -> Self::Output; 17 | } 18 | 19 | struct WriteCoroutine { 20 | pub file_handle: File, 21 | } 22 | 23 | impl WriteCoroutine { 24 | fn new(path: &str) -> io::Result { 25 | 26 | let file_handle = OpenOptions::new() 27 | .create(true) 28 | .append(true) 29 | .open(path)?; 30 | 31 | Ok(Self { file_handle }) 32 | } 33 | } 34 | 35 | impl Coroutine for WriteCoroutine { 36 | type Yield = (); 37 | type Return = (); 38 | fn resume(mut self: Pin<&mut Self>, arg: i32) 39 | -> CoroutineState { 40 | writeln!(self.file_handle, "{}", arg).unwrap(); 41 | CoroutineState::Yielded(()) 42 | } 43 | } 44 | 45 | struct ReadCoroutine { 46 | lines: io::Lines>, 47 | } 48 | 49 | impl ReadCoroutine { 50 | fn new(path: &str) -> io::Result { 51 | let file = File::open(path)?; 52 | let reader = BufReader::new(file); 53 | let lines = reader.lines(); 54 | Ok(Self { lines }) 55 | } 56 | } 57 | 58 | impl Coroutine<()> for ReadCoroutine { 59 | type Yield = i32; 60 | type Return = (); 61 | 62 | fn resume(mut self: Pin<&mut Self>, _arg: ()) 63 | -> CoroutineState { 64 | match self.lines.next() { 65 | Some(Ok(line)) => { 66 | if let Ok(number) = line.parse::() { 67 | CoroutineState::Yielded(number) 68 | } else { 69 | CoroutineState::Complete(()) 70 | } 71 | } 72 | Some(Err(_)) | None => CoroutineState::Complete(()), 73 | } 74 | } 75 | } 76 | 77 | impl SymmetricCoroutine for ReadCoroutine { 78 | type Input = (); 79 | type Output = Option; 80 | 81 | fn resume_with_input( 82 | mut self: Pin<&mut Self>, _input: () 83 | ) -> Self::Output { 84 | if let Some(Ok(line)) = self.lines.next() { 85 | line.parse::().ok() 86 | } else { 87 | None 88 | } 89 | } 90 | } 91 | 92 | impl SymmetricCoroutine for WriteCoroutine { 93 | type Input = i32; 94 | type Output = (); 95 | 96 | fn resume_with_input( 97 | mut self: Pin<&mut Self>, input: i32 98 | ) -> Self::Output { 99 | writeln!(self.file_handle, "{}", input).unwrap(); 100 | } 101 | } 102 | 103 | 104 | fn main() -> io::Result<()> { 105 | let mut reader = ReadCoroutine::new("numbers.txt")?; 106 | let mut writer = WriteCoroutine::new("output.txt")?; 107 | 108 | loop { 109 | let number = Pin::new(&mut reader).resume_with_input(()); 110 | if let Some(num) = number { 111 | Pin::new(&mut writer).resume_with_input(num); 112 | } else { 113 | break; 114 | } 115 | } 116 | Ok(()) 117 | } 118 | -------------------------------------------------------------------------------- /async_rust/chapter_5/controlling_coroutines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "controlling_coroutines" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | -------------------------------------------------------------------------------- /async_rust/chapter_5/controlling_coroutines/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines, coroutine_trait)] 2 | use std::{ 3 | ops::{Coroutine, CoroutineState}, 4 | pin::Pin, 5 | time::Duration, 6 | }; 7 | use rand::Rng; 8 | 9 | 10 | struct RandCoRoutine { 11 | pub value: u8, 12 | pub live: bool, 13 | } 14 | impl RandCoRoutine { 15 | fn new() -> Self { 16 | let mut coroutine = Self { 17 | value: 0, 18 | live: true, 19 | }; 20 | coroutine.generate(); 21 | coroutine 22 | } 23 | fn generate(&mut self) { 24 | let mut rng = rand::thread_rng(); 25 | self.value = rng.gen_range(0..=10); 26 | } 27 | } 28 | 29 | impl Coroutine<()> for RandCoRoutine { 30 | type Yield = u8; 31 | type Return = (); 32 | 33 | fn resume(mut self: Pin<&mut Self>, _: ()) 34 | -> CoroutineState { 35 | self.generate(); 36 | CoroutineState::Yielded(self.value) 37 | } 38 | } 39 | 40 | 41 | fn main() { 42 | let mut coroutines = Vec::new(); 43 | for _ in 0..10 { 44 | coroutines.push(RandCoRoutine::new()); 45 | } 46 | let mut total: u32 = 0; 47 | 48 | loop { 49 | let mut all_dead = true; 50 | for mut coroutine in coroutines.iter_mut() { 51 | if coroutine.live { 52 | all_dead = false; 53 | match Pin::new(&mut coroutine).resume(()) { 54 | CoroutineState::Yielded(result) => { 55 | total += result as u32; 56 | }, 57 | CoroutineState::Complete(_) => { 58 | panic!("Coroutine should not complete"); 59 | }, 60 | } 61 | if coroutine.value < 9 { 62 | coroutine.live = false; 63 | } 64 | } 65 | } 66 | if all_dead { 67 | break 68 | } 69 | } 70 | println!("Total: {}", total); 71 | 72 | let (sender, reciever) = std::sync::mpsc::channel::(); 73 | let _thread = std::thread::spawn(move || { 74 | loop { 75 | let mut coroutine = match reciever.recv() { 76 | Ok(coroutine) => coroutine, 77 | Err(_) => break, 78 | }; 79 | match Pin::new(&mut coroutine).resume(()) { 80 | CoroutineState::Yielded(result) => { 81 | println!("Coroutine yielded: {}", result); 82 | }, 83 | CoroutineState::Complete(_) => { 84 | panic!("Coroutine should not complete"); 85 | }, 86 | } 87 | } 88 | }); 89 | std::thread::sleep(Duration::from_secs(1)); 90 | sender.send(RandCoRoutine::new()).unwrap(); 91 | sender.send(RandCoRoutine::new()).unwrap(); 92 | std::thread::sleep(Duration::from_secs(1)); 93 | } 94 | -------------------------------------------------------------------------------- /async_rust/chapter_5/implementing_a_simple_generator_in_rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "implementing_a_simple_generator_in_rust" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_5/implementing_a_simple_generator_in_rust/data.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 -------------------------------------------------------------------------------- /async_rust/chapter_5/implementing_a_simple_generator_in_rust/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines)] 2 | #![feature(coroutine_trait)] 3 | use std::fs::File; 4 | use std::io::{self, BufRead, BufReader}; 5 | use std::ops::{Coroutine, CoroutineState}; 6 | use std::pin::Pin; 7 | 8 | struct ReadCoroutine { 9 | lines: io::Lines>, 10 | } 11 | 12 | impl ReadCoroutine { 13 | fn new(path: &str) -> io::Result { 14 | let file = File::open(path)?; 15 | let reader = BufReader::new(file); 16 | let lines = reader.lines(); 17 | Ok(Self { lines }) 18 | } 19 | } 20 | 21 | impl Coroutine<()> for ReadCoroutine { 22 | type Yield = i32; 23 | type Return = (); 24 | 25 | fn resume(mut self: Pin<&mut Self>, _arg: ()) 26 | -> CoroutineState { 27 | match self.lines.next() { 28 | Some(Ok(line)) => { 29 | if let Ok(number) = line.parse::() { 30 | CoroutineState::Yielded(number) 31 | } else { 32 | CoroutineState::Complete(()) 33 | } 34 | } 35 | Some(Err(_)) | None => CoroutineState::Complete(()), 36 | } 37 | } 38 | } 39 | 40 | 41 | fn main() -> io::Result<()> { 42 | let mut coroutine = ReadCoroutine::new("./data.txt")?; 43 | loop { 44 | match Pin::new(&mut coroutine).resume(()) { 45 | CoroutineState::Yielded(number) => println!("{:?}", number), 46 | CoroutineState::Complete(()) => break, 47 | } 48 | } 49 | 50 | Ok(()) 51 | } 52 | -------------------------------------------------------------------------------- /async_rust/chapter_5/mimicking_async_behavour_with_coroutines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mimicking_async_behavour_with_coroutines" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_5/mimicking_async_behavour_with_coroutines/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines, coroutine_trait)] 2 | use std::{ 3 | collections::VecDeque, 4 | future::Future, 5 | ops::{Coroutine, CoroutineState}, 6 | pin::Pin, 7 | task::{Context, Poll}, 8 | time::Instant, 9 | }; 10 | 11 | 12 | struct SleepCoroutine { 13 | pub start: Instant, 14 | pub duration: std::time::Duration, 15 | } 16 | impl SleepCoroutine { 17 | fn new(duration: std::time::Duration) -> Self { 18 | Self { 19 | start: Instant::now(), 20 | duration, 21 | } 22 | } 23 | } 24 | impl Coroutine<()> for SleepCoroutine { 25 | type Yield = (); 26 | type Return = (); 27 | 28 | fn resume( 29 | self: Pin<&mut Self>, _: ()) 30 | -> CoroutineState { 31 | if self.start.elapsed() >= self.duration { 32 | CoroutineState::Complete(()) 33 | } else { 34 | CoroutineState::Yielded(()) 35 | } 36 | } 37 | } 38 | 39 | impl Future for SleepCoroutine { 40 | type Output = (); 41 | 42 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 43 | -> Poll { 44 | match Pin::new(&mut self).resume(()) { 45 | CoroutineState::Complete(_) => Poll::Ready(()), 46 | CoroutineState::Yielded(_) => { 47 | cx.waker().wake_by_ref(); 48 | Poll::Pending 49 | }, 50 | } 51 | } 52 | } 53 | 54 | 55 | struct Executor { 56 | coroutines: VecDeque 58 | >>>, 59 | } 60 | 61 | impl Executor { 62 | fn new() -> Self { 63 | Self { 64 | coroutines: VecDeque::new(), 65 | } 66 | } 67 | 68 | fn add(&mut self, coroutine: Pin>>) 70 | { 71 | self.coroutines.push_back(coroutine); 72 | } 73 | 74 | fn poll(&mut self) { 75 | println!("Polling {} coroutines", self.coroutines.len()); 76 | let mut coroutine = self.coroutines.pop_front().unwrap(); 77 | match coroutine.as_mut().resume(()) { 78 | CoroutineState::Yielded(_) => { 79 | self.coroutines.push_back(coroutine); 80 | }, 81 | CoroutineState::Complete(_) => {}, 82 | } 83 | } 84 | } 85 | 86 | 87 | fn main() { 88 | let mut executor = Executor::new(); 89 | 90 | for _ in 0..3 { 91 | let coroutine = SleepCoroutine::new( 92 | std::time::Duration::from_secs(1) 93 | ); 94 | executor.add(Box::pin(coroutine)); 95 | } 96 | let start = Instant::now(); 97 | while !executor.coroutines.is_empty() { 98 | executor.poll(); 99 | } 100 | println!("Took {:?}", start.elapsed()); 101 | } 102 | -------------------------------------------------------------------------------- /async_rust/chapter_5/stacking_coroutines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stacking_coroutines" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /async_rust/chapter_5/stacking_coroutines/numbers.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 -------------------------------------------------------------------------------- /async_rust/chapter_5/stacking_coroutines/output.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 1 7 | 2 8 | 3 9 | 4 10 | 5 11 | 1 12 | 2 13 | 3 14 | 4 15 | 5 16 | 1 17 | 2 18 | 3 19 | 4 20 | 5 21 | -------------------------------------------------------------------------------- /async_rust/chapter_5/stacking_coroutines/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines)] 2 | #![feature(coroutine_trait)] 3 | use std::fs::File; 4 | use std::io::{self, BufRead, BufReader}; 5 | use std::ops::{Coroutine, CoroutineState}; 6 | use std::pin::Pin; 7 | use std::fs::OpenOptions; 8 | use std::io::Write; 9 | 10 | 11 | struct WriteCoroutine { 12 | pub file_handle: File, 13 | } 14 | 15 | impl WriteCoroutine { 16 | fn new(path: &str) -> io::Result { 17 | let file_handle = OpenOptions::new() 18 | .create(true) 19 | .append(true) 20 | .open(path)?; 21 | Ok(Self { file_handle }) 22 | } 23 | } 24 | 25 | impl Coroutine for WriteCoroutine { 26 | type Yield = (); 27 | type Return = (); 28 | fn resume(mut self: Pin<&mut Self>, arg: i32) 29 | -> CoroutineState { 30 | writeln!(self.file_handle, "{}", arg).unwrap(); 31 | CoroutineState::Yielded(()) 32 | } 33 | } 34 | 35 | 36 | struct ReadCoroutine { 37 | lines: io::Lines>, 38 | } 39 | 40 | impl ReadCoroutine { 41 | fn new(path: &str) -> io::Result { 42 | let file = File::open(path)?; 43 | let reader = BufReader::new(file); 44 | let lines = reader.lines(); 45 | Ok(Self { lines }) 46 | } 47 | } 48 | 49 | impl Coroutine<()> for ReadCoroutine { 50 | type Yield = i32; 51 | type Return = (); 52 | 53 | fn resume(mut self: Pin<&mut Self>, _arg: ()) 54 | -> CoroutineState { 55 | match self.lines.next() { 56 | Some(Ok(line)) => { 57 | if let Ok(number) = line.parse::() { 58 | CoroutineState::Yielded(number) 59 | } else { 60 | CoroutineState::Complete(()) 61 | } 62 | } 63 | Some(Err(_)) | None => CoroutineState::Complete(()), 64 | } 65 | } 66 | } 67 | 68 | struct CoroutineManager{ 69 | reader: ReadCoroutine, 70 | writer: WriteCoroutine 71 | } 72 | 73 | 74 | impl CoroutineManager { 75 | fn new(read_path: &str, write_path: &str) -> io::Result { 76 | let reader = ReadCoroutine::new(read_path)?; 77 | let writer = WriteCoroutine::new(write_path)?; 78 | 79 | Ok(Self { 80 | reader, 81 | writer, 82 | }) 83 | } 84 | fn run(&mut self) { 85 | let mut read_pin = Pin::new(&mut self.reader); 86 | let mut write_pin = Pin::new(&mut self.writer); 87 | 88 | loop { 89 | match read_pin.as_mut().resume(()) { 90 | CoroutineState::Yielded(number) => { 91 | write_pin.as_mut().resume(number); 92 | } 93 | CoroutineState::Complete(()) => break, 94 | } 95 | } 96 | } 97 | } 98 | 99 | 100 | fn main() { 101 | let mut manager = CoroutineManager::new( 102 | "numbers.txt", "output.txt" 103 | ).unwrap(); 104 | manager.run(); 105 | } 106 | -------------------------------------------------------------------------------- /async_rust/chapter_5/testing_coroutines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "testing_coroutines" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_5/testing_coroutines/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines)] 2 | #![feature(coroutine_trait)] 3 | use std::ops::{Coroutine, CoroutineState}; 4 | use std::pin::Pin; 5 | use std::sync::{Arc, Mutex}; 6 | 7 | pub struct MutexCoRoutine { 8 | pub handle: Arc>, 9 | pub threshold: u8, 10 | } 11 | 12 | impl Coroutine<()> for MutexCoRoutine { 13 | type Yield = (); 14 | type Return = (); 15 | 16 | fn resume(mut self: Pin<&mut Self>, _: ()) 17 | -> CoroutineState { 18 | match self.handle.try_lock() { 19 | Ok(mut handle) => { 20 | *handle += 1; 21 | }, 22 | Err(_) => { 23 | return CoroutineState::Yielded(()); 24 | }, 25 | } 26 | self.threshold -=1; 27 | if self.threshold == 0 { 28 | return CoroutineState::Complete(()) 29 | } 30 | return CoroutineState::Yielded(()) 31 | } 32 | } 33 | 34 | fn main() { 35 | println!("Hello, world!"); 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | use std::future::Future; 42 | use std::task::{Context, Poll}; 43 | use std::time::Duration; 44 | 45 | // sync testing inteface 46 | fn check_yield(coroutine: &mut MutexCoRoutine) -> bool { 47 | match Pin::new(coroutine).resume(()) { 48 | CoroutineState::Yielded(_) => { 49 | true 50 | }, 51 | CoroutineState::Complete(_) => { 52 | false 53 | }, 54 | } 55 | } 56 | 57 | // async runtime inteface 58 | impl Future for MutexCoRoutine { 59 | type Output = (); 60 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 61 | -> Poll { 62 | match Pin::new(&mut self).resume(()) { 63 | CoroutineState::Complete(_) => Poll::Ready(()), 64 | CoroutineState::Yielded(_) => { 65 | cx.waker().wake_by_ref(); 66 | Poll::Pending 67 | }, 68 | } 69 | } 70 | } 71 | 72 | #[test] 73 | fn basic_test() { 74 | let handle = Arc::new(Mutex::new(0)); 75 | let mut first_coroutine = MutexCoRoutine { 76 | handle: handle.clone(), 77 | threshold: 2, 78 | }; 79 | let mut second_coroutine = MutexCoRoutine { 80 | handle: handle.clone(), 81 | threshold: 2, 82 | }; 83 | 84 | let lock = handle.lock().unwrap(); 85 | for _ in 0..3 { 86 | assert_eq!(check_yield(&mut first_coroutine), true); 87 | assert_eq!(check_yield(&mut second_coroutine), true); 88 | } 89 | assert_eq!(*lock, 0); 90 | std::mem::drop(lock); 91 | 92 | assert_eq!(check_yield(&mut first_coroutine), true); 93 | assert_eq!(*handle.lock().unwrap(), 1); 94 | assert_eq!(check_yield(&mut second_coroutine), true); 95 | assert_eq!(*handle.lock().unwrap(), 2); 96 | assert_eq!(check_yield(&mut first_coroutine), false); 97 | assert_eq!(*handle.lock().unwrap(), 3); 98 | assert_eq!(check_yield(&mut second_coroutine), false); 99 | assert_eq!(*handle.lock().unwrap(), 4); 100 | } 101 | #[tokio::test] 102 | async fn async_test() { 103 | let handle = Arc::new(Mutex::new(0)); 104 | let mut first_coroutine = MutexCoRoutine { 105 | handle: handle.clone(), 106 | threshold: 2, 107 | }; 108 | let mut second_coroutine = MutexCoRoutine { 109 | handle: handle.clone(), 110 | threshold: 2, 111 | }; 112 | let handle_one = tokio::spawn(async move { 113 | first_coroutine.await; 114 | }); 115 | let handle_two = tokio::spawn(async move { 116 | second_coroutine.await; 117 | }); 118 | handle_one.await.unwrap(); 119 | handle_two.await.unwrap(); 120 | assert_eq!(*handle.lock().unwrap(), 4); 121 | } 122 | } -------------------------------------------------------------------------------- /async_rust/chapter_5/why_use_coroutines/.gitignore: -------------------------------------------------------------------------------- 1 | numbers.txt 2 | 3 | -------------------------------------------------------------------------------- /async_rust/chapter_5/why_use_coroutines/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "why_use_coroutines" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | -------------------------------------------------------------------------------- /async_rust/chapter_5/why_use_coroutines/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(coroutines)] 2 | #![feature(coroutine_trait)] 3 | use std::fs::{OpenOptions, File}; 4 | use std::io::{Write, self}; 5 | use std::time::Instant; 6 | use rand::Rng; 7 | use std::ops::{Coroutine, CoroutineState}; 8 | use std::pin::Pin; 9 | 10 | fn append_number_to_file(n: i32) -> io::Result<()> { 11 | let mut file = OpenOptions::new() 12 | .create(true) 13 | .append(true) 14 | .open("numbers.txt")?; 15 | writeln!(file, "{}", n)?; 16 | Ok(()) 17 | } 18 | 19 | 20 | struct WriteCoroutine { 21 | pub file_handle: File, 22 | } 23 | 24 | impl WriteCoroutine { 25 | fn new(path: &str) -> io::Result { 26 | let file_handle = OpenOptions::new() 27 | .create(true) 28 | .append(true) 29 | .open(path)?; 30 | Ok(Self { file_handle }) 31 | } 32 | } 33 | 34 | impl Coroutine for WriteCoroutine { 35 | type Yield = (); 36 | type Return = (); 37 | 38 | fn resume(mut self: Pin<&mut Self>, arg: i32) 39 | -> CoroutineState { 40 | writeln!(self.file_handle, "{}", arg).unwrap(); 41 | CoroutineState::Yielded(()) 42 | } 43 | } 44 | 45 | 46 | fn main() -> io::Result<()> { 47 | // for the basic write 48 | let mut rng = rand::thread_rng(); 49 | let numbers: Vec = (0..200000).map(|_| rng.gen()).collect(); 50 | 51 | let start = Instant::now(); 52 | for &number in &numbers { 53 | if let Err(e) = append_number_to_file(number) { 54 | eprintln!("Failed to write to file: {}", e); 55 | } 56 | } 57 | let duration = start.elapsed(); 58 | 59 | println!("Time elapsed in file operations is: {:?}", duration); 60 | 61 | // for the coroutine write 62 | let mut rng = rand::thread_rng(); 63 | let numbers: Vec = (0..200000).map(|_| rng.gen()).collect(); 64 | let start = Instant::now(); 65 | 66 | let mut coroutine = WriteCoroutine::new( 67 | "numbers.txt" 68 | )?; 69 | for &number in &numbers { 70 | Pin::new(&mut coroutine).resume(number); 71 | } 72 | let duration = start.elapsed(); 73 | println!("Time elapsed in file operations is: {:?}", duration); 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /async_rust/chapter_6/defining-our-subjects/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "defining-our-subjects" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.26.0", features = ["full"] } 8 | clearscreen = "2.0.1" 9 | -------------------------------------------------------------------------------- /async_rust/chapter_6/defining-our-subjects/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::sync::atomic::{AtomicI16, AtomicBool}; 3 | use core::sync::atomic::Ordering; 4 | use std::sync::LazyLock; 5 | use std::future::Future; 6 | use std::task::Poll; 7 | use std::pin::Pin; 8 | use std::task::Context; 9 | use std::time::{Instant, Duration}; 10 | 11 | 12 | static TEMP: LazyLock> = LazyLock::new(|| { 13 | Arc::new(AtomicI16::new(2090)) 14 | }); 15 | static DESIRED_TEMP: LazyLock> = LazyLock::new(|| { 16 | Arc::new(AtomicI16::new(2100)) 17 | }); 18 | static HEAT_ON: LazyLock> = LazyLock::new(|| { 19 | Arc::new(AtomicBool::new(false)) 20 | }); 21 | 22 | 23 | pub struct DisplayFuture { 24 | pub temp_snapshot: i16, 25 | } 26 | 27 | 28 | impl DisplayFuture { 29 | pub fn new() -> Self { 30 | DisplayFuture { 31 | temp_snapshot: TEMP.load(Ordering::SeqCst) 32 | } 33 | } 34 | } 35 | 36 | impl Future for DisplayFuture { 37 | type Output = (); 38 | 39 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) 40 | -> Poll { 41 | let current_snapshot = TEMP.load(Ordering::SeqCst); 42 | let desired_temp = DESIRED_TEMP.load(Ordering::SeqCst); 43 | let heat_on = HEAT_ON.load(Ordering::SeqCst); 44 | 45 | if current_snapshot == self.temp_snapshot { 46 | cx.waker().wake_by_ref(); 47 | return Poll::Pending 48 | } 49 | if current_snapshot < desired_temp && heat_on == false { 50 | HEAT_ON.store(true, Ordering::SeqCst); 51 | } 52 | else if current_snapshot > desired_temp && heat_on == true { 53 | HEAT_ON.store(false, Ordering::SeqCst); 54 | } 55 | clearscreen::clear().unwrap(); 56 | println!("Temperature: {}\nDesired Temp: {}\nHeater On: {}", 57 | current_snapshot as f32 / 100.0, 58 | desired_temp as f32 / 100.0, 59 | heat_on); 60 | self.temp_snapshot = current_snapshot; 61 | cx.waker().wake_by_ref(); 62 | return Poll::Pending 63 | } 64 | } 65 | 66 | pub struct HeaterFuture { 67 | pub time_snapshot: Instant, 68 | } 69 | 70 | impl HeaterFuture { 71 | pub fn new() -> Self { 72 | HeaterFuture { 73 | time_snapshot: Instant::now() 74 | } 75 | } 76 | } 77 | 78 | impl Future for HeaterFuture { 79 | type Output = (); 80 | 81 | 82 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 83 | if HEAT_ON.load(Ordering::SeqCst) == false { 84 | self.time_snapshot = Instant::now(); 85 | cx.waker().wake_by_ref(); 86 | return Poll::Pending 87 | } 88 | let current_snapshot = Instant::now(); 89 | if current_snapshot.duration_since(self.time_snapshot) < 90 | Duration::from_secs(3) { 91 | cx.waker().wake_by_ref(); 92 | return Poll::Pending 93 | } 94 | TEMP.fetch_add(3, Ordering::SeqCst); 95 | self.time_snapshot = Instant::now(); 96 | cx.waker().wake_by_ref(); 97 | return Poll::Pending 98 | } 99 | } 100 | 101 | pub struct HeatLossFuture { 102 | pub time_snapshot: Instant, 103 | } 104 | 105 | impl HeatLossFuture { 106 | pub fn new() -> Self { 107 | HeatLossFuture { 108 | time_snapshot: Instant::now() 109 | } 110 | } 111 | } 112 | 113 | impl Future for HeatLossFuture { 114 | type Output = (); 115 | 116 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> 117 | Poll { 118 | let current_snapshot = Instant::now(); 119 | if current_snapshot.duration_since(self.time_snapshot) > 120 | Duration::from_secs(3) { 121 | TEMP.fetch_sub(1, Ordering::SeqCst); 122 | self.time_snapshot = Instant::now(); 123 | } 124 | cx.waker().wake_by_ref(); 125 | return Poll::Pending 126 | } 127 | } 128 | 129 | #[tokio::main] 130 | async fn main() { 131 | let display = tokio::spawn(async { 132 | DisplayFuture::new().await; 133 | }); 134 | let heat_loss = tokio::spawn(async { 135 | HeatLossFuture::new().await; 136 | }); 137 | let heater = tokio::spawn(async { 138 | HeaterFuture::new().await; 139 | }); 140 | display.await.unwrap(); 141 | heat_loss.await.unwrap(); 142 | heater.await.unwrap(); 143 | } 144 | -------------------------------------------------------------------------------- /async_rust/chapter_6/enabling-broadcasting-with-an-event-bus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enabling-broadcasting-with-an-event-bus" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.26.0", features = ["full"] } 8 | futures = "0.3.28" 9 | -------------------------------------------------------------------------------- /async_rust/chapter_6/enabling-broadcasting-with-an-event-bus/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex, atomic::{AtomicU32, Ordering}}; 2 | use tokio::sync::Mutex as AsyncMutex; 3 | use std::collections::{VecDeque, HashMap}; 4 | use std::marker::Send; 5 | 6 | 7 | pub struct EventBus { 8 | chamber: AsyncMutex>>, 9 | count: AtomicU32, 10 | dead_ids: Mutex>, 11 | } 12 | 13 | 14 | impl EventBus { 15 | 16 | pub fn new() -> Self { 17 | Self { 18 | chamber: AsyncMutex::new(HashMap::new()), 19 | count: AtomicU32::new(0), 20 | dead_ids: Mutex::new(Vec::new()), 21 | } 22 | } 23 | pub async fn subscribe(&self) -> EventHandle { 24 | let mut chamber = self.chamber.lock().await; 25 | let id = self.count.fetch_add(1, Ordering::SeqCst); 26 | chamber.insert(id, VecDeque::new()); 27 | std::mem::drop(chamber); // <- drop chamber to avoid self.clone() 28 | EventHandle { 29 | id, 30 | event_bus: self.into(), // <- no need to clone! 31 | } 32 | } 33 | pub fn unsubscribe(&self, id: u32) { 34 | self.dead_ids.lock().unwrap().push(id); 35 | } 36 | pub async fn poll(&self, id: u32) -> Option { 37 | let mut chamber = self.chamber.lock().await; 38 | let queue = chamber.get_mut(&id).unwrap(); 39 | queue.pop_front() 40 | } 41 | pub async fn send(&self, event: T) { 42 | let mut chamber = self.chamber.lock().await; 43 | for (_, value) in chamber.iter_mut() { 44 | value.push_back(event.clone()); 45 | } 46 | } 47 | } 48 | 49 | 50 | pub struct EventHandle<'a, T: Clone + Send> { 51 | pub id: u32, 52 | event_bus: Arc<&'a EventBus>, 53 | } 54 | impl <'a, T: Clone + Send> EventHandle<'a, T> { 55 | 56 | pub async fn poll(&self) -> Option { 57 | self.event_bus.poll(self.id).await 58 | } 59 | } 60 | 61 | impl<'a, T: Clone + Send> Drop for EventHandle<'a, T> { 62 | fn drop(&mut self) { 63 | self.event_bus.unsubscribe(self.id); 64 | } 65 | } 66 | 67 | 68 | async fn consume_event_bus(event_bus: Arc>) { 69 | let handle = event_bus.subscribe().await; 70 | loop { 71 | let event = handle.poll().await; 72 | match event { 73 | Some(event) => { 74 | println!("id: {} value: {}", handle.id, event); 75 | if event == 3.0 { 76 | break; 77 | } 78 | }, 79 | None => {} 80 | } 81 | } 82 | } 83 | 84 | 85 | async fn garbage_collector(event_bus: Arc>) { 86 | loop { 87 | let mut chamber = event_bus.chamber.lock().await; 88 | let dead_ids = event_bus.dead_ids.lock().unwrap().clone(); 89 | event_bus.dead_ids.lock().unwrap().clear(); 90 | for id in dead_ids.iter() { 91 | chamber.remove(id); 92 | } 93 | std::mem::drop(chamber); 94 | tokio::time::sleep(std::time::Duration::from_secs(1)).await; 95 | } 96 | } 97 | 98 | 99 | #[tokio::main] 100 | async fn main() { 101 | let event_bus = Arc::new(EventBus::::new()); 102 | let bus_one = event_bus.clone(); 103 | let bus_two = event_bus.clone(); 104 | let gb_bus_ref = event_bus.clone(); 105 | 106 | let _gb = tokio::task::spawn(async { 107 | garbage_collector(gb_bus_ref).await 108 | }); 109 | let one = tokio::task::spawn(async { 110 | consume_event_bus(bus_one).await 111 | }); 112 | let two = tokio::task::spawn(async { 113 | consume_event_bus(bus_two).await 114 | }); 115 | 116 | std::thread::sleep(std::time::Duration::from_secs(1)); 117 | event_bus.send(1.0).await; 118 | event_bus.send(2.0).await; 119 | event_bus.send(3.0).await; 120 | 121 | let _ = one.await; 122 | let _ = two.await; 123 | println!("{:?}", event_bus.chamber.lock().await); 124 | std::thread::sleep(std::time::Duration::from_secs(3)); 125 | println!("{:?}", event_bus.chamber.lock().await); 126 | } 127 | -------------------------------------------------------------------------------- /async_rust/chapter_6/getting-user-input-by-callbacks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "defining-our-subjects" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.26.0", features = ["full"] } 8 | clearscreen = "2.0.1" 9 | device_query = "1.1.3" 10 | -------------------------------------------------------------------------------- /async_rust/chapter_7/README.md: -------------------------------------------------------------------------------- 1 | ## Chapter 7 2 | 3 | Order of Projects: 4 | 5 | 1. runtime_setup 6 | 2. localset 7 | 3. localstate 8 | 4. shutdown 9 | 5. metrics -------------------------------------------------------------------------------- /async_rust/chapter_7/localset/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_7/localset/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime_setup" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | tokio-util = { version = "0.7.10", features = ["full"] } -------------------------------------------------------------------------------- /async_rust/chapter_7/localset/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio_util::task::LocalPoolHandle; 2 | use std::cell::RefCell; 3 | 4 | 5 | thread_local! { 6 | pub static COUNTER: RefCell = RefCell::new(1); 7 | } 8 | 9 | 10 | async fn something(number: u32) -> u32 { 11 | std::thread::sleep(std::time::Duration::from_secs(3)); 12 | // tokio::time::sleep(std::time::Duration::from_secs(3)).await; 13 | COUNTER.with(|counter| { 14 | *counter.borrow_mut() += 1; // Mutably borrow the counter and increment it 15 | println!("Counter: {} for: {}", *counter.borrow(), number); // Immutably borrow the counter to read it 16 | }); 17 | number 18 | } 19 | 20 | 21 | #[tokio::main(flavor = "current_thread")] 22 | async fn main() { 23 | 24 | let pool = LocalPoolHandle::new(3); 25 | 26 | let one = pool.spawn_pinned_by_idx(|| async { 27 | println!("one"); 28 | something(1).await 29 | }, 0); 30 | let two = pool.spawn_pinned_by_idx(|| async { 31 | println!("two"); 32 | something(2).await 33 | }, 0); 34 | let three = pool.spawn_pinned_by_idx(|| async { 35 | println!("three"); 36 | something(3).await 37 | }, 0); 38 | 39 | let result = async { 40 | let one = one.await.unwrap(); 41 | let two = two.await.unwrap(); 42 | let three = three.await.unwrap(); 43 | one + two + three 44 | }; 45 | println!("result: {}", result.await); 46 | } 47 | -------------------------------------------------------------------------------- /async_rust/chapter_7/localstate/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_7/localstate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime_setup" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | tokio-util = { version = "0.7.10", features = ["full"] } -------------------------------------------------------------------------------- /async_rust/chapter_7/localstate/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio_util::task::LocalPoolHandle; 2 | use std::cell::UnsafeCell; 3 | use std::collections::HashMap; 4 | 5 | thread_local! { 6 | pub static COUNTER: UnsafeCell> = UnsafeCell::new(HashMap::new()); 7 | } 8 | 9 | 10 | async fn something(number: u32) { 11 | tokio::time::sleep(std::time::Duration::from_secs(number as u64)).await; 12 | COUNTER.with(|counter| { 13 | let counter = unsafe { &mut *counter.get() }; 14 | 15 | match counter.get_mut(&number) { 16 | Some(count) => { 17 | let placeholder = *count + 1; 18 | *count = placeholder; 19 | }, 20 | None => { 21 | counter.insert(number, 1); 22 | } 23 | } 24 | }); 25 | } 26 | 27 | async fn print_statement() { 28 | COUNTER.with(|counter| { 29 | let counter = unsafe { &mut *counter.get() }; 30 | println!("Counter: {:?}", counter); 31 | }); 32 | } 33 | 34 | #[tokio::main(flavor = "current_thread")] 35 | async fn main() { 36 | let pool = LocalPoolHandle::new(1); 37 | let sequence = [1, 2, 3, 4, 5]; 38 | let repeated_sequence: Vec<_> = sequence.iter().cycle().take(500000).cloned().collect(); 39 | 40 | let mut futures = Vec::new(); 41 | for number in repeated_sequence { 42 | futures.push(pool.spawn_pinned(move || async move { 43 | something(number).await; 44 | something(number).await 45 | })); 46 | } 47 | for i in futures { 48 | let _ = i.await.unwrap(); 49 | } 50 | let _ = pool.spawn_pinned(|| async { 51 | print_statement().await 52 | }).await.unwrap(); 53 | } 54 | -------------------------------------------------------------------------------- /async_rust/chapter_7/metrics/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_7/metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "metrics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | tokio-util = { version = "0.7.10", features = ["full"] } 11 | -------------------------------------------------------------------------------- /async_rust/chapter_7/metrics/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio_util::task::LocalPoolHandle; 2 | use std::cell::UnsafeCell; 3 | use std::collections::HashMap; 4 | use std::sync::LazyLock; 5 | use tokio::signal::unix::{signal, SignalKind}; 6 | 7 | 8 | thread_local! { 9 | pub static COUNTER: UnsafeCell> = UnsafeCell::new(HashMap::new()); 10 | } 11 | 12 | 13 | async fn something(number: u32) { 14 | tokio::time::sleep(std::time::Duration::from_secs(number as u64)).await; 15 | COUNTER.with(|counter| { 16 | let counter = unsafe { &mut *counter.get() }; 17 | 18 | match counter.get_mut(&number) { 19 | Some(count) => { 20 | let placeholder = *count + 1; 21 | *count = placeholder; 22 | }, 23 | None => { 24 | counter.insert(number, 1); 25 | } 26 | } 27 | }); 28 | } 29 | 30 | 31 | static RUNTIME: LazyLock = LazyLock::new(|| { 32 | LocalPoolHandle::new(4) 33 | }); 34 | 35 | 36 | fn extract_data_from_thread() -> HashMap { 37 | let mut extracted_counter: HashMap = HashMap::new(); 38 | COUNTER.with(|counter| { 39 | let counter = unsafe { &mut *counter.get() }; 40 | extracted_counter = counter.clone(); 41 | }); 42 | return extracted_counter 43 | } 44 | 45 | 46 | async fn get_complete_count() -> HashMap { 47 | let mut complete_counter = HashMap::new(); 48 | let mut extracted_counters = Vec::new(); 49 | for i in 0..4 { 50 | extracted_counters.push(RUNTIME.spawn_pinned_by_idx(|| 51 | async move { 52 | extract_data_from_thread() 53 | }, i)); 54 | } 55 | for counter_future in extracted_counters { 56 | let extracted_counter = counter_future.await 57 | .unwrap_or_default(); 58 | for (key, count) in extracted_counter { 59 | *complete_counter.entry(key).or_insert(0) += count; 60 | } 61 | } 62 | return complete_counter 63 | } 64 | 65 | 66 | 67 | #[tokio::main(flavor = "current_thread")] 68 | async fn main() { 69 | let _handle = tokio::spawn( async { 70 | let sequence = [1, 2, 3, 4, 5]; 71 | let repeated_sequence: Vec<_> = sequence.iter().cycle().take(500000).cloned().collect(); 72 | 73 | let mut futures = Vec::new(); 74 | for number in repeated_sequence { 75 | futures.push(RUNTIME.spawn_pinned(move || async move { 76 | something(number).await; 77 | something(number).await 78 | })); 79 | } 80 | for i in futures { 81 | let _ = i.await.unwrap(); 82 | } 83 | println!("All futures completed"); 84 | }); 85 | let pid = std::process::id(); 86 | println!("The PID of this process is: {}", pid); 87 | let mut stream = signal(SignalKind::hangup()).unwrap(); 88 | stream.recv().await; 89 | let complete_counter = get_complete_count().await; 90 | println!("Complete counter: {:?}", complete_counter); 91 | } 92 | -------------------------------------------------------------------------------- /async_rust/chapter_7/runtime_setup/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_7/runtime_setup/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime_setup" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_7/runtime_setup/src/different_runtimes.rs: -------------------------------------------------------------------------------- 1 | use tokio::runtime::{Builder, Runtime}; 2 | use std::sync::LazyLock; 3 | 4 | 5 | static HIGH_PRIORITY: LazyLock = LazyLock::new(|| { 6 | Builder::new_multi_thread() 7 | .worker_threads(2) 8 | .thread_name("High Priority Runtime") 9 | .enable_time() 10 | .build() 11 | .unwrap() 12 | }); 13 | static LOW_PRIORITY: LazyLock = LazyLock::new(|| { 14 | Builder::new_multi_thread() 15 | .worker_threads(1) 16 | .thread_name("Low Priority Runtime") 17 | .enable_time() 18 | .build() 19 | .unwrap() 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /async_rust/chapter_7/runtime_setup/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::time::Duration; 3 | use tokio::runtime::{Builder, Runtime}; 4 | use tokio::task::JoinHandle; 5 | use std::sync::LazyLock; 6 | 7 | mod different_runtimes; 8 | 9 | 10 | static RUNTIME: LazyLock = LazyLock::new(|| { 11 | Builder::new_multi_thread() 12 | .worker_threads(4) 13 | .max_blocking_threads(1) 14 | .on_thread_start(|| { 15 | println!("thread starting for runtime A"); 16 | }) 17 | .on_thread_stop(|| { 18 | println!("thread stopping for runtime A"); 19 | }) 20 | .thread_keep_alive(Duration::from_secs(60)) 21 | .global_queue_interval(61) 22 | .on_thread_park(|| { 23 | println!("thread parking for runtime A"); 24 | }) 25 | .thread_name("our custom runtime A") 26 | .thread_stack_size(3 * 1024 * 1024) 27 | .enable_time() 28 | .build() 29 | .unwrap() 30 | }); 31 | 32 | pub fn spawn_task(future: F) -> JoinHandle 33 | where 34 | F: Future + Send + 'static, 35 | T: Send + 'static, 36 | { 37 | RUNTIME.spawn(future) 38 | } 39 | 40 | 41 | async fn sleep_example() -> i32 { 42 | println!("sleeping for 2 seconds"); 43 | tokio::time::sleep(Duration::from_secs(2)).await; 44 | println!("done sleeping"); 45 | 20 46 | } 47 | 48 | 49 | fn main() { 50 | let handle = spawn_task(sleep_example()); 51 | println!("spawned task"); 52 | println!("task status: {}", handle.is_finished()); 53 | std::thread::sleep(Duration::from_secs(3)); 54 | println!("task status: {}", handle.is_finished()); 55 | let result = RUNTIME.block_on(handle).unwrap(); 56 | println!("task result: {}", result); 57 | } 58 | -------------------------------------------------------------------------------- /async_rust/chapter_7/shutdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shutdown" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.33.0", features = ["full"] } -------------------------------------------------------------------------------- /async_rust/chapter_7/shutdown/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | async fn cleanup() { 5 | println!("cleanup background task started"); 6 | let mut count = 0; 7 | loop { 8 | std::thread::sleep(std::time::Duration::from_secs(5)); 9 | tokio::signal::ctrl_c().await.unwrap(); 10 | println!("ctrl-c received!"); 11 | count += 1; 12 | if count > 2 { 13 | std::process::exit(0); 14 | } 15 | } 16 | } 17 | 18 | #[tokio::main(flavor = "current_thread")] 19 | async fn main() { 20 | std::thread::spawn(|| { 21 | let runtime = tokio::runtime::Builder::new_multi_thread() 22 | .enable_all() 23 | .build() 24 | .unwrap(); 25 | 26 | runtime.block_on(async { 27 | println!("Hello, world!"); 28 | }); 29 | }); 30 | let mut count = 0; 31 | loop { 32 | tokio::signal::ctrl_c().await.unwrap(); 33 | println!("ctrl-c received!"); 34 | count += 1; 35 | if count > 2 { 36 | std::process::exit(0); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /async_rust/chapter_8/actors-vs-mutexes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-a-basic-actor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.35.1", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_8/actors-vs-mutexes/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::{ 2 | mpsc::channel, 3 | mpsc::{Receiver, Sender}, 4 | oneshot 5 | }; 6 | use std::sync::Arc; 7 | use tokio::sync::Mutex; 8 | use tokio::sync::mpsc::error::TryRecvError; 9 | 10 | 11 | struct Message { 12 | value: i64 13 | } 14 | 15 | async fn basic_actor(mut rx: Receiver) { 16 | let mut state = 0; 17 | 18 | while let Some(msg) = rx.recv().await { 19 | state += msg.value; 20 | println!("Received: {}", msg.value); 21 | println!("State: {}", state); 22 | } 23 | } 24 | 25 | 26 | struct RespMessage { 27 | value: i64, 28 | responder: oneshot::Sender 29 | } 30 | 31 | async fn resp_actor(mut rx: Receiver) { 32 | let mut state = 0; 33 | 34 | while let Some(msg) = rx.recv().await { 35 | state += msg.value; 36 | if msg.responder.send(state).is_err() { 37 | eprintln!("Failed to send response"); 38 | } 39 | } 40 | } 41 | 42 | async fn actor_replacement(state: Arc>, value: i64) -> i64 { 43 | let mut state = state.lock().await; 44 | *state += value; 45 | return *state 46 | } 47 | 48 | 49 | #[tokio::main] 50 | async fn main() { 51 | let state = Arc::new(Mutex::new(0)); 52 | let mut handles = Vec::new(); 53 | 54 | let now = tokio::time::Instant::now(); 55 | 56 | for i in 0..100000000 { 57 | let state_ref = state.clone(); 58 | let future = async move { 59 | let handle = tokio::spawn(async move { 60 | actor_replacement(state_ref, i).await 61 | }); 62 | let _ = handle.await.unwrap(); 63 | }; 64 | handles.push(tokio::spawn(future)); 65 | } 66 | for handle in handles { 67 | let _ = handle.await.unwrap(); 68 | } 69 | println!("Elapsed: {:?}", now.elapsed()); 70 | 71 | let (tx, rx) = channel::(100000000); 72 | let _resp_actor_handle = tokio::spawn(async { 73 | resp_actor(rx).await; 74 | }); 75 | let mut handles = Vec::new(); 76 | 77 | let now = tokio::time::Instant::now(); 78 | for i in 0..100000000 { 79 | let tx_ref = tx.clone(); 80 | 81 | let future = async move { 82 | let (resp_tx, resp_rx) = oneshot::channel::(); 83 | let msg = RespMessage { 84 | value: i, 85 | responder: resp_tx 86 | }; 87 | tx_ref.send(msg).await.unwrap(); 88 | let _ = resp_rx.await.unwrap(); 89 | }; 90 | handles.push(tokio::spawn(future)); 91 | } 92 | for handle in handles { 93 | let _ = handle.await.unwrap(); 94 | } 95 | println!("Elapsed: {:?}", now.elapsed()); 96 | } 97 | -------------------------------------------------------------------------------- /async_rust/chapter_8/basic_actors/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_8/basic_actors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_actors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.35.1", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_8/basic_actors/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::{ 2 | mpsc::channel, 3 | mpsc::Receiver, 4 | oneshot, 5 | }; 6 | 7 | 8 | struct Message { 9 | value: i32 10 | } 11 | 12 | 13 | async fn basic_actor(mut rx: Receiver) { 14 | 15 | let mut state = 0; 16 | 17 | while let Some(msg) = rx.recv().await { 18 | state += msg.value; 19 | println!("Received: {}", msg.value); 20 | println!("State: {}", state); 21 | } 22 | } 23 | 24 | 25 | struct RespMessage { 26 | value: i32, 27 | responder: oneshot::Sender 28 | } 29 | 30 | async fn resp_actor(mut rx: Receiver) { 31 | let mut state = 0; 32 | 33 | while let Some(msg) = rx.recv().await { 34 | state += msg.value; 35 | 36 | if msg.responder.send(state).is_err() { 37 | eprintln!("Failed to send response"); 38 | } 39 | } 40 | } 41 | 42 | 43 | #[tokio::main] 44 | async fn main() { 45 | let (tx, rx) = channel::(10); 46 | 47 | let _actor_handle = tokio::spawn(async { 48 | basic_actor(rx).await; 49 | }); 50 | for i in 0..10 { 51 | let msg = Message { value: i }; 52 | tx.send(msg).await.unwrap(); 53 | } 54 | 55 | let (tx, rx) = channel::(10); 56 | let _resp_actor_handle = tokio::spawn(async { 57 | resp_actor(rx).await; 58 | }); 59 | for i in 0..10 { 60 | let (resp_tx, resp_rx) = oneshot::channel::(); 61 | let msg = RespMessage { 62 | value: i, 63 | responder: resp_tx 64 | }; 65 | tx.send(msg).await.unwrap(); 66 | let _ = resp_rx.await.unwrap(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /async_rust/chapter_8/building-a-basic-actor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "building-a-basic-actor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.35.1", features = ["full"] } 8 | -------------------------------------------------------------------------------- /async_rust/chapter_8/building-a-basic-actor/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::{ 2 | mpsc::channel, 3 | mpsc::{Receiver, Sender}, 4 | oneshot 5 | }; 6 | 7 | struct Message { 8 | value: i64 9 | } 10 | 11 | async fn basic_actor(mut rx: Receiver) { 12 | let mut state = 0; 13 | 14 | while let Some(msg) = rx.recv().await { 15 | state += msg.value; 16 | println!("Received: {}", msg.value); 17 | println!("State: {}", state); 18 | } 19 | } 20 | 21 | 22 | struct RespMessage { 23 | value: i64, 24 | responder: oneshot::Sender 25 | } 26 | 27 | async fn resp_actor(mut rx: Receiver) { 28 | let mut state = 0; 29 | 30 | while let Some(msg) = rx.recv().await { 31 | state += msg.value; 32 | if msg.responder.send(state).is_err() { 33 | eprintln!("Failed to send response"); 34 | } 35 | } 36 | } 37 | 38 | 39 | #[tokio::main] 40 | async fn main() { 41 | let (tx, rx) = channel::(100); 42 | 43 | let _actor_handle = tokio::spawn( 44 | basic_actor(rx) 45 | ); 46 | for i in 0..10 { 47 | let msg = Message { value: i }; 48 | tx.send(msg).await.unwrap(); 49 | } 50 | 51 | let (tx, rx) = channel::(100); 52 | 53 | let _resp_actor_handle = tokio::spawn(async { 54 | resp_actor(rx).await; 55 | }); 56 | for i in 0..10 { 57 | let (resp_tx, resp_rx) = oneshot::channel::(); 58 | let msg = RespMessage { 59 | value: i, 60 | responder: resp_tx 61 | }; 62 | tx.send(msg).await.unwrap(); 63 | println!("Response: {}", resp_rx.await.unwrap()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /async_rust/chapter_8/routing/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_8/routing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_actors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.35.1", features = ["full"] } -------------------------------------------------------------------------------- /async_rust/chapter_8/routing/src/main.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::{ 2 | mpsc::channel, 3 | mpsc::{Receiver, Sender}, 4 | oneshot, 5 | }; 6 | use std::sync::OnceLock; 7 | 8 | 9 | struct SetKeyValueMessage { 10 | key: String, 11 | value: Vec, 12 | response: oneshot::Sender<()>, 13 | } 14 | 15 | struct GetKeyValueMessage { 16 | key: String, 17 | response: oneshot::Sender>>, 18 | } 19 | struct DeleteKeyValueMessage { 20 | key: String, 21 | response: oneshot::Sender<()>, 22 | } 23 | enum KeyValueMessage { 24 | Get(GetKeyValueMessage), 25 | Delete(DeleteKeyValueMessage), 26 | Set(SetKeyValueMessage), 27 | } 28 | 29 | enum RoutingMessage { 30 | KeyValue(KeyValueMessage), 31 | } 32 | 33 | 34 | async fn key_value_actor(mut receiver: Receiver) { 35 | let mut map = std::collections::HashMap::new(); 36 | while let Some(message) = receiver.recv().await { 37 | match message { 38 | KeyValueMessage::Get( 39 | GetKeyValueMessage { key, response } 40 | ) => { 41 | let _ = response.send(map.get(&key).cloned()); 42 | } 43 | KeyValueMessage::Delete( 44 | DeleteKeyValueMessage { key, response } 45 | ) => { 46 | map.remove(&key); 47 | let _ = response.send(()); 48 | } 49 | KeyValueMessage::Set( 50 | SetKeyValueMessage { key, value, response } 51 | ) => { 52 | map.insert(key, value); 53 | let _ = response.send(()); 54 | } 55 | } 56 | } 57 | } 58 | 59 | 60 | static ROUTER_SENDER: OnceLock> = OnceLock::new(); 61 | 62 | 63 | async fn router(mut receiver: Receiver) { 64 | let (key_value_sender, key_value_receiver) = channel(32); 65 | tokio::spawn(key_value_actor(key_value_receiver)); 66 | 67 | while let Some(message) = receiver.recv().await { 68 | match message { 69 | RoutingMessage::KeyValue(message) => { 70 | let _ = key_value_sender.send(message).await; 71 | } 72 | } 73 | } 74 | } 75 | 76 | 77 | pub async fn set(key: String, value: Vec) -> Result<(), std::io::Error> { 78 | let (tx, rx) = oneshot::channel(); 79 | ROUTER_SENDER.get().unwrap().send( 80 | RoutingMessage::KeyValue(KeyValueMessage::Set( 81 | SetKeyValueMessage { 82 | key, 83 | value, 84 | response: tx, 85 | }))).await.unwrap(); 86 | rx.await.unwrap(); 87 | Ok(()) 88 | } 89 | pub async fn get(key: String) -> Result>, std::io::Error> { 90 | let (tx, rx) = oneshot::channel(); 91 | ROUTER_SENDER.get().unwrap().send( 92 | RoutingMessage::KeyValue(KeyValueMessage::Get( 93 | GetKeyValueMessage { 94 | key, 95 | response: tx, 96 | }))).await.unwrap(); 97 | Ok(rx.await.unwrap()) 98 | } 99 | pub async fn delete(key: String) -> Result<(), std::io::Error> { 100 | let (tx, rx) = oneshot::channel(); 101 | ROUTER_SENDER.get().unwrap().send( 102 | RoutingMessage::KeyValue(KeyValueMessage::Delete( 103 | DeleteKeyValueMessage { 104 | key, 105 | response: tx, 106 | }))).await.unwrap(); 107 | rx.await.unwrap(); 108 | Ok(()) 109 | } 110 | 111 | 112 | #[tokio::main] 113 | async fn main() -> Result<(), std::io::Error> { 114 | let (sender, receiver) = channel(32); 115 | ROUTER_SENDER.set(sender).unwrap(); 116 | 117 | tokio::spawn(router(receiver)); 118 | 119 | let _ = set("hello".to_string(), b"world".to_vec()).await?; 120 | let value = get("hello".to_string()).await?; 121 | println!("value: {:?}", String::from_utf8(value.unwrap())); 122 | let _ = delete("hello".to_string()).await?; 123 | let value = get("hello".to_string()).await?; 124 | println!("value: {:?}", value); 125 | Ok(()) 126 | } 127 | -------------------------------------------------------------------------------- /async_rust/chapter_8/state_recovery/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_8/state_recovery/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_actors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.35.1", features = ["full"] } 10 | serde = { version = "1.0.193", features = ["derive"] } 11 | serde_json = "1.0.109" 12 | -------------------------------------------------------------------------------- /async_rust/chapter_8/state_recovery/data.json: -------------------------------------------------------------------------------- 1 | {"hello":[119,111,114,108,100]} -------------------------------------------------------------------------------- /async_rust/chapter_8/supervision/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_8/supervision/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_actors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.35.1", features = ["full"] } 10 | serde = { version = "1.0.193", features = ["derive"] } 11 | serde_json = "1.0.109" 12 | -------------------------------------------------------------------------------- /async_rust/chapter_8/supervision/data.json: -------------------------------------------------------------------------------- 1 | {"hello":[119,111,114,108,100],"test":[119,111,114,108,100]} -------------------------------------------------------------------------------- /async_rust/chapter_9/async_decorator_pattern/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/async_decorator_pattern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async_decorator_pattern" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_9/async_decorator_pattern/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | // A trait for our custom future behavior 6 | trait Logging { 7 | fn log(&self); 8 | } 9 | 10 | // A decorator Future that adds logging behavior 11 | struct LoggingFuture { 12 | inner: F, 13 | } 14 | 15 | impl Future for LoggingFuture { 16 | type Output = F::Output; 17 | 18 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 19 | // Safety: we're not moving out of the inner future, just projecting through the Pin. 20 | let inner = unsafe { self.map_unchecked_mut(|s| &mut s.inner) }; 21 | inner.log(); // Log the polling action 22 | inner.poll(cx) // Delegate to the inner future's poll method 23 | } 24 | } 25 | 26 | // Implement the Logging trait for any type that also implements Future 27 | impl Logging for F { 28 | fn log(&self) { 29 | println!("Polling the future!"); 30 | } 31 | } 32 | 33 | // Example usage with an actual future 34 | async fn my_async_function() -> String { 35 | // Simulate some async work 36 | "Result of async computation".to_string() 37 | } 38 | 39 | #[tokio::main] 40 | async fn main() { 41 | let logged_future = LoggingFuture { inner: my_async_function() }; 42 | 43 | // Normally, you would await the future, but here we just want to show the logging behavior. 44 | let result = logged_future.await; 45 | println!("{}", result); 46 | } 47 | -------------------------------------------------------------------------------- /async_rust/chapter_9/circuit_breaker/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/circuit_breaker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "circuit_breaker" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_9/circuit_breaker/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; 2 | use std::future::Future; 3 | use tokio::task::JoinHandle; 4 | 5 | 6 | 7 | static OPEN: AtomicBool = AtomicBool::new(false); 8 | static COUNT : AtomicUsize = AtomicUsize::new(0); 9 | 10 | 11 | fn spawn_task(future: F) -> Result, String> 12 | where 13 | F: Future + Send + 'static, 14 | T: Send + 'static, 15 | { 16 | let open = OPEN.load(Ordering::SeqCst); 17 | if open == false { 18 | return Ok(tokio::task::spawn(future)) 19 | } 20 | Err("Circuit Open".to_string()) 21 | } 22 | 23 | 24 | async fn error_task() { 25 | println!("error task running"); 26 | let count = COUNT.fetch_add(1, Ordering::SeqCst); 27 | if count == 2 { 28 | println!("opening circuit"); 29 | OPEN.store(true, Ordering::SeqCst); 30 | } 31 | } 32 | 33 | async fn passing_task() { 34 | println!("passing task running"); 35 | } 36 | 37 | 38 | 39 | 40 | #[tokio::main] 41 | async fn main() -> Result<(), String> { 42 | let _ = spawn_task(passing_task())?.await; 43 | let _ = spawn_task(error_task())?.await; 44 | let _ = spawn_task(error_task())?.await; 45 | let _ = spawn_task(error_task())?.await; 46 | let _ = spawn_task(passing_task())?.await; 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /async_rust/chapter_9/decorator_pattern/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/decorator_pattern/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decorator_pattern" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | 11 | 12 | [features] 13 | logging_decorator = [] 14 | -------------------------------------------------------------------------------- /async_rust/chapter_9/decorator_pattern/src/main.rs: -------------------------------------------------------------------------------- 1 | trait Greeting { 2 | fn greet(&self) -> String; 3 | } 4 | 5 | // Concrete Component 6 | struct HelloWorld; 7 | impl Greeting for HelloWorld { 8 | fn greet(&self) -> String { 9 | "Hello, World!".to_string() 10 | } 11 | } 12 | 13 | // Decorator Component 14 | struct ExcitedGreeting { 15 | inner: T, 16 | } 17 | 18 | impl Greeting for ExcitedGreeting { 19 | fn greet(&self) -> String { 20 | let mut greeting = self.inner.greet(); 21 | greeting.push_str(" I'm so excited to be in Rust!"); 22 | greeting 23 | } 24 | } 25 | 26 | // Usage 27 | fn main() { 28 | 29 | #[cfg(feature = "logging_decorator")] 30 | let hello = ExcitedGreeting { inner: HelloWorld }; 31 | 32 | #[cfg(not(feature = "logging_decorator"))] 33 | let hello = HelloWorld; 34 | 35 | println!("{}", hello.greet()); 36 | } 37 | -------------------------------------------------------------------------------- /async_rust/chapter_9/modular_design/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/modular_design/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "modular_design" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | uuid = { version = "1.5.0", features = ["v4"] } 11 | -------------------------------------------------------------------------------- /async_rust/chapter_9/modular_design/src/async_mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::LazyLock; 2 | use tokio::runtime::{Runtime, Builder}; 3 | use tokio::task::JoinHandle; 4 | use std::collections::HashMap; 5 | use std::sync::{Arc, Mutex}; 6 | 7 | pub type AddFutMap = LazyLock>>>>; 8 | 9 | 10 | static TOKIO_RUNTIME: LazyLock = LazyLock::new(|| { 11 | Builder::new_multi_thread() 12 | .enable_all() 13 | .build() 14 | .expect("Failed to create Tokio runtime") 15 | }); 16 | 17 | 18 | async fn async_add(a: i32, b: i32) -> i32 { 19 | println!("starting async_add"); 20 | tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; 21 | println!("finished async_add"); 22 | a + b 23 | } 24 | 25 | 26 | fn add_handler(a: Option, b: Option, id: Option) -> Result<(Option, Option), String> { 27 | static MAP: AddFutMap = LazyLock::new(|| Arc::new(Mutex::new(HashMap::new()))); 28 | 29 | match (a, b, id) { 30 | (Some(a), Some(b), None) => { 31 | let handle = TOKIO_RUNTIME.spawn(async_add(a, b)); 32 | let id = uuid::Uuid::new_v4().to_string(); 33 | MAP.lock().unwrap().insert(id.clone(), handle); 34 | Ok((None, Some(id))) 35 | }, 36 | (None, None, Some(id)) => { 37 | let handle = match MAP.lock().unwrap().remove(&id) { 38 | Some(handle) => handle, 39 | None => return Err("No handle found".to_string()) 40 | }; 41 | let result: i32 = match TOKIO_RUNTIME.block_on(async { handle.await }){ 42 | Ok(result) => result, 43 | Err(e) => return Err(e.to_string()) 44 | }; 45 | Ok((Some(result), None)) 46 | }, 47 | _ => Err("either a or b need to be provided or a handle_id".to_string()) 48 | } 49 | } 50 | 51 | 52 | pub fn send_add(a: i32, b: i32) -> Result { 53 | match add_handler(Some(a), Some(b), None) { 54 | Ok((None, Some(id))) => Ok(id), 55 | Ok(_) => Err("Something went wrong, please contact author".to_string()), 56 | Err(e) => Err(e) 57 | } 58 | } 59 | 60 | pub fn get_add(id: String) -> Result { 61 | match add_handler(None, None, Some(id)) { 62 | Ok((Some(result), None)) => Ok(result), 63 | Ok(_) => Err("Something went wrong, please contact author".to_string()), 64 | Err(e) => Err(e) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /async_rust/chapter_9/modular_design/src/main.rs: -------------------------------------------------------------------------------- 1 | mod async_mod; 2 | 3 | fn main() { 4 | println!("Hello, world!"); 5 | let id = async_mod::send_add(1, 2).unwrap(); 6 | println!("id: {}", id); 7 | std::thread::sleep(std::time::Duration::from_secs(4)); 8 | println!("main sleep done"); 9 | let result = async_mod::get_add(id).unwrap(); 10 | println!("result: {}", result); 11 | } 12 | -------------------------------------------------------------------------------- /async_rust/chapter_9/retry_patterns/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/retry_patterns/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "retry_patterns" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_9/retry_patterns/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | async fn get_data() -> Result> { 3 | Err("Error".into()) 4 | } 5 | 6 | 7 | async fn do_something() -> Result<(), Box> { 8 | let mut miliseconds = 1000; 9 | let total_count = 5; 10 | let mut count = 0; 11 | let result: String; 12 | loop { 13 | match get_data().await { 14 | Ok(data) => { 15 | result = data; 16 | break; 17 | }, 18 | Err(err) => { 19 | println!("Error: {}", err); 20 | count += 1; 21 | if count == total_count { 22 | return Err(err); 23 | } 24 | } 25 | } 26 | tokio::time::sleep(tokio::time::Duration::from_millis(miliseconds)).await; 27 | miliseconds *= 2; 28 | } 29 | println!("{}", result); 30 | Ok(()) 31 | } 32 | 33 | 34 | #[tokio::main] 35 | async fn main() { 36 | let outcome = do_something().await; 37 | println!("Outcome: {:?}", outcome); 38 | } 39 | -------------------------------------------------------------------------------- /async_rust/chapter_9/state_machine/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/state_machine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "state_machine" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_9/state_machine/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::task::{Context, Poll}; 4 | 5 | 6 | // Define the possible states of the state machine 7 | enum State { 8 | On, 9 | Off, 10 | } 11 | 12 | // Define the possible events that can occur 13 | enum Event { 14 | SwitchOn, 15 | SwitchOff, 16 | } 17 | 18 | // Implement the state machine logic 19 | impl State { 20 | // This async function transitions the state machine based on the current state and an event 21 | async fn transition(self, event: Event) -> Self { 22 | match (&self, event) { 23 | (State::On, Event::SwitchOff) => { 24 | println!("Transitioning to the Off state"); 25 | State::Off 26 | }, 27 | (State::Off, Event::SwitchOn) => { 28 | println!("Transitioning to the On state"); 29 | State::On 30 | }, 31 | _ => { 32 | println!("No transition possible, staying in the current state"); 33 | self 34 | }, 35 | } 36 | } 37 | } 38 | 39 | 40 | struct StateFuture { 41 | pub state: State, 42 | pub on_future: F, 43 | pub off_future: X, 44 | } 45 | 46 | 47 | impl Future for StateFuture { 48 | type Output = State; 49 | 50 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 51 | match self.state { 52 | State::On => { 53 | let inner = unsafe { self.map_unchecked_mut(|s| &mut s.on_future) }; 54 | let _ = inner.poll(cx); 55 | cx.waker().wake_by_ref(); 56 | Poll::Pending 57 | }, 58 | State::Off => { 59 | let inner = unsafe { self.map_unchecked_mut(|s| &mut s.off_future) }; 60 | let _ = inner.poll(cx); 61 | cx.waker().wake_by_ref(); 62 | Poll::Pending 63 | }, 64 | } 65 | } 66 | } 67 | 68 | 69 | struct OnFuture; 70 | 71 | impl Future for OnFuture { 72 | type Output = (); 73 | 74 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 75 | println!("On future"); 76 | cx.waker().wake_by_ref(); 77 | Poll::Pending 78 | } 79 | } 80 | struct OffFuture; 81 | 82 | impl Future for OffFuture { 83 | type Output = (); 84 | 85 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 86 | println!("Off future"); 87 | cx.waker().wake_by_ref(); 88 | Poll::Pending 89 | } 90 | } 91 | 92 | 93 | #[tokio::main] 94 | async fn main() { 95 | let mut state = State::On; 96 | 97 | state = state.transition(Event::SwitchOff).await; 98 | state = state.transition(Event::SwitchOn).await; 99 | state = state.transition(Event::SwitchOn).await; 100 | 101 | match state { 102 | State::On => println!("State machine is in the On state"), 103 | _ => println!("State machine is not in the expected state"), 104 | } 105 | 106 | let state_future = StateFuture { 107 | state: State::On, 108 | on_future: OnFuture{}, 109 | off_future: OffFuture{}, 110 | }; 111 | let _ = state_future.await; 112 | } 113 | -------------------------------------------------------------------------------- /async_rust/chapter_9/waterfall/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/waterfall/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "waterfall" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_9/waterfall/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | type WaterFallResult = Result>; 4 | 5 | async fn task1() -> WaterFallResult { 6 | Ok("Task 1 completed".to_string()) 7 | } 8 | async fn task2(input: String) -> WaterFallResult { 9 | Ok(format!("{} then Task 2 completed", input)) 10 | } 11 | async fn task3(input: String) -> WaterFallResult { 12 | Ok(format!("{} and finally Task 3 completed", input)) 13 | } 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Box> { 17 | let output1 = task1().await?; 18 | let output2 = task2(output1).await?; 19 | let result = task3(output2).await?; 20 | println!("{}", result); 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /async_rust/chapter_9/waterfall_logic_flow/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /async_rust/chapter_9/waterfall_logic_flow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "waterfall" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.33.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /async_rust/chapter_9/waterfall_logic_flow/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | type WaterFallResult = Result>; 4 | 5 | async fn task1() -> WaterFallResult { 6 | Ok(5) 7 | } 8 | async fn task2(input: i32) -> WaterFallResult { 9 | Ok(input * 2) 10 | } 11 | async fn task3(input: i32) -> WaterFallResult { 12 | Ok(input - 1) 13 | } 14 | 15 | #[tokio::main] 16 | async fn main() -> Result<(), Box> { 17 | let output1 = task1().await?; 18 | let output2: i32; 19 | if output1 > 10 { 20 | output2 = task2(output1).await?; 21 | } else { 22 | output2 = task3(output1).await?; 23 | } 24 | println!("{}", output2); 25 | Ok(()) 26 | } 27 | --------------------------------------------------------------------------------