rust s journey to async await
play

Rusts Journey to Async/Await Steve Klabnik Hi, Im Steve! On the - PowerPoint PPT Presentation

Rusts Journey to Async/Await Steve Klabnik Hi, Im Steve! On the Rust team Work at Cloudflare Doing two workshops! Parallel: do multiple things at once What is async? Concurrent: do multiple things, not at once


  1. Rust’s Journey to Async/Await Steve Klabnik

  2. Hi, I’m Steve! ● On the Rust team ● Work at Cloudflare ● Doing two workshops!

  3. Parallel: do multiple things at once What is async? Concurrent: do multiple things, not at once Asynchronous: actually unrelated! Sort of...

  4. A generic term for some “Task” computation running in a parallel or concurrent system

  5. Parallel Only possible with multiple cores or CPUs

  6. Concurrent Pretend that you have multiple cores or CPUs

  7. A word we use to describe Asynchronous language features that enable parallelism and/or concurrency

  8. Even more terminology

  9. Cooperative vs Preemptive Multitasking

  10. Cooperative Multitasking Each task decides when to yield to other tasks

  11. Preemptive Multitasking The system decides when to yield to other tasks

  12. Native vs green threads

  13. Native threads Tasks provided by the operating system Sometimes called “1:1 threading”

  14. Green Threads Tasks provided by your programming language Sometimes called “N:M threading”

  15. Native vs Green threads Native thread advantages: Green thread advantages: Part of your system; OS handles Not part of the overall system; ● ● scheduling runtime handles scheduling Very straightforward, Lighter weight, can create many, ● ● well-understood many, many, many green threads Native thread disadvantages: Green thread disadvantages: Defaults can be sort of heavy Stack growth can cause issues ● ● Relatively limited number you can Overhead when calling into C ● ● create

  16. Why do we care?

  17. Control Process Apache “Pre-fork” Child Process

  18. Control Process Child Process Apache “worker” Thread pool Child Thread Child Thread Child Thread Child Thread

  19. Let’s talk about Rust

  20. Rust was built to enhance Firefox, which is an HTTP client, not server

  21. “Synchronous, non-blocking network I/O”

  22. Isn’t this a contradiction in terms?

  23. Synchronous Asynchronous Blocking Old-school implementations Doesn’t make sense Non-blocking Go, Ruby Node.js

  24. Tons of options Synchronous, blocking Synchronous, non-blocking Your code looks like it blocks, but it Your code looks like it blocks, and ● ● doesn’t! it does block The secret: the runtime is ● Very basic and straightforward ● non-blocking Asynchronous, non-blocking Your code still looks straightforward, ● but you get performance benefits Your code looks like it doesn’t ● A common path for languages built ● block, and it doesn’t block on synchronous, blocking I/O to gain Harder to write ● performance while retaining compatibility

  25. Not all was well in Rust-land

  26. A “systems programming language” that doesn’t let you use the system’s threads?

  27. Not all was well in Rust-land

  28. Rust 1.0 was approaching

  29. Ship the minimal thing that we know is good

  30. Rust 1.0 was released! 🎊

  31. … but still, not all was well in Rust-land

  32. People 💗 Rust

  33. People want to build network services in Rust

  34. Rust is supposed to be a high-performance language

  35. Rust’s I/O model feels retro, and not performant

  36. The big problem with native threads for I/O

  37. CPU bound vs I/O bound

  38. The speed of completing a task CPU Bound is based on the CPU crunching some numbers My processor is working hard

  39. The speed of completing a task I/O Bound is based on doing a lot of input and output Doing a lot of networking

  40. When you’re doing a lot of I/O, you’re doing a lot of waiting

  41. When you’re doing a lot of waiting, you’re tying up system resources

  42. Main Process Go Green threads Asynchronous I/O Child Thread Child Thread with green threads Child Thread Child Thread (Erlang does this too)

  43. Native vs Green threads PREVIOUSLY Native thread advantages: Green thread advantages: Part of your system; OS handles Not part of the overall system; ● ● scheduling runtime handles scheduling Very straightforward, Lighter weight, can create many, ● ● well-understood many, many, many green threads Native thread disadvantages: Green thread disadvantages: Defaults can be sort of heavy Stack growth can cause issues ● ● Relatively limited number you can Overhead when calling into C ● ● create

  44. A “systems programming language” that has overhead when calling into C code?

  45. Luckily, there is another way

  46. Event Loop Nginx Asynchronous I/O

  47. Evented I/O requires non-blocking APIs

  48. Blocking vs non-blocking

  49. “Callback hell”

  50. Promises let myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("Success!"); }, 250); }); myFirstPromise.then((successMessage) => { console.log("Yay! " + successMessage); });

  51. Promises let myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("Success!"); }, 250); }); myFirstPromise.then((successMessage) => { console.log("Yay! " + successMessage); }).then((...) => { // }).then((...) => { // });

  52. Futures 0.1 pub trait Future { type Item; type Error; fn poll(&mut self) -> Poll<Self::Item, Self::Error>; } id_rpc(&my_server).and_then(|id| { get_row(id) }).map(|row| { json::encode(row) }).and_then(|encoded| { write_string(my_socket, encoded) })

  53. Promises and Futures are different! Promises are built into JavaScript Futures are not built into Rust ● ● The language has no runtime The language has a runtime ● ● This means that you must submit ● This means that Promises start ● your futures to an executor to start executing upon creation execution This feels simpler, but has some ● Futures are inert until their poll ● drawbacks, namely, lots of method is called by the executor allocations This is slightly more complex, but ● extremely efficient; a single, perfectly sized allocation per task! Compiles into the state machine ● you’d write by hand with evented I/O

  54. Futures 0.1: Executors use tokio; fn main() { let addr = "127.0.0.1:6142".parse().unwrap(); let listener = TcpListener::bind(&addr).unwrap(); let server = listener.incoming().for_each(|socket| { Ok(()) }) .map_err(|err| { println!("accept error = {:?}", err); }); println!("server running on localhost:6142"); tokio::run(server); }

  55. We used Futures 0.1 to build stuff!

  56. The design had some problems

  57. Futures 0.2 trait Future { type Item; type Error; fn poll(&mut self, cx: task::Context) -> Poll<Self::Item, Self::Error>; } No implicit context, no more need for thread local storage.

  58. Async/await // with callback request('https://google.com/', (response) => { // handle response }) // with promise request('https://google.com/').then((response) => { // handle response }); // with async/await async function handler() { let response = await request('https://google.com/') // handle response }

  59. Async/await lets you write code that feels synchronous, but is actually asynchronous

  60. Async/await is more important in Rust than in other languages because Rust has no garbage collector

  61. Rust example: synchronous fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> let mut buf = [0; 1024]; let mut cursor = 0; while cursor < 1024 { cursor += socket.read(&mut buf[cursor..])?; }

  62. Rust example: async with Futures fn read<T: AsMut<[u8]>>(self, buf: T) -> impl Future<Item = (Self, T, usize), Error = (Self, T, io::Error)> … the code is too big to fit on the slide The main problem: the borrow checker doesn’t understand asynchronous code. The constraints on the code when it’s created and when it executes are different.

  63. Rust example: async with async/await async { let mut buf = [0; 1024]; let mut cursor = 0; while cursor < 1024 { cursor += socket.read(&mut buf[cursor..]).await?; }; buf } async/await can teach the borrow checker about these constraints.

  64. Not all futures can error trait Future { type Item; type Error; fn poll(&mut self, cx: task::Context) -> Poll<Self::Item, Self::Error>; }

  65. std::future pub trait Future { type Output; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>; } Pin is how async/await teaches the borrow checker. If you need a future that errors, set Output to a Result<T, E> .

  66. … but one more thing...

  67. What syntax for async/await? async is not an issue JavaScript and C# do: await value; But what about ? for error handling? await value?; await (value?); (await value)?;

  68. What syntax for async/await? What about chains of await ? (await (await value)?);

Recommend


More recommend