Repository: rust-lang/futures-rs
Pull request #2868 (2024-07-02 21:44:39)

Support shared futures on no_std
Currently, `Shared` futures are only available when the `std` feature is enabled because they use `std::sync::Mutex` internally. This PR changes this so that `Shared` futures will fall back to using a spinlock when `std` is not enabled.

Alternatively, `Shared` could be changed to be generic over a mutex trait (e.g. `lock_api::RawMutex`, though it isn't implemented for `std::sync::Mutex`) to allow arbitrary user implementations.


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 744168c0..89a998f4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -104,7 +104,7 @@ jobs:
         rust:
           # This is the minimum Rust version supported by futures, futures-util, futures-task, futures-macro, futures-executor, futures-channel, futures-test.
           # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml.
-          - '1.56'
+          - '1.63'
     runs-on: ubuntu-latest
     timeout-minutes: 60
     steps:
@@ -246,11 +246,17 @@ jobs:
       - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup toolchain install nightly --component miri && rustup default nightly
-      - run: cargo miri test --workspace --all-features
+      - run: cargo miri test --workspace --all-features -- --skip panic_on_drop_fut
         env:
           MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation
           RUSTDOCFLAGS: ${{ env.RUSTDOCFLAGS }} -Z randomize-layout
           RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout
+      # This test is expected to leak.
+      - run: cargo miri test --workspace --all-features --test stream_futures_unordered -- panic_on_drop_fut
+        env:
+          MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks
+          RUSTDOCFLAGS: ${{ env.RUSTDOCFLAGS }} -Z randomize-layout
+          RUSTFLAGS: ${{ env.RUSTFLAGS }} -Z randomize-layout
 
   san:
     name: cargo test -Z sanitizer=${{ matrix.sanitizer }}
@@ -269,7 +275,8 @@ jobs:
         run: rustup toolchain install nightly --component rust-src && rustup default nightly
       # https://github.com/google/sanitizers/issues/1716 / https://github.com/actions/runner-images/issues/9491
       - run: sudo sysctl vm.mmap_rnd_bits=28
-      - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests
+      # Exclude futures-macro to work around upstream bug since nightly-2024-10-06.
+      - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests --exclude futures-macro -- --skip panic_on_drop_fut
         env:
           # TODO: Once `cfg(sanitize = "..")` is stable, replace
           # `cfg(futures_sanitizer)` with `cfg(sanitize = "..")` and remove
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1b633b2..a14e0bb9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+Releases may yanked if there is a security bug, a soundness bug, or a regression.
+
+<!--
+Note: In this file, do not use the hard wrap in the middle of a sentence for compatibility with GitHub comment style markdown rendering.
+-->
+
+# 0.3.31 - 2024-10-05
+
+* Fix use after free of task in `FuturesUnordered` when dropped future panics (#2886)
+* Fix soundness bug in `task::waker_ref` (#2830)
+  This is a breaking change but allowed because it is soundness bug fix.
+* Fix bugs in `AsyncBufRead::read_line` and `AsyncBufReadExt::lines` (#2884)
+* Fix parsing issue in `select!`/`select_biased!` (#2832)
+  This is technically a breaking change as it will now reject a very odd undocumented syntax that was previously accidentally accepted.
+* Work around issue due to upstream `Waker::will_wake` change (#2865)
+* Add `stream::Iter::{get_ref,get_mut,into_inner}` (#2875)
+* Add `future::AlwaysReady` (#2825)
+* Relax trait bound on non-constructor methods of `io::{BufReader,BufWriter}` (#2848)
+
 # 0.3.30 - 2023-12-24
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Add `{BiLock,SplitStream,SplitSink,ReadHalf,WriteHalf}::is_pair_of` (#2797)
 * Fix panic in `FuturesUnordered::clear` (#2809)
 * Fix panic in `AsyncBufReadExt::fill_buf` (#2801, #2812)
@@ -8,6 +33,8 @@
 
 # 0.3.29 - 2023-10-26
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Add `TryStreamExt::try_ready_chunks` (#2757)
 * Add `TryStreamExt::{try_all,try_any}` (#2783)
 * Add `UnboundedSender::{len,is_empty}` (#2750)
@@ -17,11 +44,15 @@
 
 # 0.3.28 - 2023-03-30
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Update to syn 2. This raises MSRV of utility crates to 1.56. (#2730, #2733)
 * Fix bug in `FlattenUnordered` (#2726, #2728)
 
 # 0.3.27 - 2023-03-11
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Add `TryFlattenUnordered` (#2577, #2590, #2606, #2607)
 * Add `AbortHandle::is_aborted` (#2710)
 * Add `AbortRegistration::handle` (#2712)
@@ -29,6 +60,8 @@
 
 # 0.3.26 - 2023-01-30
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Add `Either::as_pin_mut` and `Either::as_pin_ref` (#2691)
 * Add `Shared::ptr_eq` and `Shared::ptr_hash` (#2691)
 * Implement `FusedStream` for `Buffered` (#2676)
@@ -39,19 +72,27 @@
 
 # 0.3.25 - 2022-10-20
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Fix soundness issue in `join!` and `try_join!` macros (#2649)
 * Implement `Clone` for `sink::Drain` (#2650)
 
 # 0.3.24 - 2022-08-29
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Fix incorrect termination of `select_with_strategy` streams (#2635)
 
 # 0.3.23 - 2022-08-14
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Work around MSRV increase due to a cargo bug.
 
 # 0.3.22 - 2022-08-14
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Fix `Sync` impl of `BiLockGuard` (#2570)
 * Fix partial iteration in `FuturesUnordered` (#2574)
 * Fix false detection of inner panics in `Shared` (#2576)
@@ -69,11 +110,13 @@
 
 # 0.3.21 - 2022-02-06
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Fix potential data race in `FlattenUnordered` that introduced in 0.3.20 (#2566)
 
 # 0.3.20 - 2022-02-06
 
-NOTE: This release has been yanked due to a bug fixed in 0.3.21.
+**Note:** This release has been yanked due to a bug fixed in 0.3.21.
 
 * Fix stacked borrows violations when `-Zmiri-tag-raw-pointers` is enabled. This raises MSRV of `futures-task` to 1.45. (#2548, #2550)
 * Change `FuturesUnordered` to respect yielding from future (#2551)
@@ -81,6 +124,8 @@ NOTE: This release has been yanked due to a bug fixed in 0.3.21.
 
 # 0.3.19 - 2021-12-18
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Remove unstable `read-initializer` feature (#2534)
 * Fix panic in `FuturesUnordered` (#2535)
 * Fix compatibility issue with `FuturesUnordered` and tokio's cooperative scheduling (#2527)
@@ -88,7 +133,7 @@ NOTE: This release has been yanked due to a bug fixed in 0.3.21.
 
 # 0.3.18 - 2021-11-23
 
-NOTE: This release has been yanked. See #2529 for details.
+**Note:** This release has been yanked. See #2529 for details.
 
 * Fix unusable `Sink` implementation on `stream::Scan` (#2499)
 * Make `task::noop_waker_ref` available without `std` feature (#2505)
@@ -97,6 +142,8 @@ NOTE: This release has been yanked. See #2529 for details.
 
 # 0.3.17 - 2021-08-30
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Use `FuturesOrdered` in `join_all` (#2412)
 * Add `{future, stream}::poll_immediate` (#2452)
 * Add `stream_select!` macro (#2262)
@@ -106,6 +153,8 @@ NOTE: This release has been yanked. See #2529 for details.
 
 # 0.3.16 - 2021-07-23
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Add `TryStreamExt::try_chunks` (#2438)
 * Add `StreamExt::{all, any}` (#2460)
 * Add `stream::select_with_strategy` (#2450)
@@ -113,6 +162,8 @@ NOTE: This release has been yanked. See #2529 for details.
 
 # 0.3.15 - 2021-05-11
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Use `#[proc_macro]` at Rust 1.45+ to fix an issue where proc macros don't work with rust-analyzer (#2407)
 * Support targets that do not have atomic CAS on stable Rust (#2400)
 * futures-test: Add async `#[test]` function attribute (#2409)
@@ -128,6 +179,8 @@ NOTE: This release has been yanked. See #2529 for details.
 
 # 0.3.14 - 2021-04-10
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Add `future::SelectAll::into_inner` (#2363)
 * Allow calling `UnboundedReceiver::try_next` after `None` (#2369)
 * Reexport non-Ext traits from the root of `futures_util` (#2377)
@@ -136,6 +189,8 @@ NOTE: This release has been yanked. See #2529 for details.
 
 # 0.3.13 - 2021-02-23
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Mitigated starvation issues in `FuturesUnordered` (#2333)
 * Fixed race with dropping `mpsc::Receiver` (#2304)
 * Added `Shared::{strong_count, weak_count}` (#2346)
@@ -144,21 +199,25 @@ NOTE: This release has been yanked. See #2529 for details.
 
 # 0.3.12 - 2021-01-15
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Fixed `Unpin` impl of `future::{MaybeDone, TryMaybeDone}` where trait bounds were accidentally added in 0.3.9. (#2317)
 
 # 0.3.11 - 2021-01-14
 
+**Note:** This release of futures-util has been yanked due to a bug fixed in 0.3.31 and a compile failure with futures-task 0.3.31 and later.
+
 * Fixed heap buffer overflow in `AsyncReadExt::{read_to_end, read_to_string}` (#2314)
 
 # 0.3.10 - 2021-01-13
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Fixed type-inference in `sink::unfold` by specifying more of its types (breaking change -- see #2311)
 
 # 0.3.9 - 2021-01-08
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Significantly improved compile time when `async-await` crate feature is disabled (#2273)
 * Added `stream::repeat_with` (#2279)
@@ -171,7 +230,7 @@ NOTE: This release has been yanked. See #2310 for details.
 
 # 0.3.8 - 2020-11-04
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Switched proc-macros to use native `#[proc_macro]` at Rust 1.45+ (#2243)
 * Added `WeakShared` (#2169)
@@ -182,7 +241,7 @@ NOTE: This release has been yanked. See #2310 for details.
 
 # 0.3.7 - 2020-10-23
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Fixed unsoundness in `MappedMutexGuard` (#2240)
 * Re-exported `TakeUntil` (#2235)
@@ -190,7 +249,7 @@ NOTE: This release has been yanked. See #2310 for details.
 
 # 0.3.6 - 2020-10-06
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Fixed UB due to missing 'static on `task::waker` (#2206)
 * Added `AsyncBufReadExt::fill_buf` (#2225)
@@ -206,7 +265,7 @@ NOTE: This release has been yanked. See #2310 for details.
 
 # 0.3.5 - 2020-05-08
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Added `StreamExt::flat_map`.
 * Added `StreamExt::ready_chunks`.
@@ -226,19 +285,19 @@ NOTE: This release has been yanked. See #2310 for details.
 
 # 0.3.4 - 2020-02-06
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Fixed missing `Drop` for `UnboundedReceiver` (#2064)
 
 # 0.3.3 - 2020-02-04
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Fixed compatibility issue with pinned facade (#2062)
 
 # 0.3.2 - 2020-02-03
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Improved buffering performance of `SplitSink` (#1969)
 * Added `select_biased!` macro (#1976)
@@ -259,13 +318,13 @@ NOTE: This release has been yanked. See #2310 for details.
 
 # 0.3.1 - 2019-11-07
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Fix signature of `SpawnExt` and `LocalSpawnExt` trait (breaking change -- see #1959)
 
 # 0.3.0 - 2019-11-05
 
-NOTE: This release has been yanked. See #2310 for details.
+**Note:** This release has been yanked. See #2310 for details.
 
 * Stable release along with stable async/await!
 * Added async/await to default features (#1953)
diff --git a/Cargo.toml b/Cargo.toml
index 13e2ebcc..3314bc3a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,7 @@ missing_debug_implementations = "warn"
 rust_2018_idioms = "warn"
 single_use_lifetimes = "warn"
 unreachable_pub = "warn"
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(futures_sanitizer)'] }
 # unsafe_op_in_unsafe_fn = "warn" # Set at crate-level instead since https://github.com/rust-lang/rust/pull/100081 is not available on MSRV
 [workspace.lints.clippy]
 incompatible_msrv = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12273, https://github.com/rust-lang/rust-clippy/issues/12257
diff --git a/README.md b/README.md
index 355d6078..1ddedca8 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ Add this to your `Cargo.toml`:
 futures = "0.3"
 ```
 
-The current `futures` requires Rust 1.56 or later.
+The current `futures` requires Rust 1.63 or later.
 
 ### Feature `std`
 
diff --git a/examples/functional/Cargo.toml b/examples/functional/Cargo.toml
index a1618d31..bb95a6f6 100644
--- a/examples/functional/Cargo.toml
+++ b/examples/functional/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "futures-example-functional"
+version = "0.0.0"
 edition = "2018"
-version = "0.1.0"
 publish = false
 
 [dependencies]
diff --git a/examples/imperative/Cargo.toml b/examples/imperative/Cargo.toml
index c8076e4b..afd7a204 100644
--- a/examples/imperative/Cargo.toml
+++ b/examples/imperative/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "futures-example-imperative"
+version = "0.0.0"
 edition = "2018"
-version = "0.1.0"
 publish = false
 
 [dependencies]
diff --git a/futures-channel/Cargo.toml b/futures-channel/Cargo.toml
index 9808d661..9f591aa7 100644
--- a/futures-channel/Cargo.toml
+++ b/futures-channel/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures-channel"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/futures-rs"
 homepage = "https://rust-lang.github.io/futures-rs"
diff --git a/futures-channel/README.md b/futures-channel/README.md
index e886bd1c..e465482a 100644
--- a/futures-channel/README.md
+++ b/futures-channel/README.md
@@ -11,7 +11,7 @@ Add this to your `Cargo.toml`:
 futures-channel = "0.3"
 ```
 
-The current `futures-channel` requires Rust 1.56 or later.
+The current `futures-channel` requires Rust 1.63 or later.
 
 ## License
 
diff --git a/futures-core/src/future.rs b/futures-core/src/future.rs
index 7540cd02..30c0323a 100644
--- a/futures-core/src/future.rs
+++ b/futures-core/src/future.rs
@@ -9,10 +9,20 @@ pub use core::future::Future;
 
 /// An owned dynamically typed [`Future`] for use in cases where you can't
 /// statically type your result or need to add some indirection.
+///
+/// This type is often created by the [`boxed`] method on [`FutureExt`]. See its documentation for more.
+///
+/// [`boxed`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html#method.boxed
+/// [`FutureExt`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html
 #[cfg(feature = "alloc")]
 pub type BoxFuture<'a, T> = Pin<alloc::boxed::Box<dyn Future<Output = T> + Send + 'a>>;
 
 /// `BoxFuture`, but without the `Send` requirement.
+///
+/// This type is often created by the [`boxed_local`] method on [`FutureExt`]. See its documentation for more.
+///
+/// [`boxed_local`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html#method.boxed_local
+/// [`FutureExt`]: https://docs.rs/futures/latest/futures/future/trait.FutureExt.html
 #[cfg(feature = "alloc")]
 pub type LocalBoxFuture<'a, T> = Pin<alloc::boxed::Box<dyn Future<Output = T> + 'a>>;
 
diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs
index ad5350b7..dd07d5aa 100644
--- a/futures-core/src/stream.rs
+++ b/futures-core/src/stream.rs
@@ -6,10 +6,20 @@ use core::task::{Context, Poll};
 
 /// An owned dynamically typed [`Stream`] for use in cases where you can't
 /// statically type your result or need to add some indirection.
+///
+/// This type is often created by the [`boxed`] method on [`StreamExt`]. See its documentation for more.
+///
+/// [`boxed`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.boxed
+/// [`StreamExt`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html
 #[cfg(feature = "alloc")]
 pub type BoxStream<'a, T> = Pin<alloc::boxed::Box<dyn Stream<Item = T> + Send + 'a>>;
 
 /// `BoxStream`, but without the `Send` requirement.
+///
+/// This type is often created by the [`boxed_local`] method on [`StreamExt`]. See its documentation for more.
+///
+/// [`boxed_local`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.boxed_local
+/// [`StreamExt`]: https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html
 #[cfg(feature = "alloc")]
 pub type LocalBoxStream<'a, T> = Pin<alloc::boxed::Box<dyn Stream<Item = T> + 'a>>;
 
@@ -38,15 +48,15 @@ pub trait Stream {
     /// stream state:
     ///
     /// - `Poll::Pending` means that this stream's next value is not ready
-    /// yet. Implementations will ensure that the current task will be notified
-    /// when the next value may be ready.
+    ///   yet. Implementations will ensure that the current task will be notified
+    ///   when the next value may be ready.
     ///
     /// - `Poll::Ready(Some(val))` means that the stream has successfully
-    /// produced a value, `val`, and may produce further values on subsequent
-    /// `poll_next` calls.
+    ///   produced a value, `val`, and may produce further values on subsequent
+    ///   `poll_next` calls.
     ///
     /// - `Poll::Ready(None)` means that the stream has terminated, and
-    /// `poll_next` should not be invoked again.
+    ///   `poll_next` should not be invoked again.
     ///
     /// # Panics
     ///
diff --git a/futures-executor/Cargo.toml b/futures-executor/Cargo.toml
index 8e142e9b..d74bf756 100644
--- a/futures-executor/Cargo.toml
+++ b/futures-executor/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures-executor"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/futures-rs"
 homepage = "https://rust-lang.github.io/futures-rs"
diff --git a/futures-executor/README.md b/futures-executor/README.md
index 724ff5bb..bc4a3fcb 100644
--- a/futures-executor/README.md
+++ b/futures-executor/README.md
@@ -11,7 +11,7 @@ Add this to your `Cargo.toml`:
 futures-executor = "0.3"
 ```
 
-The current `futures-executor` requires Rust 1.56 or later.
+The current `futures-executor` requires Rust 1.63 or later.
 
 ## License
 
diff --git a/futures-executor/src/enter.rs b/futures-executor/src/enter.rs
index b0e43e05..46e1bd14 100644
--- a/futures-executor/src/enter.rs
+++ b/futures-executor/src/enter.rs
@@ -1,7 +1,7 @@
 use std::cell::Cell;
 use std::fmt;
 
-std::thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
+std::thread_local!(static ENTERED: Cell<bool> = const { Cell::new(false) });
 
 /// Represents an executor context.
 ///
diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs
index ec3a5a40..a0dc49d7 100644
--- a/futures-executor/src/lib.rs
+++ b/futures-executor/src/lib.rs
@@ -46,7 +46,6 @@
 ))]
 #![warn(missing_docs, unsafe_op_in_unsafe_fn)]
 #![cfg_attr(docsrs, feature(doc_cfg))]
-#![allow(clippy::thread_local_initializer_can_be_made_const)] // clippy bug: this lint doesn't consider MSRV
 
 #[cfg(feature = "std")]
 extern crate std;
diff --git a/futures-io/Cargo.toml b/futures-io/Cargo.toml
index d51c4c9d..e5320d42 100644
--- a/futures-io/Cargo.toml
+++ b/futures-io/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "futures-io"
-version = "0.3.30"
+version = "0.3.31"
 edition = "2018"
 rust-version = "1.36"
 license = "MIT OR Apache-2.0"
diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml
index c6fe91cd..ced4016a 100644
--- a/futures-macro/Cargo.toml
+++ b/futures-macro/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures-macro"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/futures-rs"
 homepage = "https://rust-lang.github.io/futures-rs"
diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs
index 94e356f7..0e891b3a 100644
--- a/futures-macro/src/join.rs
+++ b/futures-macro/src/join.rs
@@ -1,4 +1,4 @@
-//! The futures-rs `join! macro implementation.
+//! The futures-rs `join!` macro implementation.
 
 use proc_macro::TokenStream;
 use proc_macro2::{Span, TokenStream as TokenStream2};
diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs
index 57bb74a8..7b45cfe4 100644
--- a/futures-macro/src/select.rs
+++ b/futures-macro/src/select.rs
@@ -1,4 +1,4 @@
-//! The futures-rs `select! macro implementation.
+//! The futures-rs `select!` macro implementation.
 
 use proc_macro::TokenStream;
 use proc_macro2::Span;
diff --git a/futures-task/Cargo.toml b/futures-task/Cargo.toml
index 7dbf85fa..4e58a570 100644
--- a/futures-task/Cargo.toml
+++ b/futures-task/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures-task"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/futures-rs"
 homepage = "https://rust-lang.github.io/futures-rs"
diff --git a/futures-task/README.md b/futures-task/README.md
index 1ebec2d7..6a8ca2dd 100644
--- a/futures-task/README.md
+++ b/futures-task/README.md
@@ -11,7 +11,7 @@ Add this to your `Cargo.toml`:
 futures-task = "0.3"
 ```
 
-The current `futures-task` requires Rust 1.56 or later.
+The current `futures-task` requires Rust 1.63 or later.
 
 ## License
 
diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs
index b1410a19..747457a5 100644
--- a/futures-task/src/waker.rs
+++ b/futures-task/src/waker.rs
@@ -36,6 +36,7 @@ unsafe fn increase_refcount<T: ArcWake + 'static>(data: *const ()) {
 }
 
 // used by `waker_ref`
+#[inline(always)]
 unsafe fn clone_arc_raw<T: ArcWake + 'static>(data: *const ()) -> RawWaker {
     unsafe { increase_refcount::<T>(data) }
     RawWaker::new(data, waker_vtable::<T>())
diff --git a/futures-util/Cargo.toml b/futures-util/Cargo.toml
index 68dbd86b..ec9afd17 100644
--- a/futures-util/Cargo.toml
+++ b/futures-util/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures-util"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/futures-rs"
 homepage = "https://rust-lang.github.io/futures-rs"
@@ -12,8 +12,8 @@ Common utilities and extension traits for the futures-rs library.
 
 [features]
 default = ["std", "async-await", "async-await-macro"]
-std = ["alloc", "futures-core/std", "futures-task/std", "slab"]
-alloc = ["futures-core/alloc", "futures-task/alloc"]
+std = ["alloc", "futures-core/std", "futures-task/std", "slab/std"]
+alloc = ["futures-core/alloc", "futures-task/alloc", "slab"]
 async-await = []
 async-await-macro = ["async-await", "futures-macro"]
 compat = ["std", "futures_01"]
@@ -34,15 +34,16 @@ write-all-vectored = ["io"]
 futures-core = { path = "../futures-core", version = "=1.0.0-alpha.0", default-features = false }
 futures-task = { path = "../futures-task", version = "=0.4.0-alpha.0", default-features = false }
 futures-channel = { path = "../futures-channel", version = "=0.4.0-alpha.0", default-features = false, features = ["std"], optional = true }
-futures-io = { path = "../futures-io", version = "0.3.30", default-features = false, features = ["std"], optional = true }
+futures-io = { path = "../futures-io", version = "0.3.31", default-features = false, features = ["std"], optional = true }
 futures-sink = { path = "../futures-sink", version = "=0.4.0-alpha.0", default-features = false, optional = true }
 futures-macro = { path = "../futures-macro", version = "=0.4.0-alpha.0", default-features = false, optional = true }
-slab = { version = "0.4.2", optional = true }
+slab = { version = "0.4.2", default-features = false, optional = true }
 memchr = { version = "2.2", optional = true }
 futures_01 = { version = "0.1.25", optional = true, package = "futures" }
 tokio-io = { version = "0.1.9", optional = true }
 pin-utils = "0.1.0"
 pin-project-lite = "0.2.6"
+spin = { version = "0.9.8", optional = true }
 
 [dev-dependencies]
 futures = { path = "../futures", features = ["async-await", "thread-pool"] }
diff --git a/futures-util/README.md b/futures-util/README.md
index 60e2c210..fc1b6d6b 100644
--- a/futures-util/README.md
+++ b/futures-util/README.md
@@ -11,7 +11,7 @@ Add this to your `Cargo.toml`:
 futures-util = "0.3"
 ```
 
-The current `futures-util` requires Rust 1.56 or later.
+The current `futures-util` requires Rust 1.63 or later.
 
 ## License
 
diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs
index 9dbcfc2b..e1e79e37 100644
--- a/futures-util/src/abortable.rs
+++ b/futures-util/src/abortable.rs
@@ -64,7 +64,7 @@ impl<T> Abortable<T> {
     /// This means that it will return `true` even if:
     /// * `abort` was called after the task had completed.
     /// * `abort` was called while the task was being polled - the task may still be running and
-    /// will not be stopped until `poll` returns.
+    ///   will not be stopped until `poll` returns.
     pub fn is_aborted(&self) -> bool {
         self.inner.aborted.load(Ordering::Relaxed)
     }
@@ -200,7 +200,7 @@ impl AbortHandle {
     /// even if:
     /// * `abort` was called after the task had completed.
     /// * `abort` was called while the task was being polled - the task may still be running and
-    /// will not be stopped until `poll` returns.
+    ///   will not be stopped until `poll` returns.
     ///
     /// This operation has a Relaxed ordering.
     pub fn is_aborted(&self) -> bool {
diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs
index 84c6ca5a..638a92eb 100644
--- a/futures-util/src/async_await/select_mod.rs
+++ b/futures-util/src/async_await/select_mod.rs
@@ -3,6 +3,7 @@
 macro_rules! document_select_macro {
     // This branch is required for `futures 0.3.1`, from before select_biased was introduced
     ($select:item) => {
+        #[allow(clippy::too_long_first_doc_paragraph)]
         /// Polls multiple futures and streams simultaneously, executing the branch
         /// for the future that finishes first. If multiple futures are ready,
         /// one will be pseudo-randomly selected at runtime. Futures directly
@@ -153,6 +154,7 @@ macro_rules! document_select_macro {
     ($select:item $select_biased:item) => {
         document_select_macro!($select);
 
+        #[allow(clippy::too_long_first_doc_paragraph)]
         /// Polls multiple futures and streams simultaneously, executing the branch
         /// for the future that finishes first. Unlike [`select!`], if multiple futures are ready,
         /// one will be selected in order of declaration. Futures directly
diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs
index 541b4cd9..039e65a6 100644
--- a/futures-util/src/async_await/stream_select_mod.rs
+++ b/futures-util/src/async_await/stream_select_mod.rs
@@ -3,6 +3,7 @@
 #[doc(hidden)]
 pub use futures_macro::stream_select_internal;
 
+#[allow(clippy::too_long_first_doc_paragraph)]
 /// Combines several streams, all producing the same `Item` type, into one stream.
 /// This is similar to `select_all` but does not require the streams to all be the same type.
 /// It also keeps the streams inline, and does not require `Box<dyn Stream>`s to be allocated.
diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs
index 2f9d65e7..3c5bb77b 100644
--- a/futures-util/src/compat/compat03as01.rs
+++ b/futures-util/src/compat/compat03as01.rs
@@ -15,6 +15,7 @@ use futures_sink::Sink as Sink03;
 use std::marker::PhantomData;
 use std::{mem, pin::Pin, sync::Arc, task::Context};
 
+#[allow(clippy::too_long_first_doc_paragraph)] // clippy bug, see https://github.com/rust-lang/rust-clippy/issues/13315
 /// Converts a futures 0.3 [`TryFuture`](futures_core::future::TryFuture) or
 /// [`TryStream`](futures_core::stream::TryStream) into a futures 0.1
 /// [`Future`](futures_01::future::Future) or
diff --git a/futures-util/src/future/future/mod.rs b/futures-util/src/future/future/mod.rs
index cf6bd868..ac251e0a 100644
--- a/futures-util/src/future/future/mod.rs
+++ b/futures-util/src/future/future/mod.rs
@@ -107,9 +107,9 @@ mod remote_handle;
 #[cfg(feature = "std")]
 pub use self::remote_handle::{Remote, RemoteHandle};
 
-#[cfg(feature = "std")]
+#[cfg(any(feature = "std", all(feature = "alloc", feature = "spin")))]
 mod shared;
-#[cfg(feature = "std")]
+#[cfg(any(feature = "std", all(feature = "alloc", feature = "spin")))]
 pub use self::shared::{Shared, WeakShared};
 
 impl<T: ?Sized> FutureExt for T where T: Future {}
@@ -440,7 +440,7 @@ pub trait FutureExt: Future {
     /// into a cloneable future. It enables a future to be polled by multiple
     /// threads.
     ///
-    /// This method is only available when the `std` feature of this
+    /// This method is only available when the `std` or 'spin' feature of this
     /// library is activated, and it is activated by default.
     ///
     /// # Examples
@@ -474,7 +474,7 @@ pub trait FutureExt: Future {
     /// join_handle.join().unwrap();
     /// # });
     /// ```
-    #[cfg(feature = "std")]
+    #[cfg(any(feature = "std", all(feature = "alloc", feature = "spin")))]
     fn shared(self) -> Shared<Self>
     where
         Self: Sized,
diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs
index b4d9bff8..f26b20d9 100644
--- a/futures-util/src/future/future/shared.rs
+++ b/futures-util/src/future/future/shared.rs
@@ -1,15 +1,20 @@
 use crate::task::{waker_ref, ArcWake};
+use alloc::sync::{Arc, Weak};
+use core::cell::UnsafeCell;
+use core::fmt;
+use core::hash::Hasher;
+use core::pin::Pin;
+use core::ptr;
+use core::sync::atomic::AtomicUsize;
+use core::sync::atomic::Ordering::{Acquire, SeqCst};
 use futures_core::future::{FusedFuture, Future};
 use futures_core::task::{Context, Poll, Waker};
 use slab::Slab;
-use std::cell::UnsafeCell;
-use std::fmt;
-use std::hash::Hasher;
-use std::pin::Pin;
-use std::ptr;
-use std::sync::atomic::AtomicUsize;
-use std::sync::atomic::Ordering::{Acquire, SeqCst};
-use std::sync::{Arc, Mutex, Weak};
+
+#[cfg(feature = "std")]
+type Mutex<T> = std::sync::Mutex<T>;
+#[cfg(not(feature = "std"))]
+type Mutex<T> = spin::Mutex<T>;
 
 /// Future for the [`shared`](super::FutureExt::shared) method.
 #[must_use = "futures do nothing unless you `.await` or poll them"]
@@ -204,7 +209,10 @@ where
 {
     /// Registers the current task to receive a wakeup when we are awoken.
     fn record_waker(&self, waker_key: &mut usize, cx: &mut Context<'_>) {
+        #[cfg(feature = "std")]
         let mut wakers_guard = self.notifier.wakers.lock().unwrap();
+        #[cfg(not(feature = "std"))]
+        let mut wakers_guard = self.notifier.wakers.lock();
 
         let wakers_mut = wakers_guard.as_mut();
 
@@ -345,7 +353,11 @@ where
         inner.notifier.state.store(COMPLETE, SeqCst);
 
         // Wake all tasks and drop the slab
+        #[cfg(feature = "std")]
         let mut wakers_guard = inner.notifier.wakers.lock().unwrap();
+        #[cfg(not(feature = "std"))]
+        let mut wakers_guard = inner.notifier.wakers.lock();
+
         let mut wakers = wakers_guard.take().unwrap();
         for waker in wakers.drain().flatten() {
             waker.wake();
@@ -375,11 +387,16 @@ where
     fn drop(&mut self) {
         if self.waker_key != NULL_WAKER_KEY {
             if let Some(ref inner) = self.inner {
+                #[cfg(feature = "std")]
                 if let Ok(mut wakers) = inner.notifier.wakers.lock() {
                     if let Some(wakers) = wakers.as_mut() {
                         wakers.remove(self.waker_key);
                     }
                 }
+                #[cfg(not(feature = "std"))]
+                if let Some(wakers) = inner.notifier.wakers.lock().as_mut() {
+                    wakers.remove(self.waker_key);
+                }
             }
         }
     }
@@ -387,7 +404,11 @@ where
 
 impl ArcWake for Notifier {
     fn wake_by_ref(arc_self: &Arc<Self>) {
+        #[cfg(feature = "std")]
         let wakers = &mut *arc_self.wakers.lock().unwrap();
+        #[cfg(not(feature = "std"))]
+        let wakers = &mut *arc_self.wakers.lock();
+
         if let Some(wakers) = wakers.as_mut() {
             for (_key, opt_waker) in wakers {
                 if let Some(waker) = opt_waker.take() {
diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs
index 1280ce98..2209ffd5 100644
--- a/futures-util/src/future/mod.rs
+++ b/futures-util/src/future/mod.rs
@@ -35,7 +35,7 @@ pub use self::future::CatchUnwind;
 #[cfg(feature = "std")]
 pub use self::future::{Remote, RemoteHandle};
 
-#[cfg(feature = "std")]
+#[cfg(any(feature = "std", all(feature = "alloc", feature = "spin")))]
 pub use self::future::{Shared, WeakShared};
 
 mod try_future;
diff --git a/futures-util/src/io/lines.rs b/futures-util/src/io/lines.rs
index 8c4d17c5..0a1abf4b 100644
--- a/futures-util/src/io/lines.rs
+++ b/futures-util/src/io/lines.rs
@@ -35,6 +35,7 @@ impl<R: AsyncBufRead> Stream for Lines<R> {
     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
         let this = self.project();
         let n = ready!(read_line_internal(this.reader, cx, this.buf, this.bytes, this.read))?;
+        *this.read = 0;
         if n == 0 && this.buf.is_empty() {
             return Poll::Ready(None);
         }
diff --git a/futures-util/src/io/read_line.rs b/futures-util/src/io/read_line.rs
index b4483223..43942add 100644
--- a/futures-util/src/io/read_line.rs
+++ b/futures-util/src/io/read_line.rs
@@ -18,13 +18,14 @@ pub struct ReadLine<'a, R: ?Sized> {
     buf: &'a mut String,
     bytes: Vec<u8>,
     read: usize,
+    finished: bool,
 }
 
 impl<R: ?Sized + Unpin> Unpin for ReadLine<'_, R> {}
 
 impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadLine<'a, R> {
     pub(super) fn new(reader: &'a mut R, buf: &'a mut String) -> Self {
-        Self { reader, bytes: mem::take(buf).into_bytes(), buf, read: 0 }
+        Self { reader, bytes: mem::take(buf).into_bytes(), buf, read: 0, finished: false }
     }
 }
 
@@ -35,26 +36,42 @@ pub(super) fn read_line_internal<R: AsyncBufRead + ?Sized>(
     bytes: &mut Vec<u8>,
     read: &mut usize,
 ) -> Poll<io::Result<usize>> {
-    let ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read));
-    if str::from_utf8(bytes).is_err() {
-        bytes.clear();
-        Poll::Ready(ret.and_then(|_| {
-            Err(io::Error::new(io::ErrorKind::InvalidData, "stream did not contain valid UTF-8"))
-        }))
-    } else {
-        debug_assert!(buf.is_empty());
-        debug_assert_eq!(*read, 0);
-        // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
-        mem::swap(unsafe { buf.as_mut_vec() }, bytes);
-        Poll::Ready(ret)
+    let mut ret = ready!(read_until_internal(reader, cx, b'\n', bytes, read));
+    if str::from_utf8(&bytes[bytes.len() - *read..bytes.len()]).is_err() {
+        bytes.truncate(bytes.len() - *read);
+        if ret.is_ok() {
+            ret = Err(io::Error::new(
+                io::ErrorKind::InvalidData,
+                "stream did not contain valid UTF-8",
+            ));
+        }
     }
+    *read = 0;
+    // Safety: `bytes` is valid UTF-8 because it was taken from a String
+    // and the newly read bytes are either valid UTF-8 or have been removed.
+    mem::swap(unsafe { buf.as_mut_vec() }, bytes);
+    Poll::Ready(ret)
 }
 
 impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadLine<'_, R> {
     type Output = io::Result<usize>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        let Self { reader, buf, bytes, read } = &mut *self;
-        read_line_internal(Pin::new(reader), cx, buf, bytes, read)
+        let Self { reader, buf, bytes, read, finished: _ } = &mut *self;
+        let ret = ready!(read_line_internal(Pin::new(reader), cx, buf, bytes, read));
+        self.finished = true;
+        Poll::Ready(ret)
+    }
+}
+
+impl<R: ?Sized> Drop for ReadLine<'_, R> {
+    fn drop(&mut self) {
+        // restore old string contents
+        if !self.finished {
+            self.bytes.truncate(self.bytes.len() - self.read);
+            // Safety: `bytes` is valid UTF-8 because it was taken from a String
+            // and the newly read bytes have been removed.
+            mem::swap(unsafe { self.buf.as_mut_vec() }, &mut self.bytes);
+        }
     }
 }
diff --git a/futures-util/src/io/read_until.rs b/futures-util/src/io/read_until.rs
index d6121d6f..adc359db 100644
--- a/futures-util/src/io/read_until.rs
+++ b/futures-util/src/io/read_until.rs
@@ -3,7 +3,6 @@ use futures_core::ready;
 use futures_core::task::{Context, Poll};
 use futures_io::AsyncBufRead;
 use std::io;
-use std::mem;
 use std::pin::Pin;
 use std::vec::Vec;
 
@@ -46,7 +45,7 @@ pub(super) fn read_until_internal<R: AsyncBufRead + ?Sized>(
         reader.as_mut().consume(used);
         *read += used;
         if done || used == 0 {
-            return Poll::Ready(Ok(mem::replace(read, 0)));
+            return Poll::Ready(Ok(*read));
         }
     }
 }
diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs
index abe35e68..c5af6960 100644
--- a/futures-util/src/lib.rs
+++ b/futures-util/src/lib.rs
@@ -10,7 +10,6 @@
     )
 ))]
 #![warn(missing_docs, unsafe_op_in_unsafe_fn)]
-#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 #![allow(clippy::needless_borrow)] // https://github.com/rust-lang/futures-rs/pull/2558#issuecomment-1030745203
 #![allow(clippy::arc_with_non_send_sync)] // false positive https://github.com/rust-lang/rust-clippy/issues/11076
diff --git a/futures-util/src/stream/futures_unordered/mod.rs b/futures-util/src/stream/futures_unordered/mod.rs
index 2d4f1515..913e260f 100644
--- a/futures-util/src/stream/futures_unordered/mod.rs
+++ b/futures-util/src/stream/futures_unordered/mod.rs
@@ -255,16 +255,6 @@ impl<Fut> FuturesUnordered<Fut> {
         // `wake` from doing any work in the future
         let prev = task.queued.swap(true, SeqCst);
 
-        // Drop the future, even if it hasn't finished yet. This is safe
-        // because we're dropping the future on the thread that owns
-        // `FuturesUnordered`, which correctly tracks `Fut`'s lifetimes and
-        // such.
-        unsafe {
-            // Set to `None` rather than `take()`ing to prevent moving the
-            // future.
-            *task.future.get() = None;
-        }
-
         // If the queued flag was previously set, then it means that this task
         // is still in our internal ready to run queue. We then transfer
         // ownership of our reference count to the ready to run queue, and it'll
@@ -276,8 +266,25 @@ impl<Fut> FuturesUnordered<Fut> {
         // enqueue the task, so our task will never see the ready to run queue
         // again. The task itself will be deallocated once all reference counts
         // have been dropped elsewhere by the various wakers that contain it.
-        if prev {
-            mem::forget(task);
+        //
+        // Use ManuallyDrop to transfer the reference count ownership before
+        // dropping the future so unwinding won't release the reference count.
+        let md_slot;
+        let task = if prev {
+            md_slot = mem::ManuallyDrop::new(task);
+            &*md_slot
+        } else {
+            &task
+        };
+
+        // Drop the future, even if it hasn't finished yet. This is safe
+        // because we're dropping the future on the thread that owns
+        // `FuturesUnordered`, which correctly tracks `Fut`'s lifetimes and
+        // such.
+        unsafe {
+            // Set to `None` rather than `take()`ing to prevent moving the
+            // future.
+            *task.future.get() = None;
         }
     }
 
@@ -566,15 +573,27 @@ impl<Fut> FuturesUnordered<Fut> {
 
 impl<Fut> Drop for FuturesUnordered<Fut> {
     fn drop(&mut self) {
+        // Before the strong reference to the queue is dropped we need all
+        // futures to be dropped. See note at the bottom of this method.
+        //
+        // If there is a panic before this completes, we leak the queue.
+        struct LeakQueueOnDrop<'a, Fut>(&'a mut FuturesUnordered<Fut>);
+        impl<Fut> Drop for LeakQueueOnDrop<'_, Fut> {
+            fn drop(&mut self) {
+                mem::forget(Arc::clone(&self.0.ready_to_run_queue));
+            }
+        }
+        let guard = LeakQueueOnDrop(self);
         // When a `FuturesUnordered` is dropped we want to drop all futures
         // associated with it. At the same time though there may be tons of
         // wakers flying around which contain `Task<Fut>` references
         // inside them. We'll let those naturally get deallocated.
-        while !self.head_all.get_mut().is_null() {
-            let head = *self.head_all.get_mut();
-            let task = unsafe { self.unlink(head) };
-            self.release_task(task);
+        while !guard.0.head_all.get_mut().is_null() {
+            let head = *guard.0.head_all.get_mut();
+            let task = unsafe { guard.0.unlink(head) };
+            guard.0.release_task(task);
         }
+        mem::forget(guard); // safe to release strong reference to queue
 
         // Note that at this point we could still have a bunch of tasks in the
         // ready to run queue. None of those tasks, however, have futures
diff --git a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs
index 77abdf4e..6ffaf554 100644
--- a/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs
+++ b/futures-util/src/stream/futures_unordered/ready_to_run_queue.rs
@@ -27,6 +27,8 @@ pub(super) struct ReadyToRunQueue<Fut> {
 /// An MPSC queue into which the tasks containing the futures are inserted
 /// whenever the future inside is scheduled for polling.
 impl<Fut> ReadyToRunQueue<Fut> {
+    // FIXME: this takes raw pointer without safety conditions.
+
     /// The enqueue function from the 1024cores intrusive MPSC queue algorithm.
     pub(super) fn enqueue(&self, task: *const Task<Fut>) {
         unsafe {
diff --git a/futures-util/src/stream/iter.rs b/futures-util/src/stream/iter.rs
index 20471c2e..48b6519a 100644
--- a/futures-util/src/stream/iter.rs
+++ b/futures-util/src/stream/iter.rs
@@ -10,6 +10,23 @@ pub struct Iter<I> {
     iter: I,
 }
 
+impl<I> Iter<I> {
+    /// Acquires a reference to the underlying iterator that this stream is pulling from.
+    pub fn get_ref(&self) -> &I {
+        &self.iter
+    }
+
+    /// Acquires a mutable reference to the underlying iterator that this stream is pulling from.
+    pub fn get_mut(&mut self) -> &mut I {
+        &mut self.iter
+    }
+
+    /// Consumes this stream, returning the underlying iterator.
+    pub fn into_inner(self) -> I {
+        self.iter
+    }
+}
+
 impl<I> Unpin for Iter<I> {}
 
 /// Converts an `Iterator` into a `Stream` which is always ready
diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs
index 3be933e9..4375f3e2 100644
--- a/futures-util/src/stream/select_with_strategy.rs
+++ b/futures-util/src/stream/select_with_strategy.rs
@@ -75,6 +75,7 @@ pin_project! {
     }
 }
 
+#[allow(clippy::too_long_first_doc_paragraph)]
 /// This function will attempt to pull items from both streams. You provide a
 /// closure to tell [`SelectWithStrategy`] which stream to poll. The closure can
 /// store state on `SelectWithStrategy` to which it will receive a `&mut` on every
diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs
index 37811a10..c79edbd2 100644
--- a/futures-util/src/stream/stream/flatten_unordered.rs
+++ b/futures-util/src/stream/stream/flatten_unordered.rs
@@ -119,7 +119,7 @@ impl SharedPollState {
     /// - `!WAKING` as
     ///   * Wakers called during the `POLLING` phase won't propagate their calls
     ///   * `POLLING` phase can't start if some of the wakers are active
-    ///   So no wrapped waker can touch the inner waker's cell, it's safe to poll again.
+    ///     So no wrapped waker can touch the inner waker's cell, it's safe to poll again.
     fn stop_polling(&self, to_poll: u8, will_be_woken: bool) -> u8 {
         self.state
             .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mut value| {
diff --git a/futures/Cargo.toml b/futures/Cargo.toml
index 84edf900..eecb07ce 100644
--- a/futures/Cargo.toml
+++ b/futures/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 readme = "../README.md"
 keywords = ["futures", "async", "future"]
@@ -19,7 +19,7 @@ futures-core = { path = "../futures-core", version = "=1.0.0-alpha.0", default-f
 futures-task = { path = "../futures-task", version = "=0.4.0-alpha.0", default-features = false }
 futures-channel = { path = "../futures-channel", version = "=0.4.0-alpha.0", default-features = false, features = ["sink"] }
 futures-executor = { path = "../futures-executor", version = "=0.4.0-alpha.0", default-features = false, optional = true }
-futures-io = { path = "../futures-io", version = "0.3.30", default-features = false }
+futures-io = { path = "../futures-io", version = "0.3.31", default-features = false }
 futures-sink = { path = "../futures-sink", version = "=0.4.0-alpha.0", default-features = false }
 futures-util = { path = "../futures-util", version = "=0.4.0-alpha.0", default-features = false, features = ["sink"] }
 
@@ -40,6 +40,7 @@ compat = ["std", "futures-util/compat"]
 io-compat = ["compat", "futures-util/io-compat"]
 executor = ["std", "futures-executor/std"]
 thread-pool = ["executor", "futures-executor/thread-pool"]
+spin = ["futures-util/spin"]
 
 # Unstable features
 # These features are outside of the normal semver guarantees and require the


Test patch:
diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml
index 3d4c6cd8..62b8eae2 100644
--- a/futures-test/Cargo.toml
+++ b/futures-test/Cargo.toml
@@ -2,7 +2,7 @@
 name = "futures-test"
 version = "0.4.0-alpha.0"
 edition = "2018"
-rust-version = "1.56"
+rust-version = "1.63"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/futures-rs"
 homepage = "https://rust-lang.github.io/futures-rs"
@@ -13,7 +13,7 @@ Common utilities for testing components built off futures-rs.
 [dependencies]
 futures-core = { version = "=1.0.0-alpha.0", path = "../futures-core", default-features = false }
 futures-task = { version = "=0.4.0-alpha.0", path = "../futures-task", default-features = false }
-futures-io = { version = "0.3.30", path = "../futures-io", default-features = false }
+futures-io = { version = "0.3.31", path = "../futures-io", default-features = false }
 futures-util = { version = "=0.4.0-alpha.0", path = "../futures-util", default-features = false }
 futures-executor = { version = "=0.4.0-alpha.0", path = "../futures-executor", default-features = false }
 futures-sink = { version = "=0.4.0-alpha.0", path = "../futures-sink", default-features = false }
diff --git a/futures-test/README.md b/futures-test/README.md
index 34595aaf..078457d8 100644
--- a/futures-test/README.md
+++ b/futures-test/README.md
@@ -11,7 +11,7 @@ Add this to your `Cargo.toml`:
 futures-test = "0.3"
 ```
 
-The current `futures-test` requires Rust 1.56 or later.
+The current `futures-test` requires Rust 1.63 or later.
 
 ## License
 
diff --git a/futures/tests/io_lines.rs b/futures/tests/io_lines.rs
index 5ce01a69..62afef32 100644
--- a/futures/tests/io_lines.rs
+++ b/futures/tests/io_lines.rs
@@ -1,6 +1,6 @@
 use futures::executor::block_on;
 use futures::future::{Future, FutureExt};
-use futures::io::{AsyncBufReadExt, Cursor};
+use futures::io::{AsyncBufReadExt, AsyncRead, Cursor};
 use futures::stream::{self, StreamExt, TryStreamExt};
 use futures::task::Poll;
 use futures_test::io::AsyncReadTestExt;
@@ -27,6 +27,24 @@ macro_rules! run_next {
     };
 }
 
+struct IOErrorRead(bool);
+
+impl AsyncRead for IOErrorRead {
+    fn poll_read(
+        mut self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+        b: &mut [u8],
+    ) -> Poll<std::io::Result<usize>> {
+        if self.0 {
+            Poll::Ready(Err(std::io::ErrorKind::InvalidInput.into()))
+        } else {
+            self.0 = true;
+            b[..16].fill(b'x');
+            Ok(16).into()
+        }
+    }
+}
+
 #[test]
 fn lines() {
     let buf = Cursor::new(&b"12\r"[..]);
@@ -58,3 +76,9 @@ fn maybe_pending() {
     assert_eq!(run_next!(s), "".to_string());
     assert!(run(s.next()).is_none());
 }
+
+#[test]
+fn issue2862() {
+    let mut lines = futures::io::BufReader::new(IOErrorRead(false)).lines();
+    assert!(block_on(lines.next()).unwrap().is_err())
+}
diff --git a/futures/tests/io_read_line.rs b/futures/tests/io_read_line.rs
index 88a87792..c7559c59 100644
--- a/futures/tests/io_read_line.rs
+++ b/futures/tests/io_read_line.rs
@@ -3,6 +3,7 @@ use futures::future::{Future, FutureExt};
 use futures::io::{AsyncBufReadExt, Cursor};
 use futures::stream::{self, StreamExt, TryStreamExt};
 use futures::task::Poll;
+use futures::AsyncRead;
 use futures_test::io::AsyncReadTestExt;
 use futures_test::task::noop_context;
 
@@ -15,6 +16,24 @@ fn run<F: Future + Unpin>(mut f: F) -> F::Output {
     }
 }
 
+struct IOErrorRead(bool);
+
+impl AsyncRead for IOErrorRead {
+    fn poll_read(
+        mut self: std::pin::Pin<&mut Self>,
+        _cx: &mut std::task::Context<'_>,
+        b: &mut [u8],
+    ) -> Poll<std::io::Result<usize>> {
+        if self.0 {
+            Poll::Ready(Err(std::io::ErrorKind::InvalidInput.into()))
+        } else {
+            self.0 = true;
+            b[..16].fill(b'x');
+            Ok(16).into()
+        }
+    }
+}
+
 #[test]
 fn read_line() {
     let mut buf = Cursor::new(b"12");
@@ -34,6 +53,30 @@ fn read_line() {
     assert_eq!(v, "");
 }
 
+#[test]
+fn read_line_drop() {
+    // string contents should be preserved if the future is dropped
+    let mut buf = Cursor::new(b"12\n\n");
+    let mut v = String::from("abc");
+    drop(buf.read_line(&mut v));
+    assert_eq!(v, "abc");
+}
+
+#[test]
+fn read_line_io_error() {
+    let mut r = futures::io::BufReader::new(IOErrorRead(false));
+    let _ = block_on(r.read_line(&mut String::new()));
+}
+
+#[test]
+fn read_line_utf8_error() {
+    let mut buf = Cursor::new(b"12\xFF\n\n");
+    let mut v = String::from("abc");
+    let res = block_on(buf.read_line(&mut v));
+    assert_eq!(res.unwrap_err().kind(), std::io::ErrorKind::InvalidData);
+    assert_eq!(v, "abc");
+}
+
 #[test]
 fn maybe_pending() {
     let mut buf = b"12".interleave_pending();
diff --git a/futures/tests/macro-reexport/Cargo.toml b/futures/tests/macro-reexport/Cargo.toml
index 5f3ae30d..8584fc1a 100644
--- a/futures/tests/macro-reexport/Cargo.toml
+++ b/futures/tests/macro-reexport/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "macro-reexport"
-version = "0.1.0"
+version = "0.0.0"
 edition = "2018"
 publish = false
 
diff --git a/futures/tests/macro-tests/Cargo.toml b/futures/tests/macro-tests/Cargo.toml
index 9e3299ad..5c248d04 100644
--- a/futures/tests/macro-tests/Cargo.toml
+++ b/futures/tests/macro-tests/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "macro-tests"
-version = "0.1.0"
+version = "0.0.0"
 edition = "2018"
 publish = false
 
diff --git a/futures/tests/no-std/Cargo.toml b/futures/tests/no-std/Cargo.toml
index f638b8a4..ec7d4c78 100644
--- a/futures/tests/no-std/Cargo.toml
+++ b/futures/tests/no-std/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "no-std"
-version = "0.1.0"
+version = "0.0.0"
 edition = "2018"
 publish = false
 
diff --git a/futures/tests/no-std/build.rs b/futures/tests/no-std/build.rs
index a96a6827..73d2a7b5 100644
--- a/futures/tests/no-std/build.rs
+++ b/futures/tests/no-std/build.rs
@@ -1,6 +1,7 @@
 use std::{env, process::Command};
 
 fn main() {
+    println!("cargo:rustc-check-cfg=cfg(nightly)");
     if is_nightly() {
         println!("cargo:rustc-cfg=nightly");
     }
@@ -10,5 +11,5 @@ fn is_nightly() -> bool {
     env::var_os("RUSTC")
         .and_then(|rustc| Command::new(rustc).arg("--version").output().ok())
         .and_then(|output| String::from_utf8(output.stdout).ok())
-        .map_or(false, |version| version.contains("nightly") || version.contains("dev"))
+        .is_some_and(|version| version.contains("nightly") || version.contains("dev"))
 }
diff --git a/futures/tests/stream_futures_unordered.rs b/futures/tests/stream_futures_unordered.rs
index 727feb06..6be7f05b 100644
--- a/futures/tests/stream_futures_unordered.rs
+++ b/futures/tests/stream_futures_unordered.rs
@@ -406,3 +406,26 @@ fn clear_in_loop() {
         }
     });
 }
+
+// https://github.com/rust-lang/futures-rs/issues/2863#issuecomment-2219441515
+#[test]
+#[should_panic]
+fn panic_on_drop_fut() {
+    struct BadFuture;
+
+    impl Drop for BadFuture {
+        fn drop(&mut self) {
+            panic!()
+        }
+    }
+
+    impl Future for BadFuture {
+        type Output = ();
+
+        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
+            Poll::Pending
+        }
+    }
+
+    FuturesUnordered::default().push(BadFuture);
+}
diff --git a/futures/tests/task_arc_wake.rs b/futures/tests/task_arc_wake.rs
index 36c7c67b..aedc15bc 100644
--- a/futures/tests/task_arc_wake.rs
+++ b/futures/tests/task_arc_wake.rs
@@ -44,18 +44,17 @@ fn create_from_arc() {
     assert_eq!(1, Arc::strong_count(&some_w));
 }
 
-// TODO: rustc regression: https://github.com/rust-lang/rust/issues/121600
-// #[test]
-// fn ref_wake_same() {
-//     let some_w = Arc::new(CountingWaker::new());
-//
-//     let w1: Waker = task::waker(some_w.clone());
-//     let w2 = task::waker_ref(&some_w);
-//     let w3 = w2.clone();
-//
-//     assert!(w1.will_wake(&w2));
-//     assert!(w2.will_wake(&w3));
-// }
+#[test]
+fn ref_wake_same() {
+    let some_w = Arc::new(CountingWaker::new());
+
+    let w1: Waker = task::waker(some_w.clone());
+    let w2 = task::waker_ref(&some_w);
+    let w3 = w2.clone();
+
+    assert!(w1.will_wake(&w2));
+    assert!(w2.will_wake(&w3));
+}
 
 #[test]
 fn proper_refcount_on_wake_panic() {