{"id":29675799,"url":"https://github.com/oxidecomputer/cancel-safe-futures","last_synced_at":"2025-07-22T23:38:48.121Z","repository":{"id":182433123,"uuid":"656402782","full_name":"oxidecomputer/cancel-safe-futures","owner":"oxidecomputer","description":"Alternative future adapters that provide cancel safety.","archived":false,"fork":false,"pushed_at":"2025-06-26T08:43:33.000Z","size":8027,"stargazers_count":64,"open_issues_count":8,"forks_count":2,"subscribers_count":22,"default_branch":"main","last_synced_at":"2025-06-26T09:39:41.356Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oxidecomputer.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-06-20T22:15:36.000Z","updated_at":"2025-06-04T12:20:03.000Z","dependencies_parsed_at":"2024-01-14T10:47:34.592Z","dependency_job_id":"4e8d1671-a1b5-4799-8874-7c9ec3efec61","html_url":"https://github.com/oxidecomputer/cancel-safe-futures","commit_stats":null,"previous_names":["oxidecomputer/cancel-safe-futures"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/oxidecomputer/cancel-safe-futures","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fcancel-safe-futures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fcancel-safe-futures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fcancel-safe-futures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fcancel-safe-futures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxidecomputer","download_url":"https://codeload.github.com/oxidecomputer/cancel-safe-futures/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidecomputer%2Fcancel-safe-futures/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266591233,"owners_count":23953082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-22T23:38:45.468Z","updated_at":"2025-07-22T23:38:48.096Z","avatar_url":"https://github.com/oxidecomputer.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cancel-safe-futures\n\n[![cancel-safe-futures on crates.io](https://img.shields.io/crates/v/cancel-safe-futures)](https://crates.io/crates/cancel-safe-futures)\n[![Documentation (latest release)](https://img.shields.io/badge/docs-latest%20version-brightgreen.svg)](https://docs.rs/cancel-safe-futures)\n[![Documentation (main)](https://img.shields.io/badge/docs-main-brightgreen)](https://oxidecomputer.github.io/cancel-safe-futures/rustdoc/cancel_safe_futures/)\n[![License](https://img.shields.io/badge/license-Apache-green.svg)](LICENSE-APACHE)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE-MIT)\n\nAlternative futures adapters that are more cancellation-aware.\n\n## What is this crate?\n\nThis crate solves a few related but distinct problems:\n\n### 1. Cancel-safe futures adapters\n\nThe [`futures`](https://docs.rs/futures/latest/futures/) library contains many adapters that\nmake writing asynchronous Rust code more pleasant. However, some of those combinators make it\nhard to write code that can withstand cancellation in the case of timeouts, `select!` branches\nor similar.\n\nFor a more detailed explanation, see the documentation for `SinkExt::reserve`.\n\n#### Example\n\nAttempt to send an item in a loop with a timeout:\n\n```rust\nuse cancel_safe_futures::prelude::*;\nuse std::time::Duration;\n\nlet mut my_sink = /* ... */;\n\n// This item is stored here and will be set to None once the loop exits successfully.\nlet mut item = Some(\"hello\".to_owned());\nlet do_flush = false;\n\nwhile item.is_some() {\n    match tokio::time::timeout(Duration::from_secs(10), my_sink.reserve()).await {\n        Ok(Ok(permit)) =\u003e {\n            let item = item.take().unwrap();\n            if !do_flush {\n                // permit.feed() feeds the item into the sink without flushing\n                // the sink afterwards. This is a synchronous method.\n                permit.feed(item)?;\n            } else {\n                // Alternatively, permit.send() writes the item into the sink\n                // synchronously, then returns a future which can be awaited to\n                // flush the sink.\n                permit.send(item)?.await?;\n            }\n        }\n        Ok(Err(error)) =\u003e return Err(error),\n        Err(timeout_error) =\u003e continue,\n    }\n}\n\n```\n\n### 2. `then_try` adapters that don't perform cancellations\n\nThe futures and tokio libraries come with a number of `try_` adapters and macros, for example\n`tokio::try_join!`. These adapters have the property that if one of the futures under\nconsideration fails, all other futures are cancelled.\n\nThis is not always desirable and has led to correctness bugs (e.g. [omicron PR\n3707](https://github.com/oxidecomputer/omicron/pull/3707)). To address this issue, this crate\nprovides a set of `then_try` adapters and macros that behave like their `try_` counterparts,\nexcept that if one or more of the futures errors out, the others will still be run to\ncompletion.\n\nThe `then_try` family includes:\n\n* `join_then_try`: similar to `tokio::try_join`.\n* `future::join_all_then_try`: similar to `futures::future::try_join_all`.\n* `TryStreamExt`: contains alternative extension methods to `futures::stream::TryStreamExt`,\n  such as `collect_then_try`.\n\n#### Example\n\nFor a detailed example, see the documentation for the `join_then_try` macro.\n\n### 3. Cancel-safe mutexes\n\nThe `tokio::sync::Mutex` shipped with Tokio has resulted in many bugs in practice,\nparticularly around cancellations.\n\nThis crate provides an alternative mutex, `sync::Mutex`, that does not have those pitfalls.\nFor more, see the documentation for `sync::Mutex`.\n\n### 4. Cooperative cancellation\n\nExecutors like Tokio support forcible cancellation for async tasks via facilities like\n`tokio::task::JoinHandle::abort`. However, this can cause cancellations at any arbitrary await\npoint. If the future is in the middle of cancel-unsafe code, this can cause invariant violations\nor other issues.\n\nInstead, async cancellation can be done cooperatively: code can check for cancellation\nexplicitly via `tokio::select!`. This crate provides the `coop_cancel` module that can be\nused to accomplish that goal.\n\n#### Example\n\nFor a detailed example, see the documentation for `coop_cancel`.\n\n## Notes\n\nThis library is not complete: adapters and macros are added on an as-needed basis. If you need\nan adapter that is not yet implemented, please open an issue or a pull request.\n\n## Optional features\n\n* `macros` (enabled by default): Enables macros.\n* `std` (enabled by default): Enables items that depend on `std`, including items that depend on\n  `alloc`.\n* `alloc` (enabled by default): Enables items that depend on `alloc`.\n* `parking_lot`: Switches to `parking_lot`'s mutexes.\n\nNo-std users must turn off default features while importing this crate.\n\n## License\n\nThis project is available under the terms of either the [Apache 2.0 license](LICENSE-APACHE) or the [MIT\nlicense](LICENSE-MIT).\n\nPortions derived from [futures-rs](https://github.com/rust-lang/futures-rs), and used under the\nApache 2.0 and MIT licenses.\n\nPortions derived from [tokio](https://github.com/tokio-rs/tokio), and used under the MIT license.\n\n\u003c!--\nREADME.md is generated from README.tpl by cargo readme. To regenerate:\n\ncargo install cargo-readme\n./scripts/regenerate-readmes.sh\n--\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Fcancel-safe-futures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxidecomputer%2Fcancel-safe-futures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidecomputer%2Fcancel-safe-futures/lists"}