{"id":8128,"date":"2025-10-04T15:22:23","date_gmt":"2025-10-04T15:22:23","guid":{"rendered":"https:\/\/pokecon.jp\/job\/?p=8128"},"modified":"2025-10-04T15:22:23","modified_gmt":"2025-10-04T15:22:23","slug":"cancelling-async-rust-%ea%9e%8f-sunshowers","status":"publish","type":"post","link":"https:\/\/pokecon.jp\/job\/8128\/","title":{"rendered":"Cancelling async Rust \ua78f sunshowers"},"content":{"rendered":"\n<\/p>\n<div>\n<p><em>This is an edited, written version of my RustConf 2025 talk about cancellations in async Rust. Like <a target=\"_blank\" href=\"..\/..\/posts\/beyond-ctrl-c-signals\/\">the written version of my RustConf 2023 talk<\/a>, I\u2019ve tried to retain the feel of a talk while making it readable as a standalone blog entry. Some links:<\/em><\/p>\n<p><iframe loading=\"lazy\" title=\"Rain: &quot;Cancelling Async Rust&quot; | RustConf 2025\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/zrv5Cy1R7r4?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n<h2 id=\"introduction\">Introduction<a target=\"_blank\" href=\"#introduction\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h2>\n<p>Let\u2019s start with a simple example \u2013 you decide to read from a channel in a loop and gather a bunch of messages:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"n\">rx<\/span><span class=\"p\">.<\/span><span class=\"n\">recv<\/span><span class=\"p\">().<\/span><span class=\"k\">await<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"n\">process<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">return<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>All good, nothing wrong with this, but you realize sometimes the channel is empty for long periods of time, so you add a timeout and print a message:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"n\">timeout<\/span><span class=\"p\">(<\/span><span class=\"n\">Duration<\/span>::<span class=\"n\">from_secs<\/span><span class=\"p\">(<\/span><span class=\"mi\">5<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"n\">rx<\/span><span class=\"p\">.<\/span><span class=\"n\">recv<\/span><span class=\"p\">()).<\/span><span class=\"k\">await<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"n\">process<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">return<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"no messages for 5 seconds\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>There\u2019s nothing wrong with this code\u2014it behaves as expected.<\/p>\n<p>Now you realize you need to write a bunch of messages out to a channel in a loop:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">msg<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">next_message<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"n\">tx<\/span><span class=\"p\">.<\/span><span class=\"n\">send<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">).<\/span><span class=\"k\">await<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"sent successfully\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">return<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>But sometimes the channel gets too full and blocks, so you add a timeout and print a message:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">msg<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">next_message<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"n\">timeout<\/span><span class=\"p\">(<\/span><span class=\"n\">Duration<\/span>::<span class=\"n\">from_secs<\/span><span class=\"p\">(<\/span><span class=\"mi\">5<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"n\">tx<\/span><span class=\"p\">.<\/span><span class=\"n\">send<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">)).<\/span><span class=\"k\">await<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"sent successfully\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">return<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"no space for 5 seconds\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>It turns out that <em>this<\/em> code is often incorrect, because not all messages make their way to the channel.<\/p>\n<p>Hi, I\u2019m Rain, and this post is about cancelling async Rust. This post is split into three parts:<\/p>\n<ol>\n<li><a target=\"_blank\" href=\"..\/..\/posts\/cancelling-async-rust\/#1-what-is-cancellation\"><em>What is cancellation?<\/em><\/a> It\u2019s an extremely powerful part of async Rust but also one that is very hard to reason thoroughly about.<\/li>\n<li><a target=\"_blank\" href=\"..\/..\/posts\/cancelling-async-rust\/#2-analyzing-cancellations\"><em>Analyzing cancellations:<\/em><\/a> Going deep into their mechanics and providing some helpful ways to think about them.<\/li>\n<li><a target=\"_blank\" href=\"..\/..\/posts\/cancelling-async-rust\/#3-what-can-be-done\"><em>What can be done?<\/em><\/a> Solutions, including practical guidance, and real bugs we\u2019ve found and fixed in production codebases.<\/li>\n<\/ol>\n<p>Before we begin, I want to lay my cards on the table \u2013 I really love async Rust!<\/p>\n<figure class=\"floatright\"><img decoding=\"async\" src=\"..\/..\/images\/beyond-ctrl-c.jpg\" alt=\"Me speaking at RustConf 2023. Beyond Ctrl-C: The dark corners of Unix signal handling.\"\/><figcaption class=\"center\">Me speaking at RustConf 2023.<\/figcaption><\/figure>\n<ul>\n<li>\n<p>I gave <a target=\"_blank\" href=\"..\/..\/posts\/beyond-ctrl-c-signals\/\">a talk<\/a> at RustConf a couple years ago talking about how async Rust is a great fit for signal handling in complex applications.<\/p>\n<\/li>\n<li>\n<p>I\u2019m also the author of <a target=\"_blank\" href=\"https:\/\/nexte.st\/\">cargo-nextest<\/a>, a next-generation test runner for Rust, where async Rust is the best way I know of to express some really complex algorithms that I wouldn\u2019t know how to express otherwise. I wrote <a target=\"_blank\" href=\"..\/..\/posts\/nextest-and-tokio\/\">a blog post<\/a> about this a few years ago.<\/p>\n<\/li>\n<\/ul>\n<p>Now, I work at <a target=\"_blank\" href=\"https:\/\/oxide.computer\/\">Oxide Computer Company<\/a>, where we make <em>cloud-in-a-box computers<\/em>. We make vertically integrated systems where you provide power and networking on one end, and the software you want to run on the other end, and we take care of everything in between.<\/p>\n<p>Of course, we use Rust everywhere, and in particular we use async Rust extensively for our higher-level software, such as <a target=\"_blank\" href=\"https:\/\/github.com\/oxidecomputer\/crucible\">storage<\/a>, <a target=\"_blank\" href=\"https:\/\/github.com\/oxidecomputer\/maghemite\">networking<\/a> and the <a target=\"_blank\" href=\"https:\/\/github.com\/oxidecomputer\/omicron\/\">customer-facing management API<\/a>. But along the way we\u2019ve encountered a number of issues around async cancellation, and a lot of this post is about what we learned along the way.<\/p>\n<h2 id=\"1-what-is-cancellation\">1. What is cancellation?<a target=\"_blank\" href=\"#1-what-is-cancellation\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h2>\n<p>What does cancellation mean? Logically, a cancellation is exactly what it sounds like: you start some work, and then change your mind and decide to stop doing that work.<\/p>\n<p>As you might imagine this is a useful thing to do:<\/p>\n<ul>\n<li>You may have started a large download or a long network request<\/li>\n<li>Maybe you\u2019ve started reading a file, similar to the <code>head<\/code> command.<\/li>\n<\/ul>\n<p>But then you change your mind: you want to cancel it rather than continue it to completion.<\/p>\n<h3 id=\"cancellations-in-synchronous-rust\">Cancellations in synchronous Rust<a target=\"_blank\" href=\"#cancellations-in-synchronous-rust\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>Before we talk about async Rust, it\u2019s worth thinking about how you\u2019d do cancellations in synchronous Rust.<\/p>\n<p>One option is to have some kind of <em>flag<\/em> you periodically check, maybe stored in an atomic:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"o\">!<\/span><span class=\"n\">should_cancel<\/span><span class=\"p\">.<\/span><span class=\"n\">load<\/span><span class=\"p\">(<\/span><span class=\"n\">Ordering<\/span>::<span class=\"n\">Relaxed<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">expensive_operation<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<ul>\n<li>The code that wishes to perform the cancellation can set that flag.<\/li>\n<li>Then, the code which checks that flag can exit early.<\/li>\n<\/ul>\n<p>This approach is fine for smaller bits of code but doesn\u2019t really scale well to large chunks of code since you\u2019d have to sprinkle these checks everywhere.<\/p>\n<p>A related option, if you\u2019re working with a framework as part of your work, is to <em>panic with a special payload<\/em> of some kind.<\/p>\n<ul>\n<li>If that feels strange to you, you\u2019re not alone! But the <a target=\"_blank\" href=\"https:\/\/github.com\/salsa-rs\/salsa\">Salsa<\/a> framework for incremental computation, used by\u2014among other things\u2014<a target=\"_blank\" href=\"https:\/\/github.com\/rust-lang\/rust-analyzer\">rust-analyzer<\/a>, uses this approach.<\/li>\n<li>Something I learned recently was that this only works on build targets which have a notion of <a target=\"_blank\" href=\"https:\/\/doc.rust-lang.org\/std\/panic\/fn.catch_unwind.html\">panic unwinding<\/a>, or being able to bubble up the panic. Not all platforms support this, and in particular, <a target=\"_blank\" href=\"https:\/\/webassembly.org\/\">Wasm<\/a> doesn\u2019t. This means that Salsa cancellations don\u2019t work if you build rust-analyzer for Wasm.<\/li>\n<\/ul>\n<p>A third option is to <em>kill the whole process<\/em>. This is a <em>very heavyweight<\/em> approach, but an effective one in case you spawn processes to do your work.<\/p>\n<p>Rather than kill the whole process, can you kill a single thread?<\/p>\n<ul>\n<li>While <a target=\"_blank\" href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/api\/processthreadsapi\/nf-processthreadsapi-terminatethread\">some OSes have APIs<\/a> to perform this action, they tend to warn very strongly against it. That\u2019s because in general, most code is <a target=\"_blank\" href=\"..\/..\/posts\/nextest-process-per-test\/#appendix\">just not ready<\/a> for a thread disappearing from underneath.<\/li>\n<li>In particular, thread killing is not permitted by safe Rust, since it can cause serious corruption. For example, Rust mutexes would likely stay locked forever.<\/li>\n<\/ul>\n<p>All of these options are suboptimal or of limited use in some way. In general, the way I think about it is that there isn\u2019t a <em>universal protocol<\/em> for cancellation in synchronous Rust.<\/p>\n<p>In contrast, there <em>is<\/em> such a protocol in async Rust, and in fact cancellations are extraordinarily easy to perform in async Rust.<\/p>\n<p>Why is that so? To understand that, let\u2019s look at what a future is.<\/p>\n<h3 id=\"what-is-a-future\">What is a future?<a target=\"_blank\" href=\"#what-is-a-future\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>Here\u2019s a simple example of a future:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ This creates a state machine.\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">async<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">request<\/span><span class=\"p\">().<\/span><span class=\"k\">await<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">process<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">).<\/span><span class=\"k\">await<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">};<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ Nothing executes yet. `future` is just a struct in memory.\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>In this future, you first perform a network request which returns some data, and then you process it.<\/p>\n<p>The Rust compiler looks at this future and generates a state machine, which is just a struct or enum in memory:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ The compiler generates something like:\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"k\">enum<\/span> <span class=\"nc\">MyFuture<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">Start<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">WaitingForNetwork<\/span><span class=\"p\">(<\/span><span class=\"n\">NetworkFuture<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">WaitingForProcess<\/span><span class=\"p\">(<\/span><span class=\"n\">ProcessFuture<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">Data<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">Done<\/span><span class=\"p\">(<\/span><span class=\"nb\">Result<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ It's just data, no running code!\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If you\u2019ve written async Rust before the <code>async<\/code> and <code>await<\/code> keywords, you\u2019ve probably written code like it by hand. It\u2019s basically just an enum describing all the possible states the future can be in.<\/p>\n<p>The compiler also generates an implementation of <a target=\"_blank\" href=\"https:\/\/doc.rust-lang.org\/std\/future\/trait.Future.html\">the <code>Future<\/code> trait<\/a> for this future:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">impl<\/span><span class=\"w\"> <\/span><span class=\"n\">Future<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">MyFuture<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">poll<\/span><span class=\"p\">(<\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nc\">Poll<\/span><span class=\"o\"><span class=\"bp\">Self<\/span>::<span class=\"n\">Output<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"bp\">self<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">            <\/span><span class=\"n\">Start<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">            <\/span><span class=\"n\">WaitingForNetwork<\/span><span class=\"p\">(<\/span><span class=\"n\">fut<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">            <\/span><span class=\"c1\">\/\/ etc\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"w\">        <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>and when you call <code>.await<\/code> on the future, it gets translated down to this underlying <code>poll<\/code> function. It is only when <code>await<\/code> or this <code>poll<\/code> function is called that something actually happens.<\/p>\n<p>Note that this is diametrically opposed to how async works in other languages like Go, JavaScript, or C#. In those languages, when you create a future to await on, it starts doing its thing, immediately, in the background:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-js\" data-lang=\"js\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ JavaScript: starts running immediately\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"kr\">const<\/span> <span class=\"nx\">promise<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">fetch<\/span><span class=\"p\">(<\/span><span class=\"s1\">'\/api\/data'<\/span><span class=\"p\">);<\/span>\n<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>That\u2019s regardless of whether you await it or not.<\/p>\n<p>In Rust, this <code>get<\/code> call does nothing until you actually call <code>.await<\/code> on it:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ Rust: just data, does nothing!\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">reqwest<\/span>::<span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s\">\"\/api\/data\"<\/span><span class=\"p\">);<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>I know I sound a bit like a broken record here, but if you can take away one thing from this post, it would be that futures are passive, and completely inert until awaited or polled.<\/p>\n<h3 id=\"the-universal-protocol\">The universal protocol<a target=\"_blank\" href=\"#the-universal-protocol\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>So what does the universal protocol to cancel futures look like? It is simply to <em>drop<\/em> the future, or to not await it, or poll it any more. Since a future is just a state machine, you can throw it away at any time the poll function isn\u2019t actively being called.<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">some_async_work<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"nb\">drop<\/span><span class=\"p\">(<\/span><span class=\"n\">future<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ cancelled\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>The upshot of all this is that any Rust future can be cancelled at any await point.<\/p>\n<p>Given how hard cancellation tends to be in synchronous environments, the ability to easily cancel futures in async Rust is extraordinarily powerful\u2014in many ways its greatest strength!<\/p>\n<p>But there is a flip side, which is that cancelling futures is far, far too easy. This is for two reasons.<\/p>\n<ol>\n<li>\n<p>First, it\u2019s just way too easy to quietly drop a future. As we\u2019re going to see, there are all kinds of code patterns that lead to silently dropping futures.<\/p>\n<\/li>\n<li>\n<p>Now this wouldn\u2019t be so bad, if not for the second reason: that cancellation of parent futures propagates down to child futures.<\/p>\n<p>Because of Rust\u2019s <a target=\"_blank\" href=\"https:\/\/doc.rust-lang.org\/book\/ch04-00-understanding-ownership.html\">single ownership model<\/a>, child futures are owned by parent ones. If a parent future is dropped or cancelled, the same happens to the child.<\/p>\n<p>To figure out whether a child future\u2019s cancellation can cause issues, you have to look at its parent, and grandparent, and so on. Reasoning about cancellation becomes a very complicated <em>non-local operation<\/em>.<\/p>\n<\/li>\n<\/ol>\n<h2 id=\"2-analyzing-cancellations\">2. Analyzing cancellations<a target=\"_blank\" href=\"#2-analyzing-cancellations\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h2>\n<p>I\u2019m going to cover some examples in a bit, but before we do that I want to talk about a couple terms, some of which you might have seen references to already.<\/p>\n<h3 id=\"cancel-safety-and-cancel-correctness\">Cancel safety and cancel correctness<a target=\"_blank\" href=\"#cancel-safety-and-cancel-correctness\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>The first term is <strong>cancel safety<\/strong>. You might have seen mentions of this in the Tokio documentation. Cancel safety, as generally defined, means the property of a future that can be cancelled (i.e. dropped) without any side effects.<\/p>\n<p>For example, a <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/time\/fn.sleep.html\">Tokio sleep future<\/a> is cancel safe: you can just stop waiting on the sleep and it\u2019s completely fine.<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">tokio<\/span>::<span class=\"n\">time<\/span>::<span class=\"n\">sleep<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"nb\">drop<\/span><span class=\"p\">(<\/span><span class=\"n\">future<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ this has no side effects\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>An example of a future that is not cancel safe is <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/sync\/mpsc\/struct.Sender.html#method.send\">Tokio\u2019s MPSC send<\/a>, which sends a message over a channel:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">message<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">sender<\/span><span class=\"p\">.<\/span><span class=\"n\">send<\/span><span class=\"p\">(<\/span><span class=\"n\">message<\/span><span class=\"p\">);<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"nb\">drop<\/span><span class=\"p\">(<\/span><span class=\"n\">future<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ message is lost!\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If this future is dropped, the message is lost forever.<\/p>\n<p>The important thing is that cancel safety is a <em>local property<\/em> of an <em>individual future<\/em>.<\/p>\n<p>But cancel safety is not all that one needs to care about. What actually matters is the context the cancellation happens in, or in other words whether the cancellation actually causes some kind of larger property in the system to be violated.<\/p>\n<ul>\n<li>For example, if you drop a future which sends a message, but for whatever reason you don\u2019t care about the message any more, it\u2019s not really a bug!<\/li>\n<\/ul>\n<p>To capture this I tend to use a different term called <strong>cancel correctness<\/strong>, which I define as a <em>global property<\/em> of <em>system correctness<\/em> in the face of cancellations. (This isn\u2019t a standard term, but it\u2019s a framing I\u2019ve found really helpful in understanding cancellations.)<\/p>\n<p>When is cancel correctness violated? It requires three things:<\/p>\n<ol>\n<li>\n<p><em>The system has a cancel-unsafe future somewhere within it.<\/em> As we\u2019ll see, many APIs that are cancel-unsafe can be reworked to be cancel-safe. If there aren\u2019t any cancel-unsafe futures in the system, then the system is cancel correct.<\/p>\n<\/li>\n<li>\n<p><em>A cancel-unsafe future is actually cancelled.<\/em> This may sound a bit trivial, but if cancel-unsafe futures are always run to completion, then the system can\u2019t have cancel correctness bugs.<\/p>\n<\/li>\n<li>\n<p><em>Cancelling the future violates some property of a system.<\/em> This could be data loss as with <code>Sender::send<\/code>, some kind of invariant violation, or some kind of cleanup that must be performed but isn\u2019t.<\/p>\n<\/li>\n<\/ol>\n<p>So a lot of making Rust async robust is about trying to tackle one of these three things.<\/p>\n<p>I want to zoom in for a second on invariant violations and talk about an example of a Tokio API that is very prone to cancel correctness issues: <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/sync\/struct.Mutex.html\">Tokio mutexes<\/a>.<\/p>\n<h3 id=\"the-pain-of-tokio-mutexes\">The pain of Tokio mutexes<a target=\"_blank\" href=\"#the-pain-of-tokio-mutexes\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>The way Tokio mutexes work is: you create a mutex, you lock it which gives you mutable access to the data underneath, and then you unlock it by releasing the mutex.<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">guard<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">mutex<\/span><span class=\"p\">.<\/span><span class=\"n\">lock<\/span><span class=\"p\">().<\/span><span class=\"k\">await<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ Access guard.data, protected by the mutex...\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"nb\">drop<\/span><span class=\"p\">(<\/span><span class=\"n\">guard<\/span><span class=\"p\">);<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If you look at the <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/sync\/struct.Mutex.html#method.lock\"><code>lock<\/code> function\u2019s documentation<\/a>, in the \u201ccancel safety\u201d section it says:<\/p>\n<blockquote>\n<p>This method uses a queue to fairly distribute locks in the order they were requested. Cancelling a call to lock makes you lose your place in the queue.<\/p>\n<\/blockquote>\n<p>Okay, so not totally cancel safe, but the only kind of unsafety is fairness, which doesn\u2019t sound too bad.<\/p>\n<p>But the problems lie in what you actually do with the mutex. In practice, most uses of mutexes are in order to <em>temporarily violate invariants<\/em> that are otherwise upheld when a lock isn\u2019t held.<\/p>\n<p>I\u2019ll use a real world example of a cancel correctness bug that we found at my job at Oxide: we had code to manage a bunch of data sent over by our computers, which we call sleds. The shared state was guarded by a mutex, and a typical operation was:<\/p>\n<ol>\n<li>Obtain a lock on the mutex.<\/li>\n<li>Obtain the sled-specific data by value, moving it to an invalid <code>None<\/code> state.<\/li>\n<li>Perform an action.<\/li>\n<li>Set the sled-specific data back to the next valid state.<\/li>\n<\/ol>\n<p>Here\u2019s a rough sketch of what that looks like:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">guard<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">mutex<\/span><span class=\"p\">.<\/span><span class=\"n\">lock<\/span><span class=\"p\">().<\/span><span class=\"k\">await<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ guard.data is Option<t>: Some to begin with\n<\/t><\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">guard<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"p\">.<\/span><span class=\"n\">take<\/span><span class=\"p\">();<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ guard.data is now None\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">new_data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">process_data<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">);<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"n\">guard<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">Some<\/span><span class=\"p\">(<\/span><span class=\"n\">new_data<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ guard.data is Some again\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>This is all well and good, but the problem is that the action being performed actually had an await point within it:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">guard<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">mutex<\/span><span class=\"p\">.<\/span><span class=\"n\">lock<\/span><span class=\"p\">().<\/span><span class=\"k\">await<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ guard.data is Option<t>: Some to begin with\n<\/t><\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">guard<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"p\">.<\/span><span class=\"n\">take<\/span><span class=\"p\">();<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ guard.data is now None\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ DANGER: cancellation here leaves data in None state!\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">new_data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">process_data<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">).<\/span><span class=\"k\">await<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"n\">guard<\/span><span class=\"p\">.<\/span><span class=\"n\">data<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">Some<\/span><span class=\"p\">(<\/span><span class=\"n\">new_data<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ guard.data is Some again\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If the code that operated on the mutex got cancelled at that await point, then the data would be stuck in the invalid <code>None<\/code> state. Not great!<\/p>\n<p>And keep in mind the non-local reasoning aspect: when doing this analysis, you need to look at the whole chain of callers.<\/p>\n<h3 id=\"cancellation-patterns\">Cancellation patterns<a target=\"_blank\" href=\"#cancellation-patterns\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>Now that we\u2019ve talked about some of the bad things that can happen during cancellations, it\u2019s worth asking what kinds of code patterns lead to futures being cancelled.<\/p>\n<p>The most straightforward example, and maybe a bit of a silly one, is that you create a future but simply forget to call <code>.await<\/code> on it.<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"n\">some_async_work<\/span><span class=\"p\">();<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ missing .await\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>Now Rust actually warns you if you don\u2019t call <code>.await<\/code> on the future:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-text\" data-lang=\"text\"><span class=\"line\"><span class=\"cl\">warning: unused implementer of `Future` that must be used\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   |\n<\/span><\/span><span class=\"line\"><span class=\"cl\">11 |     some_async_work();\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   |     ^^^^^^^^^^^^^^^^^\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   |\n<\/span><\/span><span class=\"line\"><span class=\"cl\">   = note: futures do nothing unless you `.await` or poll them\n<\/span><\/span><\/code><\/pre>\n<\/div>\n<p>But a code pattern I\u2019ve sometimes made mistakes with is that the future returns a <code>Result<\/code>, and you want to ignore the result so you assign it to an underscore like so:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">_<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">some_async_work<\/span><span class=\"p\">();<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ future returns Result\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If I forget to call <code>.await<\/code> on the future, Rust doesn\u2019t warn me about it at all, and then I\u2019m left scratching my head about why this code didn\u2019t run. I know this sounds really silly and basic, but I\u2019ve made this mistake a bunch of times.<\/p>\n<p><em>(After my talk, it was pointed out to me that Clippy 1.67 and above have a <a target=\"_blank\" href=\"https:\/\/rust-lang.github.io\/rust-clippy\/master\/index.html#let_underscore_future\"><code>let_underscore_future<\/code><\/a> warn-by-default lint for this. Hooray!)<\/em><\/p>\n<p>Another example of futures being cancelled is <code>try<\/code> operations, such as Tokio\u2019s <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/macro.try_join.html\"><code>try_join<\/code> macro<\/a>. For example:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">async<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">do_stuff_async<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nb\">Result<\/span><span class=\"o\"><span class=\"p\">(),<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"nb\">'static<\/span><span class=\"w\"> <\/span><span class=\"kt\">str<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"c1\">\/\/ async work\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"k\">async<\/span><span class=\"w\"> <\/span><span class=\"k\">fn<\/span> <span class=\"nf\">more_async_work<\/span><span class=\"p\">()<\/span><span class=\"w\"> <\/span>-&gt; <span class=\"nb\">Result<\/span><span class=\"o\"><span class=\"p\">(),<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"nb\">'static<\/span><span class=\"w\"> <\/span><span class=\"kt\">str<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"c1\">\/\/ more here\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">res<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">tokio<\/span>::<span class=\"fm\">try_join!<\/span><span class=\"p\">(<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">do_stuff_async<\/span><span class=\"p\">(),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">more_async_work<\/span><span class=\"p\">(),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">);<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"c1\">\/\/ ...\n<\/span><\/span><\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If you call <code>try_join<\/code> with a bunch of futures, and all of them succeed, it\u2019s all good. But if one of them fails, the rest simply get cancelled.<\/p>\n<p>In fact, at Oxide we had a pretty bad bug around this: we had code to stop a bunch of services, all expressed as futures. We used <code>try_join<\/code>:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"fm\">try_join!<\/span><span class=\"p\">(<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">stop_service_a<\/span><span class=\"p\">(),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">stop_service_b<\/span><span class=\"p\">(),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">stop_service_c<\/span><span class=\"p\">(),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">)<\/span><span class=\"o\">?<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>If one of these operations failed for whatever reason, we would stop running the code to wait for the other services to exit. Oops!<\/p>\n<p>But perhaps the most well-known source of cancellations is Tokio\u2019s <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/macro.select.html\"><code>select<\/code> macro<\/a>. Select is this incredibly beautiful operation. It is called with a set of futures, and it drives all of them forward concurrently:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"n\">tokio<\/span>::<span class=\"fm\">select!<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">result1<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">future1<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"n\">handle_result1<\/span><span class=\"p\">(<\/span><span class=\"n\">result1<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">result2<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">future2<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"n\">handle_result2<\/span><span class=\"p\">(<\/span><span class=\"n\">result2<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>Each future has a code block associated with it (above, <code>handle_result1<\/code> and <code>handle_result2<\/code>). If one of the futures completes, the corresponding code block is called. But also, all of the other futures are always cancelled!<\/p>\n<p>For a variety of reasons, select statements in general, and <em>select loops<\/em> in particular, are particularly prone to cancel correctness issues. So a lot of the documentation about cancel safety talks about select loops. But I want to emphasize here that select is not the <em>only<\/em> source of cancellations, just a particularly <em>notable<\/em> one.<\/p>\n<h2 id=\"3-what-can-be-done\">3. What can be done?<a target=\"_blank\" href=\"#3-what-can-be-done\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h2>\n<p>So, now that we\u2019ve looked at all of these issues with cancellations, what can be done about it?<\/p>\n<p>First, I want to break the bad news to you \u2013 there is <em>no general, fully reliable solution for this<\/em> in Rust today. But in our experience there are a few patterns that have been successful at reducing the likelihood of cancellation bugs.<\/p>\n<p>Going back to our definition of cancel correctness, there are three prongs all of which come together to produce a bug:<\/p>\n<ul>\n<li>A cancel-unsafe future exists<\/li>\n<li>This cancel-unsafe future is cancelled<\/li>\n<li>The cancellation violates a system property<\/li>\n<\/ul>\n<p>Most solutions we\u2019ve come up with try and tackle one of these prongs.<\/p>\n<h3 id=\"making-futures-cancel-safe\">Making futures cancel-safe<a target=\"_blank\" href=\"#making-futures-cancel-safe\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>Let\u2019s look at the first prong: the system has a cancel-unsafe future somewhere in it. Can we use code patterns to make futures be cancel-safe? It turns out we can! I\u2019ll give you two examples here.<\/p>\n<p>The first is MPSC sends. Let\u2019s come back to the example from earlier where we would lose messages entirely:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">msg<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">next_message<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"n\">timeout<\/span><span class=\"p\">(<\/span><span class=\"n\">Duration<\/span>::<span class=\"n\">from_secs<\/span><span class=\"p\">(<\/span><span class=\"mi\">5<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"n\">tx<\/span><span class=\"p\">.<\/span><span class=\"n\">send<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">)).<\/span><span class=\"k\">await<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"sent successfully\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">return<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"no space for 5 seconds\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>Can we find a way to make this cancel safe?<\/p>\n<p>In this case, yes, and we do so by breaking up the operation into two parts:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">msg<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">next_message<\/span><span class=\"p\">();<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"k\">match<\/span><span class=\"w\"> <\/span><span class=\"n\">timeout<\/span><span class=\"p\">(<\/span><span class=\"n\">Duration<\/span>::<span class=\"n\">from_secs<\/span><span class=\"p\">(<\/span><span class=\"mi\">5<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"n\">tx<\/span><span class=\"p\">.<\/span><span class=\"n\">reserve<\/span><span class=\"p\">()).<\/span><span class=\"k\">await<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">            <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"n\">permit<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">permit<\/span><span class=\"p\">.<\/span><span class=\"n\">send<\/span><span class=\"p\">(<\/span><span class=\"n\">msg<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"k\">break<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">            <\/span><span class=\"nb\">Ok<\/span><span class=\"p\">(<\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">return<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">            <\/span><span class=\"nb\">Err<\/span><span class=\"p\">(<\/span><span class=\"n\">_<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"fm\">println!<\/span><span class=\"p\">(<\/span><span class=\"s\">\"no space for 5 seconds\"<\/span><span class=\"p\">),<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<ul>\n<li>The first component is the operation to <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/sync\/mpsc\/struct.Sender.html#method.reserve\">reserve a permit<\/a> or slot in the channel. This is an initial async operation that\u2019s cancel-safe.<\/li>\n<li>The second is to actually <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/sync\/mpsc\/struct.Permit.html#method.send\">send the message<\/a>, which is an operation that becomes infallible.<\/li>\n<\/ul>\n<p>(I want to put an asterisk here that reserve is not entirely cancel-safe, since Tokio\u2019s MPSC follows a first-in-first-out pattern and dropping the future means losing your place in line. Keep this in mind for now.)<\/p>\n<hr\/>\n<p>The second is with <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/io\/trait.AsyncWrite.html\">Tokio\u2019s <code>AsyncWrite<\/code><\/a>.<\/p>\n<p>If you\u2019ve written synchronous Rust you\u2019re probably familiar with <a target=\"_blank\" href=\"https:\/\/doc.rust-lang.org\/std\/io\/trait.Write.html#method.write_all\">the <code>write_all<\/code> method<\/a>, which writes an entire buffer out:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">use<\/span><span class=\"w\"> <\/span><span class=\"n\">std<\/span>::<span class=\"n\">io<\/span>::<span class=\"n\">Write<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span>: <span class=\"kp\">&amp;<\/span><span class=\"p\">[<\/span><span class=\"kt\">u8<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"n\">writer<\/span><span class=\"p\">.<\/span><span class=\"n\">write_all<\/span><span class=\"p\">(<\/span><span class=\"n\">buffer<\/span><span class=\"p\">)<\/span><span class=\"o\">?<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>In synchronous Rust, this is a great API. But within async Rust, the <code>write_all<\/code> pattern is absolutely not cancel safe! If the future is dropped before completion, you have no idea how much of this buffer was written out.<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">use<\/span><span class=\"w\"> <\/span><span class=\"n\">tokio<\/span>::<span class=\"n\">io<\/span>::<span class=\"n\">AsyncWriteExt<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span>: <span class=\"kp\">&amp;<\/span><span class=\"p\">[<\/span><span class=\"kt\">u8<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"n\">writer<\/span><span class=\"p\">.<\/span><span class=\"n\">write_all<\/span><span class=\"p\">(<\/span><span class=\"n\">buffer<\/span><span class=\"p\">).<\/span><span class=\"k\">await<\/span><span class=\"o\">?<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ Not cancel-safe!\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>But there\u2019s an alternative API that is cancel-safe, called <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/io\/trait.AsyncWriteExt.html#method.write_all_buf\"><code>write_all_buf<\/code><\/a>. This API is carefully designed to enable the reporting of partial progress, and it doesn\u2019t just accept a <em>buffer<\/em>, but rather something that looks like a cursor on top of it:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"k\">use<\/span><span class=\"w\"> <\/span><span class=\"n\">tokio<\/span>::<span class=\"n\">io<\/span>::<span class=\"n\">AsyncWriteExt<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span>: <span class=\"nc\">io<\/span>::<span class=\"n\">Cursor<\/span><span class=\"o\"><span class=\"p\">[<\/span><span class=\"kt\">u8<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"cm\">\/* ... *\/<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"n\">writer<\/span><span class=\"p\">.<\/span><span class=\"n\">write_all_buf<\/span><span class=\"p\">(<\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"n\">buffer<\/span><span class=\"p\">).<\/span><span class=\"k\">await<\/span><span class=\"o\">?<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>When part of the buffer is written out, the cursor is advanced by that number of bytes. So if you call <code>write_all_buf<\/code> in a loop, you\u2019ll be resuming from this partial progress, which works great.<\/p>\n<h3 id=\"not-cancelling-futures\">Not cancelling futures<a target=\"_blank\" href=\"#not-cancelling-futures\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>Going back to the three prongs: the second prong is about actually cancelling futures. What code patterns can be used to not cancel futures? Here are a couple of examples.<\/p>\n<p>The first one is, in a place like a select loop, resume futures rather than cancelling them each time. You\u2019d typically achieve this by <a target=\"_blank\" href=\"https:\/\/doc.rust-lang.org\/std\/pin\/\">pinning a future<\/a>, and then polling a mutable reference to that future. For example:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line hl\"><span class=\"cl\"><span class=\"kd\">let<\/span><span class=\"w\"> <\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nb\">Box<\/span>::<span class=\"n\">pin<\/span><span class=\"p\">(<\/span><span class=\"n\">channel<\/span><span class=\"p\">.<\/span><span class=\"n\">reserve<\/span><span class=\"p\">());<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"k\">loop<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"n\">tokio<\/span>::<span class=\"fm\">select!<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"n\">result<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;<\/span><span class=\"k\">mut<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">break<\/span><span class=\"w\"> <\/span><span class=\"n\">result<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">        <\/span><span class=\"n\">_<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">other_condition<\/span><span class=\"w\"> <\/span><span class=\"o\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">continue<\/span><span class=\"p\">,<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">    <\/span><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\"\/><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>Coming back to our example of MPSC sends, the one asterisk with <code>reserve<\/code> is that cancelling it makes you lose your place in line. Instead, if you pin the <code>reserve<\/code> future and poll a mutable reference to it, you don\u2019t lose your place in line.<\/p>\n<p>(Does the difference here matter? It depends, but you can now have this strategy available to you.)<\/p>\n<hr\/>\n<p>The second example is to use tasks. I mentioned earlier that futures are Rust are diametrically opposed to similar notions in languages like JavaScript. Well, there\u2019s an alternative in async Rust that\u2019s much closer to the JavaScript idea, and that\u2019s <a target=\"_blank\" href=\"https:\/\/docs.rs\/tokio\/latest\/tokio\/task\/\">tasks<\/a>.<\/p>\n<ul>\n<li>Unlike futures which are driven by the caller, tasks are driven by the runtime (such as Tokio).<\/li>\n<li>With Tokio, dropping a handle to a task does not cause it to be cancelled, which means they\u2019re a good place to run cancel-unsafe code.<\/li>\n<\/ul>\n<p>A fun example is that at Oxide, we have an HTTP server called <a target=\"_blank\" href=\"https:\/\/docs.rs\/dropshot\">Dropshot<\/a>. Previously, whenever an HTTP request came in, we\u2019d use a future for it, and drop the future if the TCP connection was closed.<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ Before: Future cancelled on TCP close\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"n\">handle_request<\/span><span class=\"p\">(<\/span><span class=\"n\">req<\/span><span class=\"p\">).<\/span><span class=\"k\">await<\/span><span class=\"p\">;<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<p>This was really bad because future cancellations could happen due to the behavior of not just the parent future, but of a process that was running across a network! This is a rather extreme form of non-local reasoning.<\/p>\n<p>We addressed this by spinning up a task for each HTTP request, and by running the code to completion even if the connection is closed:<\/p>\n<div class=\"highlight\">\n<pre tabindex=\"0\" class=\"chroma\"><code class=\"language-rust\" data-lang=\"rust\"><span class=\"line\"><span class=\"cl\"><span class=\"c1\">\/\/ After: Task runs to completion\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"c1\"\/><span class=\"n\">tokio<\/span>::<span class=\"n\">spawn<\/span><span class=\"p\">(<\/span><span class=\"n\">handle_request<\/span><span class=\"p\">(<\/span><span class=\"n\">req<\/span><span class=\"p\">));<\/span><span class=\"w\">\n<\/span><\/span><\/span><\/code><\/pre>\n<\/div>\n<h3 id=\"systematic-solutions\">Systematic solutions?<a target=\"_blank\" href=\"#systematic-solutions\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h3>\n<p>The last thing I want to say is that <strong>this sucks<\/strong>!<\/p>\n<p>The promise of Rust is that you don\u2019t need to do this kind of non-local reasoning\u2014that you can analyze small bits of code for local correctness, and scale that up to global correctness. Almost everything in Rust, from <code>&amp;<\/code> and <code>&amp;mut<\/code> to <code>unsafe<\/code>, is geared towards making that possible. Future cancellations fly directly in the face of that, and I think they\u2019re probably the <strong>least Rusty part of Rust<\/strong>. This is all really unfortunate.<\/p>\n<p>Can we come up with something more systematic than this kind of ad-hoc reasoning?<\/p>\n<p>There doesn\u2019t exist anything in safe Rust today, but there are a few different ideas people have come up with. I wanted to give a nod to those ideas:<\/p>\n<ul>\n<li><em>Async drop<\/em> would let you run async code when a future is cancelled. This would handle some, though not all, of the cases we discussed today.<\/li>\n<li>There\u2019s also a couple different proposals for what are called <em>linear types<\/em>, where you could force some code to be run on drop, or mark a particular future as non-cancellable (once it\u2019s been created it must be driven to completion).<\/li>\n<\/ul>\n<p>All of these options have really significant implementation challenges, though. This <a target=\"_blank\" href=\"https:\/\/without.boats\/blog\/asynchronous-clean-up\/\">blog post from boats<\/a> covers some of these solutions, and the implementation challenges with them.<\/p>\n<h2 id=\"conclusion\">Conclusion<a target=\"_blank\" href=\"#conclusion\" class=\"hanchor\" arialabel=\"Anchor\">#<\/a><\/h2>\n<p>In this post, we:<\/p>\n<ul>\n<li>Saw that futures are passive<\/li>\n<li>Introduced cancel safety and cancel correctness as concepts<\/li>\n<li>Examined some bugs that can occur with cancellation<\/li>\n<li>Looked at some recommendations you can use to mitigate the downsides of cancellation<\/li>\n<\/ul>\n<p>Some of the recommendations are:<\/p>\n<ul>\n<li>Avoid Tokio mutexes<\/li>\n<li>Rewrite APIs to make futures cancel-safe<\/li>\n<li>Find ways to ensure that cancel-unsafe futures are driven to completion<\/li>\n<\/ul>\n<p>There\u2019s a very deep well of complexity here, a lot more than I can cover in one blog post:<\/p>\n<ul>\n<li>Why are futures passive, anyway?<\/li>\n<li>Cooperative cancellation: cancellation tokens<\/li>\n<li>Actor model as an alternative to Tokio mutexes<\/li>\n<li>Task aborts<\/li>\n<li>Structured concurrency<\/li>\n<li>Relationship to panic safety and mutex poisoning<\/li>\n<\/ul>\n<p>If you\u2019re curious about any of these, check out <a target=\"_blank\" href=\"https:\/\/github.com\/sunshowers\/cancelling-async-rust\">this link<\/a> where I\u2019ve put together a collection of documents and blog posts about these concepts. In particular, I\u2019d recommend reading these two Oxide RFDs:<\/p>\n<p><em>Thank you for reading this post to the end! And thanks to many of my coworkers at Oxide for reviewing the talk and the RFDs linked above, and for suggestions and constructive feedback.<\/em><\/p>\n<\/div>\n\n<br \/><a href=\"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/\">\u5143\u306e\u8a18\u4e8b\u3092\u78ba\u8a8d\u3059\u308b <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"This is an edited, written version of my RustConf 2025 talk about cancellations in async Rust. Like the writte [&hellip;]","protected":false},"author":1,"featured_media":8129,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[],"class_list":["post-8128","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hatena-blog"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Cancelling async Rust \ua78f sunshowers - \u30dd\u30b1\u30b3\u30f3<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/\" \/>\n<meta property=\"og:locale\" content=\"ja_JP\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Cancelling async Rust \ua78f sunshowers - \u30dd\u30b1\u30b3\u30f3\" \/>\n<meta property=\"og:description\" content=\"This is an edited, written version of my RustConf 2025 talk about cancellations in async Rust. Like the writte [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/\" \/>\n<meta property=\"og:site_name\" content=\"\u30dd\u30b1\u30b3\u30f3\" \/>\n<meta property=\"article:published_time\" content=\"2025-10-04T15:22:23+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/pokecon.jp\/job\/wp-content\/uploads\/2025\/10\/cancelling-async-rust-cover-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1453\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"info@pokecon.jp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u57f7\u7b46\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"info@pokecon.jp\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u63a8\u5b9a\u8aad\u307f\u53d6\u308a\u6642\u9593\" \/>\n\t<meta name=\"twitter:data2\" content=\"21\u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/8128\\\/\"},\"author\":{\"name\":\"info@pokecon.jp\",\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/#\\\/schema\\\/person\\\/16c9f07b1ba984d165d9aee259bda997\"},\"headline\":\"Cancelling async Rust \ua78f sunshowers\",\"datePublished\":\"2025-10-04T15:22:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/8128\\\/\"},\"wordCount\":3613,\"image\":{\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/cancelling-async-rust-cover-scaled.jpg\",\"articleSection\":[\"\u306f\u3066\u306a\u30d6\u30ed\u30b0\"],\"inLanguage\":\"ja\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/8128\\\/\",\"url\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/\",\"name\":\"Cancelling async Rust \ua78f sunshowers - \u30dd\u30b1\u30b3\u30f3\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/cancelling-async-rust-cover-scaled.jpg\",\"datePublished\":\"2025-10-04T15:22:23+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/#\\\/schema\\\/person\\\/16c9f07b1ba984d165d9aee259bda997\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#breadcrumb\"},\"inLanguage\":\"ja\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ja\",\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#primaryimage\",\"url\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/cancelling-async-rust-cover-scaled.jpg\",\"contentUrl\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/cancelling-async-rust-cover-scaled.jpg\",\"width\":2560,\"height\":1453},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/sunshowers.io\\\/posts\\\/cancelling-async-rust\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u30db\u30fc\u30e0\",\"item\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Cancelling async Rust \ua78f sunshowers\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/#website\",\"url\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/\",\"name\":\"\u30dd\u30b1\u30b3\u30f3\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ja\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/#\\\/schema\\\/person\\\/16c9f07b1ba984d165d9aee259bda997\",\"name\":\"info@pokecon.jp\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ja\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2b0549cd9f7907c092ca5fbb283baf72337f235726e4b46fa39ec0b701ac2fe2?s=96&d=wavatar&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2b0549cd9f7907c092ca5fbb283baf72337f235726e4b46fa39ec0b701ac2fe2?s=96&d=wavatar&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/2b0549cd9f7907c092ca5fbb283baf72337f235726e4b46fa39ec0b701ac2fe2?s=96&d=wavatar&r=g\",\"caption\":\"info@pokecon.jp\"},\"url\":\"https:\\\/\\\/pokecon.jp\\\/job\\\/author\\\/infopokecon-jp\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Cancelling async Rust \ua78f sunshowers - \u30dd\u30b1\u30b3\u30f3","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/","og_locale":"ja_JP","og_type":"article","og_title":"Cancelling async Rust \ua78f sunshowers - \u30dd\u30b1\u30b3\u30f3","og_description":"This is an edited, written version of my RustConf 2025 talk about cancellations in async Rust. Like the writte [&hellip;]","og_url":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/","og_site_name":"\u30dd\u30b1\u30b3\u30f3","article_published_time":"2025-10-04T15:22:23+00:00","og_image":[{"width":2560,"height":1453,"url":"https:\/\/pokecon.jp\/job\/wp-content\/uploads\/2025\/10\/cancelling-async-rust-cover-scaled.jpg","type":"image\/jpeg"}],"author":"info@pokecon.jp","twitter_card":"summary_large_image","twitter_misc":{"\u57f7\u7b46\u8005":"info@pokecon.jp","\u63a8\u5b9a\u8aad\u307f\u53d6\u308a\u6642\u9593":"21\u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#article","isPartOf":{"@id":"https:\/\/pokecon.jp\/job\/8128\/"},"author":{"name":"info@pokecon.jp","@id":"https:\/\/pokecon.jp\/job\/#\/schema\/person\/16c9f07b1ba984d165d9aee259bda997"},"headline":"Cancelling async Rust \ua78f sunshowers","datePublished":"2025-10-04T15:22:23+00:00","mainEntityOfPage":{"@id":"https:\/\/pokecon.jp\/job\/8128\/"},"wordCount":3613,"image":{"@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#primaryimage"},"thumbnailUrl":"https:\/\/pokecon.jp\/job\/wp-content\/uploads\/2025\/10\/cancelling-async-rust-cover-scaled.jpg","articleSection":["\u306f\u3066\u306a\u30d6\u30ed\u30b0"],"inLanguage":"ja"},{"@type":"WebPage","@id":"https:\/\/pokecon.jp\/job\/8128\/","url":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/","name":"Cancelling async Rust \ua78f sunshowers - \u30dd\u30b1\u30b3\u30f3","isPartOf":{"@id":"https:\/\/pokecon.jp\/job\/#website"},"primaryImageOfPage":{"@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#primaryimage"},"image":{"@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#primaryimage"},"thumbnailUrl":"https:\/\/pokecon.jp\/job\/wp-content\/uploads\/2025\/10\/cancelling-async-rust-cover-scaled.jpg","datePublished":"2025-10-04T15:22:23+00:00","author":{"@id":"https:\/\/pokecon.jp\/job\/#\/schema\/person\/16c9f07b1ba984d165d9aee259bda997"},"breadcrumb":{"@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#breadcrumb"},"inLanguage":"ja","potentialAction":[{"@type":"ReadAction","target":["https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/"]}]},{"@type":"ImageObject","inLanguage":"ja","@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#primaryimage","url":"https:\/\/pokecon.jp\/job\/wp-content\/uploads\/2025\/10\/cancelling-async-rust-cover-scaled.jpg","contentUrl":"https:\/\/pokecon.jp\/job\/wp-content\/uploads\/2025\/10\/cancelling-async-rust-cover-scaled.jpg","width":2560,"height":1453},{"@type":"BreadcrumbList","@id":"https:\/\/sunshowers.io\/posts\/cancelling-async-rust\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u30db\u30fc\u30e0","item":"https:\/\/pokecon.jp\/job\/"},{"@type":"ListItem","position":2,"name":"Cancelling async Rust \ua78f sunshowers"}]},{"@type":"WebSite","@id":"https:\/\/pokecon.jp\/job\/#website","url":"https:\/\/pokecon.jp\/job\/","name":"\u30dd\u30b1\u30b3\u30f3","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/pokecon.jp\/job\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ja"},{"@type":"Person","@id":"https:\/\/pokecon.jp\/job\/#\/schema\/person\/16c9f07b1ba984d165d9aee259bda997","name":"info@pokecon.jp","image":{"@type":"ImageObject","inLanguage":"ja","@id":"https:\/\/secure.gravatar.com\/avatar\/2b0549cd9f7907c092ca5fbb283baf72337f235726e4b46fa39ec0b701ac2fe2?s=96&d=wavatar&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/2b0549cd9f7907c092ca5fbb283baf72337f235726e4b46fa39ec0b701ac2fe2?s=96&d=wavatar&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/2b0549cd9f7907c092ca5fbb283baf72337f235726e4b46fa39ec0b701ac2fe2?s=96&d=wavatar&r=g","caption":"info@pokecon.jp"},"url":"https:\/\/pokecon.jp\/job\/author\/infopokecon-jp\/"}]}},"_links":{"self":[{"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/posts\/8128","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/comments?post=8128"}],"version-history":[{"count":1,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/posts\/8128\/revisions"}],"predecessor-version":[{"id":8130,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/posts\/8128\/revisions\/8130"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/media\/8129"}],"wp:attachment":[{"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/media?parent=8128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/categories?post=8128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pokecon.jp\/job\/wp-json\/wp\/v2\/tags?post=8128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}