diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 000000000..708d20212
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,3 @@
+Our contribution policy can be found at [async.rs/contribute][policy].
+
+[policy]: https://async.rs/contribute/
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 55c32e744..a3eb933dc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,55 +1,209 @@
-on: pull_request
+name: CI
+
+on:
+  pull_request:
+  push:
+    branches:
+      - main
+      - staging
+      - trying
+
+env:
+  RUSTFLAGS: -Dwarnings
 
 jobs:
   build_and_test:
-    name: Build and test on ${{ matrix.os }}
+    name: Build and test
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
         os: [ubuntu-latest, windows-latest, macOS-latest]
+        rust: [nightly, beta, stable, 1.63]
 
     steps:
-    - uses: actions/checkout@master
+    - uses: actions/checkout@v3
 
-    - name: Install nightly
+    - name: Install ${{ matrix.rust }}
       uses: actions-rs/toolchain@v1
       with:
-        toolchain: nightly
+        toolchain: ${{ matrix.rust }}
         override: true
 
     - name: check
       uses: actions-rs/cargo@v1
       with:
         command: check
-        args: --all --benches --bins --examples --tests
+        args: --all --bins --tests
 
     - name: check unstable
       uses: actions-rs/cargo@v1
       with:
         command:  check
-        args: --features unstable --all --benches --bins --examples --tests
+        args: --features unstable --all --bins --examples --tests
+
+    - name: check wasm
+      uses: actions-rs/cargo@v1
+      with:
+        command:  check
+        target: wasm32-unknown-unknown
+        override: true
+        args: --features unstable --all --bins --tests
+
+    - name: check bench
+      uses: actions-rs/cargo@v1
+      if: matrix.rust == 'nightly'
+      with:
+        command:  check
+        args: --benches
+
+    - name: check std only
+      uses: actions-rs/cargo@v1
+      with:
+        command:  check
+        args: --no-default-features --features std
+
+    - name: check attributes
+      uses: actions-rs/cargo@v1
+      with:
+        command:  check
+        args: --features attributes
+
+    - name: build unstable only
+      uses: actions-rs/cargo@v1
+      with:
+        command:  build
+        args: --no-default-features --features unstable
 
     - name: tests
       uses: actions-rs/cargo@v1
       with:
         command: test
-        args: --all --doc --features unstable
+        args: --all --features "unstable attributes"
 
-  check_fmt_and_docs:
-    name: Checking fmt and docs
+  build__with_no_std:
+    name: Build with no-std
     runs-on: ubuntu-latest
+
     steps:
-    - uses: actions/checkout@master
+    - uses: actions/checkout@v3
+
+    - name: setup
+      run: |
+        rustup default nightly
+        rustup target add thumbv7m-none-eabi
+
+    - name: check no_std
+      uses: actions-rs/cargo@v1
+      with:
+        command:  check
+        args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps
 
-    - id: component
-      uses: actions-rs/components-nightly@v1
+  check_tokio_02_feature:
+    name: Check tokio02 feature
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
+    - name: check tokio02
+      uses: actions-rs/cargo@v1
       with:
-        component: rustfmt
+        command:  check
+        args: --all --features tokio02
+ 
+  check_io_safety_feature:
+    name: Check io_safety feature
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest]
+    steps:
+    - uses: actions/checkout@v3
+    - name: check io_safety
+      uses: actions-rs/cargo@v1
+      with:
+        command:  check
+        args: --all --features io_safety
+ 
+
+  cross:
+    name: Cross compile
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        target:
+          - i686-unknown-linux-gnu
+          - powerpc-unknown-linux-gnu
+          - powerpc64-unknown-linux-gnu
+#          - mips-unknown-linux-gnu
+          - arm-linux-androideabi
+
+    steps:
+    - uses: actions/checkout@v3
+
+    - name: Install nightly
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: nightly
+        override: true
+
+    - name: Install cross
+      run: cargo install cross
+
+    - name: check
+      run: cross check --all --target ${{ matrix.target }}
+
+    - name: check unstable
+      run: cross check --all --features unstable --target ${{ matrix.target }}
+
+    - name: test
+      run: cross test --all --features unstable --target ${{ matrix.target }}
+
+  check_wasm:
+    name: Check wasm targets
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        rust: [nightly, beta, stable]
+
+    steps:
+    - uses: actions/checkout@v3
+
+    - name: Install rust with wasm32-unknown-unknown
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: ${{ matrix.rust }}
+        target: wasm32-unknown-unknown
+        override: true
+
+    - name: Cache cargo registry
+      uses: actions/cache@v2
+      with:
+        path: ~/.cargo/registry
+        key: wasm32-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }}
+
+    - name: check
+      uses: actions-rs/cargo@v1
+      with:
+        command: check
+        args: --target wasm32-unknown-unknown 
+
+    - name: check unstable
+      uses: actions-rs/cargo@v1
+      with:
+        command: check
+        args: --target wasm32-unknown-unknown --tests --all --features unstable
+        
+  check_fmt_and_docs:
+    name: Checking fmt and docs
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
 
     - uses: actions-rs/toolchain@v1
       with:
-          toolchain: ${{ steps.component.outputs.toolchain }}
+          profile: minimal
+          toolchain: nightly
           override: true
+          components: rustfmt
 
     - name: setup
       run: |
@@ -64,4 +218,4 @@ jobs:
       run: cargo fmt --all -- --check
 
     - name: Docs
-      run: cargo doc --features docs,unstable
+      run: cargo doc --features docs
diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml
deleted file mode 100644
index c26affbf4..000000000
--- a/.github/workflows/clippy.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-on: pull_request
-
-name: Clippy check
-jobs:
-  clippy_check:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v1
-      - id: component
-        uses: actions-rs/components-nightly@v1
-        with:
-          component: clippy
-      - uses: actions-rs/toolchain@v1
-        with:
-            toolchain: ${{ steps.component.outputs.toolchain }}
-            override: true
-      - run: rustup component add clippy
-      - uses: actions-rs/clippy-check@v1
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0249f8ede..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-language: rust
-
-env:
-  - RUSTFLAGS="-D warnings"
-
-# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`.
-cache:
-  directories:
-    - /home/travis/.cargo
-
-# Don't cache the cargo registry because it's too big.
-before_cache:
-  - rm -rf /home/travis/.cargo/registry
-
-
-branches:
-  only:
-    - master
-    - staging
-    - trying
-
-matrix:
-  fast_finish: true
-  include:
-    - rust: nightly
-      os: linux
-
-    - rust: nightly
-      os: osx
-      osx_image: xcode9.2
-
-    - rust: nightly-x86_64-pc-windows-msvc
-      os: windows
-
-    - name: fmt
-      rust: nightly
-      os: linux
-      before_script: |
-        if ! rustup component add rustfmt; then
-          target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`;
-          echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead";
-          rustup toolchain install nightly-$target;
-          rustup default nightly-$target;
-          rustup component add rustfmt;
-        fi
-      script:
-        - cargo fmt --all -- --check
-
-    - name: docs
-      rust: nightly
-      os: linux
-      script:
-        - cargo doc --features docs,unstable
-
-    - name: book
-      rust: nightly
-      os: linux
-      before_script:
-        - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh
-        - cargo build # to find 'extern crate async_std' by `mdbook test`
-      script:
-        - mdbook build docs
-        - mdbook test -L ./target/debug/deps docs
-
-script:
-  - cargo check --all --benches --bins --examples --tests
-  - cargo check --features unstable --all --benches --bins --examples --tests
-  - cargo test --all --doc --features unstable
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cdce1340f..ebb3ffd80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,824 @@ All notable changes to async-std will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html).
 
-## [Unreleased]
+# [1.13.1] - 2025-02-21
+
+`async-std` has officially been discontinued. We recommend that all users and
+libraries migrate to the excellent [`smol`](https://github.com/smol-rs/smol/)
+project.
+
+# [1.13.0] - 2024-09-06
+
+## Added
+- IO Safety traits implementations
+
+## Changed
+- Various dependencies updates
+- Export `BufReadExt` and `SeekExt` from `async_std::io`
+
+# [1.12.0] - 2022-06-18
+
+## Added
+- `async_std::task::spawn_blocking` is now stabilized. We consider it a fundamental API for bridging between blocking code and async code, and we widely use it within async-std's own implementation.
+- Add `TryFrom` implementations to convert `TcpListener`, `TcpStream`, `UdpSocket`, `UnixDatagram`, `UnixListener`, and `UnixStream` to their synchronous equivalents, including putting them back into blocking mode.
+
+## Changed
+- async-std no longer depends on `num_cpus`; it uses functionality in the standard library instead (via `async-global-executor`).
+- Miscellaneous documentation fixes and cleanups.
+
+# [1.11.0] - 2022-03-22
+
+This release improves compile times by up to 55% on initial builds, and up to 75% on recompilation. Additionally we've added a few new APIs and made some tweaks.
+
+## Added
+- `TcpListener::into_incoming` to convert a `TcpListener` into a stream of incoming TCP connections
+
+## Removed
+- The internal `extension_trait` macro had been removed. This drastically improves compile times for `async-std`, but changes the way our documentation is rendered. This is a cosmetic change only, and all existing code should continue to work as it did before.
+
+## Changed
+- Some internal code has been de-macro-ified, making for quicker compile times.
+- We now use the default recursion limit.
+
+## Docs
+- Several docs improvements / fixes.
+
+# [1.10.0] - 2021-08-25
+
+This release comes with an assortment of small features and fixes.
+
+## Added
+- `File` now implements `Clone` so that `File`s can be passed into closures for use in `spawn_blocking`.
+    - `File`'s contents are already wrapped in `Arc`s, so the implementation of `Clone` is straightforward.
+- `task::try_current()` which returns a handle to the current task if called within the context of a task created by async-std.
+- `async_std::io` now re-exports `WriteExt` directly.
+
+## Fixed
+- `write!` now takes already written bytes into account on `File`.
+
+## Internal
+- `TcpStream` now properly makes use of vectored IO.
+- The `net::*::Incoming` implementations now do less allocation.
+
+## Docs
+- Several docs improvements / fixes.
+
+# [1.9.0] - 2021-01-15
+
+This patch stabilizes the `async_std::channel` submodule, removes the
+deprecated `sync::channel` types, and introduces the `tokio1` feature.
+
+## New Channels
+
+As part of our `1.8.0` release last month we introduced the new
+`async_std::channel` submodule and deprecated the unstable
+`async_std::sync::channel` types. You can read our full motivation for this
+change in the last patch notes. But the short version is that the old
+channels had some fundamental problems, and the `sync` submodule is a bit of
+a mess.
+
+This release of `async-std` promotes `async_std::channel` to stable, and
+fully removes the `async_std::sync::channel` types. In practice many
+libraries have already been upgraded to the new channels in the past month,
+and this will enable much of the ecosystem to switch off "unstable" versions
+of `async-std`.
+
+```rust
+use async_std::channel;
+
+let (sender, receiver) = channel::unbounded();
+
+assert_eq!(sender.send("Hello").await, Ok(()));
+assert_eq!(receiver.recv().await, Ok("Hello"));
+```
+
+## Tokio 1.0 compat
+
+The Tokio project recently released version 1.0 of their runtime, and the
+async-std team would like to congratulate the Tokio team on achieving this
+milestone.
+
+This release of `async-std` adds the `tokio1` feature flag, enabling Tokio's
+TLS constructors to be initialized within the `async-std` runtime. This is in
+addition to the `tokio02` and `tokio03` feature flags which we were already
+exposing.
+
+In terms of stability it's worth noting that we will continue to provide
+support for the `tokio02`, `tokio03`, and `tokio1` on the current major
+release line of `async-std`. These flags are part of our public API, and
+removing compat support for older Tokio versions is considered a breaking
+change.
+
+## Added
+
+- Added the `tokio1` feature ([#924](https://github.com/async-rs/async-std/pull/924))
+- Stabilized the `async_std::channel` submodule ([#934](https://github.com/async-rs/async-std/pull/934))
+
+## Removed
+
+- Removed deprecated `sync::channel` ([#933](https://github.com/async-rs/async-std/pull/933))
+
+## Fixed
+
+- Fixed a typo for [sic] `FuturesExt` trait ([#930](https://github.com/async-rs/async-std/pull/930))
+- Update the link to `cargo-edit` in the installation section of the docs ([#932](https://github.com/async-rs/async-std/pull/932))
+- Fixed a small typo for stream ([#926](https://github.com/async-rs/async-std/pull/926))
+
+## Internal
+
+- Updated `rand` to 0.8 ([#923](https://github.com/async-rs/async-std/pull/923))
+- Migrated `RwLock` and `Barrier` to use the `async-lock` crate internally ([#925](https://github.com/async-rs/async-std/pull/925))
+- Replaced uses of deprecated the `compare_and_swap` method with `compare_exchange` ([#927](https://github.com/async-rs/async-std/pull/927))
+
+# [1.8.0] - 2020-12-04
+
+This patch introduces `async_std::channel`, a new submodule for our async channels implementation. `channels` have been one of async-std's most requested features, and have existed as "unstable" for the past year. We've been cautious about stabilizing channels, and this caution turned out to be warranted: we realized our channels could hang indefinitely under certain circumstances, and people ended up expressing a need for unbounded channels.
+
+So today we're introducing the new `async_std::channel` submodule which exports the `async-channel` crate, and we're marking the older unstable `async_std::sync::channel` API as "deprecated". This release includes both APIs, but we intend to stabilize `async_std::channel` and remove the older API in January. This should give dependent projects a month to upgrade, though we can extend that if it proves to be too short.
+
+The rationale for adding a new top-level `channel` submodule, rather than extending `sync` is that the `std::sync` and `async_std::sync` submodule are a bit of a mess, and the libs team [has been talking about splitting `std::sync` up]([https://github.com/rust-lang/rfcs/pull/2788#discussion_r339092478](https://github.com/rust-lang/rfcs/pull/2788#discussion_r339092478)) into separate modules. The stdlib has to guarantee it'll forever be backwards compatible, but `async-std` does not (we fully expect a 2.0 once we have async closures & traits). So we're experimenting with this change before `std` does, with the expectation that this change can serve as a data point when the libs team decides how to proceed in std.
+
+### Added
+
+- `async_std::channel` as "unstable" #915
+- `async_std::process` as "unstable" #916
+
+### Fixed
+
+- Fixed mentions of the `tokio03` flags in the docs #909
+- Fixed a double drop issue in `StreamExt::cycle` #903
+
+### Internal
+
+- updated `pin-project` to `v0.2.0`
+
+# [1.7.0] - 2020-10-30
+
+This patch adds a feature to enable compatibility with the new `tokio` 0.3.0
+release, and updates internal dependencies.
+
+## Added
+
+- Add `tokio03` feature ([#895](https://github.com/async-rs/async-std/pull/895))
+
+## Internal
+
+- chore: update dependencies ([#897](https://github.com/async-rs/async-std/pull/897))
+
+# [1.6.5] - 2020-09-28
+
+## Fixed
+
+- Fix `TcpListener::incoming`. ([#889](https://github.com/async-rs/async-std/pull/889))
+- Fix tokio compatibility flag. ([#882](https://github.com/async-rs/async-std/pull/882))
+
+# [1.6.4] - 2020-09-16
+
+## Added
+
+- Added `UdpSocket::peek` and `UdpSocket::peek_from` ([#853](https://github.com/async-rs/async-std/pull/853))
+
+## Changed
+
+- Extracted the executor into [async-global-executor](https://github.com/async-rs/async-global-executor) ([#867](https://github.com/async-rs/async-std/pull/867))
+
+- Updated various dependencies
+
+## Fixed
+
+- Ensure `UnixStream::into_raw_fd` doesn't close the file descriptor ([#855](https://github.com/async-rs/async-std/issues/855))
+- Fixed wasm builds and ensured better dependency management depending on the compile target ([#863](https://github.com/async-rs/async-std/pull/863))
+
+
+# [1.6.3] - 2020-07-31
+
+## Added
+
+## Changed
+
+- Switched from smol to individual executor parts. ([#836](https://github.com/async-rs/async-std/pull/836))
+- Replaced internal `Mutex` implementation with `async-mutex`. ([#822](https://github.com/async-rs/async-std/pull/822))
+
+## Fixed
+
+- Added missing `Send` guards to `Stream::collect`. ([#665](https://github.com/async-rs/async-std/pull/665))
+
+
+# [1.6.2] - 2020-06-19
+
+## Added
+
+- Add `UdpSocket::peer_addr` ([#816](https://github.com/async-rs/async-std/pull/816))
+
+## Changed
+
+## Fixed
+
+- Ensure the reactor is running for sockets and timers ([#819](https://github.com/async-rs/async-std/pull/819)).
+- Avoid excessive polling in `flatten` and `flat_map` ([#701](https://github.com/async-rs/async-std/pull/701))
+
+
+# [1.6.1] - 2020-06-11
+
+## Added
+
+- Added `tokio02` feature flag, to allow compatibility usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)).
+
+## Changed
+
+- Removed unstable `stdio` lock methods, due to their unsoundness ([#807](https://github.com/async-rs/async-std/pull/807)).
+
+## Fixed
+
+- Fixed wrong slice index for file reading ([#802](https://github.com/async-rs/async-std/pull/802)).
+- Fixed recursive calls to `block_on` ([#799](https://github.com/async-rs/async-std/pull/799)) and ([#809](https://github.com/async-rs/async-std/pull/809)).
+- Remove `default` feature requirement for the `unstable` feature ([#806](https://github.com/async-rs/async-std/pull/806)).
+
+# [1.6.0] - 2020-05-22
+
+See `1.6.0-beta.1` and `1.6.0-beta.2`.
+
+# [1.6.0-beta.2] - 2020-05-19
+
+## Added 
+
+- Added an environment variable to configure the thread pool size of the runtime. ([#774](https://github.com/async-rs/async-std/pull/774))
+- Implement `Clone` for `UnixStream` ([#772](https://github.com/async-rs/async-std/pull/772))
+
+## Changed
+
+- For `wasm`, switched underlying `Timer` implementation to [`futures-timer`](https://github.com/async-rs/futures-timer). ([#776](https://github.com/async-rs/async-std/pull/776))
+
+## Fixed
+
+- Use `smol::block_on` to handle drop of `File`, avoiding nested executor panic. ([#768](https://github.com/async-rs/async-std/pull/768))
+
+# [1.6.0-beta.1] - 2020-05-07
+
+## Added
+
+- Added `task::spawn_local`. ([#757](https://github.com/async-rs/async-std/pull/757))
+- Added out of the box support for `wasm`. ([#757](https://github.com/async-rs/async-std/pull/757))
+- Added `JoinHandle::cancel` ([#757](https://github.com/async-rs/async-std/pull/757))
+- Added `sync::Condvar` ([#369](https://github.com/async-rs/async-std/pull/369))
+- Added `sync::Sender::try_send` and `sync::Receiver::try_recv` ([#585](https://github.com/async-rs/async-std/pull/585))
+- Added `no_std` support for `task`, `future` and `stream` ([#680](https://github.com/async-rs/async-std/pull/680))
+
+## Changed
+
+- Switched underlying runtime to [`smol`](https://github.com/stjepang/smol/). ([#757](https://github.com/async-rs/async-std/pull/757))
+- Switched implementation of `sync::Barrier` to use `sync::Condvar` like `std` does. ([#581](https://github.com/async-rs/async-std/pull/581))
+
+## Fixed
+
+- Allow compilation on 32 bit targets, by using `AtomicUsize` for `TaskId`. ([#756](https://github.com/async-rs/async-std/pull/756))
+
+# [1.5.0] - 2020-02-03
+
+[API Documentation](https://docs.rs/async-std/1.5.0/async-std)
+
+This patch includes various quality of life improvements to async-std.
+Including improved performance, stability, and the addition of various
+`Clone` impls that replace the use of `Arc` in many cases.
+
+## Added
+
+- Added links to various ecosystem projects from the README ([#660](https://github.com/async-rs/async-std/pull/660))
+- Added an example on `FromStream` for `Result<T, E>` ([#643](https://github.com/async-rs/async-std/pull/643))
+- Added `stream::pending` as "unstable" ([#615](https://github.com/async-rs/async-std/pull/615))
+- Added an example of `stream::timeout` to document the error flow ([#675](https://github.com/async-rs/async-std/pull/675))
+- Implement `Clone` for `DirEntry` ([#682](https://github.com/async-rs/async-std/pull/682))
+- Implement `Clone` for `TcpStream` ([#689](https://github.com/async-rs/async-std/pull/689))
+
+## Changed
+
+- Removed internal comment on `stream::Interval` ([#645](https://github.com/async-rs/async-std/pull/645))
+- The "unstable" feature can now be used without requiring the "default" feature ([#647](https://github.com/async-rs/async-std/pull/647))
+- Removed unnecessary trait bound on `stream::FlatMap` ([#651](https://github.com/async-rs/async-std/pull/651))
+- Updated the "broadcaster" dependency used by "unstable" to `1.0.0` ([#681](https://github.com/async-rs/async-std/pull/681))
+- Updated `async-task` to 1.2.1 ([#676](https://github.com/async-rs/async-std/pull/676))
+- `task::block_on` now parks after a single poll, improving performance in many cases ([#684](https://github.com/async-rs/async-std/pull/684))
+- Improved reading flow of the "client" part of the async-std tutorial ([#550](https://github.com/async-rs/async-std/pull/550))
+- Use `take_while` instead of `scan` in `impl` of `Product`, `Sum` and `FromStream` ([#667](https://github.com/async-rs/async-std/pull/667))
+- `TcpStream::connect` no longer uses a thread from the threadpool, improving performance ([#687](https://github.com/async-rs/async-std/pull/687))
+
+## Fixed
+
+- Fixed crate documentation typo ([#655](https://github.com/async-rs/async-std/pull/655))
+- Fixed documentation for `UdpSocket::recv` ([#648](https://github.com/async-rs/async-std/pull/648))
+- Fixed documentation for `UdpSocket::send` ([#671](https://github.com/async-rs/async-std/pull/671))
+- Fixed typo in stream documentation ([#650](https://github.com/async-rs/async-std/pull/650))
+- Fixed typo on `sync::JoinHandle` documentation ([#659](https://github.com/async-rs/async-std/pull/659))
+- Removed use of `std::error::Error::description` which failed CI ([#661](https://github.com/async-rs/async-std/pull/661))
+- Removed the use of rustfmt's unstable `format_code_in_doc_comments` option which failed CI ([#685](https://github.com/async-rs/async-std/pull/685))
+- Fixed a code typo in the `task::sleep` example ([#688](https://github.com/async-rs/async-std/pull/688))
+
+# [1.4.0] - 2019-12-20
+
+[API Documentation](https://docs.rs/async-std/1.4.0/async-std)
+
+This patch adds `Future::timeout`, providing a method counterpart to the
+`future::timeout` free function. And includes several bug fixes around missing
+APIs. Notably we're not shipping our new executor yet, first announced [on our
+blog](https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/).
+
+## Examples
+
+```rust
+use async_std::prelude::*;
+use async_std::future;
+use std::time::Duration;
+
+let fut = future::pending::<()>(); // This future will never resolve.
+let res = fut.timeout(Duration::from_millis(100)).await;
+assert!(res.is_err()); // The future timed out, returning an err.
+```
+
+## Added
+
+- Added `Future::timeout` as "unstable" [(#600)](https://github.com/async-rs/async-std/pull/600)
+
+## Fixes
+
+- Fixed a doc test and enabled it on CI [(#597)](https://github.com/async-rs/async-std/pull/597)
+- Fixed a rendering issue with the `stream` submodule documentation [(#621)](https://github.com/async-rs/async-std/pull/621)
+- `Write::write_fmt`'s future is now correctly marked as `#[must_use]` [(#628)](https://github.com/async-rs/async-std/pull/628)
+- Fixed the missing `io::Bytes` export [(#633)](https://github.com/async-rs/async-std/pull/633)
+- Fixed the missing `io::Chain` export [(#633)](https://github.com/async-rs/async-std/pull/633)
+- Fixed the missing `io::Take` export [(#633)](https://github.com/async-rs/async-std/pull/633)
+
+# [1.3.0] - 2019-12-12
+
+[API Documentation](https://docs.rs/async-std/1.3.0/async-std)
+
+This patch introduces `Stream::delay`, more methods on `DoubleEndedStream`,
+and improves compile times. `Stream::delay` is a new API that's similar to
+[`task::sleep`](https://docs.rs/async-std/1.2.0/async_std/task/fn.sleep.html),
+but can be passed as part of as stream, rather than as a separate block. This is
+useful for examples, or when manually debugging race conditions.
+
+## Examples
+
+```rust
+let start = Instant::now();
+let mut s = stream::from_iter(vec![0u8, 1]).delay(Duration::from_millis(200));
+
+// The first time will take more than 200ms due to delay.
+s.next().await;
+assert!(start.elapsed().as_millis() >= 200);
+
+// There will be no delay after the first time.
+s.next().await;
+assert!(start.elapsed().as_millis() <= 210);
+```
+
+## Added
+
+- Added `Stream::delay` as "unstable" [(#309)](https://github.com/async-rs/async-std/pull/309)
+- Added `DoubleEndedStream::next_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
+- Added `DoubleEndedStream::nth_back` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
+- Added `DoubleEndedStream::rfind` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
+- Added `DoubleEndedStream::rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
+- Added `DoubleEndedStream::try_rfold` as "unstable" [(#562)](https://github.com/async-rs/async-std/pull/562)
+- `stream::Once` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562)
+- `stream::FromIter` now implements `DoubleEndedStream` [(#562)](https://github.com/async-rs/async-std/pull/562)
+
+## Changed
+
+- Removed our dependency on `async-macros`, speeding up compilation [(#610)](https://github.com/async-rs/async-std/pull/610)
+
+## Fixes
+
+- Fixed a link in the task docs [(#598)](https://github.com/async-rs/async-std/pull/598)
+- Fixed the `UdpSocket::recv` example [(#603)](https://github.com/async-rs/async-std/pull/603)
+- Fixed a link to `task::block_on` [(#608)](https://github.com/async-rs/async-std/pull/608)
+- Fixed an incorrect API mention in `task::Builder` [(#612)](https://github.com/async-rs/async-std/pull/612)
+- Fixed leftover mentions of `futures-preview` [(#595)](https://github.com/async-rs/async-std/pull/595)
+- Fixed a typo in the tutorial [(#614)](https://github.com/async-rs/async-std/pull/614)
+- `<TcpStream as Write>::poll_close` now closes the write half of the stream [(#618)](https://github.com/async-rs/async-std/pull/618)
+
+# [1.2.0] - 2019-11-27
+
+[API Documentation](https://docs.rs/async-std/1.2.0/async-std)
+
+This patch includes some minor quality-of-life improvements, introduces a
+new `Stream::unzip` API, and adds verbose errors to our networking types.
+
+This means if you can't connect to a socket, you'll never have to wonder again
+*which* address it was you couldn't connect to, instead of having to go through
+the motions to debug what the address was.
+
+## Example
+
+Unzip a stream of tuples into two collections:
+
+```rust
+use async_std::prelude::*;
+use async_std::stream;
+
+let s = stream::from_iter(vec![(1,2), (3,4)]);
+
+let (left, right): (Vec<_>, Vec<_>) = s.unzip().await;
+
+assert_eq!(left, [1, 3]);
+assert_eq!(right, [2, 4]);
+```
+
+## Added
+
+- Added `Stream::unzip` as "unstable".
+- Added verbose errors to the networking types.
+
+## Changed
+
+- Enabled CI.
+- `Future::join` and `Future::try_join` can now join futures with different
+  output types.
+
+## Fixed
+
+- Fixed the docs and `Debug` output of `BufWriter`.
+- Fixed a bug in `Stream::throttle` that made it consume too much CPU.
+
+# [1.1.0] - 2019-11-21
+
+[API Documentation](https://docs.rs/async-std/1.1.0/async-std)
+
+This patch introduces a faster scheduler algorithm, `Stream::throttle`, and
+stabilizes `task::yield_now`. Additionally we're introducing several more stream
+APIs, bringing us to almost complete parity with the standard library.
+
+Furthermore our `path` submodule now returns more context in errors. So if
+opening a file fails, async-std will tell you *which* file was failed to open,
+making it easier to write and debug programs.
+
+## Examples
+
+```rust
+let start = Instant::now();
+
+let mut s = stream::interval(Duration::from_millis(5))
+    .throttle(Duration::from_millis(10))
+    .take(2);
+
+s.next().await;
+assert!(start.elapsed().as_millis() >= 5);
+
+s.next().await;
+assert!(start.elapsed().as_millis() >= 15);
+
+s.next().await;
+assert!(start.elapsed().as_millis() >= 25);
+```
+
+## Added
+
+- Added `Stream::throttle` as "unstable".
+- Added `Stream::count` as "unstable".
+- Added `Stream::max` as "unstable".
+- Added `Stream::successors` as "unstable".
+- Added `Stream::by_ref` as "unstable".
+- Added `Stream::partition` as "unstable".
+- Added contextual errors to the `path` submodule.
+- Added `os::windows::symlink_dir` as "unstable".
+- Added `os::windows::symlink_file` as "unstable".
+- Stabilized `task::yield_now`.
+
+## Fixes
+
+- We now ignore seek errors when rolling back failed `read` calls on `File`.
+- Fixed a bug where `Stream::max_by_key` was returning the wrong result.
+- Fixed a bug where `Stream::min_by_key` was returning the wrong result.
+
+## Changed
+
+- Applied various fixes to the tutorial.
+- Fixed an issue with Clippy.
+- Optimized an internal code generation macro, improving compilation speeds.
+- Removed an `Unpin` bound from `stream::Once`.
+- Removed various extra internal uses of `pin_mut!`.
+- Simplified `Stream::any` and `Stream::all`'s internals.
+- The `surf` example is now enabled again.
+- Tweaked some streams internals.
+- Updated `futures-timer` to 2.0.0, improving compilation speed.
+- Upgraded `async-macros` to 2.0.0.
+- `Stream::merge` now uses randomized ordering to reduce overall latency.
+- The scheduler is now more efficient by keeping a slot for the next task to
+  run. This is similar to Go's scheduler, and Tokio's scheduler.
+- Fixed the documentation of the `channel` types to link back to the `channel`
+  function.
+
+# [1.0.1] - 2019-11-12
+
+[API Documentation](https://docs.rs/async-std/1.0.1/async-std)
+
+We were seeing a regression in our fs performance, caused by too many
+long-running tasks. This patch fixes that regression by being more proactive
+about closing down idle threads.
+
+## Changes
+
+- Improved thread startup/shutdown algorithm in `task::spawn_blocking`.
+- Fixed a typo in the tutorial.
+
+# [1.0.0] - 2019-11-11
+
+[API Documentation](https://docs.rs/async-std/1.0.0/async-std)
+
+This release marks the `1.0.0` release of async-std; a major milestone for our
+development. This release itself mostly includes quality of life improvements
+for all of modules, including more consistent API bounds for a lot of our
+submodules.
+
+The biggest change is that we're now using the full semver range,
+`major.minor.patch`, and any breaking changes to our "stable" APIs will require
+an update of the `major` number.
+
+We're excited we've hit this milestone together with you all. Thank you!
+
+## Added
+
+- Added `Future::join` as "unstable", replacing `future::join!`.
+- Added `Future::try_join` as "unstable", replacing `future::try_join!`.
+- Enabled `stable` and `beta` channel testing on CI.
+- Implemented `FromIterator` and `Extend` for `PathBuf`.
+- Implemented `FromStream` for `PathBuf`.
+- Loosened the trait bounds of `io::copy` on "unstable".
+
+## Changed
+
+- Added a `Sync` bound to `RwLock`, resolving a memory safety issue.
+- Fixed a bug in `Stream::take_while` where it could continue after it should've
+  ended.
+- Fixed a bug where our `attributes` Cargo feature wasn't working as intended.
+- Improved documentation of `Stream::merge`, documenting  ordering guarantees.
+- Update doc imports in examples to prefer async-std's types.
+- Various quality of life improvements to the `future` submodule.
+- Various quality of life improvements to the `path` submodule.
+- Various quality of life improvements to the `stream` submodule.
+
+## Removed
+
+- Removed `future::join!` in favor of `Future::join`.
+- Removed `future::try_join!` in favor of `Future::try_join`.
+
+# [0.99.12] - 2019-11-07
+
+[API Documentation](https://docs.rs/async-std/0.99.12/async-std)
+
+This patch upgrades us to `futures` 0.3, support for `async/await` on Rust
+Stable, performance improvements, and brand new module-level documentation.
+
+## Added
+
+- Added `Future::flatten` as "unstable".
+- Added `Future::race` as "unstable" (replaces `future::select!`).
+- Added `Future::try_race` as "unstable" (replaces `future::try_select!`).
+- Added `Stderr::lock` as "unstable".
+- Added `Stdin::lock` as "unstable".
+- Added `Stdout::lock` as "unstable".
+- Added `Stream::copied` as "unstable".
+- Added `Stream::eq` as "unstable".
+- Added `Stream::max_by_key` as "unstable".
+- Added `Stream::min` as "unstable".
+- Added `Stream::ne` as "unstable".
+- Added `Stream::position` as "unstable".
+- Added `StreamExt` and `FutureExt` as enumerable in the `prelude`.
+- Added `TcpListener` and `TcpStream` integration tests.
+- Added `stream::from_iter`.
+- Added `sync::WakerSet` for internal use.
+- Added an example to handle both `IP v4` and `IP v6` connections.
+- Added the `default` Cargo feature.
+- Added the `attributes` Cargo feature.
+- Added the `std` Cargo feature.
+
+## Changed
+
+- Fixed a bug in the blocking threadpool where it didn't spawn more than one thread.
+- Fixed a bug with `Stream::merge` where sometimes it ended too soon.
+- Fixed a bug with our GitHub actions setup.
+- Fixed an issue where our channels could spuriously deadlock.
+- Refactored the `task` module.
+- Removed a deprecated GitHub action.
+- Replaced `futures-preview` with `futures`.
+- Replaced `lazy_static` with `once_cell`.
+- Replaced all uses of `VecDequeue` in the examples with `stream::from_iter`.
+- Simplified `sync::RwLock` using the internal `sync::WakerSet` type.
+- Updated the `path` submodule documentation to match std.
+- Updated the mod-level documentation to match std.
+
+## Removed
+
+- Removed `future::select!` (replaced by `Future::race`).
+- Removed `future::try_select!` (replaced by `Future::try_race`).
+
+# [0.99.11] - 2019-10-29
+
+This patch introduces `async_std::sync::channel`, a novel asynchronous port of
+the ultra-fast Crossbeam channels. This has been one of the most anticipated
+features for async-std, and we're excited to be providing a first version of
+this!
+
+In addition to channels, this patch has the regular list of new methods, types,
+and doc fixes.
+
+## Examples
+
+__Send and receive items from a channel__
+```rust
+// Create a bounded channel with a max-size of 1
+let (s, r) = channel(1);
+
+// This call returns immediately because there is enough space in the channel.
+s.send(1).await;
+
+task::spawn(async move {
+    // This call blocks the current task because the channel is full.
+    // It will be able to complete only after the first message is received.
+    s.send(2).await;
+});
+
+// Receive items from the channel
+task::sleep(Duration::from_secs(1)).await;
+assert_eq!(r.recv().await, Some(1));
+assert_eq!(r.recv().await, Some(2));
+```
+
+## Added
+- Added `Future::delay` as "unstable"
+- Added `Stream::flat_map` as "unstable"
+- Added `Stream::flatten` as "unstable"
+- Added `Stream::product` as "unstable"
+- Added `Stream::sum` as "unstable"
+- Added `Stream::min_by_key`
+- Added `Stream::max_by`
+- Added `Stream::timeout` as "unstable"
+- Added `sync::channel` as "unstable".
+- Added doc links from instantiated structs to the methods that create them.
+- Implemented `Extend` + `FromStream` for `PathBuf`.
+
+## Changed
+- Fixed an issue with `block_on` so it works even when nested.
+- Fixed issues with our Clippy check on CI.
+- Replaced our uses of `cfg_if` with our own macros, simplifying the codebase.
+- Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs).
+- Updated the module-level documentation for `stream` and `sync`.
+- Various typos and grammar fixes.
+- Removed redundant file flushes, improving the performance of `File` operations
+
+## Removed
+Nothing was removed in this release.
+
+# [0.99.10] - 2019-10-16
+
+This patch stabilizes several core concurrency macros, introduces async versions
+of `Path` and `PathBuf`, and adds almost 100 other commits.
+
+## Examples
+
+__Asynchronously read directories from the filesystem__
+```rust
+use async_std::fs;
+use async_std::path::Path;
+use async_std::prelude::*;
+
+let path = Path::new("/laputa");
+let mut dir = fs::read_dir(&path).await.unwrap();
+while let Some(entry) = dir.next().await {
+    if let Ok(entry) = entry {
+        println!("{:?}", entry.path());
+    }
+}
+```
+
+__Cooperatively reschedule the current task on the executor__
+```rust
+use async_std::prelude::*;
+use async_std::task;
+
+task::spawn(async {
+    let x = fibonacci(1000); // Do expensive work
+    task::yield_now().await;  // Allow other tasks to run
+    x + fibonacci(100)       // Do more work
+})
+```
+
+__Create an interval stream__
+```rust
+use async_std::prelude::*;
+use async_std::stream;
+use std::time::Duration;
+
+let mut interval = stream::interval(Duration::from_secs(4));
+while let Some(_) = interval.next().await {
+    println!("prints every four seconds");
+}
+```
+
+## Added
+
+- Added `FutureExt` to the `prelude`, allowing us to extend `Future`
+- Added `Stream::cmp`
+- Added `Stream::ge`
+- Added `Stream::last`
+- Added `Stream::le`
+- Added `Stream::lt`
+- Added `Stream::merge` as "unstable", replacing `stream::join!`
+- Added `Stream::partial_cmp`
+- Added `Stream::take_while`
+- Added `Stream::try_fold`
+- Added `future::IntoFuture` as "unstable"
+- Added `io::BufRead::split`
+- Added `io::Write::write_fmt`
+- Added `print!`, `println!`, `eprint!`, `eprintln!` macros as "unstable"
+- Added `process` as "unstable", re-exporting std types only for now
+- Added `std::net` re-exports to the `net` submodule
+- Added `std::path::PathBuf` with all associated methods
+- Added `std::path::Path` with all associated methods
+- Added `stream::ExactSizeStream` as "unstable"
+- Added `stream::FusedStream` as "unstable"
+- Added `stream::Product`
+- Added `stream::Sum`
+- Added `stream::from_fn`
+- Added `stream::interval` as "unstable"
+- Added `stream::repeat_with`
+- Added `task::spawn_blocking` as "unstable", replacing `task::blocking`
+- Added `task::yield_now`
+- Added `write!` and `writeln!` macros as "unstable"
+- Stabilized `future::join!` and `future::try_join!`
+- Stabilized `future::timeout`
+- Stabilized `path`
+- Stabilized `task::ready!`
+
+## Changed
+
+- Fixed `BufWriter::into_inner` so it calls `flush` before yielding
+- Refactored `io::BufWriter` internals
+- Refactored `net::ToSocketAddrs` internals
+- Removed Travis CI entirely
+- Rewrote the README.md
+- Stabilized `io::Cursor`
+- Switched bors over to use GitHub actions
+- Updated the `io` documentation to match std's `io` docs
+- Updated the `task` documentation to match std's `thread` docs
+
+## Removed
+
+- Removed the "unstable" `stream::join!` in favor of `Stream::merge`
+- Removed the "unstable" `task::blocking` in favor of `task::spawn_blocking`
+
+# [0.99.9] - 2019-10-08
+
+This patch upgrades our `futures-rs` version, allowing us to build on the 1.39
+beta. Additionally we've introduced `map` and `for_each` to `Stream`. And we've
+added about a dozen new `FromStream` implementations for `std` types, bringing
+us up to par with std's `FromIterator` implementations.
+
+And finally we've added a new "unstable" `task::blocking` function which can be
+used to convert blocking code into async code using a threadpool. We've been
+using this internally for a while now to async-std to power our `fs` and
+`net::SocketAddr` implementations. With this patch userland code now finally has
+access to this too.
+
+## Example
+
+__Create a stream of tuples, and collect into a hashmap__
+```rust
+let a = stream::once(1u8);
+let b = stream::once(0u8);
+
+let s = a.zip(b);
+
+let map: HashMap<u8, u8> = s.collect().await;
+assert_eq!(map.get(&1), Some(&0u8));
+```
+
+__Spawn a blocking task on a dedicated threadpool__
+```rust
+task::blocking(async {
+    println!("long-running task here");
+}).await;
+```
+
+## Added
+
+- Added `stream::Stream::map`
+- Added `stream::Stream::for_each`
+- Added `stream::Stream::try_for_each`
+- Added `task::blocking` as "unstable"
+- Added `FromStream` for all `std::{option, collections, result, string, sync}` types.
+- Added the `path` submodule as "unstable".
+
+## Changed
+
+- Updated `futures-preview` to `0.3.0-alpha.19`, allowing us to build on `rustc 1.39.0-beta`.
+- As a consequence of this upgrade, all of our concrete stream implementations
+  now make use of `Stream::size_hint` to optimize internal allocations.
+- We now use GitHub Actions through [actions-rs](https://github.com/actions-rs),
+  in addition to Travis CI. We intend to fully switch in the near future.
+- Fixed a bug introduced in 0.99.6 where Unix Domain Listeners would sometimes become unresponsive.
+- Updated our `sync::Barrier` docs to match std.
+- Updated our `stream::FromStream` docs to match std's `FromIterator`.
 
 # [0.99.8] - 2019-09-28
 
@@ -130,7 +947,27 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
 
 - Initial beta release
 
-[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.8...HEAD
+[Unreleased]: https://github.com/async-rs/async-std/compare/v1.7.0...HEAD
+[1.7.0]: https://github.com/async-rs/async-std/compare/v1.6.5...1.7.0
+[1.6.5]: https://github.com/async-rs/async-std/compare/v1.6.4...v1.6.5
+[1.6.4]: https://github.com/async-rs/async-std/compare/v1.6.3...v1.6.4
+[1.6.3]: https://github.com/async-rs/async-std/compare/v1.6.2...v1.6.3
+[1.6.2]: https://github.com/async-rs/async-std/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0
+[1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2
+[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1
+[1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/async-rs/async-std/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/async-rs/async-std/compare/v1.0.0...v1.0.1
+[1.0.0]: https://github.com/async-rs/async-std/compare/v0.99.12...v1.0.0
+[0.99.12]: https://github.com/async-rs/async-std/compare/v0.99.11...v0.99.12
+[0.99.11]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11
+[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10
+[0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9
 [0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8
 [0.99.7]: https://github.com/async-rs/async-std/compare/v0.99.6...v0.99.7
 [0.99.6]: https://github.com/async-rs/async-std/compare/v0.99.5...v0.99.6
diff --git a/Cargo.toml b/Cargo.toml
index 5b988bb47..fed440172 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,55 +1,115 @@
 [package]
 name = "async-std"
-version = "0.99.8"
+version = "1.13.1"
 authors = [
   "Stjepan Glavina <stjepang@gmail.com>",
   "Yoshua Wuyts <yoshuawuyts@gmail.com>",
+  "Friedel Ziegelmayer <me@dignifiedquire.com>",
   "Contributors to async-std",
 ]
 edition = "2018"
-license = "Apache-2.0/MIT"
+rust-version = "1.63"
+license = "Apache-2.0 OR MIT"
 repository = "https://github.com/async-rs/async-std"
-homepage = "https://github.com/async-rs/async-std"
-documentation = "https://docs.rs/async-std"
-description = "Async version of the Rust standard library"
+homepage = "https://async.rs"
+description = "Deprecated in favor of `smol` - Async version of the Rust standard library"
 keywords = ["async", "await", "future", "std", "task"]
 categories = ["asynchronous", "concurrency", "network-programming"]
-readme = "README.md"
 
 [package.metadata.docs.rs]
 features = ["docs"]
 rustdoc-args = ["--cfg", "feature=\"docs\""]
 
 [features]
-docs = ["broadcaster"]
-unstable = ["broadcaster"]
+default = [
+  "std",
+  "async-global-executor",
+  "async-io",
+  "futures-lite",
+  "kv-log-macro",
+  "log",
+  "pin-project-lite",
+  "gloo-timers",
+]
+docs = ["attributes", "unstable", "default"]
+unstable = [
+  "std",
+  "async-io",
+  "async-process",
+]
+attributes = ["async-attributes"]
+std = [
+  "alloc",
+  "crossbeam-utils",
+  "futures-core/std",
+  "futures-io",
+  "memchr",
+  "once_cell",
+  "pin-utils",
+  "slab",
+  "wasm-bindgen-futures",
+  "futures-channel",
+  "async-channel",
+  "async-lock",
+]
+alloc = [
+  "futures-core/alloc",
+  "pin-project-lite",
+]
+tokio1 = ["async-global-executor/tokio"]
+tokio02 = ["async-global-executor/tokio02"]
+tokio03 = ["async-global-executor/tokio03"]
+io_safety = []
 
 [dependencies]
-async-macros = "1.0.0"
-async-task = "1.0.0"
-cfg-if = "0.1.9"
-crossbeam-channel = "0.3.9"
-crossbeam-deque = "0.7.1"
-futures-core-preview = "=0.3.0-alpha.19"
-futures-io-preview = "=0.3.0-alpha.19"
-futures-timer = "0.4.0"
-lazy_static = "1.4.0"
-log = { version = "0.4.8", features = ["kv_unstable"] }
-memchr = "2.2.1"
-mio = "0.6.19"
-mio-uds = "0.6.7"
-num_cpus = "1.10.1"
-pin-utils = "0.1.0-alpha.4"
-slab = "0.4.2"
-kv-log-macro = "1.0.4"
-broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] }
+async-attributes = { version = "1.1.2", optional = true }
+async-lock = { version = "3.1.0", optional = true }
+crossbeam-utils = { version = "0.8.0", optional = true }
+futures-core = { version = "0.3.4", optional = true, default-features = false }
+futures-io = { version = "0.3.4", optional = true }
+kv-log-macro = { version = "1.0.6", optional = true }
+log = { version = "0.4.8", features = ["kv_unstable"], optional = true }
+memchr = { version = "2.3.3", optional = true }
+once_cell = { version = "1.3.1", optional = true }
+pin-project-lite = { version = "0.2.0", optional = true }
+pin-utils = { version = "0.1.0-alpha.4", optional = true }
+slab = { version = "0.4.2", optional = true }
+async-channel = { version = "1.8.0", optional = true }
+
+# dev dependency, but they are not allowed to be optional :/
+surf = { version = "2.0.0", optional = true }
+
+
+[target.'cfg(not(target_os = "unknown"))'.dependencies]
+async-global-executor = { version = "2.4.0", optional = true, features = ["async-io"] }
+async-io = { version = "2.2.0", optional = true }
+futures-lite = { version = "2.0.0", optional = true }
+async-process = { version = "2.0.0", optional = true }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+gloo-timers = { version = "0.3.0", features = ["futures"], optional = true }
+wasm-bindgen-futures = { version = "0.4.10", optional = true }
+futures-channel = { version = "0.3.4", optional = true }
+
+[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
+wasm-bindgen-test = "0.3.10"
+getrandom = { version = "0.2.0", features = ["js"] }
 
 [dev-dependencies]
-femme = "1.2.0"
-# surf = "1.0.2"
-tempdir = "0.3.7"
-futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] }
-
-# These are used by the book for examples
-futures-channel-preview = "=0.3.0-alpha.19"
-futures-util-preview = "=0.3.0-alpha.19"
+femme = "2.1.1"
+rand = "0.8.0"
+tempfile = "3.1.0"
+futures = "0.3.4"
+rand_xorshift = "0.3.0"
+
+[[test]]
+name = "stream"
+required-features = ["unstable"]
+
+[[example]]
+name = "tcp-ipv4-and-6-echo"
+required-features = ["unstable"]
+
+[[example]]
+name = "surf-web"
+required-features = ["surf"]
diff --git a/README.md b/README.md
index 978e1c0e3..f432898dc 100644
--- a/README.md
+++ b/README.md
@@ -1,142 +1,99 @@
-# Async version of the Rust standard library
-
-[![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std)
-[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std)
-[![Cargo](https://img.shields.io/crates/v/async-std.svg)](https://crates.io/crates/async-std)
-[![Documentation](https://docs.rs/async-std/badge.svg)](https://docs.rs/async-std)
-[![chat](https://img.shields.io/discord/598880689856970762.svg?logo=discord)](https://discord.gg/JvZeVNe)
-
-This crate provides an async version of [`std`]. It provides all the interfaces you
-are used to, but in an async version and ready for Rust's `async`/`await` syntax.
+# `async-std` has been discontinued; use `smol` instead
+
+We created `async-std` to demonstrate the value of making a library as close to
+`std` as possible, but async. We think that demonstration was successful, and
+we hope it will influence future design and development directions of async in
+`std`. However, in the meantime, the [`smol`](https://github.com/smol-rs/smol/)
+project came about and provided a great executor and libraries for asynchronous
+use in the Rust ecosystem. We think that resources would be better spent
+consolidating around `smol`, rather than continuing to provide occasional
+maintenance of `async-std`. As such, we recommend that all users of
+`async-std`, and all libraries built on `async-std`, switch to `smol` instead.
+
+In addition to the `smol` project as a direct replacement, you may find other
+parts of the futures ecosystem useful, including `futures-concurrency`,
+`async-io`, `futures-lite`, and `async-compat`.
+
+
+<div align="center">
+  <h3>
+    <a href="https://docs.rs/async-std">
+      API Docs
+    </a>
+    <span> | </span>
+    <a href="https://book.async.rs">
+      Book
+    </a>
+  </h3>
+</div>
+
+<br/>
+
+This crate provides an async version of [`std`]. It provides all the interfaces
+you are used to, but in an async version and ready for Rust's `async`/`await`
+syntax.
 
 [`std`]: https://doc.rust-lang.org/std/index.html
 
-## Documentation
+## Features
 
-`async-std` comes with [extensive API documentation][docs] and a [book][book].
+- __Modern:__ Built from the ground up for `std::future` and `async/await` with
+    blazing fast compilation time.
+- __Fast:__ Our robust allocator and threadpool designs provide ultra-high
+    throughput with predictably low latency.
+- __Intuitive:__ Complete parity with the stdlib means you only need to learn
+    APIs once.
+- __Clear:__ [Detailed documentation][docs] and [accessible guides][book] mean
+    using async Rust was never easier.
 
 [docs]: https://docs.rs/async-std
 [book]: https://book.async.rs
 
-## Quickstart
-
-Add the following lines to your `Cargo.toml`:
-
-```toml
-[dependencies]
-async-std = "0.99"
-```
-
-Or use [cargo add][cargo-add] if you have it installed:
-
-```sh
-$ cargo add async-std
-```
-
-[cargo-add]: https://github.com/killercup/cargo-edit
-
-## Hello world
+## Examples
 
 ```rust
 use async_std::task;
 
-fn main() {
-    task::block_on(async {
-        println!("Hello, world!");
-    })
-}
-```
-
-## Low-Friction Sockets with Built-In Timeouts
-
-```rust
-use std::time::Duration;
-
-use async_std::{
-    prelude::*,
-    task,
-    io,
-    net::TcpStream,
-};
-
-async fn get() -> io::Result<Vec<u8>> {
-    let mut stream = TcpStream::connect("example.com:80").await?;
-    stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?;
-
-    let mut buf = vec![];
-
-    io::timeout(Duration::from_secs(5), async {
-        stream.read_to_end(&mut buf).await?;
-        Ok(buf)
-    }).await
+async fn say_hello() {
+    println!("Hello, world!");
 }
 
 fn main() {
-    task::block_on(async {
-        let raw_response = get().await.expect("request");
-        let response = String::from_utf8(raw_response)
-            .expect("utf8 conversion");
-        println!("received: {}", response);
-    });
+    task::block_on(say_hello())
 }
 ```
 
-## Features
-
-`async-std` is strongly commited to following semver. This means your code won't
-break unless _you_ decide to upgrade.
-
-However every now and then we come up with something that we think will work
-_great_ for `async-std`, and we want to provide a sneak-peek so you can try it
-out. This is what we call _"unstable"_ features. You can try out the unstable
-features by enabling the `unstable` feature in your `Cargo.toml` file:
-
-```toml
-[dependencies.async-std]
-version = "0.99"
-features = ["unstable"]
-```
-
-Just be careful when using these features, as they may change between
-versions.
-
-## Take a look around
-
-Clone the repo:
-
-```
-git clone git@github.com:async-rs/async-std.git && cd async-std
-```
-
-Generate docs:
-
-```
-cargo doc --features docs.rs --open
-```
+More examples, including networking and file access, can be found in our
+[`examples`] directory and in our [documentation].
 
-Check out the [examples](examples). To run an example:
+[`examples`]: https://github.com/async-rs/async-std/tree/HEAD/examples
+[documentation]: https://docs.rs/async-std#examples
+[`task::block_on`]: https://docs.rs/async-std/*/async_std/task/fn.block_on.html
+[`"attributes"` feature]: https://docs.rs/async-std/#features
 
-```
-cargo run --example hello-world
-```
-
-## Contributing
+## Philosophy
 
-See [our contribution document][contribution].
+We believe Async Rust should be as easy to pick up as Sync Rust. We also believe
+that the best API is the one you already know. And finally, we believe that
+providing an asynchronous counterpart to the standard library is the best way
+stdlib provides a reliable basis for both performance and productivity.
 
-[contribution]: https://async.rs/contribute
+Async-std is the embodiment of that vision. It combines single-allocation task
+creation, with an adaptive lock-free executor, threadpool and network driver to
+create a smooth system that processes work at a high pace with low latency,
+using Rust's familiar stdlib API.
 
 ## License
 
-Licensed under either of
-
- * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
-
-at your option.
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
 
-#### Contribution
+<br/>
 
+<sub>
 Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
-dual licensed as above, without any additional terms or conditions.
+for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
+be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/benches/mutex.rs b/benches/mutex.rs
new file mode 100644
index 000000000..4f1910a6f
--- /dev/null
+++ b/benches/mutex.rs
@@ -0,0 +1,40 @@
+#![feature(test)]
+
+extern crate test;
+
+use async_std::sync::{Arc, Mutex};
+use async_std::task;
+use test::Bencher;
+
+#[bench]
+fn create(b: &mut Bencher) {
+    b.iter(|| Mutex::new(()));
+}
+
+#[bench]
+fn contention(b: &mut Bencher) {
+    b.iter(|| task::block_on(run(10, 1000)));
+}
+
+#[bench]
+fn no_contention(b: &mut Bencher) {
+    b.iter(|| task::block_on(run(1, 10000)));
+}
+
+async fn run(task: usize, iter: usize) {
+    let m = Arc::new(Mutex::new(()));
+    let mut tasks = Vec::new();
+
+    for _ in 0..task {
+        let m = m.clone();
+        tasks.push(task::spawn(async move {
+            for _ in 0..iter {
+                let _ = m.lock().await;
+            }
+        }));
+    }
+
+    for t in tasks {
+        t.await;
+    }
+}
diff --git a/benches/task.rs b/benches/task.rs
new file mode 100644
index 000000000..b31447130
--- /dev/null
+++ b/benches/task.rs
@@ -0,0 +1,11 @@
+#![feature(test)]
+
+extern crate test;
+
+use async_std::task;
+use test::Bencher;
+
+#[bench]
+fn block_on(b: &mut Bencher) {
+    b.iter(|| task::block_on(async {}));
+}
diff --git a/bors.toml b/bors.toml
deleted file mode 100644
index 359f8947b..000000000
--- a/bors.toml
+++ /dev/null
@@ -1 +0,0 @@
-status = ["continuous-integration/travis-ci/push"]
diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh
index 01c87f883..52422b970 100755
--- a/ci/install-mdbook.sh
+++ b/ci/install-mdbook.sh
@@ -1,7 +1,7 @@
 set -euxo pipefail
 
 # Based on the Rust-Embedded WG's book CI
-# https://github.com/rust-embedded/book/blob/master/ci/install.sh
+# https://github.com/rust-embedded/book/blob/HEAD/ci/install.sh
 
 main() {
     # Note - this will only accept releases tagged with v0.3.x
diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md
index d679825e8..9e828f660 100644
--- a/docs/src/SUMMARY.md
+++ b/docs/src/SUMMARY.md
@@ -2,7 +2,6 @@
 
 - [Introduction](./introduction.md)
   - [Welcome to `async-std`!](./overview/async-std.md)
-  - [`std::future` and `futures-rs`](./overview/std-and-library-futures.md)
   - [Stability guarantees](./overview/stability-guarantees.md)
 - [Async concepts using async-std](./concepts.md)
   - [Futures](./concepts/futures.md)
@@ -19,8 +18,9 @@
   - [Clean Shutdown](./tutorial/clean_shutdown.md)
   - [Handling Disconnection](./tutorial/handling_disconnection.md)
   - [Implementing a Client](./tutorial/implementing_a_client.md)
-- [TODO: Async Patterns](./patterns.md)
+- [Async Patterns](./patterns.md)
   - [TODO: Collected Small Patterns](./patterns/small-patterns.md)
+  - [Production-Ready Accept Loop](./patterns/accept-loop.md)
 - [Security practices](./security/index.md)
   - [Security Disclosures and Policy](./security/policy.md)
 - [Glossary](./glossary.md)
diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md
index 67db720ca..7d9cc6360 100644
--- a/docs/src/concepts/futures.md
+++ b/docs/src/concepts/futures.md
@@ -24,11 +24,7 @@ To sum up: Rust gives us the ability to safely abstract over important propertie
 
 ## An easy view of computation
 
-While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us:
-
-- computation is a sequence of composable operations
-- they can branch based on a decision
-- they either run to succession and yield a result, or they can yield an error
+While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: A sequence of composable operations which can branch based on a decision, run to succession and yield a result or yield an error
 
 ## Deferring computation
 
@@ -136,11 +132,11 @@ When executing 2 or more of these functions at the same time, our runtime system
 
 ## Conclusion
 
-Working from values, we searched for something that expresses *working towards a value available sometime later*. From there, we talked about the concept of polling.
+Working from values, we searched for something that expresses *working towards a value available later*. From there, we talked about the concept of polling.
 
 A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple.
 
-Next, we will introduce you to `tasks`, which we need to actually *run* Futures.
+Next, we will introduce you to `tasks`, which we will use to actually *run* Futures.
 
 [^1]: Two parties reading while it is guaranteed that no one is writing is always safe.
 
diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md
index d4037a3bb..c3dbbe202 100644
--- a/docs/src/concepts/tasks.md
+++ b/docs/src/concepts/tasks.md
@@ -2,7 +2,7 @@
 
 Now that we know what Futures are, we want to run them!
 
-In `async-std`, the [`tasks`][tasks] module is responsible for this. The simplest way is using the `block_on` function:
+In `async-std`, the [`task`][tasks] module is responsible for this. The simplest way is using the `block_on` function:
 
 ```rust,edition2018
 # extern crate async_std;
@@ -80,7 +80,7 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread`
 
 ## Blocking
 
-`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
+`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and of itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
 
 ```rust,edition2018
 # extern crate async_std;
@@ -118,7 +118,7 @@ thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 ```
 
-While panicing a spawned task will abort:
+While panicking a spawned task will abort:
 
 ```rust,edition2018,should_panic
 # extern crate async_std;
diff --git a/docs/src/overview/async-std.md b/docs/src/overview/async-std.md
index 2b59ffb03..0086599f1 100644
--- a/docs/src/overview/async-std.md
+++ b/docs/src/overview/async-std.md
@@ -4,4 +4,4 @@
 
 `async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib.  But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`.
 
-[organization]: https://github.com/async-rs/async-std
+[organization]: https://github.com/async-rs
diff --git a/docs/src/overview/stability-guarantees.md b/docs/src/overview/stability-guarantees.md
index 84bb68d7d..8c14e20fd 100644
--- a/docs/src/overview/stability-guarantees.md
+++ b/docs/src/overview/stability-guarantees.md
@@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte
 
 ## Security fixes
 
-Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 month_ of ahead notice.
+Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give a notice at least _3 months_ ahead.
 
 ## Credits
 
diff --git a/docs/src/overview/std-and-library-futures.md b/docs/src/overview/std-and-library-futures.md
deleted file mode 100644
index 5c5f96f5f..000000000
--- a/docs/src/overview/std-and-library-futures.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# `std::future` and `futures-rs`
-
-Rust has two kinds of types commonly referred to as `Future`: 
-
-
-- the first is `std::future::Future` from Rust’s [standard library](https://doc.rust-lang.org/std/future/trait.Future.html). 
-- the second is `futures::future::Future` from the [futures-rs crate](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html), currently released as `futures-preview`. 
-
-The future defined in the [futures-rs](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/prelude/trait.Future.html) crate was the original implementation of the type. To enable the `async/await` syntax, the core Future trait was moved into Rust’s standard library and became `std::future::Future`. In some sense, the `std::future::Future` can be seen as a minimal subset of `futures::future::Future`.
-
-It is critical to understand the difference between `std::future::Future` and `futures::future::Future`, and the approach that `async-std` takes towards them. In itself, `std::future::Future` is not something you want to interact with as a user—except by calling `.await` on it. The inner workings of `std::future::Future` are mostly of interest to people implementing `Future`. Make no mistake—this is very useful! Most of the functionality that used to be defined on `Future` itself has been moved to an extension trait called [`FuturesExt`](https://docs.rs/futures-preview/0.3.0-alpha.17/futures/future/trait.FutureExt.html). From this information, you might be able to infer that the `futures` library serves as an extension to the core Rust async features.
-
-In the same tradition as `futures`, `async-std` re-exports the core `std::future::Future` type. You can actively opt into the extensions provided by the `futures-preview` crate by adding it to your `Cargo.toml` and importing `FuturesExt`.
-
-## Interfaces and Stability
-
- `async-std` aims to be a stable and reliable library, at the level of the Rust standard library. This also means that we don't rely on the `futures` library for our interface. Yet, we appreciate that many users have come to like the conveniences that `futures-rs` brings. For that reason, `async-std` implements all `futures` traits for its types.
- 
- Luckily, the approach from above gives you full flexibility. If you care about stability a lot, you can just use `async-std` as is. If you prefer the `futures` library interfaces, you link those in. Both uses are first class.
-
-## `async_std::future`
-
-There’s some support functions that we see as important for working with futures of any kind. These can be found in the `async_std::future` module and are covered by our stability guarantees.
-
-## Streams and Read/Write/Seek/BufRead traits
-
-Due to limitations of the Rust compiler, those are currently implemented in `async_std`, but cannot be implemented by users themselves.
diff --git a/docs/src/patterns/accept-loop.md b/docs/src/patterns/accept-loop.md
new file mode 100644
index 000000000..4bc43e7d1
--- /dev/null
+++ b/docs/src/patterns/accept-loop.md
@@ -0,0 +1,266 @@
+# Production-Ready Accept Loop
+
+A production-ready accept loop needs the following things:
+1. Handling errors
+2. Limiting the number of simultanteous connections to avoid deny-of-service
+   (DoS) attacks
+
+
+## Handling errors
+
+There are two kinds of errors in an accept loop:
+1. Per-connection errors. The system uses them to notify that there was a
+   connection in the queue and it's dropped by the peer. Subsequent connections
+   can be already queued so next connection must be accepted immediately.
+2. Resource shortages. When these are encountered it doesn't make sense to
+   accept the next socket immediately. But the listener stays active, so you server
+   should try to accept socket later.
+
+Here is the example of a per-connection error (printed in normal and debug mode):
+```
+Error: Connection reset by peer (os error 104)
+Error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" }
+```
+
+And the following is the most common example of a resource shortage error:
+```
+Error: Too many open files (os error 24)
+Error: Os { code: 24, kind: Other, message: "Too many open files" }
+```
+
+### Testing Application
+
+To test your application for these errors try the following (this works
+on unixes only).
+
+Lower limits and start the application:
+```
+$ ulimit -n 100
+$ cargo run --example your_app
+   Compiling your_app v0.1.0 (/work)
+    Finished dev [unoptimized + debuginfo] target(s) in 5.47s
+     Running `target/debug/examples/your_app`
+Server is listening on: http://127.0.0.1:1234
+```
+Then in another console run the [`wrk`] benchmark tool:
+```
+$ wrk -c 1000 http://127.0.0.1:1234
+Running 10s test @ http://localhost:8080/
+  2 threads and 1000 connections
+$ telnet localhost 1234
+Trying ::1...
+Connected to localhost.
+```
+
+Important is to check the following things:
+
+1. The application doesn't crash on error (but may log errors, see below)
+2. It's possible to connect to the application again once load is stopped
+   (few seconds after `wrk`). This is what `telnet` does in example above,
+   make sure it prints `Connected to <hostname>`.
+3. The `Too many open files` error is logged in the appropriate log. This
+   requires to set "maximum number of simultaneous connections" parameter (see
+   below) of your application to a value greater then `100` for this example.
+4. Check CPU usage of the app while doing a test. It should not occupy 100%
+   of a single CPU core (it's unlikely that you can exhaust CPU by 1000
+   connections in Rust, so this means error handling is not right).
+
+#### Testing non-HTTP applications
+
+If it's possible, use the appropriate benchmark tool and set the appropriate
+number of connections. For example `redis-benchmark` has a `-c` parameter for
+that, if you implement redis protocol.
+
+Alternatively, can still use `wrk`, just make sure that connection is not
+immediately closed. If it is, put a temporary timeout before handing
+the connection to the protocol handler, like this:
+
+```rust,edition2018
+# extern crate async_std;
+# use std::time::Duration;
+# use async_std::{
+#     net::{TcpListener, ToSocketAddrs},
+#     prelude::*,
+# };
+#
+# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+#
+#async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
+#    let listener = TcpListener::bind(addr).await?;
+#    let mut incoming = listener.incoming();
+while let Some(stream) = incoming.next().await {
+    task::spawn(async {
+        task::sleep(Duration::from_secs(10)).await; // 1
+        connection_loop(stream).await;
+    });
+}
+#     Ok(())
+# }
+```
+
+1. Make sure the sleep coroutine is inside the spawned task, not in the loop.
+
+[`wrk`]: https://github.com/wg/wrk
+
+
+### Handling Errors Manually
+
+Here is how basic accept loop could look like:
+
+```rust,edition2018
+# extern crate async_std;
+# use std::time::Duration;
+# use async_std::{
+#     net::{TcpListener, ToSocketAddrs},
+#     prelude::*,
+# };
+#
+# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+#
+async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
+    let listener = TcpListener::bind(addr).await?;
+    let mut incoming = listener.incoming();
+    while let Some(result) = incoming.next().await {
+        let stream = match result {
+            Err(ref e) if is_connection_error(e) => continue, // 1
+            Err(e) => {
+                eprintln!("Error: {}. Pausing for 500ms.", e); // 3
+                task::sleep(Duration::from_millis(500)).await; // 2
+                continue;
+            }
+            Ok(s) => s,
+        };
+        // body
+    }
+    Ok(())
+}
+```
+
+1. Ignore per-connection errors.
+2. Sleep and continue on resource shortage.
+3. It's important to log the message, because these errors commonly mean the
+   misconfiguration of the system and are helpful for operations people running
+   the application.
+
+Be sure to [test your application](#testing-application).
+
+
+### External Crates
+
+The crate [`async-listen`] has a helper to achieve this task:
+```rust,edition2018
+# extern crate async_std;
+# extern crate async_listen;
+# use std::time::Duration;
+# use async_std::{
+#     net::{TcpListener, ToSocketAddrs},
+#     prelude::*,
+# };
+#
+# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+#
+use async_listen::{ListenExt, error_hint};
+
+async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
+
+    let listener = TcpListener::bind(addr).await?;
+    let mut incoming = listener
+        .incoming()
+        .log_warnings(log_accept_error) // 1
+        .handle_errors(Duration::from_millis(500));
+    while let Some(socket) = incoming.next().await { // 2
+        // body
+    }
+    Ok(())
+}
+
+fn log_accept_error(e: &io::Error) {
+    eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)) // 3
+}
+```
+
+1. Logs resource shortages (`async-listen` calls them warnings). If you use
+   `log` crate or any other in your app this should go to the log.
+2. Stream yields sockets without `Result` wrapper after `handle_errors` because
+   all errors are already handled.
+3. Together with the error we print a hint, which explains some errors for end
+   users. For example, it recommends increasing open file limit and gives
+   a link.
+
+[`async-listen`]: https://crates.io/crates/async-listen/
+
+Be sure to [test your application](#testing-application).
+
+
+## Connections Limit
+
+Even if you've applied everything described in
+[Handling Errors](#handling-errors) section, there is still a problem.
+
+Let's imagine you have a server that needs to open a file to process
+client request. At some point, you might encounter the following situation:
+
+1. There are as many client connection as max file descriptors allowed for
+   the application.
+2. Listener gets `Too many open files` error so it sleeps.
+3. Some client sends a request via the previously open connection.
+4. Opening a file to serve request fails, because of the same
+   `Too many open files` error, until some other client drops a connection.
+
+There are many more possible situations, this is just a small illustation that
+limiting number of connections is very useful. Generally, it's one of the ways
+to control resources used by a server and avoiding some kinds of deny of
+service (DoS) attacks.
+
+### `async-listen` crate
+
+Limiting maximum number of simultaneous connections with [`async-listen`]
+looks like the following:
+
+```rust,edition2018
+# extern crate async_std;
+# extern crate async_listen;
+# use std::time::Duration;
+# use async_std::{
+#     net::{TcpListener, TcpStream, ToSocketAddrs},
+#     prelude::*,
+# };
+#
+# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+#
+use async_listen::{ListenExt, Token, error_hint};
+
+async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
+
+    let listener = TcpListener::bind(addr).await?;
+    let mut incoming = listener
+        .incoming()
+        .log_warnings(log_accept_error)
+        .handle_errors(Duration::from_millis(500)) // 1
+        .backpressure(100);
+    while let Some((token, socket)) = incoming.next().await { // 2
+         task::spawn(async move {
+             connection_loop(&token, stream).await; // 3
+         });
+    }
+    Ok(())
+}
+async fn connection_loop(_token: &Token, stream: TcpStream) { // 4
+    // ...
+}
+# fn log_accept_error(e: &io::Error) {
+#     eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e));
+# }
+```
+
+1. We need to handle errors first, because [`backpressure`] helper expects
+   stream of `TcpStream` rather than `Result`.
+2. The token yielded by a new stream is what is counted by backpressure helper.
+   I.e. if you drop a token, new connection can be established.
+3. We give the connection loop a reference to token to bind token's lifetime to
+   the lifetime of the connection.
+4. The token itsellf in the function can be ignored, hence `_token`
+
+[`backpressure`]: https://docs.rs/async-listen/0.1.2/async_listen/trait.ListenExt.html#method.backpressure
+
+Be sure to [test this behavior](#testing-application).
diff --git a/docs/src/security/index.md b/docs/src/security/index.md
index 5a94a5401..02fef4c0a 100644
--- a/docs/src/security/index.md
+++ b/docs/src/security/index.md
@@ -1,6 +1,6 @@
 # Security
 
-Writing a highly perfomant async core library is a task involving some instances of unsafe code.
+Writing a highly performant async core library is a task involving some instances of unsafe code.
 
 We take great care in vetting all unsafe code included in `async-std` and do follow generally accepted practices.
 
diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md
index dcc06616a..b8174d300 100644
--- a/docs/src/tutorial/all_together.md
+++ b/docs/src/tutorial/all_together.md
@@ -4,16 +4,15 @@ At this point, we only need to start the broker to get a fully-functioning (in t
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 use async_std::{
-    io::{self, BufReader},
+    io::BufReader,
     net::{TcpListener, TcpStream, ToSocketAddrs},
     prelude::*,
     task,
 };
-use futures_channel::mpsc;
-use futures_util::SinkExt;
+use futures::channel::mpsc;
+use futures::sink::SinkExt;
 use std::{
     collections::hash_map::{HashMap, Entry},
     sync::Arc,
@@ -125,8 +124,8 @@ async fn broker_loop(mut events: Receiver<Event>) -> Result<()> {
                     Entry::Occupied(..) => (),
                     Entry::Vacant(entry) => {
                         let (client_sender, client_receiver) = mpsc::unbounded();
-                        entry.insert(client_sender); // 4
-                        spawn_and_log_error(connection_writer_loop(client_receiver, stream)); // 5
+                        entry.insert(client_sender);
+                        spawn_and_log_error(connection_writer_loop(client_receiver, stream));
                     }
                 }
             }
diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md
index 234067a3a..0937d7086 100644
--- a/docs/src/tutorial/clean_shutdown.md
+++ b/docs/src/tutorial/clean_shutdown.md
@@ -22,16 +22,15 @@ Let's add waiting to the server:
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 # use async_std::{
 #     io::{self, BufReader},
 #     net::{TcpListener, TcpStream, ToSocketAddrs},
 #     prelude::*,
 #     task,
 # };
-# use futures_channel::mpsc;
-# use futures_util::SinkExt;
+# use futures::channel::mpsc;
+# use futures::sink::SinkExt;
 # use std::{
 #     collections::hash_map::{HashMap, Entry},
 #     sync::Arc,
@@ -156,16 +155,15 @@ And to the broker:
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 # use async_std::{
 #     io::{self, BufReader},
 #     net::{TcpListener, TcpStream, ToSocketAddrs},
 #     prelude::*,
 #     task,
 # };
-# use futures_channel::mpsc;
-# use futures_util::SinkExt;
+# use futures::channel::mpsc;
+# use futures::sink::SinkExt;
 # use std::{
 #     collections::hash_map::{HashMap, Entry},
 #     sync::Arc,
@@ -247,7 +245,7 @@ async fn broker_loop(mut events: Receiver<Event>) -> Result<()> {
 Notice what happens with all of the channels once we exit the accept loop:
 
 1. First, we drop the main broker's sender.
-   That way when the readers are done, there's no sender for the broker's channel, and the chanel closes.
+   That way when the readers are done, there's no sender for the broker's channel, and the channel closes.
 2. Next, the broker exits `while let Some(event) = events.next().await` loop.
 3. It's crucial that, at this stage, we drop the `peers` map.
    This drops writer's senders.
diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md
index c1ebe9b8a..921cf90ca 100644
--- a/docs/src/tutorial/connecting_readers_and_writers.md
+++ b/docs/src/tutorial/connecting_readers_and_writers.md
@@ -2,25 +2,24 @@
 ## Connecting Readers and Writers
 
 So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`?
-We should somehow maintain an `peers: HashMap<String, Sender<String>>` map which allows a client to find destination channels.
+We should somehow maintain a `peers: HashMap<String, Sender<String>>` map which allows a client to find destination channels.
 However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message.
 
 One trick to make reasoning about state simpler comes from the actor model.
-We can create a dedicated broker tasks which owns the `peers` map and communicates with other tasks by channels.
-By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit.
+We can create a dedicated broker task which owns the `peers` map and communicates with other tasks using channels.
+By hiding `peers` inside such an "actor" task, we remove the need for mutexes and also make the serialization point explicit.
 The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue.
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 # use async_std::{
 #     net::TcpStream,
 #     prelude::*,
 #     task,
 # };
-# use futures_channel::mpsc;
-# use futures_util::sink::SinkExt;
+# use futures::channel::mpsc;
+# use futures::sink::SinkExt;
 # use std::sync::Arc;
 #
 # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -93,9 +92,9 @@ async fn broker_loop(mut events: Receiver<Event>) -> Result<()> {
 }
 ```
 
-1. Broker should handle two types of events: a message or an arrival of a new peer.
-2. Internal state of the broker is a `HashMap`.
+1. The broker task should handle two types of events: a message or an arrival of a new peer.
+2. The internal state of the broker is a `HashMap`.
    Note how we don't need a `Mutex` here and can confidently say, at each iteration of the broker's loop, what is the current set of peers
 3. To handle a message, we send it over a channel to each destination
-4. To handle new peer, we first register it in the peer's map ...
+4. To handle a new peer, we first register it in the peer's map ...
 5. ... and then spawn a dedicated task to actually write the messages to the socket.
diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md
index a1f51d134..b6e53641c 100644
--- a/docs/src/tutorial/handling_disconnection.md
+++ b/docs/src/tutorial/handling_disconnection.md
@@ -19,11 +19,10 @@ First, let's add a shutdown channel to the `connection_loop`:
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 # use async_std::net::TcpStream;
-# use futures_channel::mpsc;
-# use futures_util::SinkExt;
+# use futures::channel::mpsc;
+# use futures::sink::SinkExt;
 # use std::sync::Arc;
 #
 # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -61,8 +60,8 @@ async fn connection_loop(mut broker: Sender<Event>, stream: Arc<TcpStream>) -> R
 }
 ```
 
-1. To enforce that no messages are send along the shutdown channel, we use an uninhabited type.
-2. We pass the shutdown channel to the writer task
+1. To enforce that no messages are sent along the shutdown channel, we use an uninhabited type.
+2. We pass the shutdown channel to the writer task.
 3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped.
 
 In the `connection_writer_loop`, we now need to choose between shutdown and message channels.
@@ -70,17 +69,14 @@ We use the `select` macro for this purpose:
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 # use async_std::{net::TcpStream, prelude::*};
-use futures_channel::mpsc;
-use futures_util::{select, FutureExt};
+# use futures::channel::mpsc;
+use futures::{select, FutureExt};
 # use std::sync::Arc;
-
 # type Receiver<T> = mpsc::UnboundedReceiver<T>;
 # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
 # type Sender<T> = mpsc::UnboundedSender<T>;
-
 # #[derive(Debug)]
 # enum Void {} // 1
 
@@ -94,12 +90,12 @@ async fn connection_writer_loop(
     let mut shutdown = shutdown.fuse();
     loop { // 2
         select! {
-            msg = messages.next().fuse() => match msg {
+            msg = messages.next().fuse() => match msg { // 3
                 Some(msg) => stream.write_all(msg.as_bytes()).await?,
                 None => break,
             },
             void = shutdown.next().fuse() => match void {
-                Some(void) => match void {}, // 3
+                Some(void) => match void {}, // 4
                 None => break,
             }
         }
@@ -110,11 +106,12 @@ async fn connection_writer_loop(
 
 1. We add shutdown channel as an argument.
 2. Because of `select`, we can't use a `while let` loop, so we desugar it further into a `loop`.
-3. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`.
+3. Function fuse() is used to turn any `Stream` into a `FusedStream`. This is used for fusing a stream such that poll_next will never again be called once it has finished.
+4. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`.
 
 Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel.
 To not lose these messages completely, we'll return the messages channel back to the broker.
-This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable.
+This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infallible.
 
 ## Final Code
 
@@ -122,16 +119,16 @@ The final code looks like this:
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 use async_std::{
     io::BufReader,
     net::{TcpListener, TcpStream, ToSocketAddrs},
     prelude::*,
     task,
 };
-use futures_channel::mpsc;
-use futures_util::{select, FutureExt, SinkExt};
+use futures::channel::mpsc;
+use futures::sink::SinkExt;
+use futures::{select, FutureExt};
 use std::{
     collections::hash_map::{Entry, HashMap},
     future::Future,
diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md
index 3aac67f35..ba9d6f335 100644
--- a/docs/src/tutorial/implementing_a_client.md
+++ b/docs/src/tutorial/implementing_a_client.md
@@ -1,29 +1,27 @@
 ## Implementing a client
 
-Let's now implement the client for the chat.
-Because the protocol is line-based, the implementation is pretty straightforward:
+Since the protocol is line-based, implementing a client for the chat is straightforward:
 
 * Lines read from stdin should be sent over the socket.
 * Lines read from the socket should be echoed to stdout.
 
-Unlike the server, the client needs only limited concurrency, as it interacts with only a single user.
-For this reason, async doesn't bring a lot of performance benefits in this case.
+Although async does not significantly affect client performance (as unlike the server, the client interacts solely with one user and only needs limited concurrency), async is still useful for managing concurrency!
+
+The client has to read from stdin and the socket *simultaneously*.
+Programming this with threads is cumbersome, especially when implementing a clean shutdown.
+With async, the `select!` macro is all that is needed.
 
-However, async is still useful for managing concurrency!
-Specifically, the client should *simultaneously* read from stdin and from the socket.
-Programming this with threads is cumbersome, especially when implementing clean shutdown.
-With async, we can just use the `select!` macro.
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_util;
+# extern crate futures;
 use async_std::{
     io::{stdin, BufReader},
     net::{TcpStream, ToSocketAddrs},
     prelude::*,
     task,
 };
-use futures_util::{select, FutureExt};
+use futures::{select, FutureExt};
 
 type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
 
diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md
index 99ddf8eb3..076ecf418 100644
--- a/docs/src/tutorial/index.md
+++ b/docs/src/tutorial/index.md
@@ -1,11 +1,14 @@
 # Tutorial: Writing a chat
 
-Nothing is as simple as a chat server, right? Not quite, chat servers
-already expose you to all the fun of asynchronous programming: how
-do you handle clients connecting concurrently. How do you handle them disconnecting?
+Nothing is simpler than creating a chat server, right?
+Not quite, chat servers expose you to all the fun of asynchronous programming:
 
-How do you distribute the messages?
+How will the server handle clients connecting concurrently?
 
-In this tutorial, we will show you how to write one in `async-std`.
+How will it handle them disconnecting?
 
-You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat).
+How will it distribute the messages?
+
+This tutorial explains how to write a chat server in `async-std`.
+
+You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/HEAD/examples/a-chat).
diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md
index 213589c07..036bc4597 100644
--- a/docs/src/tutorial/receiving_messages.md
+++ b/docs/src/tutorial/receiving_messages.md
@@ -10,14 +10,18 @@ We need to:
 ```rust,edition2018
 # extern crate async_std;
 # use async_std::{
-#     io::BufReader,
-#     net::{TcpListener, TcpStream, ToSocketAddrs},
+#     net::{TcpListener, ToSocketAddrs},
 #     prelude::*,
 #     task,
 # };
 #
 # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
 #
+use async_std::{
+    io::BufReader,
+    net::TcpStream,
+};
+
 async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
     let listener = TcpListener::bind(addr).await?;
     let mut incoming = listener.incoming();
@@ -46,7 +50,7 @@ async fn connection_loop(stream: TcpStream) -> Result<()> {
             Some(idx) => (&line[..idx], line[idx + 1 ..].trim()),
         };
         let dest: Vec<String> = dest.split(',').map(|name| name.trim().to_string()).collect();
-        let msg: String = msg.trim().to_string();
+        let msg: String = msg.to_string();
     }
     Ok(())
 }
@@ -107,14 +111,14 @@ We can "fix" it by waiting for the task to be joined, like this:
 #
 # async move |stream| {
 let handle = task::spawn(connection_loop(stream));
-handle.await
+handle.await?
 # };
 ```
 
 The `.await` waits until the client finishes, and `?` propagates the result.
 
 There are two problems with this solution however!
-*First*, because we immediately await the client, we can only handle one client at time, and that completely defeats the purpose of async!
+*First*, because we immediately await the client, we can only handle one client at a time, and that completely defeats the purpose of async!
 *Second*, if a client encounters an IO error, the whole server immediately exits.
 That is, a flaky internet connection of one peer brings down the whole chat room!
 
@@ -130,7 +134,7 @@ So let's use a helper function for this:
 # };
 fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
 where
-    F: Future<Output = io::Result<()>> + Send + 'static,
+    F: Future<Output = Result<()>> + Send + 'static,
 {
     task::spawn(async move {
         if let Err(e) = fut.await {
diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md
index b381aacbc..3f426d020 100644
--- a/docs/src/tutorial/sending_messages.md
+++ b/docs/src/tutorial/sending_messages.md
@@ -13,14 +13,13 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the
 
 ```rust,edition2018
 # extern crate async_std;
-# extern crate futures_channel;
-# extern crate futures_util;
+# extern crate futures;
 # use async_std::{
 #     net::TcpStream,
 #     prelude::*,
 # };
-use futures_channel::mpsc; // 1
-use futures_util::sink::SinkExt;
+use futures::channel::mpsc; // 1
+use futures::sink::SinkExt;
 use std::sync::Arc;
 
 # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md
index bda457660..7b1a01670 100644
--- a/docs/src/tutorial/specification.md
+++ b/docs/src/tutorial/specification.md
@@ -12,7 +12,7 @@ After that, the client can send messages to other clients using the following sy
 login1, login2, ... loginN: message
 ```
 
-Each of the specified clients than receives a `from login: message` message.
+Each of the specified clients then receives a `from login: message` message.
 
 A possible session might look like this
 
@@ -38,18 +38,10 @@ $ cargo new a-chat
 $ cd a-chat
 ```
 
-At the moment `async-std` requires Rust nightly, so let's add a rustup override for convenience:
-
-```bash
-$ rustup override add nightly
-$ rustc --version
-rustc 1.38.0-nightly (c4715198b 2019-08-05)
-```
-
 Add the following lines to `Cargo.toml`:
 
 ```toml
 [dependencies]
-futures-preview = { version = "0.3.0-alpha.19", features = [ "async-await" ] }
-async-std = "0.99"
+futures = "0.3.0"
+async-std = "1"
 ```
diff --git a/examples/README.md b/examples/README.md
index 903ed2cdb..bcaae42c8 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -154,18 +154,18 @@ Make requests by running the client example:
 cargo run --example udp-client
 ```
 
-[hello-world]: https://github.com/async-rs/async-std/blob/master/examples/hello-world.rs
-[line-count]: https://github.com/async-rs/async-std/blob/master/examples/line-count.rs
-[list-dir]: https://github.com/async-rs/async-std/blob/master/examples/list-dir.rs
-[logging]: https://github.com/async-rs/async-std/blob/master/examples/logging.rs
-[print-file]: https://github.com/async-rs/async-std/blob/master/examples/print-file.rs
-[socket-timeouts]: https://github.com/async-rs/async-std/blob/master/examples/socket-timeouts.rs
-[stdin-echo]: https://github.com/async-rs/async-std/blob/master/examples/stdin-echo.rs
-[stdin-timeout]: https://github.com/async-rs/async-std/blob/master/examples/stdin-timeout.rs
-[surf-web]: https://github.com/async-rs/async-std/blob/master/examples/surf-web.rs
-[task-local]: https://github.com/async-rs/async-std/blob/master/examples/task-local.rs
-[task-name]: https://github.com/async-rs/async-std/blob/master/examples/task-name.rs
-[tcp-client]: https://github.com/async-rs/async-std/blob/master/examples/tcp-client.rs
-[tcp-echo]: https://github.com/async-rs/async-std/blob/master/examples/tcp-echo.rs
-[udp-client]: https://github.com/async-rs/async-std/blob/master/examples/udp-client.rs
-[udp-echo]: https://github.com/async-rs/async-std/blob/master/examples/udp-echo.rs
+[hello-world]: https://github.com/async-rs/async-std/blob/HEAD/examples/hello-world.rs
+[line-count]: https://github.com/async-rs/async-std/blob/HEAD/examples/line-count.rs
+[list-dir]: https://github.com/async-rs/async-std/blob/HEAD/examples/list-dir.rs
+[logging]: https://github.com/async-rs/async-std/blob/HEAD/examples/logging.rs
+[print-file]: https://github.com/async-rs/async-std/blob/HEAD/examples/print-file.rs
+[socket-timeouts]: https://github.com/async-rs/async-std/blob/HEAD/examples/socket-timeouts.rs
+[stdin-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/stdin-echo.rs
+[stdin-timeout]: https://github.com/async-rs/async-std/blob/HEAD/examples/stdin-timeout.rs
+[surf-web]: https://github.com/async-rs/async-std/blob/HEAD/examples/surf-web.rs
+[task-local]: https://github.com/async-rs/async-std/blob/HEAD/examples/task-local.rs
+[task-name]: https://github.com/async-rs/async-std/blob/HEAD/examples/task-name.rs
+[tcp-client]: https://github.com/async-rs/async-std/blob/HEAD/examples/tcp-client.rs
+[tcp-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/tcp-echo.rs
+[udp-client]: https://github.com/async-rs/async-std/blob/HEAD/examples/udp-client.rs
+[udp-echo]: https://github.com/async-rs/async-std/blob/HEAD/examples/udp-echo.rs
diff --git a/examples/a-chat/main.rs b/examples/a-chat/main.rs
index ced7cac24..89e5e2b62 100644
--- a/examples/a-chat/main.rs
+++ b/examples/a-chat/main.rs
@@ -8,6 +8,6 @@ fn main() -> Result<()> {
     match (args.nth(1).as_ref().map(String::as_str), args.next()) {
         (Some("client"), None) => client::main(),
         (Some("server"), None) => server::main(),
-        _ => Err("Usage: a-chat [client|server]")?,
+        _ => Err("Usage: a-chat [client|server]".into()),
     }
 }
diff --git a/examples/a-chat/server.rs b/examples/a-chat/server.rs
index 77ebfd1e3..d3ac74699 100644
--- a/examples/a-chat/server.rs
+++ b/examples/a-chat/server.rs
@@ -45,7 +45,7 @@ async fn connection_loop(mut broker: Sender<Event>, stream: TcpStream) -> Result
     let mut lines = reader.lines();
 
     let name = match lines.next().await {
-        None => Err("peer disconnected immediately")?,
+        None => return Err("peer disconnected immediately".into()),
         Some(line) => line?,
     };
     let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();
@@ -96,6 +96,7 @@ async fn connection_writer_loop(
                 None => break,
             },
             void = shutdown.next().fuse() => match void {
+                #[allow(unreachable_patterns)]
                 Some(void) => match void {},
                 None => break,
             }
diff --git a/examples/logging.rs b/examples/logging.rs
index bd55aaa0c..0e4526b38 100644
--- a/examples/logging.rs
+++ b/examples/logging.rs
@@ -3,7 +3,7 @@
 use async_std::task;
 
 fn main() {
-    femme::start(log::LevelFilter::Trace).unwrap();
+    femme::with_level(log::LevelFilter::Trace);
 
     task::block_on(async {
         let handle = task::spawn(async {
diff --git a/examples/surf-web.rs b/examples/surf-web.rs
index b3101d15c..6ff043d99 100644
--- a/examples/surf-web.rs
+++ b/examples/surf-web.rs
@@ -1,23 +1,18 @@
-/* TODO: Once the next version of surf released, re-enable this example.
-//! Sends an HTTP request to the Rust website.
-
 use async_std::task;
 
-fn main() -> Result<(), surf::Exception> {
+fn main() -> Result<(), surf::Error> {
     task::block_on(async {
         let url = "https://www.rust-lang.org";
-        let mut response = surf::get(url).await?;
+        let mut response = surf::get(url).send().await?;
         let body = response.body_string().await?;
 
         dbg!(url);
         dbg!(response.status());
         dbg!(response.version());
-        dbg!(response.headers());
+        dbg!(response.header_names());
+        dbg!(response.header_values());
         dbg!(body.len());
 
         Ok(())
     })
 }
-*/
-
-fn main() {}
diff --git a/examples/tcp-echo.rs b/examples/tcp-echo.rs
index 7c50be016..c04f07765 100644
--- a/examples/tcp-echo.rs
+++ b/examples/tcp-echo.rs
@@ -14,8 +14,9 @@ use async_std::task;
 async fn process(stream: TcpStream) -> io::Result<()> {
     println!("Accepted from: {}", stream.peer_addr()?);
 
-    let (reader, writer) = &mut (&stream, &stream);
-    io::copy(reader, writer).await?;
+    let mut reader = stream.clone();
+    let mut writer = stream;
+    io::copy(&mut reader, &mut writer).await?;
 
     Ok(())
 }
diff --git a/examples/tcp-ipv4-and-6-echo.rs b/examples/tcp-ipv4-and-6-echo.rs
new file mode 100644
index 000000000..a00e1f386
--- /dev/null
+++ b/examples/tcp-ipv4-and-6-echo.rs
@@ -0,0 +1,45 @@
+//! TCP echo server, accepting connections both on both ipv4 and ipv6 sockets.
+//!
+//! To send messages, do:
+//!
+//! ```sh
+//! $ nc 127.0.0.1 8080
+//! $ nc ::1 8080
+//! ```
+
+use async_std::io;
+use async_std::net::{TcpListener, TcpStream};
+use async_std::prelude::*;
+use async_std::task;
+
+async fn process(stream: TcpStream) -> io::Result<()> {
+    println!("Accepted from: {}", stream.peer_addr()?);
+
+    let mut reader = stream.clone();
+    let mut writer = stream;
+    io::copy(&mut reader, &mut writer).await?;
+
+    Ok(())
+}
+
+fn main() -> io::Result<()> {
+    task::block_on(async {
+        let ipv4_listener = TcpListener::bind("127.0.0.1:8080").await?;
+        println!("Listening on {}", ipv4_listener.local_addr()?);
+        let ipv6_listener = TcpListener::bind("[::1]:8080").await?;
+        println!("Listening on {}", ipv6_listener.local_addr()?);
+
+        let ipv4_incoming = ipv4_listener.incoming();
+        let ipv6_incoming = ipv6_listener.incoming();
+
+        let mut incoming = ipv4_incoming.merge(ipv6_incoming);
+
+        while let Some(stream) = incoming.next().await {
+            let stream = stream?;
+            task::spawn(async {
+                process(stream).await.unwrap();
+            });
+        }
+        Ok(())
+    })
+}
diff --git a/rustfmt.toml b/rustfmt.toml
deleted file mode 100644
index 1082fd888..000000000
--- a/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-version = "Two"
diff --git a/src/channel.rs b/src/channel.rs
new file mode 100644
index 000000000..8eb30a42d
--- /dev/null
+++ b/src/channel.rs
@@ -0,0 +1,8 @@
+//! Channels
+//!
+//! Multi-producer, multi-consumer queues, used for message-based
+//! communication. Can provide a lightweight inter-task synchronisation
+//! mechanism, at the cost of some extra memory.
+
+#[doc(inline)]
+pub use async_channel::*;
diff --git a/src/collections/binary_heap/extend.rs b/src/collections/binary_heap/extend.rs
index 8538f9b0c..1a9479e14 100644
--- a/src/collections/binary_heap/extend.rs
+++ b/src/collections/binary_heap/extend.rs
@@ -2,17 +2,20 @@ use std::collections::BinaryHeap;
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<T: Ord> Extend<T> for BinaryHeap<T> {
-    fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
+impl<T: Ord + Send> stream::Extend<T> for BinaryHeap<T> {
+    fn extend<'a, S: IntoStream<Item = T> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         let stream = stream.into_stream();
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        //let (lower_bound, _) = stream.size_hint();
-        //self.reserve(lower_bound);
+
+        self.reserve(stream.size_hint().0);
+
         Box::pin(stream.for_each(move |item| self.push(item)))
     }
 }
diff --git a/src/collections/binary_heap/from_stream.rs b/src/collections/binary_heap/from_stream.rs
index c8e44e93a..614f5f5de 100644
--- a/src/collections/binary_heap/from_stream.rs
+++ b/src/collections/binary_heap/from_stream.rs
@@ -1,23 +1,22 @@
 use std::collections::BinaryHeap;
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
-impl<T: Ord> FromStream<T> for BinaryHeap<T> {
+impl<T: Ord + Send> FromStream<T> for BinaryHeap<T> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = BinaryHeap::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/collections/btree_map/extend.rs b/src/collections/btree_map/extend.rs
index ae02c4248..4a1ad0618 100644
--- a/src/collections/btree_map/extend.rs
+++ b/src/collections/btree_map/extend.rs
@@ -2,13 +2,16 @@ use std::collections::BTreeMap;
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> {
-    fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
+impl<K: Ord + Send, V: Send> stream::Extend<(K, V)> for BTreeMap<K, V> {
+    fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         Box::pin(stream.into_stream().for_each(move |(k, v)| {
             self.insert(k, v);
         }))
diff --git a/src/collections/btree_map/from_stream.rs b/src/collections/btree_map/from_stream.rs
index bd80c0697..ef01b6e0d 100644
--- a/src/collections/btree_map/from_stream.rs
+++ b/src/collections/btree_map/from_stream.rs
@@ -1,23 +1,22 @@
 use std::collections::BTreeMap;
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
-impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> {
+impl<K: Ord + Send, V: Send> FromStream<(K, V)> for BTreeMap<K, V> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = (K, V)>>(
+    fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = BTreeMap::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/collections/btree_set/extend.rs b/src/collections/btree_set/extend.rs
index ccf033783..0f7421023 100644
--- a/src/collections/btree_set/extend.rs
+++ b/src/collections/btree_set/extend.rs
@@ -2,13 +2,16 @@ use std::collections::BTreeSet;
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<T: Ord> Extend<T> for BTreeSet<T> {
-    fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
+impl<T: Ord + Send> stream::Extend<T> for BTreeSet<T> {
+    fn extend<'a, S: IntoStream<Item = T> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         Box::pin(stream.into_stream().for_each(move |item| {
             self.insert(item);
         }))
diff --git a/src/collections/btree_set/from_stream.rs b/src/collections/btree_set/from_stream.rs
index bd2a744ba..5fdb33e6c 100644
--- a/src/collections/btree_set/from_stream.rs
+++ b/src/collections/btree_set/from_stream.rs
@@ -1,23 +1,22 @@
 use std::collections::BTreeSet;
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
-impl<T: Ord> FromStream<T> for BTreeSet<T> {
+impl<T: Ord + Send> FromStream<T> for BTreeSet<T> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = BTreeSet::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/collections/hash_map/extend.rs b/src/collections/hash_map/extend.rs
index 5f96b8abe..15b7e7d46 100644
--- a/src/collections/hash_map/extend.rs
+++ b/src/collections/hash_map/extend.rs
@@ -3,17 +3,21 @@ use std::hash::{BuildHasher, Hash};
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<K, V, H> Extend<(K, V)> for HashMap<K, V, H>
+impl<K, V, H> stream::Extend<(K, V)> for HashMap<K, V, H>
 where
-    K: Eq + Hash,
-    H: BuildHasher + Default,
+    K: Eq + Hash + Send,
+    V: Send,
+    H: BuildHasher + Default + Send,
 {
-    fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
+    fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         let stream = stream.into_stream();
 
         // The following is adapted from the hashbrown source code:
@@ -23,13 +27,12 @@ where
         // hint lower bound if the map is empty. Otherwise reserve half the hint (rounded up), so
         // the map will only resize twice in the worst case.
 
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        //let reserve = if self.is_empty() {
-        //    stream.size_hint().0
-        //} else {
-        //    (stream.size_hint().0 + 1) / 2
-        //};
-        //self.reserve(reserve);
+        let additional = if self.is_empty() {
+            stream.size_hint().0
+        } else {
+            (stream.size_hint().0 + 1) / 2
+        };
+        self.reserve(additional);
 
         Box::pin(stream.for_each(move |(k, v)| {
             self.insert(k, v);
diff --git a/src/collections/hash_map/from_stream.rs b/src/collections/hash_map/from_stream.rs
index 2b7bbc9b7..ae3f11a56 100644
--- a/src/collections/hash_map/from_stream.rs
+++ b/src/collections/hash_map/from_stream.rs
@@ -2,27 +2,27 @@ use std::collections::HashMap;
 use std::hash::{BuildHasher, Hash};
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
 impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H>
 where
-    K: Eq + Hash,
-    H: BuildHasher + Default,
+    K: Eq + Hash + Send,
+    H: BuildHasher + Default + Send,
+    V: Send,
 {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = (K, V)>>(
+    fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = HashMap::with_hasher(Default::default());
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/collections/hash_set/extend.rs b/src/collections/hash_set/extend.rs
index 72400e11f..0246a1e88 100644
--- a/src/collections/hash_set/extend.rs
+++ b/src/collections/hash_set/extend.rs
@@ -3,17 +3,20 @@ use std::hash::{BuildHasher, Hash};
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<T, H> Extend<T> for HashSet<T, H>
+impl<T, H> stream::Extend<T> for HashSet<T, H>
 where
-    T: Eq + Hash,
-    H: BuildHasher + Default,
+    T: Eq + Hash + Send,
+    H: BuildHasher + Default + Send,
 {
-    fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
+    fn extend<'a, S: IntoStream<Item = T> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         // The Extend impl for HashSet in the standard library delegates to the internal HashMap.
         // Thus, this impl is just a copy of the async Extend impl for HashMap in this crate.
 
@@ -26,13 +29,12 @@ where
         // hint lower bound if the map is empty. Otherwise reserve half the hint (rounded up), so
         // the map will only resize twice in the worst case.
 
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        //let reserve = if self.is_empty() {
-        //    stream.size_hint().0
-        //} else {
-        //    (stream.size_hint().0 + 1) / 2
-        //};
-        //self.reserve(reserve);
+        let additional = if self.is_empty() {
+            stream.size_hint().0
+        } else {
+            (stream.size_hint().0 + 1) / 2
+        };
+        self.reserve(additional);
 
         Box::pin(stream.for_each(move |item| {
             self.insert(item);
diff --git a/src/collections/hash_set/from_stream.rs b/src/collections/hash_set/from_stream.rs
index 42447fef0..6f640695a 100644
--- a/src/collections/hash_set/from_stream.rs
+++ b/src/collections/hash_set/from_stream.rs
@@ -2,27 +2,26 @@ use std::collections::HashSet;
 use std::hash::{BuildHasher, Hash};
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
 impl<T, H> FromStream<T> for HashSet<T, H>
 where
-    T: Eq + Hash,
-    H: BuildHasher + Default,
+    T: Eq + Hash + Send,
+    H: BuildHasher + Default + Send,
 {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = HashSet::with_hasher(Default::default());
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/collections/linked_list/extend.rs b/src/collections/linked_list/extend.rs
index 567a92d30..8adc41d73 100644
--- a/src/collections/linked_list/extend.rs
+++ b/src/collections/linked_list/extend.rs
@@ -2,17 +2,17 @@ use std::collections::LinkedList;
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<T> Extend<T> for LinkedList<T> {
-    fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
+impl<T: Send> stream::Extend<T> for LinkedList<T> {
+    fn extend<'a, S: IntoStream<Item = T> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         let stream = stream.into_stream();
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        //let (lower_bound, _) = stream.size_hint();
-        //self.reserve(lower_bound);
         Box::pin(stream.for_each(move |item| self.push_back(item)))
     }
 }
diff --git a/src/collections/linked_list/from_stream.rs b/src/collections/linked_list/from_stream.rs
index 3ffe149bb..7c3eb738c 100644
--- a/src/collections/linked_list/from_stream.rs
+++ b/src/collections/linked_list/from_stream.rs
@@ -1,23 +1,22 @@
 use std::collections::LinkedList;
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
-impl<T> FromStream<T> for LinkedList<T> {
+impl<T: Send> FromStream<T> for LinkedList<T> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = LinkedList::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/collections/mod.rs b/src/collections/mod.rs
index ae9efaa92..745aed01c 100644
--- a/src/collections/mod.rs
+++ b/src/collections/mod.rs
@@ -11,10 +11,17 @@ pub mod hash_set;
 pub mod linked_list;
 pub mod vec_deque;
 
+#[allow(unused)]
 pub use binary_heap::BinaryHeap;
+#[allow(unused)]
 pub use btree_map::BTreeMap;
+#[allow(unused)]
 pub use btree_set::BTreeSet;
+#[allow(unused)]
 pub use hash_map::HashMap;
+#[allow(unused)]
 pub use hash_set::HashSet;
+#[allow(unused)]
 pub use linked_list::LinkedList;
+#[allow(unused)]
 pub use vec_deque::VecDeque;
diff --git a/src/collections/vec_deque/extend.rs b/src/collections/vec_deque/extend.rs
index d034bdcd9..9dea92231 100644
--- a/src/collections/vec_deque/extend.rs
+++ b/src/collections/vec_deque/extend.rs
@@ -2,17 +2,20 @@ use std::collections::VecDeque;
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<T> Extend<T> for VecDeque<T> {
-    fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
+impl<T: Send> stream::Extend<T> for VecDeque<T> {
+    fn extend<'a, S: IntoStream<Item = T> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         let stream = stream.into_stream();
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        //let (lower_bound, _) = stream.size_hint();
-        //self.reserve(lower_bound);
+
+        self.reserve(stream.size_hint().0);
+
         Box::pin(stream.for_each(move |item| self.push_back(item)))
     }
 }
diff --git a/src/collections/vec_deque/from_stream.rs b/src/collections/vec_deque/from_stream.rs
index 8903de0ea..3e0719ab2 100644
--- a/src/collections/vec_deque/from_stream.rs
+++ b/src/collections/vec_deque/from_stream.rs
@@ -1,23 +1,22 @@
 use std::collections::VecDeque;
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
-impl<T> FromStream<T> for VecDeque<T> {
+impl<T: Send> FromStream<T> for VecDeque<T> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = VecDeque::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs
index c484aeeb5..38a5a6b90 100644
--- a/src/fs/canonicalize.rs
+++ b/src/fs/canonicalize.rs
@@ -1,7 +1,7 @@
-use std::path::{Path, PathBuf};
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::{Path, PathBuf};
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Returns the canonical form of a path.
 ///
@@ -33,5 +33,10 @@ use crate::task::blocking;
 /// ```
 pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::canonicalize(path) }).await
+    spawn_blocking(move || {
+        std::fs::canonicalize(&path)
+            .map(Into::into)
+            .context(|| format!("could not canonicalize `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/copy.rs b/src/fs/copy.rs
index fc3fd3e69..8fb447bb0 100644
--- a/src/fs/copy.rs
+++ b/src/fs/copy.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Copies the contents and permissions of a file to a new location.
 ///
@@ -42,5 +42,9 @@ use crate::task::blocking;
 pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
     let from = from.as_ref().to_owned();
     let to = to.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::copy(&from, &to) }).await
+    spawn_blocking(move || {
+        std::fs::copy(&from, &to)
+            .context(|| format!("could not copy `{}` to `{}`", from.display(), to.display()))
+    })
+    .await
 }
diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs
index aa2d5724f..37923c055 100644
--- a/src/fs/create_dir.rs
+++ b/src/fs/create_dir.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Creates a new directory.
 ///
@@ -35,5 +35,9 @@ use crate::task::blocking;
 /// ```
 pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::create_dir(path) }).await
+    spawn_blocking(move || {
+        std::fs::create_dir(&path)
+            .context(|| format!("could not create directory `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs
index 33176f75d..753dfd492 100644
--- a/src/fs/create_dir_all.rs
+++ b/src/fs/create_dir_all.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Creates a new directory and all of its parents if they are missing.
 ///
@@ -30,5 +30,9 @@ use crate::task::blocking;
 /// ```
 pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::create_dir_all(path) }).await
+    spawn_blocking(move || {
+        std::fs::create_dir_all(&path)
+            .context(|| format!("could not create directory path `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs
index 0de230db8..9ee6b55ac 100644
--- a/src/fs/dir_builder.rs
+++ b/src/fs/dir_builder.rs
@@ -1,10 +1,8 @@
-use std::path::Path;
+use std::future::Future;
 
-use cfg_if::cfg_if;
-
-use crate::future::Future;
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
 
 /// A builder for creating directories with configurable options.
 ///
@@ -14,7 +12,7 @@ use crate::task::blocking;
 ///
 /// [`os::unix::fs::DirBuilderExt`]: ../os/unix/fs/trait.DirBuilderExt.html
 /// [`std::fs::DirBuilder`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html
-#[derive(Debug)]
+#[derive(Debug, Default)]
 pub struct DirBuilder {
     /// Set to `true` if non-existent parent directories should be created.
     recursive: bool,
@@ -109,26 +107,17 @@ impl DirBuilder {
         }
 
         let path = path.as_ref().to_owned();
-        async move { blocking::spawn(async move { builder.create(path) }).await }
+        async move { spawn_blocking(move || builder.create(path)).await }
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::fs::DirBuilderExt;
-    } else if #[cfg(unix)] {
-        use std::os::unix::fs::DirBuilderExt;
-    }
-}
+cfg_unix! {
+    use crate::os::unix::fs::DirBuilderExt;
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl DirBuilderExt for DirBuilder {
-            fn mode(&mut self, mode: u32) -> &mut Self {
-                self.mode = Some(mode);
-                self
-            }
+    impl DirBuilderExt for DirBuilder {
+        fn mode(&mut self, mode: u32) -> &mut Self {
+            self.mode = Some(mode);
+            self
         }
     }
 }
diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs
index 66b3cb7c9..e1622eec6 100644
--- a/src/fs/dir_entry.rs
+++ b/src/fs/dir_entry.rs
@@ -1,13 +1,11 @@
 use std::ffi::OsString;
 use std::fmt;
-use std::path::PathBuf;
 use std::sync::Arc;
 
-use cfg_if::cfg_if;
-
 use crate::fs::{FileType, Metadata};
 use crate::io;
-use crate::task::blocking;
+use crate::path::PathBuf;
+use crate::task::spawn_blocking;
 
 /// An entry in a directory.
 ///
@@ -50,7 +48,7 @@ impl DirEntry {
     /// # Ok(()) }) }
     /// ```
     pub fn path(&self) -> PathBuf {
-        self.0.path()
+        self.0.path().into()
     }
 
     /// Reads the metadata for this entry.
@@ -89,7 +87,7 @@ impl DirEntry {
     /// ```
     pub async fn metadata(&self) -> io::Result<Metadata> {
         let inner = self.0.clone();
-        blocking::spawn(async move { inner.metadata() }).await
+        spawn_blocking(move || inner.metadata()).await
     }
 
     /// Reads the file type for this entry.
@@ -127,7 +125,7 @@ impl DirEntry {
     /// ```
     pub async fn file_type(&self) -> io::Result<FileType> {
         let inner = self.0.clone();
-        blocking::spawn(async move { inner.file_type() }).await
+        spawn_blocking(move || inner.file_type()).await
     }
 
     /// Returns the bare name of this entry without the leading path.
@@ -160,21 +158,18 @@ impl fmt::Debug for DirEntry {
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::fs::DirEntryExt;
-    } else if #[cfg(unix)] {
-        use std::os::unix::fs::DirEntryExt;
+impl Clone for DirEntry {
+    fn clone(&self) -> Self {
+        DirEntry(self.0.clone())
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl DirEntryExt for DirEntry {
-            fn ino(&self) -> u64 {
-                self.0.ino()
-            }
+cfg_unix! {
+    use crate::os::unix::fs::DirEntryExt;
+
+    impl DirEntryExt for DirEntry {
+        fn ino(&self) -> u64 {
+            self.0.ino()
         }
     }
 }
diff --git a/src/fs/file.rs b/src/fs/file.rs
index bdf934748..7dc12dd0e 100644
--- a/src/fs/file.rs
+++ b/src/fs/file.rs
@@ -3,18 +3,19 @@ use std::cmp;
 use std::fmt;
 use std::io::{Read as _, Seek as _, Write as _};
 use std::ops::{Deref, DerefMut};
-use std::path::Path;
 use std::pin::Pin;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::{Arc, Mutex};
 
-use cfg_if::cfg_if;
-
 use crate::fs::{Metadata, Permissions};
 use crate::future;
 use crate::io::{self, Read, Seek, SeekFrom, Write};
+use crate::path::Path;
 use crate::prelude::*;
-use crate::task::{self, blocking, Context, Poll, Waker};
+use crate::task::{spawn_blocking, Context, Poll, Waker};
+use crate::utils::Context as _;
+
+const ARC_TRY_UNWRAP_EXPECT: &str = "cannot acquire ownership of the file handle after drop";
 
 /// An open file on the filesystem.
 ///
@@ -59,6 +60,7 @@ use crate::task::{self, blocking, Context, Poll, Waker};
 /// #
 /// # Ok(()) }) }
 /// ```
+#[derive(Clone)]
 pub struct File {
     /// A reference to the inner file.
     file: Arc<std::fs::File>,
@@ -68,6 +70,23 @@ pub struct File {
 }
 
 impl File {
+    /// Creates an async file handle.
+    pub(crate) fn new(file: std::fs::File, is_flushed: bool) -> File {
+        let file = Arc::new(file);
+
+        File {
+            file: file.clone(),
+            lock: Lock::new(State {
+                file,
+                mode: Mode::Idle,
+                cache: Vec::new(),
+                is_flushed,
+                last_read_err: None,
+                last_write_err: None,
+            }),
+        }
+    }
+
     /// Opens a file in read-only mode.
     ///
     /// See the [`OpenOptions::open`] function for more options.
@@ -97,8 +116,11 @@ impl File {
     /// ```
     pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
         let path = path.as_ref().to_owned();
-        let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?;
-        Ok(file.into())
+        let file = spawn_blocking(move || {
+            std::fs::File::open(&path).context(|| format!("could not open `{}`", path.display()))
+        })
+        .await?;
+        Ok(File::new(file, true))
     }
 
     /// Opens a file in write-only mode.
@@ -132,8 +154,11 @@ impl File {
     /// ```
     pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
         let path = path.as_ref().to_owned();
-        let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?;
-        Ok(file.into())
+        let file = spawn_blocking(move || {
+            std::fs::File::create(&path)
+        })
+        .await?;
+        Ok(File::new(file, true))
     }
 
     /// Synchronizes OS-internal buffered contents and metadata to disk.
@@ -165,7 +190,7 @@ impl File {
         })
         .await?;
 
-        blocking::spawn(async move { state.file.sync_all() }).await
+        spawn_blocking(move || state.file.sync_all()).await
     }
 
     /// Synchronizes OS-internal buffered contents to disk.
@@ -201,7 +226,7 @@ impl File {
         })
         .await?;
 
-        blocking::spawn(async move { state.file.sync_data() }).await
+        spawn_blocking(move || state.file.sync_data()).await
     }
 
     /// Truncates or extends the file.
@@ -234,7 +259,7 @@ impl File {
         })
         .await?;
 
-        blocking::spawn(async move { state.file.set_len(size) }).await
+        spawn_blocking(move || state.file.set_len(size)).await
     }
 
     /// Reads the file's metadata.
@@ -253,7 +278,7 @@ impl File {
     /// ```
     pub async fn metadata(&self) -> io::Result<Metadata> {
         let file = self.file.clone();
-        blocking::spawn(async move { file.metadata() }).await
+        spawn_blocking(move || file.metadata()).await
     }
 
     /// Changes the permissions on the file.
@@ -282,7 +307,7 @@ impl File {
     /// ```
     pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
         let file = self.file.clone();
-        blocking::spawn(async move { file.set_permissions(perm) }).await
+        spawn_blocking(move || file.set_permissions(perm)).await
     }
 }
 
@@ -292,7 +317,7 @@ impl Drop for File {
         // non-blocking fashion, but our only other option here is losing data remaining in the
         // write cache. Good task schedulers should be resilient to occasional blocking hiccups in
         // file destructors so we don't expect this to be a common problem in practice.
-        let _ = task::block_on(self.flush());
+        let _ = futures_lite::future::block_on(self.flush());
     }
 }
 
@@ -385,82 +410,110 @@ impl Seek for &File {
 
 impl From<std::fs::File> for File {
     fn from(file: std::fs::File) -> File {
-        let file = Arc::new(file);
+        File::new(file, false)
+    }
+}
 
-        File {
-            file: file.clone(),
-            lock: Lock::new(State {
-                file,
-                mode: Mode::Idle,
-                cache: Vec::new(),
-                is_flushed: false,
-                last_read_err: None,
-                last_write_err: None,
-            }),
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+
+    impl File {
+        fn into_std_file(self) -> std::fs::File {
+            let file = self.file.clone();
+            drop(self);
+            Arc::try_unwrap(file)
+                .expect(ARC_TRY_UNWRAP_EXPECT)
         }
     }
-}
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-        use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+    impl AsRawFd for File {
+        fn as_raw_fd(&self) -> RawFd {
+            self.file.as_raw_fd()
+        }
     }
-}
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for File {
-            fn as_raw_fd(&self) -> RawFd {
-                self.file.as_raw_fd()
+    impl FromRawFd for File {
+        unsafe fn from_raw_fd(fd: RawFd) -> File {
+            std::fs::File::from_raw_fd(fd).into()
+        }
+    }
+
+    impl IntoRawFd for File {
+        fn into_raw_fd(self) -> RawFd {
+            self.into_std_file().into_raw_fd()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+        impl AsFd for File {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                self.file.as_fd()
             }
         }
 
-        impl FromRawFd for File {
-            unsafe fn from_raw_fd(fd: RawFd) -> File {
-                std::fs::File::from_raw_fd(fd).into()
+        impl From<OwnedFd> for File {
+            fn from(fd: OwnedFd) -> Self {
+                std::fs::File::from(fd).into()
             }
         }
 
-        impl IntoRawFd for File {
-            fn into_raw_fd(self) -> RawFd {
-                let file = self.file.clone();
-                drop(self);
-                Arc::try_unwrap(file)
-                    .expect("cannot acquire ownership of the file handle after drop")
-                    .into_raw_fd()
+        impl From<File> for OwnedFd {
+            fn from(val: File) -> OwnedFd {
+                val.into_std_file().into()
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(windows)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        impl AsRawHandle for File {
-            fn as_raw_handle(&self) -> RawHandle {
-                self.file.as_raw_handle()
+cfg_windows! {
+    use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+
+    impl AsRawHandle for File {
+        fn as_raw_handle(&self) -> RawHandle {
+            self.file.as_raw_handle()
+        }
+    }
+
+    impl FromRawHandle for File {
+        unsafe fn from_raw_handle(handle: RawHandle) -> File {
+            std::fs::File::from_raw_handle(handle).into()
+        }
+    }
+
+    impl IntoRawHandle for File {
+        fn into_raw_handle(self) -> RawHandle {
+            let file = self.file.clone();
+            drop(self);
+            Arc::try_unwrap(file)
+                .expect(ARC_TRY_UNWRAP_EXPECT)
+                .into_raw_handle()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};
+
+        impl AsHandle for File {
+            fn as_handle(&self) -> BorrowedHandle<'_> {
+                self.file.as_handle()
             }
         }
 
-        impl FromRawHandle for File {
-            unsafe fn from_raw_handle(handle: RawHandle) -> File {
-                std::fs::File::from_raw_handle(handle).into()
+        impl From<OwnedHandle> for File {
+            fn from(handle: OwnedHandle) -> Self {
+                std::fs::File::from(handle).into()
             }
         }
 
-        impl IntoRawHandle for File {
-            fn into_raw_handle(self) -> RawHandle {
-                let file = self.file.clone();
-                drop(self);
+        impl From<File> for OwnedHandle {
+            fn from(val: File) -> OwnedHandle {
+                let file = val.file.clone();
+                drop(val);
                 Arc::try_unwrap(file)
-                    .expect("cannot acquire ownership of the file handle after drop")
-                    .into_raw_handle()
+                    .expect(ARC_TRY_UNWRAP_EXPECT)
+                    .into()
             }
         }
     }
@@ -472,6 +525,13 @@ struct Lock<T>(Arc<LockState<T>>);
 unsafe impl<T: Send> Send for Lock<T> {}
 unsafe impl<T: Send> Sync for Lock<T> {}
 
+impl<T> Clone for Lock<T> {
+    #[inline]
+    fn clone(&self) -> Self {
+        Self(Arc::clone(&self.0))
+    }
+}
+
 /// The state of a lock.
 struct LockState<T> {
     /// Set to `true` when locked.
@@ -512,14 +572,16 @@ impl<T> Lock<T> {
         }
 
         // The lock was successfully acquired.
-        Poll::Ready(LockGuard(self.0.clone()))
+        Poll::Ready(LockGuard(Some(self.0.clone())))
     }
 }
 
 /// A lock guard.
 ///
 /// When dropped, ownership of the inner value is returned back to the lock.
-struct LockGuard<T>(Arc<LockState<T>>);
+/// The inner value is always Some, except when the lock is dropped, where we
+/// set it to None. See comment in drop().
+struct LockGuard<T>(Option<Arc<LockState<T>>>);
 
 unsafe impl<T: Send> Send for LockGuard<T> {}
 unsafe impl<T: Sync> Sync for LockGuard<T> {}
@@ -529,7 +591,7 @@ impl<T> LockGuard<T> {
     ///
     /// When this lock guard gets dropped, all registered tasks will be woken up.
     fn register(&self, cx: &Context<'_>) {
-        let mut list = self.0.wakers.lock().unwrap();
+        let mut list = self.0.as_ref().unwrap().wakers.lock().unwrap();
 
         if list.iter().all(|w| !w.will_wake(cx.waker())) {
             list.push(cx.waker().clone());
@@ -539,11 +601,22 @@ impl<T> LockGuard<T> {
 
 impl<T> Drop for LockGuard<T> {
     fn drop(&mut self) {
+        // Set the Option to None and take its value so we can drop the Arc
+        // before we wake up the tasks.
+        let lock = self.0.take().unwrap();
+
+        // Prepare to wake up all registered tasks interested in acquiring the lock.
+        let wakers: Vec<_> = lock.wakers.lock().unwrap().drain(..).collect();
+
         // Release the lock.
-        self.0.locked.store(false, Ordering::Release);
+        lock.locked.store(false, Ordering::Release);
+
+        // Drop the Arc _before_ waking up the tasks, to avoid races. See
+        // reproducer and discussion in https://github.com/async-rs/async-std/issues/1001.
+        drop(lock);
 
         // Wake up all registered tasks interested in acquiring the lock.
-        for w in self.0.wakers.lock().unwrap().drain(..) {
+        for w in wakers {
             w.wake();
         }
     }
@@ -553,13 +626,19 @@ impl<T> Deref for LockGuard<T> {
     type Target = T;
 
     fn deref(&self) -> &T {
-        unsafe { &*self.0.value.get() }
+        // SAFETY: Safe because the lock is held when this method is called. And
+        // the inner value is always Some since it is only set to None in
+        // drop().
+        unsafe { &*self.0.as_ref().unwrap().value.get() }
     }
 }
 
 impl<T> DerefMut for LockGuard<T> {
     fn deref_mut(&mut self) -> &mut T {
-        unsafe { &mut *self.0.value.get() }
+        // SAFETY: Safe because the lock is held when this method is called. And
+        // the inner value is always Some since it is only set to None in
+        // drop().
+        unsafe { &mut *self.0.as_ref().unwrap().value.get() }
     }
 }
 
@@ -665,17 +744,22 @@ impl LockGuard<State> {
 
         match self.mode {
             Mode::Idle => {}
+            Mode::Reading(0) if self.cache.is_empty() => {
+                // If the cache is empty in reading mode, the last operation didn't read any bytes,
+                // which indicates that it reached the end of the file. In this case we need to
+                // reset the mode to idle so that next time we try to read again, since the file
+                // may grow after the first EOF.
+                self.mode = Mode::Idle;
+                return Poll::Ready(Ok(0));
+            }
             Mode::Reading(start) => {
                 // How many bytes in the cache are available for reading.
                 let available = self.cache.len() - start;
 
-                // If there is cached unconsumed data or if the cache is empty, we can read from
-                // it. Empty cache in reading mode indicates that the last operation didn't read
-                // any bytes, i.e. it reached the end of the file.
-                if available > 0 || self.cache.is_empty() {
+                if available > 0 {
                     // Copy data from the cache into the buffer.
                     let n = cmp::min(available, buf.len());
-                    buf[..n].copy_from_slice(&self.cache[start..n]);
+                    buf[..n].copy_from_slice(&self.cache[start..(start + n)]);
 
                     // Move the read cursor forward.
                     self.mode = Mode::Reading(start + n);
@@ -702,7 +786,7 @@ impl LockGuard<State> {
         self.register(cx);
 
         // Start a read operation asynchronously.
-        blocking::spawn(async move {
+        spawn_blocking(move || {
             // Read some data from the file into the cache.
             let res = {
                 let State { file, cache, .. } = &mut *self;
@@ -743,7 +827,10 @@ impl LockGuard<State> {
                 if n > 0 {
                     // Seek `n` bytes backwards. This call should not block because it only changes
                     // the internal offset into the file and doesn't touch the actual file on disk.
-                    (&*self.file).seek(SeekFrom::Current(-(n as i64)))?;
+                    //
+                    // We ignore errors here because special files like `/dev/random` are not
+                    // seekable.
+                    let _ = (&*self.file).seek(SeekFrom::Current(-(n as i64)));
                 }
 
                 // Switch to idle mode.
@@ -811,7 +898,7 @@ impl LockGuard<State> {
                 self.register(cx);
 
                 // Start a write operation asynchronously.
-                blocking::spawn(async move {
+                spawn_blocking(move || {
                     match (&*self.file).write_all(&self.cache) {
                         Ok(_) => {
                             // Switch to idle mode.
@@ -844,7 +931,7 @@ impl LockGuard<State> {
         self.register(cx);
 
         // Start a flush operation asynchronously.
-        blocking::spawn(async move {
+        spawn_blocking(move || {
             match (&*self.file).flush() {
                 Ok(()) => {
                     // Mark the file as flushed.
@@ -866,3 +953,79 @@ impl LockGuard<State> {
         Poll::Ready(Ok(()))
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn async_file_drop() {
+        crate::task::block_on(async move {
+            File::open(file!()).await.unwrap();
+        });
+    }
+
+    #[test]
+    fn async_file_clone() {
+        crate::task::block_on(async move {
+            let file = File::open(file!()).await.unwrap();
+            let mut clone = file.clone();
+            let len = crate::task::spawn_blocking(move || {
+                let mut buf = Vec::new();
+                crate::task::block_on(async move {
+                    clone.read_to_end(&mut buf).await.unwrap();
+                    drop(clone);
+                    buf.len()
+                })
+            }).await;
+            assert_eq!(len as u64, file.metadata().await.unwrap().len());
+        });
+    }
+
+    #[test]
+    fn async_file_create_error() {
+        let file_name = Path::new("/tmp/does_not_exist/test");
+        let expect = std::fs::File::create(file_name).unwrap_err();
+
+        crate::task::block_on(async move {
+            let actual = File::create(file_name).await.unwrap_err();
+            assert_eq!(format!("{}", expect), format!("{}", actual));
+        })
+    }
+
+    #[test]
+    fn file_eof_is_not_permanent() -> crate::io::Result<()> {
+        let tempdir = tempfile::Builder::new()
+            .prefix("async-std-file-eof-test")
+            .tempdir()?;
+        let path = tempdir.path().join("testfile");
+
+        crate::task::block_on(async {
+            let mut file_w = File::create(&path).await?;
+            let mut file_r = File::open(&path).await?;
+
+            file_w.write_all(b"data").await?;
+            file_w.flush().await?;
+
+            let mut buf = [0u8; 4];
+            let mut len = file_r.read(&mut buf).await?;
+            assert_eq!(len, 4);
+            assert_eq!(&buf, b"data");
+
+            len = file_r.read(&mut buf).await?;
+            assert_eq!(len, 0);
+
+            file_w.write_all(b"more").await?;
+            file_w.flush().await?;
+
+            len = file_r.read(&mut buf).await?;
+            assert_eq!(len, 4);
+            assert_eq!(&buf, b"more");
+
+            len = file_r.read(&mut buf).await?;
+            assert_eq!(len, 0);
+
+            Ok(())
+        })
+    }
+}
diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs
index a1627984c..d7ce25704 100644
--- a/src/fs/file_type.rs
+++ b/src/fs/file_type.rs
@@ -1,86 +1,84 @@
-use cfg_if::cfg_if;
+cfg_not_docs! {
+    pub use std::fs::FileType;
+}
+
+cfg_docs! {
+    /// The type of a file or directory.
+    ///
+    /// A file type is returned by [`Metadata::file_type`].
+    ///
+    /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
+    /// [`is_file`], and [`is_symlink`] can return `true`.
+    ///
+    /// This type is a re-export of [`std::fs::FileType`].
+    ///
+    /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
+    /// [`is_dir`]: #method.is_dir
+    /// [`is_file`]: #method.is_file
+    /// [`is_symlink`]: #method.is_symlink
+    /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
+    #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+    pub struct FileType {
+        _private: (),
+    }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        /// The type of a file or directory.
+    impl FileType {
+        /// Returns `true` if this file type represents a regular directory.
         ///
-        /// A file type is returned by [`Metadata::file_type`].
+        /// If this file type represents a symbolic link, this method returns `false`.
         ///
-        /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`],
-        /// [`is_file`], and [`is_symlink`] can return `true`.
+        /// # Examples
         ///
-        /// This type is a re-export of [`std::fs::FileType`].
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
         ///
-        /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type
-        /// [`is_dir`]: #method.is_dir
-        /// [`is_file`]: #method.is_file
-        /// [`is_symlink`]: #method.is_symlink
-        /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html
-        #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-        pub struct FileType {
-            _private: (),
+        /// let file_type = fs::metadata(".").await?.file_type();
+        /// println!("{:?}", file_type.is_dir());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn is_dir(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
         }
 
-        impl FileType {
-            /// Returns `true` if this file type represents a regular directory.
-            ///
-            /// If this file type represents a symbolic link, this method returns `false`.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let file_type = fs::metadata(".").await?.file_type();
-            /// println!("{:?}", file_type.is_dir());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn is_dir(&self) -> bool {
-                unimplemented!()
-            }
-
-            /// Returns `true` if this file type represents a regular file.
-            ///
-            /// If this file type represents a symbolic link, this method returns `false`.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let file_type = fs::metadata("a.txt").await?.file_type();
-            /// println!("{:?}", file_type.is_file());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn is_file(&self) -> bool {
-                unimplemented!()
-            }
+        /// Returns `true` if this file type represents a regular file.
+        ///
+        /// If this file type represents a symbolic link, this method returns `false`.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let file_type = fs::metadata("a.txt").await?.file_type();
+        /// println!("{:?}", file_type.is_file());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn is_file(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns `true` if this file type represents a symbolic link.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let file_type = fs::metadata("a.txt").await?.file_type();
-            /// println!("{:?}", file_type.is_symlink());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn is_symlink(&self) -> bool {
-                unimplemented!()
-            }
+        /// Returns `true` if this file type represents a symbolic link.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let file_type = fs::metadata("a.txt").await?.file_type();
+        /// println!("{:?}", file_type.is_symlink());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn is_symlink(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
         }
-    } else {
-        pub use std::fs::FileType;
     }
 }
diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs
index 5f950cde1..a6a406989 100644
--- a/src/fs/hard_link.rs
+++ b/src/fs/hard_link.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Creates a hard link on the filesystem.
 ///
@@ -33,5 +33,14 @@ use crate::task::blocking;
 pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
     let from = from.as_ref().to_owned();
     let to = to.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::hard_link(&from, &to) }).await
+    spawn_blocking(move || {
+        std::fs::hard_link(&from, &to).context(|| {
+            format!(
+                "could not create a hard link from `{}` to `{}`",
+                from.display(),
+                to.display()
+            )
+        })
+    })
+    .await
 }
diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs
index 2c9e41ecd..2948016e2 100644
--- a/src/fs/metadata.rs
+++ b/src/fs/metadata.rs
@@ -1,9 +1,6 @@
-use std::path::Path;
-
-use cfg_if::cfg_if;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
 
 /// Reads metadata for a path.
 ///
@@ -37,196 +34,196 @@ use crate::task::blocking;
 /// ```
 pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::metadata(path) }).await
+    spawn_blocking(move || std::fs::metadata(path)).await
+}
+
+cfg_not_docs! {
+    pub use std::fs::Metadata;
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::time::SystemTime;
+cfg_docs! {
+    use std::time::SystemTime;
+
+    use crate::fs::{FileType, Permissions};
 
-        use crate::fs::{FileType, Permissions};
+    /// Metadata for a file or directory.
+    ///
+    /// Metadata is returned by [`metadata`] and [`symlink_metadata`].
+    ///
+    /// This type is a re-export of [`std::fs::Metadata`].
+    ///
+    /// [`metadata`]: fn.metadata.html
+    /// [`symlink_metadata`]: fn.symlink_metadata.html
+    /// [`is_dir`]: #method.is_dir
+    /// [`is_file`]: #method.is_file
+    /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html
+    #[derive(Clone, Debug)]
+    pub struct Metadata {
+        _private: (),
+    }
 
-        /// Metadata for a file or directory.
+    impl Metadata {
+        /// Returns the file type from this metadata.
         ///
-        /// Metadata is returned by [`metadata`] and [`symlink_metadata`].
+        /// # Examples
         ///
-        /// This type is a re-export of [`std::fs::Metadata`].
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
         ///
-        /// [`metadata`]: fn.metadata.html
-        /// [`symlink_metadata`]: fn.symlink_metadata.html
-        /// [`is_dir`]: #method.is_dir
-        /// [`is_file`]: #method.is_file
-        /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html
-        #[derive(Clone, Debug)]
-        pub struct Metadata {
-            _private: (),
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{:?}", metadata.file_type());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn file_type(&self) -> FileType {
+            unreachable!("this impl only appears in the rendered docs")
         }
 
-        impl Metadata {
-            /// Returns the file type from this metadata.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{:?}", metadata.file_type());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn file_type(&self) -> FileType {
-                unimplemented!()
-            }
-
-            /// Returns `true` if this metadata is for a regular directory.
-            ///
-            /// If this metadata is for a symbolic link, this method returns `false`.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata(".").await?;
-            /// println!("{:?}", metadata.is_dir());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn is_dir(&self) -> bool {
-                unimplemented!()
-            }
+        /// Returns `true` if this metadata is for a regular directory.
+        ///
+        /// If this metadata is for a symbolic link, this method returns `false`.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata(".").await?;
+        /// println!("{:?}", metadata.is_dir());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn is_dir(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns `true` if this metadata is for a regular file.
-            ///
-            /// If this metadata is for a symbolic link, this method returns `false`.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{:?}", metadata.is_file());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn is_file(&self) -> bool {
-                unimplemented!()
-            }
+        /// Returns `true` if this metadata is for a regular file.
+        ///
+        /// If this metadata is for a symbolic link, this method returns `false`.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{:?}", metadata.is_file());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn is_file(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns the file size in bytes.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{}", metadata.len());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn len(&self) -> u64 {
-                unimplemented!()
-            }
+        /// Returns the file size in bytes.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{}", metadata.len());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn len(&self) -> u64 {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns the permissions from this metadata.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{:?}", metadata.permissions());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn permissions(&self) -> Permissions {
-                unimplemented!()
-            }
+        /// Returns the permissions from this metadata.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{:?}", metadata.permissions());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn permissions(&self) -> Permissions {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns the last modification time.
-            ///
-            /// # Errors
-            ///
-            /// This data may not be available on all platforms, in which case an error will be
-            /// returned.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{:?}", metadata.modified());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn modified(&self) -> io::Result<SystemTime> {
-                unimplemented!()
-            }
+        /// Returns the last modification time.
+        ///
+        /// # Errors
+        ///
+        /// This data may not be available on all platforms, in which case an error will be
+        /// returned.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{:?}", metadata.modified());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn modified(&self) -> io::Result<SystemTime> {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns the last access time.
-            ///
-            /// # Errors
-            ///
-            /// This data may not be available on all platforms, in which case an error will be
-            /// returned.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{:?}", metadata.accessed());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn accessed(&self) -> io::Result<SystemTime> {
-                unimplemented!()
-            }
+        /// Returns the last access time.
+        ///
+        /// # Errors
+        ///
+        /// This data may not be available on all platforms, in which case an error will be
+        /// returned.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{:?}", metadata.accessed());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn accessed(&self) -> io::Result<SystemTime> {
+            unreachable!("this impl only appears in the rendered docs")
+        }
 
-            /// Returns the creation time.
-            ///
-            /// # Errors
-            ///
-            /// This data may not be available on all platforms, in which case an error will be
-            /// returned.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let metadata = fs::metadata("a.txt").await?;
-            /// println!("{:?}", metadata.created());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn created(&self) -> io::Result<SystemTime> {
-                unimplemented!()
-            }
+        /// Returns the creation time.
+        ///
+        /// # Errors
+        ///
+        /// This data may not be available on all platforms, in which case an error will be
+        /// returned.
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let metadata = fs::metadata("a.txt").await?;
+        /// println!("{:?}", metadata.created());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn created(&self) -> io::Result<SystemTime> {
+            unreachable!("this impl only appears in the rendered docs")
         }
-    } else {
-        pub use std::fs::Metadata;
     }
 }
diff --git a/src/fs/mod.rs b/src/fs/mod.rs
index 4598ec849..5cf086def 100644
--- a/src/fs/mod.rs
+++ b/src/fs/mod.rs
@@ -3,11 +3,13 @@
 //! This module is an async version of [`std::fs`].
 //!
 //! [`os::unix::fs`]: ../os/unix/fs/index.html
+//! [`os::windows::fs`]: ../os/windows/fs/index.html
 //! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html
 //!
 //! # Platform-specific extensions
 //!
 //! * Unix: use the [`os::unix::fs`] module.
+//! * Windows: use the [`os::windows::fs`] module.
 //!
 //! # Examples
 //!
diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs
index c6cc74a0d..18aa89eb3 100644
--- a/src/fs/open_options.rs
+++ b/src/fs/open_options.rs
@@ -1,11 +1,9 @@
-use std::path::Path;
-
-use cfg_if::cfg_if;
+use std::future::Future;
 
 use crate::fs::File;
-use crate::future::Future;
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
 
 /// A builder for opening files with configurable options.
 ///
@@ -138,7 +136,7 @@ impl OpenOptions {
     /// Configures the option for append mode.
     ///
     /// When set to `true`, this option means the file will be writable after opening and the file
-    /// cursor will be moved to the end of file before every write operaiton.
+    /// cursor will be moved to the end of file before every write operation.
     ///
     /// # Examples
     ///
@@ -286,31 +284,31 @@ impl OpenOptions {
     pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
         let path = path.as_ref().to_owned();
         let options = self.0.clone();
-        async move { blocking::spawn(async move { options.open(path).map(|f| f.into()) }).await }
+        async move {
+            let file = spawn_blocking(move || options.open(path)).await?;
+            Ok(File::new(file, true))
+        }
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::fs::OpenOptionsExt;
-    } else if #[cfg(unix)] {
-        use std::os::unix::fs::OpenOptionsExt;
+impl Default for OpenOptions {
+    fn default() -> Self {
+        Self::new()
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl OpenOptionsExt for OpenOptions {
-            fn mode(&mut self, mode: u32) -> &mut Self {
-                self.0.mode(mode);
-                self
-            }
+cfg_unix! {
+    use crate::os::unix::fs::OpenOptionsExt;
+
+    impl OpenOptionsExt for OpenOptions {
+        fn mode(&mut self, mode: u32) -> &mut Self {
+            self.0.mode(mode);
+            self
+        }
 
-            fn custom_flags(&mut self, flags: i32) -> &mut Self {
-                self.0.custom_flags(flags);
-                self
-            }
+        fn custom_flags(&mut self, flags: i32) -> &mut Self {
+            self.0.custom_flags(flags);
+            self
         }
     }
 }
diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs
index 628dd392b..50aa45cd1 100644
--- a/src/fs/permissions.rs
+++ b/src/fs/permissions.rs
@@ -1,58 +1,56 @@
-use cfg_if::cfg_if;
+cfg_not_docs! {
+    pub use std::fs::Permissions;
+}
+
+cfg_docs! {
+    /// A set of permissions on a file or directory.
+    ///
+    /// This type is a re-export of [`std::fs::Permissions`].
+    ///
+    /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
+    #[derive(Clone, PartialEq, Eq, Debug)]
+    pub struct Permissions {
+        _private: (),
+    }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        /// A set of permissions on a file or directory.
+    impl Permissions {
+        /// Returns the read-only flag.
         ///
-        /// This type is a re-export of [`std::fs::Permissions`].
+        /// # Examples
         ///
-        /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html
-        #[derive(Clone, PartialEq, Eq, Debug)]
-        pub struct Permissions {
-            _private: (),
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let perm = fs::metadata("a.txt").await?.permissions();
+        /// println!("{:?}", perm.readonly());
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn readonly(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
         }
 
-        impl Permissions {
-            /// Returns the read-only flag.
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let perm = fs::metadata("a.txt").await?.permissions();
-            /// println!("{:?}", perm.readonly());
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn readonly(&self) -> bool {
-                unimplemented!()
-            }
-
-            /// Configures the read-only flag.
-            ///
-            /// [`fs::set_permissions`]: fn.set_permissions.html
-            ///
-            /// # Examples
-            ///
-            /// ```no_run
-            /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            /// #
-            /// use async_std::fs;
-            ///
-            /// let mut perm = fs::metadata("a.txt").await?.permissions();
-            /// perm.set_readonly(true);
-            /// fs::set_permissions("a.txt", perm).await?;
-            /// #
-            /// # Ok(()) }) }
-            /// ```
-            pub fn set_readonly(&mut self, readonly: bool) {
-                unimplemented!()
-            }
+        /// Configures the read-only flag.
+        ///
+        /// [`fs::set_permissions`]: fn.set_permissions.html
+        ///
+        /// # Examples
+        ///
+        /// ```no_run
+        /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        /// #
+        /// use async_std::fs;
+        ///
+        /// let mut perm = fs::metadata("a.txt").await?.permissions();
+        /// perm.set_readonly(true);
+        /// fs::set_permissions("a.txt", perm).await?;
+        /// #
+        /// # Ok(()) }) }
+        /// ```
+        pub fn set_readonly(&mut self, readonly: bool) {
+            unreachable!("this impl only appears in the rendered docs")
         }
-    } else {
-        pub use std::fs::Permissions;
     }
 }
diff --git a/src/fs/read.rs b/src/fs/read.rs
index 6b3560db7..3b568f7a6 100644
--- a/src/fs/read.rs
+++ b/src/fs/read.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Reads the entire contents of a file as raw bytes.
 ///
@@ -37,5 +37,8 @@ use crate::task::blocking;
 /// ```
 pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::read(path) }).await
+    spawn_blocking(move || {
+        std::fs::read(&path).context(|| format!("could not read file `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs
index c6f80feed..d8261a949 100644
--- a/src/fs/read_dir.rs
+++ b/src/fs/read_dir.rs
@@ -1,11 +1,12 @@
-use std::path::Path;
+use std::future::Future;
 use std::pin::Pin;
 
 use crate::fs::DirEntry;
-use crate::future::Future;
 use crate::io;
+use crate::path::Path;
 use crate::stream::Stream;
-use crate::task::{blocking, Context, Poll};
+use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
+use crate::utils::Context as _;
 
 /// Returns a stream of entries in a directory.
 ///
@@ -45,9 +46,12 @@ use crate::task::{blocking, Context, Poll};
 /// ```
 pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::read_dir(path) })
-        .await
-        .map(ReadDir::new)
+    spawn_blocking(move || {
+        std::fs::read_dir(&path)
+            .context(|| format!("could not read directory `{}`", path.display()))
+    })
+    .await
+    .map(ReadDir::new)
 }
 
 /// A stream of entries in a directory.
@@ -71,7 +75,7 @@ pub struct ReadDir(State);
 #[derive(Debug)]
 enum State {
     Idle(Option<std::fs::ReadDir>),
-    Busy(blocking::JoinHandle<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>),
+    Busy(JoinHandle<(std::fs::ReadDir, Option<io::Result<std::fs::DirEntry>>)>),
 }
 
 impl ReadDir {
@@ -91,7 +95,7 @@ impl Stream for ReadDir {
                     let mut inner = opt.take().unwrap();
 
                     // Start the operation asynchronously.
-                    self.0 = State::Busy(blocking::spawn(async move {
+                    self.0 = State::Busy(spawn_blocking(move || {
                         let next = inner.next();
                         (inner, next)
                     }));
diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs
index aede99bc8..d8cabb725 100644
--- a/src/fs/read_link.rs
+++ b/src/fs/read_link.rs
@@ -1,7 +1,7 @@
-use std::path::{Path, PathBuf};
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::{Path, PathBuf};
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Reads a symbolic link and returns the path it points to.
 ///
@@ -29,5 +29,10 @@ use crate::task::blocking;
 /// ```
 pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::read_link(path) }).await
+    spawn_blocking(move || {
+        std::fs::read_link(&path)
+            .map(Into::into)
+            .context(|| format!("could not read link `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs
index 345f76efa..2378aaedc 100644
--- a/src/fs/read_to_string.rs
+++ b/src/fs/read_to_string.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Reads the entire contents of a file as a string.
 ///
@@ -38,5 +38,9 @@ use crate::task::blocking;
 /// ```
 pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::read_to_string(path) }).await
+    spawn_blocking(move || {
+        std::fs::read_to_string(&path)
+            .context(|| format!("could not read file `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs
index a176edc85..8fdba18c1 100644
--- a/src/fs/remove_dir.rs
+++ b/src/fs/remove_dir.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Removes an empty directory.
 ///
@@ -30,5 +30,9 @@ use crate::task::blocking;
 /// ```
 pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::remove_dir(path) }).await
+    spawn_blocking(move || {
+        std::fs::remove_dir(&path)
+            .context(|| format!("could not remove directory `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs
index 9db0c31f5..d4bad3a28 100644
--- a/src/fs/remove_dir_all.rs
+++ b/src/fs/remove_dir_all.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Removes a directory and all of its contents.
 ///
@@ -30,5 +30,9 @@ use crate::task::blocking;
 /// ```
 pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::remove_dir_all(path) }).await
+    spawn_blocking(move || {
+        std::fs::remove_dir_all(&path)
+            .context(|| format!("could not remove directory `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs
index cc0eeb259..b881f8be5 100644
--- a/src/fs/remove_file.rs
+++ b/src/fs/remove_file.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Removes a file.
 ///
@@ -30,5 +30,9 @@ use crate::task::blocking;
 /// ```
 pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::remove_file(path) }).await
+    spawn_blocking(move || {
+        std::fs::remove_file(&path)
+            .context(|| format!("could not remove file `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/fs/rename.rs b/src/fs/rename.rs
index 72cd227f9..25fc55fa2 100644
--- a/src/fs/rename.rs
+++ b/src/fs/rename.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Renames a file or directory to a new location.
 ///
@@ -35,5 +35,14 @@ use crate::task::blocking;
 pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
     let from = from.as_ref().to_owned();
     let to = to.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::rename(&from, &to) }).await
+    spawn_blocking(move || {
+        std::fs::rename(&from, &to).context(|| {
+            format!(
+                "could not rename `{}` to `{}`",
+                from.display(),
+                to.display()
+            )
+        })
+    })
+    .await
 }
diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs
index 6fa6306fc..60a6d6f16 100644
--- a/src/fs/set_permissions.rs
+++ b/src/fs/set_permissions.rs
@@ -1,8 +1,7 @@
-use std::path::Path;
-
 use crate::fs::Permissions;
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
 
 /// Changes the permissions of a file or directory.
 ///
@@ -33,5 +32,5 @@ use crate::task::blocking;
 /// ```
 pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::set_permissions(path, perm) }).await
+    spawn_blocking(move || std::fs::set_permissions(path, perm)).await
 }
diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs
index 6f1b9d501..45be6d99d 100644
--- a/src/fs/symlink_metadata.rs
+++ b/src/fs/symlink_metadata.rs
@@ -1,8 +1,7 @@
-use std::path::Path;
-
 use crate::fs::Metadata;
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
 
 /// Reads metadata for a path without following symbolic links.
 ///
@@ -35,5 +34,5 @@ use crate::task::blocking;
 /// ```
 pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
     let path = path.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::symlink_metadata(path) }).await
+    spawn_blocking(move || std::fs::symlink_metadata(path)).await
 }
diff --git a/src/fs/write.rs b/src/fs/write.rs
index b0d7abcc2..7c14098b0 100644
--- a/src/fs/write.rs
+++ b/src/fs/write.rs
@@ -1,7 +1,7 @@
-use std::path::Path;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+use crate::utils::Context as _;
 
 /// Writes a slice of bytes as the new contents of a file.
 ///
@@ -34,5 +34,9 @@ use crate::task::blocking;
 pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
     let path = path.as_ref().to_owned();
     let contents = contents.as_ref().to_owned();
-    blocking::spawn(async move { std::fs::write(path, contents) }).await
+    spawn_blocking(move || {
+        std::fs::write(&path, contents)
+            .context(|| format!("could not write to file `{}`", path.display()))
+    })
+    .await
 }
diff --git a/src/future/future/delay.rs b/src/future/future/delay.rs
new file mode 100644
index 000000000..092639d91
--- /dev/null
+++ b/src/future/future/delay.rs
@@ -0,0 +1,43 @@
+use std::future::Future;
+use std::pin::Pin;
+use std::time::Duration;
+
+use pin_project_lite::pin_project;
+
+use crate::task::{Context, Poll};
+use crate::utils::{timer_after, Timer};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct DelayFuture<F> {
+        #[pin]
+        future: F,
+        #[pin]
+        delay: Timer,
+    }
+}
+
+impl<F> DelayFuture<F> {
+    pub fn new(future: F, dur: Duration) -> DelayFuture<F> {
+        let delay = timer_after(dur);
+
+        DelayFuture { future, delay }
+    }
+}
+
+impl<F: Future> Future for DelayFuture<F> {
+    type Output = F::Output;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+
+        match this.delay.poll(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(_) => match this.future.poll(cx) {
+                Poll::Ready(v) => Poll::Ready(v),
+                Poll::Pending => Poll::Pending,
+            },
+        }
+    }
+}
diff --git a/src/future/future/flatten.rs b/src/future/future/flatten.rs
new file mode 100644
index 000000000..0ea77bbfb
--- /dev/null
+++ b/src/future/future/flatten.rs
@@ -0,0 +1,65 @@
+use pin_project_lite::pin_project;
+use std::future::Future;
+use std::pin::Pin;
+
+use crate::future::IntoFuture;
+use crate::task::{ready, Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct FlattenFuture<Fut1, Fut2> {
+        #[pin]
+        state: State<Fut1, Fut2>,
+    }
+}
+
+pin_project! {
+    #[project = StateProj]
+    #[derive(Debug)]
+    enum State<Fut1, Fut2> {
+        First {
+            #[pin]
+            fut1: Fut1,
+        },
+        Second {
+            #[pin]
+            fut2: Fut2,
+        },
+        Empty,
+    }
+}
+
+impl<Fut1, Fut2> FlattenFuture<Fut1, Fut2> {
+    pub(crate) fn new(fut1: Fut1) -> FlattenFuture<Fut1, Fut2> {
+        FlattenFuture {
+            state: State::First { fut1 },
+        }
+    }
+}
+
+impl<Fut1> Future for FlattenFuture<Fut1, <Fut1::Output as IntoFuture>::Future>
+where
+    Fut1: Future,
+    Fut1::Output: IntoFuture,
+{
+    type Output = <Fut1::Output as IntoFuture>::Output;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut state = self.project().state;
+        loop {
+            match state.as_mut().project() {
+                StateProj::First { fut1 } => {
+                    let fut2 = ready!(fut1.poll(cx)).into_future();
+                    state.set(State::Second { fut2 });
+                }
+                StateProj::Second { fut2 } => {
+                    let v = ready!(fut2.poll(cx));
+                    state.set(State::Empty);
+                    return Poll::Ready(v);
+                }
+                StateProj::Empty => panic!("polled a completed future"),
+            }
+        }
+    }
+}
diff --git a/src/future/future/join.rs b/src/future/future/join.rs
new file mode 100644
index 000000000..4a508ce8e
--- /dev/null
+++ b/src/future/future/join.rs
@@ -0,0 +1,60 @@
+use std::pin::Pin;
+
+use crate::future::MaybeDone;
+use pin_project_lite::pin_project;
+
+use crate::task::{Context, Poll};
+use std::future::Future;
+
+pin_project! {
+    #[allow(missing_docs)]
+    #[allow(missing_debug_implementations)]
+    pub struct Join<L, R>
+    where
+        L: Future,
+        R: Future,
+    {
+        #[pin] left: MaybeDone<L>,
+        #[pin] right: MaybeDone<R>,
+    }
+}
+
+impl<L, R> Join<L, R>
+where
+    L: Future,
+    R: Future,
+{
+    pub(crate) fn new(left: L, right: R) -> Self {
+        Self {
+            left: MaybeDone::new(left),
+            right: MaybeDone::new(right),
+        }
+    }
+}
+
+impl<L, R> Future for Join<L, R>
+where
+    L: Future,
+    R: Future,
+{
+    type Output = (L::Output, R::Output);
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+
+        let mut left = this.left;
+        let mut right = this.right;
+
+        let is_left_ready = Future::poll(Pin::new(&mut left), cx).is_ready();
+        if is_left_ready && right.as_ref().output().is_some() {
+            return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
+        }
+
+        let is_right_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
+        if is_right_ready && left.as_ref().output().is_some() {
+            return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
+        }
+
+        Poll::Pending
+    }
+}
diff --git a/src/future/future/mod.rs b/src/future/future/mod.rs
new file mode 100644
index 000000000..9a8bfcc1f
--- /dev/null
+++ b/src/future/future/mod.rs
@@ -0,0 +1,290 @@
+cfg_unstable! {
+    mod delay;
+    mod flatten;
+    mod race;
+    mod try_race;
+    mod join;
+    mod try_join;
+
+    use std::time::Duration;
+    use delay::DelayFuture;
+    use flatten::FlattenFuture;
+    use crate::future::IntoFuture;
+    use race::Race;
+    use try_race::TryRace;
+    use join::Join;
+    use try_join::TryJoin;
+}
+
+cfg_unstable_default! {
+    use crate::future::timeout::TimeoutFuture;
+}
+
+pub use core::future::Future as Future;
+
+#[doc = r#"
+    Extension methods for [`Future`].
+
+    [`Future`]: ../future/trait.Future.html
+"#]
+#[cfg(any(feature = "std", feature = "docs"))]
+pub trait FutureExt: Future {
+    /// Returns a Future that delays execution for a specified time.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// use async_std::prelude::*;
+    /// use async_std::future;
+    /// use std::time::Duration;
+    ///
+    /// let a = future::ready(1).delay(Duration::from_millis(2000));
+    /// dbg!(a.await);
+    /// # })
+    /// ```
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn delay(self, dur: Duration) -> DelayFuture<Self>
+    where
+        Self: Sized,
+    {
+        DelayFuture::new(self, dur)
+    }
+
+    /// Flatten out the execution of this future when the result itself
+    /// can be converted into another future.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// use async_std::prelude::*;
+    ///
+    /// let nested_future = async { async { 1 } };
+    /// let future = nested_future.flatten();
+    /// assert_eq!(future.await, 1);
+    /// # })
+    /// ```
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn flatten(
+        self,
+    ) -> FlattenFuture<Self, <Self::Output as IntoFuture>::Future>
+    where
+        Self: Sized,
+        <Self as Future>::Output: IntoFuture,
+    {
+       FlattenFuture::new(self)
+    }
+
+    #[doc = r#"
+        Waits for one of two similarly-typed futures to complete.
+
+        Awaits multiple futures simultaneously, returning the output of the
+        first future that completes.
+
+        This function will return a new future which awaits for either one of both
+        futures to complete. If multiple futures are completed at the same time,
+        resolution will occur in the order that they have been passed.
+
+        Note that this function consumes all futures passed, and once a future is
+        completed, all other futures are dropped.
+
+        # Examples
+
+        ```
+        # async_std::task::block_on(async {
+        use async_std::prelude::*;
+        use async_std::future;
+
+        let a = future::pending();
+        let b = future::ready(1u8);
+        let c = future::ready(2u8);
+
+        let f = a.race(b).race(c);
+        assert_eq!(f.await, 1u8);
+        # });
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn race<F>(
+        self,
+        other: F,
+    ) -> Race<Self, F>
+    where
+        Self: std::future::Future + Sized,
+        F: std::future::Future<Output = <Self as std::future::Future>::Output>,
+    {
+        Race::new(self, other)
+    }
+
+    #[doc = r#"
+        Waits for one of two similarly-typed fallible futures to complete.
+
+        Awaits multiple futures simultaneously, returning all results once complete.
+
+        `try_race` is similar to [`race`], but keeps going if a future
+        resolved to an error until all futures have been resolved. In which case
+        an error is returned.
+
+        The ordering of which value is yielded when two futures resolve
+        simultaneously is intentionally left unspecified.
+
+        [`race`]: #method.race
+
+        # Examples
+
+        ```
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::future;
+        use std::io::{Error, ErrorKind};
+
+        let a = future::pending::<Result<_, Error>>();
+        let b = future::ready(Err(Error::from(ErrorKind::Other)));
+        let c = future::ready(Ok(1u8));
+
+        let f = a.try_race(b).try_race(c);
+        assert_eq!(f.await?, 1u8);
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn try_race<F, T, E>(
+        self,
+        other: F
+    ) -> TryRace<Self, F>
+    where
+        Self: std::future::Future<Output = Result<T, E>> + Sized,
+        F: std::future::Future<Output = <Self as std::future::Future>::Output>,
+    {
+        TryRace::new(self, other)
+    }
+
+    #[doc = r#"
+        Waits for two similarly-typed futures to complete.
+
+        Awaits multiple futures simultaneously, returning the output of the
+        futures once both complete.
+
+        This function returns a new future which polls both futures
+        concurrently.
+
+        # Examples
+
+        ```
+        # async_std::task::block_on(async {
+        use async_std::prelude::*;
+        use async_std::future;
+
+        let a = future::ready(1u8);
+        let b = future::ready(2u16);
+
+        let f = a.join(b);
+        assert_eq!(f.await, (1u8, 2u16));
+        # });
+        ```
+    "#]
+    #[cfg(any(feature = "unstable", feature = "docs"))]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn join<F>(
+        self,
+        other: F
+    ) -> Join<Self, F>
+    where
+        Self: std::future::Future + Sized,
+        F: std::future::Future,
+    {
+        Join::new(self, other)
+    }
+
+    #[doc = r#"
+        Waits for two similarly-typed fallible futures to complete.
+
+        Awaits multiple futures simultaneously, returning all results once
+        complete.
+
+        `try_join` is similar to [`join`], but returns an error immediately
+        if a future resolves to an error.
+
+        [`join`]: #method.join
+
+        # Examples
+
+        ```
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::future;
+
+        let a = future::ready(Err::<u8, &str>("Error"));
+        let b = future::ready(Ok(1u8));
+
+        let f = a.try_join(b);
+        assert_eq!(f.await, Err("Error"));
+
+        let a = future::ready(Ok::<u8, String>(1u8));
+        let b = future::ready(Ok::<u16, String>(2u16));
+
+        let f = a.try_join(b);
+        assert_eq!(f.await, Ok((1u8, 2u16)));
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    #[cfg(any(feature = "unstable", feature = "docs"))]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn try_join<F, A, B, E>(
+        self,
+        other: F
+    ) -> TryJoin<Self, F>
+    where
+        Self: std::future::Future<Output = Result<A, E>> + Sized,
+        F: std::future::Future<Output = Result<B, E>>,
+    {
+        TryJoin::new(self, other)
+    }
+
+    #[doc = r#"
+        Waits for both the future and a timeout, if the timeout completes before
+        the future, it returns a TimeoutError.
+
+        # Example
+        ```
+        # async_std::task::block_on(async {
+        #
+        use std::time::Duration;
+
+        use async_std::prelude::*;
+        use async_std::future;
+
+        let fut = future::ready(0);
+        let dur = Duration::from_millis(100);
+        let res = fut.timeout(dur).await;
+        assert!(res.is_ok());
+
+        let fut = future::pending::<()>();
+        let dur = Duration::from_millis(100);
+        let res = fut.timeout(dur).await;
+        assert!(res.is_err())
+        #
+        # });
+        ```
+    "#]
+    #[cfg(any(all(feature = "default", feature = "unstable"), feature = "docs"))]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn timeout(self, dur: Duration) -> TimeoutFuture<Self>
+        where Self: Sized
+    {
+        TimeoutFuture::new(self, dur)
+    }
+}
+
+#[cfg(any(feature = "std", feature = "docs"))]
+impl<T: Future + ?Sized> FutureExt for T {}
+
diff --git a/src/future/future/race.rs b/src/future/future/race.rs
new file mode 100644
index 000000000..82165a0f1
--- /dev/null
+++ b/src/future/future/race.rs
@@ -0,0 +1,57 @@
+use std::future::Future;
+use std::pin::Pin;
+
+use crate::future::MaybeDone;
+use pin_project_lite::pin_project;
+
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[allow(missing_docs)]
+    #[allow(missing_debug_implementations)]
+    pub struct Race<L, R>
+    where
+        L: Future,
+        R: Future<Output = L::Output>
+    {
+        #[pin] left: MaybeDone<L>,
+        #[pin] right: MaybeDone<R>,
+    }
+}
+
+impl<L, R> Race<L, R>
+where
+    L: Future,
+    R: Future<Output = L::Output>,
+{
+    pub(crate) fn new(left: L, right: R) -> Self {
+        Self {
+            left: MaybeDone::new(left),
+            right: MaybeDone::new(right),
+        }
+    }
+}
+
+impl<L, R> Future for Race<L, R>
+where
+    L: Future,
+    R: Future<Output = L::Output>,
+{
+    type Output = L::Output;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+
+        let mut left = this.left;
+        if Future::poll(Pin::new(&mut left), cx).is_ready() {
+            return Poll::Ready(left.take().unwrap());
+        }
+
+        let mut right = this.right;
+        if Future::poll(Pin::new(&mut right), cx).is_ready() {
+            return Poll::Ready(right.take().unwrap());
+        }
+
+        Poll::Pending
+    }
+}
diff --git a/src/future/future/try_join.rs b/src/future/future/try_join.rs
new file mode 100644
index 000000000..5277ae317
--- /dev/null
+++ b/src/future/future/try_join.rs
@@ -0,0 +1,72 @@
+use std::pin::Pin;
+
+use crate::future::MaybeDone;
+use pin_project_lite::pin_project;
+
+use crate::task::{Context, Poll};
+use std::future::Future;
+
+pin_project! {
+    #[allow(missing_docs)]
+    #[allow(missing_debug_implementations)]
+    pub struct TryJoin<L, R>
+    where
+        L: Future,
+        R: Future,
+    {
+        #[pin] left: MaybeDone<L>,
+        #[pin] right: MaybeDone<R>,
+    }
+}
+
+impl<L, R> TryJoin<L, R>
+where
+    L: Future,
+    R: Future,
+{
+    pub(crate) fn new(left: L, right: R) -> Self {
+        Self {
+            left: MaybeDone::new(left),
+            right: MaybeDone::new(right),
+        }
+    }
+}
+
+impl<L, R, A, B, E> Future for TryJoin<L, R>
+where
+    L: Future<Output = Result<A, E>>,
+    R: Future<Output = Result<B, E>>,
+{
+    type Output = Result<(A, B), E>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+
+        let mut left = this.left;
+        let mut right = this.right;
+
+        if Future::poll(Pin::new(&mut left), cx).is_ready() {
+            if left.as_ref().output().unwrap().is_err() {
+                return Poll::Ready(Err(left.take().unwrap().err().unwrap()));
+            } else if right.as_ref().output().is_some() {
+                return Poll::Ready(Ok((
+                    left.take().unwrap().ok().unwrap(),
+                    right.take().unwrap().ok().unwrap(),
+                )));
+            }
+        }
+
+        if Future::poll(Pin::new(&mut right), cx).is_ready() {
+            if right.as_ref().output().unwrap().is_err() {
+                return Poll::Ready(Err(right.take().unwrap().err().unwrap()));
+            } else if left.as_ref().output().is_some() {
+                return Poll::Ready(Ok((
+                    left.take().unwrap().ok().unwrap(),
+                    right.take().unwrap().ok().unwrap(),
+                )));
+            }
+        }
+
+        Poll::Pending
+    }
+}
diff --git a/src/future/future/try_race.rs b/src/future/future/try_race.rs
new file mode 100644
index 000000000..45199570f
--- /dev/null
+++ b/src/future/future/try_race.rs
@@ -0,0 +1,66 @@
+use std::pin::Pin;
+
+use crate::future::MaybeDone;
+use pin_project_lite::pin_project;
+
+use crate::task::{Context, Poll};
+use std::future::Future;
+
+pin_project! {
+    #[allow(missing_docs)]
+    #[allow(missing_debug_implementations)]
+    pub struct TryRace<L, R>
+    where
+        L: Future,
+        R: Future<Output = L::Output>
+    {
+        #[pin] left: MaybeDone<L>,
+        #[pin] right: MaybeDone<R>,
+    }
+}
+
+impl<L, R> TryRace<L, R>
+where
+    L: Future,
+    R: Future<Output = L::Output>,
+{
+    pub(crate) fn new(left: L, right: R) -> Self {
+        Self {
+            left: MaybeDone::new(left),
+            right: MaybeDone::new(right),
+        }
+    }
+}
+
+impl<L, R, T, E> Future for TryRace<L, R>
+where
+    L: Future<Output = Result<T, E>>,
+    R: Future<Output = L::Output>,
+{
+    type Output = L::Output;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let mut left_errored = false;
+
+        // Check if the left future is ready & successful. Continue if not.
+        let mut left = this.left;
+        if Future::poll(Pin::new(&mut left), cx).is_ready() {
+            if left.as_ref().output().unwrap().is_ok() {
+                return Poll::Ready(left.take().unwrap());
+            } else {
+                left_errored = true;
+            }
+        }
+
+        // Check if the right future is ready & successful. Return err if left
+        // future also resolved to err. Continue if not.
+        let mut right = this.right;
+        let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
+        if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) {
+            return Poll::Ready(right.take().unwrap());
+        }
+
+        Poll::Pending
+    }
+}
diff --git a/src/future/into_future.rs b/src/future/into_future.rs
new file mode 100644
index 000000000..8e5e5e046
--- /dev/null
+++ b/src/future/into_future.rs
@@ -0,0 +1,53 @@
+use std::future::Future;
+
+/// Convert a type into a `Future`.
+///
+/// # Examples
+///
+/// ```
+/// use async_std::future::{Future, IntoFuture};
+/// use async_std::io;
+/// use async_std::pin::Pin;
+///
+/// struct Client;
+///
+/// impl Client {
+///     pub async fn send(self) -> io::Result<()> {
+///         // Send a request
+///         Ok(())
+///     }
+/// }
+///
+/// impl IntoFuture for Client {
+///     type Output = io::Result<()>;
+///
+///     type Future = Pin<Box<dyn Future<Output = Self::Output>>>;
+///
+///     fn into_future(self) -> Self::Future {
+///         Box::pin(async {
+///             self.send().await
+///         })
+///     }
+/// }
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub trait IntoFuture {
+    /// The type of value produced on completion.
+    type Output;
+
+    /// Which kind of future are we turning this into?
+    type Future: Future<Output = Self::Output>;
+
+    /// Create a future from a value
+    fn into_future(self) -> Self::Future;
+}
+
+impl<T: Future> IntoFuture for T {
+    type Output = T::Output;
+    type Future = T;
+
+    fn into_future(self) -> Self::Future {
+        self
+    }
+}
diff --git a/src/future/maybe_done.rs b/src/future/maybe_done.rs
new file mode 100644
index 000000000..7a3a7fa33
--- /dev/null
+++ b/src/future/maybe_done.rs
@@ -0,0 +1,79 @@
+//! A type that wraps a future to keep track of its completion status.
+//!
+//! This implementation was taken from the original `macro_rules` `join/try_join`
+//! macros in the `futures-preview` crate.
+
+use std::future::Future;
+use std::mem;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+use futures_core::ready;
+
+/// A future that may have completed.
+#[derive(Debug)]
+pub(crate) enum MaybeDone<Fut: Future> {
+    /// A not-yet-completed future
+    Future(Fut),
+
+    /// The output of the completed future
+    Done(Fut::Output),
+
+    /// The empty variant after the result of a [`MaybeDone`] has been
+    /// taken using the [`take`](MaybeDone::take) method.
+    Gone,
+}
+
+impl<Fut: Future> MaybeDone<Fut> {
+    /// Create a new instance of `MaybeDone`.
+    pub(crate) fn new(future: Fut) -> MaybeDone<Fut> {
+        Self::Future(future)
+    }
+
+    /// Returns an [`Option`] containing a reference to the output of the future.
+    /// The output of this method will be [`Some`] if and only if the inner
+    /// future has been completed and [`take`](MaybeDone::take)
+    /// has not yet been called.
+    #[inline]
+    pub(crate) fn output(self: Pin<&Self>) -> Option<&Fut::Output> {
+        let this = self.get_ref();
+        match this {
+            MaybeDone::Done(res) => Some(res),
+            _ => None,
+        }
+    }
+
+    /// Attempt to take the output of a `MaybeDone` without driving it
+    /// towards completion.
+    #[inline]
+    pub(crate) fn take(self: Pin<&mut Self>) -> Option<Fut::Output> {
+        unsafe {
+            let this = self.get_unchecked_mut();
+            match this {
+                MaybeDone::Done(_) => {}
+                MaybeDone::Future(_) | MaybeDone::Gone => return None,
+            };
+            if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) {
+                Some(output)
+            } else {
+                unreachable!()
+            }
+        }
+    }
+}
+
+impl<Fut: Future> Future for MaybeDone<Fut> {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let res = unsafe {
+            match Pin::as_mut(&mut self).get_unchecked_mut() {
+                MaybeDone::Future(a) => ready!(Pin::new_unchecked(a).poll(cx)),
+                MaybeDone::Done(_) => return Poll::Ready(()),
+                MaybeDone::Gone => panic!("MaybeDone polled after value taken"),
+            }
+        };
+        self.set(MaybeDone::Done(res));
+        Poll::Ready(())
+    }
+}
diff --git a/src/future/mod.rs b/src/future/mod.rs
index 1f67e1aa5..1b3cd5863 100644
--- a/src/future/mod.rs
+++ b/src/future/mod.rs
@@ -2,65 +2,73 @@
 //!
 //! ## Base Futures Concurrency
 //!
-//! Often it's desireable to await multiple futures as if it was a single
+//! Often it's desirable to await multiple futures as if it was a single
 //! future. The `join` family of operations converts multiple futures into a
-//! single future that returns all of their outputs. The `select` family of
+//! single future that returns all of their outputs. The `race` family of
 //! operations converts multiple future into a single future that returns the
 //! first output.
 //!
-//! For operating on futures the following macros can be used:
+//! For operating on futures the following functions can be used:
 //!
-//! | Name             | Return signature | When does it return?     |
-//! | ---              | ---              | ---                      |
-//! | `future::join`   | `(T1, T2)`       | Wait for all to complete
-//! | `future::select` | `T`              | Return on first value
+//! | Name               | Return signature | When does it return?     |
+//! | ---                | ---              | ---                      |
+//! | [`Future::join`]   | `(T1, T2)`       | Wait for all to complete
+//! | [`Future::race`]   | `T`              | Return on first value
 //!
 //! ## Fallible Futures Concurrency
 //!
 //! For operating on futures that return `Result` additional `try_` variants of
-//! the macros mentioned before can be used. These macros are aware of `Result`,
+//! the functions mentioned before can be used. These functions are aware of `Result`,
 //! and will behave slightly differently from their base variants.
 //!
 //! In the case of `try_join`, if any of the futures returns `Err` all
 //! futures are dropped and an error is returned. This is referred to as
 //! "short-circuiting".
 //!
-//! In the case of `try_select`, instead of returning the first future that
+//! In the case of `try_race`, instead of returning the first future that
 //! completes it returns the first future that _successfully_ completes. This
-//! means `try_select` will keep going until any one of the futures returns
+//! means `try_race` will keep going until any one of the futures returns
 //! `Ok`, or _all_ futures have returned `Err`.
 //!
-//! However sometimes it can be useful to use the base variants of the macros
+//! However sometimes it can be useful to use the base variants of the functions
 //! even on futures that return `Result`. Here is an overview of operations that
 //! work on `Result`, and their respective semantics:
 //!
-//! | Name                 | Return signature               | When does it return? |
-//! | ---                  | ---                            | ---                  |
-//! | `future::join`       | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
-//! | `future::try_join`   | `Result<(T1, T2), E>`          | Return on first `Err`, wait for all to complete
-//! | `future::select`     | `Result<T, E>`                 | Return on first value
-//! | `future::try_select` | `Result<T, E>`                 | Return on first `Ok`, reject on last Err
-
-#[doc(inline)]
-pub use std::future::Future;
+//! | Name                   | Return signature               | When does it return? |
+//! | ---                    | ---                            | ---                  |
+//! | [`Future::join`]       | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
+//! | [`Future::try_join`]   | `Result<(T1, T2), E>`          | Return on first `Err`, wait for all to complete
+//! | [`Future::race`]       | `Result<T, E>`                 | Return on first value
+//! | [`Future::try_race`]   | `Result<T, E>`                 | Return on first `Ok`, reject on last Err
+//!
+//! [`Future::join`]: trait.Future.html#method.join
+//! [`Future::try_join`]: trait.Future.html#method.try_join
+//! [`Future::race`]: trait.Future.html#method.race
+//! [`Future::try_race`]: trait.Future.html#method.try_race
 
-#[doc(inline)]
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-pub use async_macros::{join, select, try_join, try_select};
+cfg_alloc! {
+    pub use future::Future;
+    pub(crate) mod future;
+}
 
-use cfg_if::cfg_if;
+cfg_std! {
+    pub use pending::pending;
+    pub use poll_fn::poll_fn;
+    pub use ready::ready;
 
-pub use pending::pending;
-pub use poll_fn::poll_fn;
-pub use ready::ready;
+    mod pending;
+    mod poll_fn;
+    mod ready;
+}
 
-mod pending;
-mod poll_fn;
-mod ready;
+#[cfg(any(feature = "unstable", feature = "default"))]
+pub use timeout::{timeout, TimeoutError};
+#[cfg(any(feature = "unstable", feature = "default"))]
+mod timeout;
 
-cfg_if! {
-    if #[cfg(any(feature = "unstable", feature = "docs"))] {
-        mod timeout;
-        pub use timeout::{timeout, TimeoutError};
-    }
+cfg_unstable! {
+    pub use into_future::IntoFuture;
+    pub(crate) use maybe_done::MaybeDone;
+    mod into_future;
+    mod maybe_done;
 }
diff --git a/src/future/pending.rs b/src/future/pending.rs
index aaee70656..968972b51 100644
--- a/src/future/pending.rs
+++ b/src/future/pending.rs
@@ -1,7 +1,7 @@
+use std::future::Future;
 use std::marker::PhantomData;
 use std::pin::Pin;
 
-use crate::future::Future;
 use crate::task::{Context, Poll};
 
 /// Never resolves to a value.
@@ -9,7 +9,7 @@ use crate::task::{Context, Poll};
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use std::time::Duration;
 ///
@@ -22,7 +22,7 @@ use crate::task::{Context, Poll};
 /// let res: io::Result<()> = io::timeout(dur, fut).await;
 /// assert!(res.is_err());
 /// #
-/// # }) }
+/// # })
 /// ```
 pub async fn pending<T>() -> T {
     let fut = Pending {
diff --git a/src/future/poll_fn.rs b/src/future/poll_fn.rs
index 116e71c69..194526400 100644
--- a/src/future/poll_fn.rs
+++ b/src/future/poll_fn.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::task::{Context, Poll};
 
 /// Creates a new future wrapping around a function returning [`Poll`].
@@ -10,7 +10,7 @@ use crate::task::{Context, Poll};
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use async_std::future;
 /// use async_std::task::{Context, Poll};
@@ -21,7 +21,7 @@ use crate::task::{Context, Poll};
 ///
 /// assert_eq!(future::poll_fn(poll_greeting).await, "hello world");
 /// #
-/// # }) }
+/// # })
 /// ```
 pub async fn poll_fn<F, T>(f: F) -> T
 where
diff --git a/src/future/ready.rs b/src/future/ready.rs
index 04f37b87c..65cba563d 100644
--- a/src/future/ready.rs
+++ b/src/future/ready.rs
@@ -7,13 +7,13 @@
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use async_std::future;
 ///
 /// assert_eq!(future::ready(10).await, 10);
 /// #
-/// # }) }
+/// # })
 /// ```
 pub async fn ready<T>(val: T) -> T {
     val
diff --git a/src/future/timeout.rs b/src/future/timeout.rs
index aa88f6461..384662149 100644
--- a/src/future/timeout.rs
+++ b/src/future/timeout.rs
@@ -1,12 +1,13 @@
 use std::error::Error;
 use std::fmt;
+use std::future::Future;
 use std::pin::Pin;
 use std::time::Duration;
 
-use futures_timer::Delay;
+use pin_project_lite::pin_project;
 
-use crate::future::Future;
 use crate::task::{Context, Poll};
+use crate::utils::{timer_after, Timer};
 
 /// Awaits a future or times out after a duration of time.
 ///
@@ -28,39 +29,41 @@ use crate::task::{Context, Poll};
 /// #
 /// # Ok(()) }) }
 /// ```
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[cfg(any(feature = "unstable", feature = "docs"))]
 pub async fn timeout<F, T>(dur: Duration, f: F) -> Result<T, TimeoutError>
 where
     F: Future<Output = T>,
 {
-    let f = TimeoutFuture {
-        future: f,
-        delay: Delay::new(dur),
-    };
-    f.await
+    TimeoutFuture::new(f, dur).await
 }
 
-/// A future that times out after a duration of time.
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-struct TimeoutFuture<F> {
-    future: F,
-    delay: Delay,
+pin_project! {
+    /// A future that times out after a duration of time.
+    pub struct TimeoutFuture<F> {
+        #[pin]
+        future: F,
+        #[pin]
+        delay: Timer,
+    }
 }
 
 impl<F> TimeoutFuture<F> {
-    pin_utils::unsafe_pinned!(future: F);
-    pin_utils::unsafe_pinned!(delay: Delay);
+    #[allow(dead_code)]
+    pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture<F> {
+        TimeoutFuture {
+            future,
+            delay: timer_after(dur),
+        }
+    }
 }
 
 impl<F: Future> Future for TimeoutFuture<F> {
     type Output = Result<F::Output, TimeoutError>;
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match self.as_mut().future().poll(cx) {
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        match this.future.poll(cx) {
             Poll::Ready(v) => Poll::Ready(Ok(v)),
-            Poll::Pending => match self.delay().poll(cx) {
+            Poll::Pending => match this.delay.poll(cx) {
                 Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })),
                 Poll::Pending => Poll::Pending,
             },
@@ -69,8 +72,6 @@ impl<F: Future> Future for TimeoutFuture<F> {
 }
 
 /// An error returned when a future times out.
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[cfg(any(feature = "unstable", feature = "docs"))]
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub struct TimeoutError {
     _private: (),
diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs
index 6cb4a0769..c60529cd7 100644
--- a/src/io/buf_read/lines.rs
+++ b/src/io/buf_read/lines.rs
@@ -2,50 +2,55 @@ use std::mem;
 use std::pin::Pin;
 use std::str;
 
+use pin_project_lite::pin_project;
+
 use super::read_until_internal;
 use crate::io::{self, BufRead};
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream of lines in a byte stream.
-///
-/// This stream is created by the [`lines`] method on types that implement [`BufRead`].
-///
-/// This type is an async version of [`std::io::Lines`].
-///
-/// [`lines`]: trait.BufRead.html#method.lines
-/// [`BufRead`]: trait.BufRead.html
-/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html
-#[derive(Debug)]
-pub struct Lines<R> {
-    pub(crate) reader: R,
-    pub(crate) buf: String,
-    pub(crate) bytes: Vec<u8>,
-    pub(crate) read: usize,
+pin_project! {
+    /// A stream of lines in a byte stream.
+    ///
+    /// This stream is created by the [`lines`] method on types that implement [`BufRead`].
+    ///
+    /// This type is an async version of [`std::io::Lines`].
+    ///
+    /// [`lines`]: trait.BufRead.html#method.lines
+    /// [`BufRead`]: trait.BufRead.html
+    /// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html
+    #[derive(Debug)]
+    pub struct Lines<R> {
+        #[pin]
+        pub(crate) reader: R,
+        pub(crate) buf: String,
+        pub(crate) bytes: Vec<u8>,
+        pub(crate) read: usize,
+    }
 }
 
 impl<R: BufRead> Stream for Lines<R> {
     type Item = io::Result<String>;
 
     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let Self {
-            reader,
-            buf,
-            bytes,
-            read,
-        } = unsafe { self.get_unchecked_mut() };
-        let reader = unsafe { Pin::new_unchecked(reader) };
-        let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?;
-        if n == 0 && buf.is_empty() {
+        let this = self.project();
+        let n = futures_core::ready!(read_line_internal(
+            this.reader,
+            cx,
+            this.buf,
+            this.bytes,
+            this.read
+        ))?;
+        if n == 0 && this.buf.is_empty() {
             return Poll::Ready(None);
         }
-        if buf.ends_with('\n') {
-            buf.pop();
-            if buf.ends_with('\r') {
-                buf.pop();
+        if this.buf.ends_with('\n') {
+            this.buf.pop();
+            if this.buf.ends_with('\r') {
+                this.buf.pop();
             }
         }
-        Poll::Ready(Some(Ok(mem::replace(buf, String::new()))))
+        Poll::Ready(Some(Ok(mem::replace(this.buf, String::new()))))
     }
 }
 
diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs
index d34b2ae26..bcb9d907c 100644
--- a/src/io/buf_read/mod.rs
+++ b/src/io/buf_read/mod.rs
@@ -1,291 +1,245 @@
 mod lines;
 mod read_line;
 mod read_until;
+mod split;
 
 pub use lines::Lines;
+pub use split::Split;
+
 use read_line::ReadLineFuture;
 use read_until::ReadUntilFuture;
 
 use std::mem;
 use std::pin::Pin;
 
-use cfg_if::cfg_if;
-
 use crate::io;
 use crate::task::{Context, Poll};
-use crate::utils::extension_trait;
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::ops::{Deref, DerefMut};
-    }
-}
+pub use futures_io::AsyncBufRead as BufRead;
+
+#[doc = r#"
+    Extension methods for [`BufRead`].
 
-extension_trait! {
+    [`BufRead`]: ../trait.BufRead.html
+"#]
+pub trait BufReadExt: BufRead {
     #[doc = r#"
-        Allows reading from a buffered byte stream.
+        Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.
 
-        This trait is a re-export of [`futures::io::AsyncBufRead`] and is an async version of
-        [`std::io::BufRead`].
+        This function will read bytes from the underlying stream until the delimiter or EOF
+        is found. Once found, all bytes up to, and including, the delimiter (if found) will
+        be appended to `buf`.
 
-        The [provided methods] do not really exist in the trait itself, but they become
-        available when the prelude is imported:
+        If successful, this function will return the total number of bytes read.
 
-        ```
-        # #[allow(unused_imports)]
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::io::BufReader;
         use async_std::prelude::*;
+
+        let mut file = BufReader::new(File::open("a.txt").await?);
+
+        let mut buf = Vec::with_capacity(1024);
+        let n = file.read_until(b'\n', &mut buf).await?;
+        #
+        # Ok(()) }) }
         ```
 
-        [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
-        [`futures::io::AsyncBufRead`]:
-        https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html
-        [provided methods]: #provided-methods
+        Multiple successful calls to `read_until` append all bytes up to and including to
+        `buf`:
+        ```
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::io::BufReader;
+        use async_std::prelude::*;
+
+        let from: &[u8] = b"append\nexample\n";
+        let mut reader = BufReader::new(from);
+        let mut buf = vec![];
+
+        let mut size = reader.read_until(b'\n', &mut buf).await?;
+        assert_eq!(size, 7);
+        assert_eq!(buf, b"append\n");
+
+        size += reader.read_until(b'\n', &mut buf).await?;
+        assert_eq!(size, from.len());
+
+        assert_eq!(buf, from);
+        #
+        # Ok(()) }) }
+        ```
     "#]
-    pub trait BufRead [BufReadExt: futures_io::AsyncBufRead] {
-        #[doc = r#"
-            Returns the contents of the internal buffer, filling it with more data from the
-            inner reader if it is empty.
-
-            This function is a lower-level call. It needs to be paired with the [`consume`]
-            method to function properly. When calling this method, none of the contents will be
-            "read" in the sense that later calling `read` may return the same contents. As
-            such, [`consume`] must be called with the number of bytes that are consumed from
-            this buffer to ensure that the bytes are never returned twice.
-
-            [`consume`]: #tymethod.consume
-
-            An empty buffer returned indicates that the stream has reached EOF.
-        "#]
-        // TODO: write a proper doctest with `consume`
-        fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>>;
-
-        #[doc = r#"
-            Tells this buffer that `amt` bytes have been consumed from the buffer, so they
-            should no longer be returned in calls to `read`.
-        "#]
-        fn consume(self: Pin<&mut Self>, amt: usize);
-
-        #[doc = r#"
-            Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.
-
-            This function will read bytes from the underlying stream until the delimiter or EOF
-            is found. Once found, all bytes up to, and including, the delimiter (if found) will
-            be appended to `buf`.
-
-            If successful, this function will return the total number of bytes read.
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::io::BufReader;
-            use async_std::prelude::*;
-
-            let mut file = BufReader::new(File::open("a.txt").await?);
-
-            let mut buf = Vec::with_capacity(1024);
-            let n = file.read_until(b'\n', &mut buf).await?;
-            #
-            # Ok(()) }) }
-            ```
-
-            Multiple successful calls to `read_until` append all bytes up to and including to
-            `buf`:
-            ```
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::io::BufReader;
-            use async_std::prelude::*;
-
-            let from: &[u8] = b"append\nexample\n";
-            let mut reader = BufReader::new(from);
-            let mut buf = vec![];
-
-            let mut size = reader.read_until(b'\n', &mut buf).await?;
-            assert_eq!(size, 7);
-            assert_eq!(buf, b"append\n");
-
-            size += reader.read_until(b'\n', &mut buf).await?;
-            assert_eq!(size, from.len());
-
-            assert_eq!(buf, from);
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn read_until<'a>(
-            &'a mut self,
-            byte: u8,
-            buf: &'a mut Vec<u8>,
-        ) -> impl Future<Output = usize> + 'a [ReadUntilFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            ReadUntilFuture {
-                reader: self,
-                byte,
-                buf,
-                read: 0,
-            }
+    fn read_until<'a>(
+        &'a mut self,
+        byte: u8,
+        buf: &'a mut Vec<u8>,
+    ) -> ReadUntilFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        ReadUntilFuture {
+            reader: self,
+            byte,
+            buf,
+            read: 0,
         }
+    }
 
-        #[doc = r#"
-            Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is
-            reached.
-
-            This function will read bytes from the underlying stream until the newline
-            delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and
-            including, the delimiter (if found) will be appended to `buf`.
-
-            If successful, this function will return the total number of bytes read.
-
-            If this function returns `Ok(0)`, the stream has reached EOF.
-
-            # Errors
-
-            This function has the same error semantics as [`read_until`] and will also return
-            an error if the read bytes are not valid UTF-8. If an I/O error is encountered then
-            `buf` may contain some bytes already read in the event that all data read so far
-            was valid UTF-8.
-
-            [`read_until`]: #method.read_until
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::io::BufReader;
-            use async_std::prelude::*;
-
-            let mut file = BufReader::new(File::open("a.txt").await?);
-
-            let mut buf = String::new();
-            file.read_line(&mut buf).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn read_line<'a>(
-            &'a mut self,
-            buf: &'a mut String,
-        ) -> impl Future<Output = io::Result<usize>> + 'a [ReadLineFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            ReadLineFuture {
-                reader: self,
-                bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) },
-                buf,
-                read: 0,
-            }
-        }
+    #[doc = r#"
+        Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is
+        reached.
 
-        #[doc = r#"
-            Returns a stream over the lines of this byte stream.
+        This function will read bytes from the underlying stream until the newline
+        delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and
+        including, the delimiter (if found) will be appended to `buf`.
 
-            The stream returned from this function will yield instances of
-            [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte
-            (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end.
+        If successful, this function will return the total number of bytes read.
 
-            [`io::Result`]: type.Result.html
-            [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
+        If this function returns `Ok(0)`, the stream has reached EOF.
 
-            # Examples
+        # Errors
 
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::io::BufReader;
-            use async_std::prelude::*;
+        This function has the same error semantics as [`read_until`] and will also return
+        an error if the read bytes are not valid UTF-8. If an I/O error is encountered then
+        `buf` may contain some bytes already read in the event that all data read so far
+        was valid UTF-8.
 
-            let file = File::open("a.txt").await?;
-            let mut lines = BufReader::new(file).lines();
-            let mut count = 0;
+        [`read_until`]: #method.read_until
 
-            while let Some(line) = lines.next().await {
-                line?;
-                count += 1;
-            }
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn lines(self) -> Lines<Self>
-        where
-            Self: Unpin + Sized,
-        {
-            Lines {
-                reader: self,
-                buf: String::new(),
-                bytes: Vec::new(),
-                read: 0,
-            }
-        }
-    }
+        # Examples
 
-    impl<T: BufRead + Unpin + ?Sized> BufRead for Box<T> {
-        fn poll_fill_buf(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-        ) -> Poll<io::Result<&[u8]>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::io::BufReader;
+        use async_std::prelude::*;
 
-        fn consume(self: Pin<&mut Self>, amt: usize) {
-            unreachable!("this impl only appears in the rendered docs")
+        let mut file = BufReader::new(File::open("a.txt").await?);
+
+        let mut buf = String::new();
+        file.read_line(&mut buf).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn read_line<'a>(
+        &'a mut self,
+        buf: &'a mut String,
+    ) -> ReadLineFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        ReadLineFuture {
+            reader: self,
+            bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) },
+            buf,
+            read: 0,
         }
     }
 
-    impl<T: BufRead + Unpin + ?Sized> BufRead for &mut T {
-        fn poll_fill_buf(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-        ) -> Poll<io::Result<&[u8]>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    #[doc = r#"
+        Returns a stream over the lines of this byte stream.
 
-        fn consume(self: Pin<&mut Self>, amt: usize) {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-    }
+        The stream returned from this function will yield instances of
+        [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte
+        (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end.
+
+        [`io::Result`]: type.Result.html
+        [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
+
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::io::BufReader;
+        use async_std::prelude::*;
+
+        let file = File::open("a.txt").await?;
+        let mut lines = BufReader::new(file).lines();
+        let mut count = 0;
 
-    impl<P> BufRead for Pin<P>
+        while let Some(line) = lines.next().await {
+            line?;
+            count += 1;
+        }
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn lines(self) -> Lines<Self>
     where
-        P: DerefMut + Unpin,
-        <P as Deref>::Target: BufRead,
+        Self: Unpin + Sized,
     {
-        fn poll_fill_buf(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-        ) -> Poll<io::Result<&[u8]>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn consume(self: Pin<&mut Self>, amt: usize) {
-            unreachable!("this impl only appears in the rendered docs")
+        Lines {
+            reader: self,
+            buf: String::new(),
+            bytes: Vec::new(),
+            read: 0,
         }
     }
 
-    impl BufRead for &[u8] {
-        fn poll_fill_buf(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-        ) -> Poll<io::Result<&[u8]>> {
-            unreachable!()
-        }
+    #[doc = r#"
+        Returns a stream over the contents of this reader split on the byte `byte`.
+
+        The stream returned from this function will return instances of
+        [`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have
+        the delimiter byte at the end.
+
+        This function will yield errors whenever [`read_until`] would have
+        also yielded an error.
+
+        [`io::Result`]: type.Result.html
+        [`Vec<u8>`]: ../vec/struct.Vec.html
+        [`read_until`]: #method.read_until
+
+        # Examples
+
+        [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
+        this example, we use [`Cursor`] to iterate over all hyphen delimited
+        segments in a byte slice
+
+        [`Cursor`]: struct.Cursor.html
+
+        ```
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::io;
 
-        fn consume(self: Pin<&mut Self>, amt: usize) {
-            unreachable!("this impl only appears in the rendered docs")
+        let cursor = io::Cursor::new(b"lorem-ipsum-dolor");
+
+        let mut split_iter = cursor.split(b'-').map(|l| l.unwrap());
+        assert_eq!(split_iter.next().await, Some(b"lorem".to_vec()));
+        assert_eq!(split_iter.next().await, Some(b"ipsum".to_vec()));
+        assert_eq!(split_iter.next().await, Some(b"dolor".to_vec()));
+        assert_eq!(split_iter.next().await, None);
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn split(self, byte: u8) -> Split<Self>
+    where
+        Self: Sized,
+    {
+        Split {
+            reader: self,
+            buf: Vec::new(),
+            delim: byte,
+            read: 0,
         }
     }
 }
 
-pub fn read_until_internal<R: BufReadExt + ?Sized>(
+impl<T: BufRead + ?Sized> BufReadExt for T {}
+
+pub(crate) fn read_until_internal<R: BufReadExt + ?Sized>(
     mut reader: Pin<&mut R>,
     cx: &mut Context<'_>,
     byte: u8,
diff --git a/src/io/buf_read/read_line.rs b/src/io/buf_read/read_line.rs
index 29866be0f..b66079bc8 100644
--- a/src/io/buf_read/read_line.rs
+++ b/src/io/buf_read/read_line.rs
@@ -1,9 +1,9 @@
 use std::mem;
 use std::pin::Pin;
 use std::str;
+use std::future::Future;
 
 use super::read_until_internal;
-use crate::future::Future;
 use crate::io::{self, BufRead};
 use crate::task::{Context, Poll};
 
@@ -37,8 +37,12 @@ impl<T: BufRead + Unpin + ?Sized> Future for ReadLineFuture<'_, T> {
                 ))
             }))
         } else {
-            debug_assert!(buf.is_empty());
-            debug_assert_eq!(*read, 0);
+            #[allow(clippy::debug_assert_with_mut_call)]
+            {
+                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)
diff --git a/src/io/buf_read/read_until.rs b/src/io/buf_read/read_until.rs
index 72385abbe..bda1eee90 100644
--- a/src/io/buf_read/read_until.rs
+++ b/src/io/buf_read/read_until.rs
@@ -1,7 +1,7 @@
 use std::pin::Pin;
+use std::future::Future;
 
 use super::read_until_internal;
-use crate::future::Future;
 use crate::io::{self, BufRead};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/buf_read/split.rs b/src/io/buf_read/split.rs
new file mode 100644
index 000000000..229a99b3a
--- /dev/null
+++ b/src/io/buf_read/split.rs
@@ -0,0 +1,51 @@
+use std::mem;
+use std::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::read_until_internal;
+use crate::io::{self, BufRead};
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// A stream over the contents of an instance of [`BufRead`] split on a particular byte.
+    ///
+    /// This stream is created by the [`split`] method on types that implement [`BufRead`].
+    ///
+    /// This type is an async version of [`std::io::Split`].
+    ///
+    /// [`split`]: trait.BufRead.html#method.lines
+    /// [`BufRead`]: trait.BufRead.html
+    /// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html
+    #[derive(Debug)]
+    pub struct Split<R> {
+        #[pin]
+        pub(crate) reader: R,
+        pub(crate) buf: Vec<u8>,
+        pub(crate) read: usize,
+        pub(crate) delim: u8,
+    }
+}
+
+impl<R: BufRead> Stream for Split<R> {
+    type Item = io::Result<Vec<u8>>;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let n = futures_core::ready!(read_until_internal(
+            this.reader,
+            cx,
+            *this.delim,
+            this.buf,
+            this.read
+        ))?;
+        if n == 0 && this.buf.is_empty() {
+            return Poll::Ready(None);
+        }
+        if this.buf[this.buf.len() - 1] == *this.delim {
+            this.buf.pop();
+        }
+        Poll::Ready(Some(Ok(mem::replace(this.buf, vec![]))))
+    }
+}
diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs
index 13cb4cc56..e6d8e669c 100644
--- a/src/io/buf_reader.rs
+++ b/src/io/buf_reader.rs
@@ -2,51 +2,54 @@ use std::io::{IoSliceMut, Read as _};
 use std::pin::Pin;
 use std::{cmp, fmt};
 
-use crate::io::{self, BufRead, Read, Seek, SeekFrom};
-use crate::task::{Context, Poll};
+use pin_project_lite::pin_project;
 
-const DEFAULT_CAPACITY: usize = 8 * 1024;
+use crate::io::{self, BufRead, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE};
+use crate::task::{Context, Poll};
 
-/// Adds buffering to any reader.
-///
-/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
-/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
-/// of the incoming byte stream.
-///
-/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
-/// the same file or network socket. It does not help when reading very large amounts at once, or
-/// reading just one or a few times. It also provides no advantage when reading from a source that
-/// is already in memory, like a `Vec<u8>`.
-///
-/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
-/// multiple instances of a `BufReader` on the same stream can cause data loss.
-///
-/// This type is an async version of [`std::io::BufReader`].
-///
-/// [`Read`]: trait.Read.html
-/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
-///
-/// # Examples
-///
-/// ```no_run
-/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-/// #
-/// use async_std::fs::File;
-/// use async_std::io::BufReader;
-/// use async_std::prelude::*;
-///
-/// let mut file = BufReader::new(File::open("a.txt").await?);
-///
-/// let mut line = String::new();
-/// file.read_line(&mut line).await?;
-/// #
-/// # Ok(()) }) }
-/// ```
-pub struct BufReader<R> {
-    inner: R,
-    buf: Box<[u8]>,
-    pos: usize,
-    cap: usize,
+pin_project! {
+    /// Adds buffering to any reader.
+    ///
+    /// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
+    /// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
+    /// of the incoming byte stream.
+    ///
+    /// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
+    /// the same file or network socket. It does not help when reading very large amounts at once, or
+    /// reading just one or a few times. It also provides no advantage when reading from a source that
+    /// is already in memory, like a `Vec<u8>`.
+    ///
+    /// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
+    /// multiple instances of a `BufReader` on the same stream can cause data loss.
+    ///
+    /// This type is an async version of [`std::io::BufReader`].
+    ///
+    /// [`Read`]: trait.Read.html
+    /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::fs::File;
+    /// use async_std::io::BufReader;
+    /// use async_std::prelude::*;
+    ///
+    /// let mut file = BufReader::new(File::open("a.txt").await?);
+    ///
+    /// let mut line = String::new();
+    /// file.read_line(&mut line).await?;
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    pub struct BufReader<R> {
+        #[pin]
+        inner: R,
+        buf: Box<[u8]>,
+        pos: usize,
+        cap: usize,
+    }
 }
 
 impl<R: io::Read> BufReader<R> {
@@ -67,7 +70,7 @@ impl<R: io::Read> BufReader<R> {
     /// # Ok(()) }) }
     /// ```
     pub fn new(inner: R) -> BufReader<R> {
-        BufReader::with_capacity(DEFAULT_CAPACITY, inner)
+        BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
     }
 
     /// Creates a new buffered reader with the specified capacity.
@@ -95,10 +98,6 @@ impl<R: io::Read> BufReader<R> {
 }
 
 impl<R> BufReader<R> {
-    pin_utils::unsafe_pinned!(inner: R);
-    pin_utils::unsafe_unpinned!(pos: usize);
-    pin_utils::unsafe_unpinned!(cap: usize);
-
     /// Gets a reference to the underlying reader.
     ///
     /// It is inadvisable to directly read from the underlying reader.
@@ -141,6 +140,13 @@ impl<R> BufReader<R> {
         &mut self.inner
     }
 
+    /// Gets a pinned mutable reference to the underlying reader.
+    ///
+    /// It is inadvisable to directly read from the underlying reader.
+    fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> {
+        self.project().inner
+    }
+
     /// Returns a reference to the internal buffer.
     ///
     /// This function will not attempt to fill the buffer if it is empty.
@@ -185,9 +191,10 @@ impl<R> BufReader<R> {
 
     /// Invalidates all data in the internal buffer.
     #[inline]
-    fn discard_buffer(mut self: Pin<&mut Self>) {
-        *self.as_mut().pos() = 0;
-        *self.cap() = 0;
+    fn discard_buffer(self: Pin<&mut Self>) {
+        let this = self.project();
+        *this.pos = 0;
+        *this.cap = 0;
     }
 }
 
@@ -201,7 +208,7 @@ impl<R: Read> Read for BufReader<R> {
         // (larger than our internal buffer), bypass our internal buffer
         // entirely.
         if self.pos == self.cap && buf.len() >= self.buf.len() {
-            let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf));
+            let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf));
             self.discard_buffer();
             return Poll::Ready(res);
         }
@@ -218,7 +225,8 @@ impl<R: Read> Read for BufReader<R> {
     ) -> Poll<io::Result<usize>> {
         let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
         if self.pos == self.cap && total_len >= self.buf.len() {
-            let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs));
+            let res =
+                futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs));
             self.discard_buffer();
             return Poll::Ready(res);
         }
@@ -234,28 +242,23 @@ impl<R: Read> BufRead for BufReader<R> {
         self: Pin<&'a mut Self>,
         cx: &mut Context<'_>,
     ) -> Poll<io::Result<&'a [u8]>> {
-        let Self {
-            inner,
-            buf,
-            cap,
-            pos,
-        } = unsafe { self.get_unchecked_mut() };
-        let mut inner = unsafe { Pin::new_unchecked(inner) };
+        let mut this = self.project();
 
         // If we've reached the end of our internal buffer then we need to fetch
         // some more data from the underlying reader.
         // Branch using `>=` instead of the more correct `==`
         // to tell the compiler that the pos..cap slice is always valid.
-        if *pos >= *cap {
-            debug_assert!(*pos == *cap);
-            *cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?;
-            *pos = 0;
+        if *this.pos >= *this.cap {
+            debug_assert!(*this.pos == *this.cap);
+            *this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?;
+            *this.pos = 0;
         }
-        Poll::Ready(Ok(&buf[*pos..*cap]))
+        Poll::Ready(Ok(&this.buf[*this.pos..*this.cap]))
     }
 
-    fn consume(mut self: Pin<&mut Self>, amt: usize) {
-        *self.as_mut().pos() = cmp::min(self.pos + amt, self.cap);
+    fn consume(self: Pin<&mut Self>, amt: usize) {
+        let this = self.project();
+        *this.pos = cmp::min(*this.pos + amt, *this.cap);
     }
 }
 
@@ -305,24 +308,26 @@ impl<R: Seek> Seek for BufReader<R> {
             if let Some(offset) = n.checked_sub(remainder) {
                 result = futures_core::ready!(
                     self.as_mut()
-                        .inner()
+                        .get_pin_mut()
                         .poll_seek(cx, SeekFrom::Current(offset))
                 )?;
             } else {
                 // seek backwards by our remainder, and then by the offset
                 futures_core::ready!(
                     self.as_mut()
-                        .inner()
+                        .get_pin_mut()
                         .poll_seek(cx, SeekFrom::Current(-remainder))
                 )?;
                 self.as_mut().discard_buffer();
                 result = futures_core::ready!(
-                    self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n))
+                    self.as_mut()
+                        .get_pin_mut()
+                        .poll_seek(cx, SeekFrom::Current(n))
                 )?;
             }
         } else {
             // Seeking with Start/End doesn't care about our buffer length.
-            result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?;
+            result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?;
         }
         self.discard_buffer();
         Poll::Ready(Ok(result))
diff --git a/src/io/buf_writer.rs b/src/io/buf_writer.rs
index 2b7545a1f..230954e88 100644
--- a/src/io/buf_writer.rs
+++ b/src/io/buf_writer.rs
@@ -1,91 +1,122 @@
-use crate::task::{Context, Poll};
-use futures_core::ready;
-use futures_io::{AsyncSeek, AsyncWrite, SeekFrom};
 use std::fmt;
-use std::io;
 use std::pin::Pin;
 
-const DEFAULT_CAPACITY: usize = 8 * 1024;
+use pin_project_lite::pin_project;
 
-/// Wraps a writer and buffers its output.
-///
-/// It can be excessively inefficient to work directly with something that
-/// implements [`Write`]. For example, every call to
-/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
-/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying
-/// writer in large, infrequent batches.
-///
-/// `BufWriter` can improve the speed of programs that make *small* and
-/// *repeated* write calls to the same file or network socket. It does not
-/// help when writing very large amounts at once, or writing just one or a few
-/// times. It also provides no advantage when writing to a destination that is
-/// in memory, like a `Vec<u8>`.
-///
-/// When the `BufWriter` is dropped, the contents of its buffer will be written
-/// out. However, any errors that happen in the process of flushing the buffer
-/// when the writer is dropped will be ignored. Code that wishes to handle such
-/// errors must manually call [`flush`] before the writer is dropped.
-///
-/// This type is an async version of [`std::io::BufReader`].
-///
-/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
+use crate::io::write::WriteExt;
+use crate::io::{self, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE};
+use crate::task::{Context, Poll, ready};
+
+pin_project! {
+    /// Wraps a writer and buffers its output.
+    ///
+    /// It can be excessively inefficient to work directly with something that
+    /// implements [`Write`]. For example, every call to
+    /// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
+    /// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying
+    /// writer in large, infrequent batches.
+    ///
+    /// `BufWriter` can improve the speed of programs that make *small* and
+    /// *repeated* write calls to the same file or network socket. It does not
+    /// help when writing very large amounts at once, or writing just one or a few
+    /// times. It also provides no advantage when writing to a destination that is
+    /// in memory, like a `Vec<u8>`.
+    ///
+    /// Unlike the `BufWriter` type in `std`, this type does not write out the
+    /// contents of its buffer when it is dropped. Therefore, it is absolutely
+    /// critical that users explicitly flush the buffer before dropping a
+    /// `BufWriter`.
+    ///
+    /// This type is an async version of [`std::io::BufWriter`].
+    ///
+    /// [`std::io::BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html
+    ///
+    /// # Examples
+    ///
+    /// Let's write the numbers one through ten to a [`TcpStream`]:
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// use async_std::net::TcpStream;
+    /// use async_std::prelude::*;
+    ///
+    /// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
+    ///
+    /// for i in 0..10 {
+    ///     let arr = [i+1];
+    ///     stream.write(&arr).await?;
+    /// }
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    ///
+    /// Because we're not buffering, we write each one in turn, incurring the
+    /// overhead of a system call per byte written. We can fix this with a
+    /// `BufWriter`:
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// use async_std::io::BufWriter;
+    /// use async_std::net::TcpStream;
+    /// use async_std::prelude::*;
+    ///
+    /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
+    ///
+    /// for i in 0..10 {
+    ///     let arr = [i+1];
+    ///     stream.write(&arr).await?;
+    /// };
+    ///
+    /// stream.flush().await?;
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    ///
+    /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
+    /// together by the buffer, and will all be written out in one system call when
+    /// `stream.flush()` completes. (As mentioned above, dropping a `BufWriter`
+    /// does not flush its buffers, so a `flush` call is essential.)
+    ///
+    /// [`Write`]: trait.Write.html
+    /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
+    /// [`TcpStream`]: ../net/struct.TcpStream.html
+    /// [`flush`]: trait.Write.html#tymethod.flush
+    pub struct BufWriter<W> {
+        #[pin]
+        inner: W,
+        buf: Vec<u8>,
+        written: usize,
+    }
+}
+
+/// An error returned by `into_inner` which combines an error that
+/// happened while writing out the buffer, and the buffered writer object
+/// which may be used to recover from the condition.
 ///
 /// # Examples
 ///
-/// Let's write the numbers one through ten to a [`TcpStream`]:
-///
-/// ```no_run
-/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-/// use async_std::net::TcpStream;
-/// use async_std::prelude::*;
-///
-/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
-///
-/// for i in 0..10 {
-///     let arr = [i+1];
-///     stream.write(&arr).await?;
-/// }
-/// #
-/// # Ok(()) }) }
-/// ```
-///
-/// Because we're not buffering, we write each one in turn, incurring the
-/// overhead of a system call per byte written. We can fix this with a
-/// `BufWriter`:
-///
 /// ```no_run
 /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
 /// use async_std::io::BufWriter;
 /// use async_std::net::TcpStream;
-/// use async_std::prelude::*;
 ///
-/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
-/// for i in 0..10 {
-///     let arr = [i+1];
-///     stream.write(&arr).await?;
+/// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?);
+///
+/// // unwrap the TcpStream and flush the buffer
+/// let stream = match buf_writer.into_inner().await {
+///     Ok(s) => s,
+///     Err(e) => {
+///         // Here, e is an IntoInnerError
+///         panic!("An error occurred");
+///     }
 /// };
 /// #
 /// # Ok(()) }) }
-/// ```
-///
-/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
-/// together by the buffer, and will all be written out in one system call when
-/// the `stream` is dropped.
-///
-/// [`Write`]: trait.Write.html
-/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
-/// [`TcpStream`]: ../net/struct.TcpStream.html
-/// [`flush`]: trait.Write.html#tymethod.flush
-pub struct BufWriter<W> {
-    inner: W,
-    buf: Vec<u8>,
-    written: usize,
-}
-
-impl<W: AsyncWrite> BufWriter<W> {
-    pin_utils::unsafe_pinned!(inner: W);
-    pin_utils::unsafe_unpinned!(buf: Vec<u8>);
+///```
+#[derive(Debug)]
+pub struct IntoInnerError<W>(W, #[allow(dead_code)] crate::io::Error);
 
+impl<W: Write> BufWriter<W> {
     /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB,
     /// but may change in the future.
     ///
@@ -102,7 +133,7 @@ impl<W: AsyncWrite> BufWriter<W> {
     /// # Ok(()) }) }
     /// ```
     pub fn new(inner: W) -> BufWriter<W> {
-        BufWriter::with_capacity(DEFAULT_CAPACITY, inner)
+        BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
     }
 
     /// Creates a new `BufWriter` with the specified buffer capacity.
@@ -173,9 +204,12 @@ impl<W: AsyncWrite> BufWriter<W> {
         &mut self.inner
     }
 
-    //    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
-    //        self.inner()
-    //    }
+    /// Gets a pinned mutable reference to the underlying writer.
+    ///
+    /// It is inadvisable to directly write to the underlying writer.
+    fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
+        self.project().inner
+    }
 
     /// Consumes BufWriter, returning the underlying writer
     ///
@@ -183,14 +217,30 @@ impl<W: AsyncWrite> BufWriter<W> {
     /// For method that will attempt to write before returning the writer see [`poll_into_inner`]
     ///
     /// [`poll_into_inner`]: #method.poll_into_inner
-    pub fn into_inner(self) -> W {
-        self.inner
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// use async_std::io::BufWriter;
+    /// use async_std::net::TcpStream;
+    ///
+    /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34251").await?);
+    ///
+    /// // unwrap the TcpStream and flush the buffer
+    /// let stream = buf_writer.into_inner().await.unwrap();
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    pub async fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>>
+    where
+        Self: Unpin,
+    {
+        match self.flush().await {
+            Err(e) => Err(IntoInnerError(self, e)),
+            Ok(()) => Ok(self.inner),
+        }
     }
 
-    //    pub fn poll_into_inner(self: Pin<&mut Self>, _cx: Context<'_>) -> Poll<io::Result<usize>> {
-    //        unimplemented!("poll into inner method")
-    //    }
-
     /// Returns a reference to the internally buffered data.
     ///
     /// # Examples
@@ -217,16 +267,15 @@ impl<W: AsyncWrite> BufWriter<W> {
     ///
     /// [`LineWriter`]: struct.LineWriter.html
     fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-        let Self {
-            inner,
-            buf,
-            written,
-        } = unsafe { Pin::get_unchecked_mut(self) };
-        let mut inner = unsafe { Pin::new_unchecked(inner) };
-        let len = buf.len();
+        let mut this = self.project();
+        let len = this.buf.len();
         let mut ret = Ok(());
-        while *written < len {
-            match inner.as_mut().poll_write(cx, &buf[*written..]) {
+        while *this.written < len {
+            match this
+                .inner
+                .as_mut()
+                .poll_write(cx, &this.buf[*this.written..])
+            {
                 Poll::Ready(Ok(0)) => {
                     ret = Err(io::Error::new(
                         io::ErrorKind::WriteZero,
@@ -234,7 +283,7 @@ impl<W: AsyncWrite> BufWriter<W> {
                     ));
                     break;
                 }
-                Poll::Ready(Ok(n)) => *written += n,
+                Poll::Ready(Ok(n)) => *this.written += n,
                 Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {}
                 Poll::Ready(Err(e)) => {
                     ret = Err(e);
@@ -243,15 +292,15 @@ impl<W: AsyncWrite> BufWriter<W> {
                 Poll::Pending => return Poll::Pending,
             }
         }
-        if *written > 0 {
-            buf.drain(..*written);
+        if *this.written > 0 {
+            this.buf.drain(..*this.written);
         }
-        *written = 0;
+        *this.written = 0;
         Poll::Ready(ret)
     }
 }
 
-impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
+impl<W: Write> Write for BufWriter<W> {
     fn poll_write(
         mut self: Pin<&mut Self>,
         cx: &mut Context<'_>,
@@ -261,120 +310,42 @@ impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
             ready!(self.as_mut().poll_flush_buf(cx))?;
         }
         if buf.len() >= self.buf.capacity() {
-            self.inner().poll_write(cx, buf)
+            self.get_pin_mut().poll_write(cx, buf)
         } else {
-            Pin::new(&mut *self.buf()).poll_write(cx, buf)
+            Pin::new(&mut *self.project().buf).poll_write(cx, buf)
         }
     }
 
     fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
         ready!(self.as_mut().poll_flush_buf(cx))?;
-        self.inner().poll_flush(cx)
+        self.get_pin_mut().poll_flush(cx)
     }
 
     fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
         ready!(self.as_mut().poll_flush_buf(cx))?;
-        self.inner().poll_close(cx)
+        self.get_pin_mut().poll_close(cx)
     }
 }
 
-impl<W: AsyncWrite + fmt::Debug> fmt::Debug for BufWriter<W> {
+impl<W: Write + fmt::Debug> fmt::Debug for BufWriter<W> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("BufReader")
+        f.debug_struct("BufWriter")
             .field("writer", &self.inner)
             .field("buf", &self.buf)
             .finish()
     }
 }
 
-impl<W: AsyncWrite + AsyncSeek> AsyncSeek for BufWriter<W> {
+impl<W: Write + Seek> Seek for BufWriter<W> {
     /// Seek to the offset, in bytes, in the underlying writer.
     ///
     /// Seeking always writes out the internal buffer before seeking.
-
     fn poll_seek(
         mut self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         pos: SeekFrom,
     ) -> Poll<io::Result<u64>> {
         ready!(self.as_mut().poll_flush_buf(cx))?;
-        self.inner().poll_seek(cx, pos)
-    }
-}
-
-mod tests {
-    #![allow(unused_imports)]
-
-    use super::BufWriter;
-    use crate::io::{self, SeekFrom};
-    use crate::prelude::*;
-    use crate::task;
-
-    #[test]
-    fn test_buffered_writer() {
-        task::block_on(async {
-            let inner = Vec::new();
-            let mut writer = BufWriter::with_capacity(2, inner);
-
-            writer.write(&[0, 1]).await.unwrap();
-            assert_eq!(writer.buffer(), []);
-            assert_eq!(*writer.get_ref(), [0, 1]);
-
-            writer.write(&[2]).await.unwrap();
-            assert_eq!(writer.buffer(), [2]);
-            assert_eq!(*writer.get_ref(), [0, 1]);
-
-            writer.write(&[3]).await.unwrap();
-            assert_eq!(writer.buffer(), [2, 3]);
-            assert_eq!(*writer.get_ref(), [0, 1]);
-
-            writer.flush().await.unwrap();
-            assert_eq!(writer.buffer(), []);
-            assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
-
-            writer.write(&[4]).await.unwrap();
-            writer.write(&[5]).await.unwrap();
-            assert_eq!(writer.buffer(), [4, 5]);
-            assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
-
-            writer.write(&[6]).await.unwrap();
-            assert_eq!(writer.buffer(), [6]);
-            assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
-
-            writer.write(&[7, 8]).await.unwrap();
-            assert_eq!(writer.buffer(), []);
-            assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
-
-            writer.write(&[9, 10, 11]).await.unwrap();
-            assert_eq!(writer.buffer(), []);
-            assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
-
-            writer.flush().await.unwrap();
-            assert_eq!(writer.buffer(), []);
-            assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
-        })
-    }
-
-    #[test]
-    fn test_buffered_writer_inner_into_inner_does_not_flush() {
-        task::block_on(async {
-            let mut w = BufWriter::with_capacity(3, Vec::new());
-            w.write(&[0, 1]).await.unwrap();
-            assert_eq!(*w.get_ref(), []);
-            let w = w.into_inner();
-            assert_eq!(w, []);
-        })
-    }
-
-    #[test]
-    fn test_buffered_writer_seek() {
-        task::block_on(async {
-            let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new()));
-            w.write_all(&[0, 1, 2, 3, 4, 5]).await.unwrap();
-            w.write_all(&[6, 7]).await.unwrap();
-            assert_eq!(w.seek(SeekFrom::Current(0)).await.ok(), Some(8));
-            assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]);
-            assert_eq!(w.seek(SeekFrom::Start(2)).await.ok(), Some(2));
-        })
+        self.get_pin_mut().poll_seek(cx, pos)
     }
 }
diff --git a/src/io/copy.rs b/src/io/copy.rs
index 3840d2af9..e8d5d733f 100644
--- a/src/io/copy.rs
+++ b/src/io/copy.rs
@@ -1,8 +1,17 @@
+use std::future::Future;
 use std::pin::Pin;
 
-use crate::future::Future;
+use pin_project_lite::pin_project;
+
 use crate::io::{self, BufRead, BufReader, Read, Write};
 use crate::task::{Context, Poll};
+use crate::utils::Context as _;
+
+// Note: There are two otherwise-identical implementations of this
+// function because unstable has removed the `?Sized` bound for the
+// reader and writer and accepts `R` and `W` instead of `&mut R` and
+// `&mut W`. If making a change to either of the implementations,
+// ensure that you copy it into the other.
 
 /// Copies the entire contents of a reader into a writer.
 ///
@@ -41,52 +50,148 @@ use crate::task::{Context, Poll};
 /// #
 /// # Ok(()) }) }
 /// ```
+#[cfg(any(feature = "docs", not(feature = "unstable")))]
 pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
 where
     R: Read + Unpin + ?Sized,
     W: Write + Unpin + ?Sized,
 {
-    pub struct CopyFuture<'a, R, W: ?Sized> {
-        reader: R,
-        writer: &'a mut W,
-        amt: u64,
+    pin_project! {
+        struct CopyFuture<R, W> {
+            #[pin]
+            reader: R,
+            #[pin]
+            writer: W,
+            amt: u64,
+            reader_eof: bool
+        }
     }
 
-    impl<R, W: Unpin + ?Sized> CopyFuture<'_, R, W> {
-        fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) {
-            unsafe {
-                let this = self.get_unchecked_mut();
-                (
-                    Pin::new_unchecked(&mut this.reader),
-                    Pin::new(&mut *this.writer),
-                    &mut this.amt,
-                )
+    impl<R, W> Future for CopyFuture<R, W>
+    where
+        R: BufRead,
+        W: Write + Unpin,
+    {
+        type Output = io::Result<u64>;
+
+        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+            let mut this = self.project();
+
+            loop {
+                if *this.reader_eof {
+                    futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
+                    return Poll::Ready(Ok(*this.amt));
+                }
+
+                let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
+
+                if buffer.is_empty() {
+                    *this.reader_eof = true;
+                    continue;
+                }
+
+                let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
+                if i == 0 {
+                    return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
+                }
+                *this.amt += i as u64;
+                this.reader.as_mut().consume(i);
             }
         }
     }
 
-    impl<R, W> Future for CopyFuture<'_, R, W>
+    let future = CopyFuture {
+        reader: BufReader::new(reader),
+        writer,
+        reader_eof: false,
+        amt: 0,
+    };
+    future.await.context(|| String::from("io::copy failed"))
+}
+
+/// Copies the entire contents of a reader into a writer.
+///
+/// This function will continuously read data from `reader` and then
+/// write it into `writer` in a streaming fashion until `reader`
+/// returns EOF.
+///
+/// On success, the total number of bytes that were copied from
+/// `reader` to `writer` is returned.
+///
+/// If you’re wanting to copy the contents of one file to another and you’re
+/// working with filesystem paths, see the [`fs::copy`] function.
+///
+/// This function is an async version of [`std::io::copy`].
+///
+/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
+/// [`fs::copy`]: ../fs/fn.copy.html
+///
+/// # Errors
+///
+/// This function will return an error immediately if any call to `read` or
+/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
+/// handled by this function and the underlying operation is retried.
+///
+/// # Examples
+///
+/// ```
+/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
+/// use async_std::io;
+///
+/// let mut reader: &[u8] = b"hello";
+/// let mut writer = io::stdout();
+///
+/// io::copy(&mut reader, &mut writer).await?;
+/// #
+/// # Ok(()) }) }
+/// ```
+#[cfg(all(feature = "unstable", not(feature = "docs")))]
+pub async fn copy<R, W>(reader: R, writer: W) -> io::Result<u64>
+where
+    R: Read + Unpin,
+    W: Write + Unpin,
+{
+    pin_project! {
+        struct CopyFuture<R, W> {
+            #[pin]
+            reader: R,
+            #[pin]
+            writer: W,
+            amt: u64,
+            reader_eof: bool
+        }
+    }
+
+    impl<R, W> Future for CopyFuture<R, W>
     where
         R: BufRead,
-        W: Write + Unpin + ?Sized,
+        W: Write + Unpin,
     {
         type Output = io::Result<u64>;
 
         fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-            let (mut reader, mut writer, amt) = self.project();
+            let mut this = self.project();
+
             loop {
-                let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?;
+                if *this.reader_eof {
+                    futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
+                    return Poll::Ready(Ok(*this.amt));
+                }
+
+                let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
+
                 if buffer.is_empty() {
-                    futures_core::ready!(writer.as_mut().poll_flush(cx))?;
-                    return Poll::Ready(Ok(*amt));
+                    *this.reader_eof = true;
+                    continue;
                 }
 
-                let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?;
+                let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
                 if i == 0 {
                     return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
                 }
-                *amt += i as u64;
-                reader.as_mut().consume(i);
+                *this.amt += i as u64;
+                this.reader.as_mut().consume(i);
             }
         }
     }
@@ -94,7 +199,8 @@ where
     let future = CopyFuture {
         reader: BufReader::new(reader),
         writer,
+        reader_eof: false,
         amt: 0,
     };
-    future.await
+    future.await.context(|| String::from("io::copy failed"))
 }
diff --git a/src/io/cursor.rs b/src/io/cursor.rs
index 2dfc8a75a..603b85a14 100644
--- a/src/io/cursor.rs
+++ b/src/io/cursor.rs
@@ -21,7 +21,6 @@ use crate::task::{Context, Poll};
 /// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
 /// [bytes]: https://doc.rust-lang.org/std/primitive.slice.html
 /// [`File`]: struct.File.html
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
 #[derive(Clone, Debug, Default)]
 pub struct Cursor<T> {
     inner: std::io::Cursor<T>,
diff --git a/src/io/empty.rs b/src/io/empty.rs
index d8d768e02..904426757 100644
--- a/src/io/empty.rs
+++ b/src/io/empty.rs
@@ -28,9 +28,10 @@ pub fn empty() -> Empty {
 
 /// A reader that contains no data.
 ///
-/// This reader is constructed by the [`sink`] function.
+/// This reader is created by the [`empty`] function. See its
+/// documentation for more.
 ///
-/// [`sink`]: fn.sink.html
+/// [`empty`]: fn.empty.html
 pub struct Empty {
     _private: (),
 }
diff --git a/src/io/mod.rs b/src/io/mod.rs
index 7a9428544..8a415eb71 100644
--- a/src/io/mod.rs
+++ b/src/io/mod.rs
@@ -1,59 +1,330 @@
-//! Basic input and output.
+//! Traits, helpers, and type definitions for core I/O functionality.
+//!
+//! The `async_std::io` module contains a number of common things you'll need
+//! when doing input and output. The most core part of this module is
+//! the [`Read`] and [`Write`] traits, which provide the
+//! most general interface for reading and writing input and output.
 //!
 //! This module is an async version of [`std::io`].
 //!
 //! [`std::io`]: https://doc.rust-lang.org/std/io/index.html
 //!
-//! # Examples
+//! # Read and Write
+//!
+//! Because they are traits, [`Read`] and [`Write`] are implemented by a number
+//! of other types, and you can implement them for your types too. As such,
+//! you'll see a few different types of I/O throughout the documentation in
+//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For
+//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on
+//! [`File`]s:
+//!
+//! ```no_run
+//! use async_std::fs::File;
+//! use async_std::prelude::*;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let mut f = File::open("foo.txt").await?;
+//! let mut buffer = [0; 10];
+//!
+//! // read up to 10 bytes
+//! let n = f.read(&mut buffer).await?;
+//!
+//! println!("The bytes: {:?}", &buffer[..n]);
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! [`Read`] and [`Write`] are so important, implementors of the two traits have a
+//! nickname: readers and writers. So you'll sometimes see 'a reader' instead
+//! of 'a type that implements the [`Read`] trait'. Much easier!
+//!
+//! ## Seek and BufRead
+//!
+//! Beyond that, there are two important traits that are provided: [`Seek`]
+//! and [`BufRead`]. Both of these build on top of a reader to control
+//! how the reading happens. [`Seek`] lets you control where the next byte is
+//! coming from:
+//!
+//! ```no_run
+//! use async_std::fs::File;
+//! use async_std::io::SeekFrom;
+//! use async_std::prelude::*;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let mut f = File::open("foo.txt").await?;
+//! let mut buffer = [0; 10];
+//!
+//! // skip to the last 10 bytes of the file
+//! f.seek(SeekFrom::End(-10)).await?;
+//!
+//! // read up to 10 bytes
+//! let n = f.read(&mut buffer).await?;
+//!
+//! println!("The bytes: {:?}", &buffer[..n]);
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but
+//! to show it off, we'll need to talk about buffers in general. Keep reading!
+//!
+//! ## BufReader and BufWriter
+//!
+//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be
+//! making near-constant calls to the operating system. To help with this,
+//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap
+//! readers and writers. The wrapper uses a buffer, reducing the number of
+//! calls and providing nicer methods for accessing exactly what you want.
+//!
+//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra
+//! methods to any reader:
+//!
+//! ```no_run
+//! use async_std::fs::File;
+//! use async_std::io::BufReader;
+//! use async_std::prelude::*;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let f = File::open("foo.txt").await?;
+//! let mut reader = BufReader::new(f);
+//! let mut buffer = String::new();
+//!
+//! // read a line into buffer
+//! reader.read_line(&mut buffer).await?;
+//!
+//! println!("{}", buffer);
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
+//! to [`write`][`Write::write`]:
+//!
+//! ```no_run
+//! use async_std::fs::File;
+//! use async_std::io::prelude::*;
+//! use async_std::io::BufWriter;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let f = File::create("foo.txt").await?;
+//! {
+//!     let mut writer = BufWriter::new(f);
+//!
+//!     // write a byte to the buffer
+//!     writer.write(&[42]).await?;
+//! } // the buffer is flushed once writer goes out of scope
+//! //
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! ## Standard input and output
+//!
+//! A very common source of input is standard input:
+//!
+//! ```no_run
+//! use async_std::io;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let mut input = String::new();
+//!
+//! io::stdin().read_line(&mut input).await?;
+//!
+//! println!("You typed: {}", input.trim());
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! Note that you cannot use the [`?` operator] in functions that do not return
+//! a [`Result<T, E>`][`Result`]. Instead, you can call [`.unwrap()`]
+//! or `match` on the return value to catch any possible errors:
+//!
+//! ```no_run
+//! use async_std::io;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let mut input = String::new();
+//!
+//! io::stdin().read_line(&mut input).await.unwrap();
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! And a very common source of output is standard output:
+//!
+//! ```no_run
+//! use async_std::io;
+//! use async_std::io::prelude::*;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! io::stdout().write(&[42]).await?;
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! Of course, using [`io::stdout`] directly is less common than something like
+//! [`println!`].
+//!
+//! ## Iterator types
+//!
+//! A large number of the structures provided by `std::io` are for various
+//! ways of iterating over I/O. For example, [`Lines`] is used to split over
+//! lines:
+//!
+//! ```no_run
+//! use async_std::fs::File;
+//! use async_std::io::BufReader;
+//! use async_std::prelude::*;
+//!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let f = File::open("foo.txt").await?;
+//! let reader = BufReader::new(f);
+//!
+//! let mut lines = reader.lines();
+//! while let Some(line) = lines.next().await {
+//!     println!("{}", line?);
+//! }
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! ## Functions
 //!
-//! Read a line from the standard input:
+//! There are a number of [functions][functions-list] that offer access to various
+//! features. For example, we can use three of these functions to copy everything
+//! from standard input to standard output:
 //!
 //! ```no_run
+//! use async_std::io;
+//!
 //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
 //! #
+//! io::copy(&mut io::stdin(), &mut io::stdout()).await?;
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! [functions-list]: #functions-1
+//!
+//! ## io::Result
+//!
+//! Last, but certainly not least, is [`io::Result`]. This type is used
+//! as the return type of many `std::io` functions that can cause an error, and
+//! can be returned from your own functions as well. Many of the examples in this
+//! module use the [`?` operator]:
+//!
+//! ```
+//! #![allow(dead_code)]
 //! use async_std::io;
 //!
-//! let stdin = io::stdin();
-//! let mut line = String::new();
-//! stdin.read_line(&mut line).await?;
-//! #
-//! # Ok(()) }) }
-//! ```
-
-#[doc(inline)]
-pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
-
-pub use buf_read::{BufRead, Lines};
-pub use buf_reader::BufReader;
-pub use buf_writer::BufWriter;
-pub use copy::copy;
-pub use cursor::Cursor;
-pub use empty::{empty, Empty};
-pub use read::Read;
-pub use repeat::{repeat, Repeat};
-pub use seek::Seek;
-pub use sink::{sink, Sink};
-pub use stderr::{stderr, Stderr};
-pub use stdin::{stdin, Stdin};
-pub use stdout::{stdout, Stdout};
-pub use timeout::timeout;
-pub use write::Write;
-
-pub mod prelude;
-
-pub(crate) mod buf_read;
-pub(crate) mod read;
-pub(crate) mod seek;
-pub(crate) mod write;
-
-mod buf_reader;
-mod buf_writer;
-mod copy;
-mod cursor;
-mod empty;
-mod repeat;
-mod sink;
-mod stderr;
-mod stdin;
-mod stdout;
-mod timeout;
+//! async fn read_input() -> io::Result<()> {
+//!     let mut input = String::new();
+//!
+//!     io::stdin().read_line(&mut input).await?;
+//!
+//!     println!("You typed: {}", input.trim());
+//!
+//!     Ok(())
+//! }
+//! ```
+//!
+//! The return type of `read_input`, [`io::Result<()>`][`io::Result`], is a very
+//! common type for functions which don't have a 'real' return value, but do want to
+//! return errors if they happen. In this case, the only purpose of this function is
+//! to read the line and print it, so we use `()`.
+//!
+//! ## Platform-specific behavior
+//!
+//! Many I/O functions throughout the standard library are documented to indicate
+//! what various library or syscalls they are delegated to. This is done to help
+//! applications both understand what's happening under the hood as well as investigate
+//! any possibly unclear semantics. Note, however, that this is informative, not a binding
+//! contract. The implementation of many of these functions are subject to change over
+//! time and may call fewer or more syscalls/library functions.
+//!
+//! [`Read`]: trait.Read.html
+//! [`Write`]: trait.Write.html
+//! [`Seek`]: trait.Seek.html
+//! [`BufRead`]: trait.BufRead.html
+//! [`File`]: ../fs/struct.File.html
+//! [`TcpStream`]: ../net/struct.TcpStream.html
+//! [`Vec<T>`]: ../vec/struct.Vec.html
+//! [`BufReader`]: struct.BufReader.html
+//! [`BufWriter`]: struct.BufWriter.html
+//! [`Write::write`]: trait.Write.html#tymethod.write
+//! [`io::stdout`]: fn.stdout.html
+//! [`println!`]: ../macro.println.html
+//! [`Lines`]: struct.Lines.html
+//! [`io::Result`]: type.Result.html
+//! [`?` operator]: https://doc.rust-lang.org/stable/book/appendix-02-operators.html
+//! [`Read::read`]: trait.Read.html#tymethod.read
+//! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+//! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap
+
+const DEFAULT_BUF_SIZE: usize = 8 * 1024;
+
+cfg_std! {
+    #[doc(inline)]
+    pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
+
+    pub use buf_read::*;
+    pub use buf_reader::BufReader;
+    pub use buf_writer::{BufWriter, IntoInnerError};
+    pub use copy::copy;
+    pub use cursor::Cursor;
+    pub use empty::{empty, Empty};
+    pub use read::*;
+    pub use repeat::{repeat, Repeat};
+    pub use seek::*;
+    pub use sink::{sink, Sink};
+    pub use write::*;
+
+    pub mod prelude;
+
+    pub(crate) mod buf_read;
+    pub(crate) mod read;
+    pub(crate) mod seek;
+    pub(crate) mod write;
+    pub(crate) mod utils;
+
+    mod buf_reader;
+    mod buf_writer;
+    mod copy;
+    mod cursor;
+    mod empty;
+    mod repeat;
+    mod sink;
+}
+
+cfg_default! {
+    // For use in the print macros.
+    #[doc(hidden)]
+    #[cfg(not(target_os = "unknown"))]
+    pub use stdio::{_eprint, _print};
+
+    #[cfg(not(target_os = "unknown"))]
+    pub use stderr::{stderr, Stderr};
+    #[cfg(not(target_os = "unknown"))]
+    pub use stdin::{stdin, Stdin};
+    #[cfg(not(target_os = "unknown"))]
+    pub use stdout::{stdout, Stdout};
+    pub use timeout::timeout;
+
+    mod timeout;
+    #[cfg(not(target_os = "unknown"))]
+    mod stderr;
+    #[cfg(not(target_os = "unknown"))]
+    mod stdin;
+    #[cfg(not(target_os = "unknown"))]
+    mod stdio;
+    #[cfg(not(target_os = "unknown"))]
+    mod stdout;
+}
diff --git a/src/io/prelude.rs b/src/io/prelude.rs
index fb1b94562..c90b289af 100644
--- a/src/io/prelude.rs
+++ b/src/io/prelude.rs
@@ -1,4 +1,4 @@
-//! The async I/O Prelude
+//! The async I/O prelude.
 //!
 //! The purpose of this module is to alleviate imports of many common I/O traits
 //! by adding a glob import to the top of I/O heavy modules:
@@ -17,11 +17,11 @@ pub use crate::io::Seek;
 #[doc(no_inline)]
 pub use crate::io::Write;
 
-#[doc(hidden)]
-pub use crate::io::buf_read::BufReadExt as _;
-#[doc(hidden)]
-pub use crate::io::read::ReadExt as _;
-#[doc(hidden)]
-pub use crate::io::seek::SeekExt as _;
-#[doc(hidden)]
-pub use crate::io::write::WriteExt as _;
+#[doc(inline)]
+pub use crate::io::buf_read::BufReadExt;
+#[doc(inline)]
+pub use crate::io::read::ReadExt;
+#[doc(inline)]
+pub use crate::io::seek::SeekExt;
+#[doc(inline)]
+pub use crate::io::write::WriteExt;
diff --git a/src/io/read/bytes.rs b/src/io/read/bytes.rs
index 422452433..786fdaa57 100644
--- a/src/io/read/bytes.rs
+++ b/src/io/read/bytes.rs
@@ -32,7 +32,7 @@ impl<T: Read + Unpin> Stream for Bytes<T> {
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, feature = "default", not(target_arch = "wasm32")))]
 mod tests {
     use crate::io;
     use crate::prelude::*;
diff --git a/src/io/read/chain.rs b/src/io/read/chain.rs
index 09517ccad..b5eac5814 100644
--- a/src/io/read/chain.rs
+++ b/src/io/read/chain.rs
@@ -1,20 +1,25 @@
-use crate::io::IoSliceMut;
 use std::fmt;
 use std::pin::Pin;
 
-use crate::io::{self, BufRead, Read};
+use pin_project_lite::pin_project;
+
+use crate::io::{self, BufRead, IoSliceMut, Read};
 use crate::task::{Context, Poll};
 
-/// Adaptor to chain together two readers.
-///
-/// This struct is generally created by calling [`chain`] on a reader.
-/// Please see the documentation of [`chain`] for more details.
-///
-/// [`chain`]: trait.Read.html#method.chain
-pub struct Chain<T, U> {
-    pub(crate) first: T,
-    pub(crate) second: U,
-    pub(crate) done_first: bool,
+pin_project! {
+    /// Adaptor to chain together two readers.
+    ///
+    /// This struct is generally created by calling [`chain`] on a reader.
+    /// Please see the documentation of [`chain`] for more details.
+    ///
+    /// [`chain`]: trait.Read.html#method.chain
+    pub struct Chain<T, U> {
+        #[pin]
+        pub(crate) first: T,
+        #[pin]
+        pub(crate) second: U,
+        pub(crate) done_first: bool,
+    }
 }
 
 impl<T, U> Chain<T, U> {
@@ -98,81 +103,69 @@ impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Chain<T, U> {
     }
 }
 
-impl<T: Read + Unpin, U: Read + Unpin> Read for Chain<T, U> {
+impl<T: Read, U: Read> Read for Chain<T, U> {
     fn poll_read(
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         buf: &mut [u8],
     ) -> Poll<io::Result<usize>> {
-        if !self.done_first {
-            let rd = Pin::new(&mut self.first);
-
-            match futures_core::ready!(rd.poll_read(cx, buf)) {
-                Ok(0) if !buf.is_empty() => self.done_first = true,
+        let this = self.project();
+        if !*this.done_first {
+            match futures_core::ready!(this.first.poll_read(cx, buf)) {
+                Ok(0) if !buf.is_empty() => *this.done_first = true,
                 Ok(n) => return Poll::Ready(Ok(n)),
                 Err(err) => return Poll::Ready(Err(err)),
             }
         }
 
-        let rd = Pin::new(&mut self.second);
-        rd.poll_read(cx, buf)
+        this.second.poll_read(cx, buf)
     }
 
     fn poll_read_vectored(
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         bufs: &mut [IoSliceMut<'_>],
     ) -> Poll<io::Result<usize>> {
-        if !self.done_first {
-            let rd = Pin::new(&mut self.first);
-
-            match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) {
-                Ok(0) if !bufs.is_empty() => self.done_first = true,
+        let this = self.project();
+        if !*this.done_first {
+            match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) {
+                Ok(0) if !bufs.is_empty() => *this.done_first = true,
                 Ok(n) => return Poll::Ready(Ok(n)),
                 Err(err) => return Poll::Ready(Err(err)),
             }
         }
 
-        let rd = Pin::new(&mut self.second);
-        rd.poll_read_vectored(cx, bufs)
+        this.second.poll_read_vectored(cx, bufs)
     }
 }
 
-impl<T: BufRead + Unpin, U: BufRead + Unpin> BufRead for Chain<T, U> {
+impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
-        let Self {
-            first,
-            second,
-            done_first,
-        } = unsafe { self.get_unchecked_mut() };
-
-        if !*done_first {
-            let first = unsafe { Pin::new_unchecked(first) };
-            match futures_core::ready!(first.poll_fill_buf(cx)) {
+        let this = self.project();
+        if !*this.done_first {
+            match futures_core::ready!(this.first.poll_fill_buf(cx)) {
                 Ok(buf) if buf.is_empty() => {
-                    *done_first = true;
+                    *this.done_first = true;
                 }
                 Ok(buf) => return Poll::Ready(Ok(buf)),
                 Err(err) => return Poll::Ready(Err(err)),
             }
         }
 
-        let second = unsafe { Pin::new_unchecked(second) };
-        second.poll_fill_buf(cx)
+        this.second.poll_fill_buf(cx)
     }
 
-    fn consume(mut self: Pin<&mut Self>, amt: usize) {
-        if !self.done_first {
-            let rd = Pin::new(&mut self.first);
-            rd.consume(amt)
+    fn consume(self: Pin<&mut Self>, amt: usize) {
+        let this = self.project();
+        if !*this.done_first {
+            this.first.consume(amt)
         } else {
-            let rd = Pin::new(&mut self.second);
-            rd.consume(amt)
+            this.second.consume(amt)
         }
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, feature = "default", not(target_arch = "wasm32")))]
 mod tests {
     use crate::io;
     use crate::prelude::*;
diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs
index 6fd95c120..405422585 100644
--- a/src/io/read/mod.rs
+++ b/src/io/read/mod.rs
@@ -13,454 +13,368 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture};
 use read_to_string::ReadToStringFuture;
 use read_vectored::ReadVectoredFuture;
 
-use cfg_if::cfg_if;
 use std::mem;
 
 use crate::io::IoSliceMut;
-use crate::utils::extension_trait;
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::pin::Pin;
-        use std::ops::{Deref, DerefMut};
+pub use bytes::Bytes;
+pub use chain::Chain;
+pub use take::Take;
 
-        use crate::io;
-        use crate::task::{Context, Poll};
-    }
-}
+pub use futures_io::AsyncRead as Read;
 
-extension_trait! {
+#[doc = r#"
+    Extension methods for [`Read`].
+
+    [`Read`]: ../trait.Read.html
+"#]
+pub trait ReadExt: Read {
     #[doc = r#"
-        Allows reading from a byte stream.
+        Reads some bytes from the byte stream.
 
-        This trait is a re-export of [`futures::io::AsyncRead`] and is an async version of
-        [`std::io::Read`].
+        Returns the number of bytes read from the start of the buffer.
 
-        Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the
-        trait itself, but they become available when the prelude is imported:
+        If the return value is `Ok(n)`, then it must be guaranteed that
+        `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been
+        filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two
+        scenarios:
 
-        ```
-        # #[allow(unused_imports)]
+        1. This reader has reached its "end of file" and will likely no longer be able to
+           produce bytes. Note that this does not mean that the reader will always no
+           longer be able to produce bytes.
+        2. The buffer specified was 0 bytes in length.
+
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
         use async_std::prelude::*;
+
+        let mut file = File::open("a.txt").await?;
+
+        let mut buf = vec![0; 1024];
+        let n = file.read(&mut buf).await?;
+        #
+        # Ok(()) }) }
         ```
+    "#]
+    fn read<'a>(
+        &'a mut self,
+        buf: &'a mut [u8],
+    ) -> ReadFuture<'a, Self>
+    where
+        Self: Unpin
+    {
+        ReadFuture { reader: self, buf }
+    }
+
+    #[doc = r#"
+        Like [`read`], except that it reads into a slice of buffers.
 
-        [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
-        [`futures::io::AsyncRead`]:
-        https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html
-        [`poll_read`]: #tymethod.poll_read
-        [`poll_read_vectored`]: #method.poll_read_vectored
+        Data is copied to fill each buffer in order, with the final buffer written to
+        possibly being only partially filled. This method must behave as a single call to
+        [`read`] with the buffers concatenated would.
+
+        The default implementation calls [`read`] with either the first nonempty buffer
+        provided, or an empty one if none exists.
+
+        [`read`]: #tymethod.read
     "#]
-    pub trait Read [ReadExt: futures_io::AsyncRead] {
-        #[doc = r#"
-            Attempt to read from the `AsyncRead` into `buf`.
-        "#]
-        fn poll_read(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &mut [u8],
-        ) -> Poll<io::Result<usize>>;
-
-        #[doc = r#"
-            Attempt to read from the `AsyncRead` into `bufs` using vectored IO operations.
-        "#]
-        fn poll_read_vectored(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            bufs: &mut [IoSliceMut<'_>],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    fn read_vectored<'a>(
+        &'a mut self,
+        bufs: &'a mut [IoSliceMut<'a>],
+    ) -> ReadVectoredFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        ReadVectoredFuture { reader: self, bufs }
+    }
 
-        #[doc = r#"
-            Reads some bytes from the byte stream.
-
-            Returns the number of bytes read from the start of the buffer.
-
-            If the return value is `Ok(n)`, then it must be guaranteed that
-            `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been
-            filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two
-            scenarios:
-
-            1. This reader has reached its "end of file" and will likely no longer be able to
-               produce bytes. Note that this does not mean that the reader will always no
-               longer be able to produce bytes.
-            2. The buffer specified was 0 bytes in length.
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
-
-            let mut file = File::open("a.txt").await?;
-
-            let mut buf = vec![0; 1024];
-            let n = file.read(&mut buf).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn read<'a>(
-            &'a mut self,
-            buf: &'a mut [u8],
-        ) -> impl Future<Output = io::Result<usize>> + 'a [ReadFuture<'a, Self>]
-        where
-            Self: Unpin
-        {
-            ReadFuture { reader: self, buf }
-        }
+    #[doc = r#"
+        Reads all bytes from the byte stream.
 
-        #[doc = r#"
-            Like [`read`], except that it reads into a slice of buffers.
+        All bytes read from this stream will be appended to the specified buffer `buf`.
+        This function will continuously call [`read`] to append more data to `buf` until
+        [`read`] returns either `Ok(0)` or an error.
 
-            Data is copied to fill each buffer in order, with the final buffer written to
-            possibly being only partially filled. This method must behave as a single call to
-            [`read`] with the buffers concatenated would.
+        If successful, this function will return the total number of bytes read.
 
-            The default implementation calls [`read`] with either the first nonempty buffer
-            provided, or an empty one if none exists.
+        [`read`]: #tymethod.read
 
-            [`read`]: #tymethod.read
-        "#]
-        fn read_vectored<'a>(
-            &'a mut self,
-            bufs: &'a mut [IoSliceMut<'a>],
-        ) -> impl Future<Output = io::Result<usize>> + 'a [ReadVectoredFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            ReadVectoredFuture { reader: self, bufs }
-        }
+        # Examples
 
-        #[doc = r#"
-            Reads all bytes from the byte stream.
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::prelude::*;
 
-            All bytes read from this stream will be appended to the specified buffer `buf`.
-            This function will continuously call [`read`] to append more data to `buf` until
-            [`read`] returns either `Ok(0)` or an error.
+        let mut file = File::open("a.txt").await?;
 
-            If successful, this function will return the total number of bytes read.
+        let mut buf = Vec::new();
+        file.read_to_end(&mut buf).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn read_to_end<'a>(
+        &'a mut self,
+        buf: &'a mut Vec<u8>,
+    ) -> ReadToEndFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        let start_len = buf.len();
+        ReadToEndFuture {
+            reader: self,
+            buf,
+            start_len,
+        }
+    }
 
-            [`read`]: #tymethod.read
+    #[doc = r#"
+        Reads all bytes from the byte stream and appends them into a string.
 
-            # Examples
+        If successful, this function will return the number of bytes read.
 
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
+        If the data in this stream is not valid UTF-8 then an error will be returned and
+        `buf` will be left unmodified.
 
-            let mut file = File::open("a.txt").await?;
+        # Examples
 
-            let mut buf = Vec::new();
-            file.read_to_end(&mut buf).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn read_to_end<'a>(
-            &'a mut self,
-            buf: &'a mut Vec<u8>,
-        ) -> impl Future<Output = io::Result<usize>> + 'a [ReadToEndFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            let start_len = buf.len();
-            ReadToEndFuture {
-                reader: self,
-                buf,
-                start_len,
-            }
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::prelude::*;
+
+        let mut file = File::open("a.txt").await?;
+
+        let mut buf = String::new();
+        file.read_to_string(&mut buf).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn read_to_string<'a>(
+        &'a mut self,
+        buf: &'a mut String,
+    ) -> ReadToStringFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        let start_len = buf.len();
+        ReadToStringFuture {
+            reader: self,
+            bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) },
+            buf,
+            start_len,
         }
+    }
 
-        #[doc = r#"
-            Reads all bytes from the byte stream and appends them into a string.
+    #[doc = r#"
+        Reads the exact number of bytes required to fill `buf`.
 
-            If successful, this function will return the number of bytes read.
+        This function reads as many bytes as necessary to completely fill the specified
+        buffer `buf`.
 
-            If the data in this stream is not valid UTF-8 then an error will be returned and
-            `buf` will be left unmodified.
+        No guarantees are provided about the contents of `buf` when this function is
+        called, implementations cannot rely on any property of the contents of `buf` being
+        true. It is recommended that implementations only write data to `buf` instead of
+        reading its contents.
 
-            # Examples
+        If this function encounters an "end of file" before completely filling the buffer,
+        it returns an error of the kind [`ErrorKind::UnexpectedEof`].  The contents of
+        `buf` are unspecified in this case.
 
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
+        If any other read error is encountered then this function immediately returns. The
+        contents of `buf` are unspecified in this case.
 
-            let mut file = File::open("a.txt").await?;
+        If this function returns an error, it is unspecified how many bytes it has read,
+        but it will never read more than would be necessary to completely fill the buffer.
 
-            let mut buf = String::new();
-            file.read_to_string(&mut buf).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn read_to_string<'a>(
-            &'a mut self,
-            buf: &'a mut String,
-        ) -> impl Future<Output = io::Result<usize>> + 'a [ReadToStringFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            let start_len = buf.len();
-            ReadToStringFuture {
-                reader: self,
-                bytes: unsafe { mem::replace(buf.as_mut_vec(), Vec::new()) },
-                buf,
-                start_len,
-            }
-        }
+        [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof
 
-        #[doc = r#"
-            Reads the exact number of bytes required to fill `buf`.
+        # Examples
 
-            This function reads as many bytes as necessary to completely fill the specified
-            buffer `buf`.
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::prelude::*;
 
-            No guarantees are provided about the contents of `buf` when this function is
-            called, implementations cannot rely on any property of the contents of `buf` being
-            true. It is recommended that implementations only write data to `buf` instead of
-            reading its contents.
+        let mut file = File::open("a.txt").await?;
 
-            If this function encounters an "end of file" before completely filling the buffer,
-            it returns an error of the kind [`ErrorKind::UnexpectedEof`].  The contents of
-            `buf` are unspecified in this case.
+        let mut buf = vec![0; 10];
+        file.read_exact(&mut buf).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn read_exact<'a>(
+        &'a mut self,
+        buf: &'a mut [u8],
+    ) -> ReadExactFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        ReadExactFuture { reader: self, buf }
+    }
 
-            If any other read error is encountered then this function immediately returns. The
-            contents of `buf` are unspecified in this case.
+    #[doc = r#"
+        Creates an adaptor which will read at most `limit` bytes from it.
 
-            If this function returns an error, it is unspecified how many bytes it has read,
-            but it will never read more than would be necessary to completely fill the buffer.
+        This function returns a new instance of `Read` which will read at most
+        `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
+        read errors will not count towards the number of bytes read and future
+        calls to [`read`] may succeed.
 
-            [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof
+        # Examples
 
-            # Examples
+        [`File`]s implement `Read`:
 
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
+        [`File`]: ../fs/struct.File.html
+        [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
+        [`read`]: tymethod.read
 
-            let mut file = File::open("a.txt").await?;
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::io::prelude::*;
+        use async_std::fs::File;
 
-            let mut buf = vec![0; 10];
-            file.read_exact(&mut buf).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn read_exact<'a>(
-            &'a mut self,
-            buf: &'a mut [u8],
-        ) -> impl Future<Output = io::Result<()>> + 'a [ReadExactFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            ReadExactFuture { reader: self, buf }
-        }
+        let f = File::open("foo.txt").await?;
+        let mut buffer = [0; 5];
 
-        #[doc = r#"
-            Creates an adaptor which will read at most `limit` bytes from it.
+        // read at most five bytes
+        let mut handle = f.take(5);
 
-            This function returns a new instance of `Read` which will read at most
-            `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
-            read errors will not count towards the number of bytes read and future
-            calls to [`read()`] may succeed.
+        handle.read(&mut buffer).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn take(self, limit: u64) -> Take<Self>
+    where
+        Self: Sized,
+    {
+        Take { inner: self, limit }
+    }
 
-            # Examples
+    #[doc = r#"
+        Creates a "by reference" adaptor for this instance of `Read`.
 
-            [`File`]s implement `Read`:
+        The returned adaptor also implements `Read` and will simply borrow this
+        current reader.
 
-            [`File`]: ../fs/struct.File.html
-            [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
-            [`read()`]: tymethod.read
+        # Examples
 
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::io::prelude::*;
-            use async_std::fs::File;
+        [`File`][file]s implement `Read`:
 
-            let f = File::open("foo.txt").await?;
-            let mut buffer = [0; 5];
+        [file]: ../fs/struct.File.html
 
-            // read at most five bytes
-            let mut handle = f.take(5);
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::fs::File;
+
+        let mut f = File::open("foo.txt").await?;
+        let mut buffer = Vec::new();
+        let mut other_buffer = Vec::new();
 
-            handle.read(&mut buffer).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn take(self, limit: u64) -> take::Take<Self>
-        where
-            Self: Sized,
         {
-            take::Take { inner: self, limit }
-        }
+            let reference = f.by_ref();
 
-        #[doc = r#"
-            Creates a "by reference" adaptor for this instance of `Read`.
-           
-            The returned adaptor also implements `Read` and will simply borrow this
-            current reader.
-           
-            # Examples
-           
-            [`File`][file]s implement `Read`:
-           
-            [file]: ../fs/struct.File.html
-           
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::fs::File;
-           
-            let mut f = File::open("foo.txt").await?;
-            let mut buffer = Vec::new();
-            let mut other_buffer = Vec::new();
-           
-            {
-                let reference = f.by_ref();
-           
-                // read at most 5 bytes
-                reference.take(5).read_to_end(&mut buffer).await?;
-           
-            } // drop our &mut reference so we can use f again
-           
-            // original file still usable, read the rest
-            f.read_to_end(&mut other_buffer).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn by_ref(&mut self) -> &mut Self where Self: Sized { self }
-
-
-        #[doc = r#"
-            Transforms this `Read` instance to a `Stream` over its bytes.
-           
-            The returned type implements `Stream` where the `Item` is
-            `Result<u8, io::Error>`.
-            The yielded item is `Ok` if a byte was successfully read and `Err`
-            otherwise. EOF is mapped to returning `None` from this iterator.
-           
-            # Examples
-           
-            [`File`][file]s implement `Read`:
-           
-            [file]: ../fs/struct.File.html
-           
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::fs::File;
-           
-            let f = File::open("foo.txt").await?;
-            let mut s = f.bytes();
-           
-            while let Some(byte) = s.next().await {
-                println!("{}", byte.unwrap());
-            }
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn bytes(self) -> bytes::Bytes<Self> where Self: Sized {
-            bytes::Bytes { inner: self }
-        }
+            // read at most 5 bytes
+            reference.take(5).read_to_end(&mut buffer).await?;
 
-        #[doc = r#"
-            Creates an adaptor which will chain this stream with another.
-           
-            The returned `Read` instance will first read all bytes from this object
-            until EOF is encountered. Afterwards the output is equivalent to the
-            output of `next`.
-           
-            # Examples
-           
-            [`File`][file]s implement `Read`:
-           
-            [file]: ../fs/struct.File.html
-           
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::fs::File;
-           
-            let f1 = File::open("foo.txt").await?;
-            let f2 = File::open("bar.txt").await?;
-           
-            let mut handle = f1.chain(f2);
-            let mut buffer = String::new();
-           
-            // read the value into a String. We could use any Read method here,
-            // this is just one example.
-            handle.read_to_string(&mut buffer).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn chain<R: Read>(self, next: R) -> chain::Chain<Self, R> where Self: Sized {
-            chain::Chain { first: self, second: next, done_first: false }
-        }
+        } // drop our &mut reference so we can use f again
 
-    }
+        // original file still usable, read the rest
+        f.read_to_end(&mut other_buffer).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn by_ref(&mut self) -> &mut Self where Self: Sized { self }
 
-    impl<T: Read + Unpin + ?Sized> Read for Box<T> {
-        fn poll_read(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &mut [u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-    }
 
-    impl<T: Read + Unpin + ?Sized> Read for &mut T {
-        fn poll_read(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &mut [u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-    }
+    #[doc = r#"
+        Transforms this `Read` instance to a `Stream` over its bytes.
 
-    impl<P> Read for Pin<P>
-    where
-        P: DerefMut + Unpin,
-        <P as Deref>::Target: Read,
-    {
-        fn poll_read(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &mut [u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
+        The returned type implements `Stream` where the `Item` is
+        `Result<u8, io::Error>`.
+        The yielded item is `Ok` if a byte was successfully read and `Err`
+        otherwise. EOF is mapped to returning `None` from this iterator.
+
+        # Examples
+
+        [`File`][file]s implement `Read`:
+
+        [file]: ../fs/struct.File.html
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::fs::File;
+
+        let f = File::open("foo.txt").await?;
+        let mut s = f.bytes();
+
+        while let Some(byte) = s.next().await {
+            println!("{}", byte.unwrap());
         }
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn bytes(self) -> Bytes<Self> where Self: Sized {
+        Bytes { inner: self }
     }
 
-    impl Read for &[u8] {
-        fn poll_read(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &mut [u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    #[doc = r#"
+        Creates an adaptor which will chain this stream with another.
+
+        The returned `Read` instance will first read all bytes from this object
+        until EOF is encountered. Afterwards the output is equivalent to the
+        output of `next`.
+
+        # Examples
+
+        [`File`][file]s implement `Read`:
+
+        [file]: ../fs/struct.File.html
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::fs::File;
+
+        let f1 = File::open("foo.txt").await?;
+        let f2 = File::open("bar.txt").await?;
+
+        let mut handle = f1.chain(f2);
+        let mut buffer = String::new();
+
+        // read the value into a String. We could use any Read method here,
+        // this is just one example.
+        handle.read_to_string(&mut buffer).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn chain<R: Read>(self, next: R) -> Chain<Self, R> where Self: Sized {
+        Chain { first: self, second: next, done_first: false }
     }
 }
 
+impl<T: Read + ?Sized> ReadExt for T {}
+
 /// Initializes a buffer if necessary.
 ///
 /// Currently, a buffer is always initialized because `read_initializer`
@@ -470,13 +384,13 @@ unsafe fn initialize<R: futures_io::AsyncRead>(_reader: &R, buf: &mut [u8]) {
     std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len())
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "unknown")))]
 mod tests {
     use crate::io;
     use crate::prelude::*;
 
     #[test]
-    fn test_read_by_ref() -> io::Result<()> {
+    fn test_read_by_ref() {
         crate::task::block_on(async {
             let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]);
             let mut buffer = Vec::new();
@@ -486,14 +400,13 @@ mod tests {
                 let reference = f.by_ref();
 
                 // read at most 5 bytes
-                assert_eq!(reference.take(5).read_to_end(&mut buffer).await?, 5);
+                assert_eq!(reference.take(5).read_to_end(&mut buffer).await.unwrap(), 5);
                 assert_eq!(&buffer, &[0, 1, 2, 3, 4])
             } // drop our &mut reference so we can use f again
 
             // original file still usable, read the rest
-            assert_eq!(f.read_to_end(&mut other_buffer).await?, 4);
+            assert_eq!(f.read_to_end(&mut other_buffer).await.unwrap(), 4);
             assert_eq!(&other_buffer, &[5, 6, 7, 8]);
-            Ok(())
-        })
+        });
     }
 }
diff --git a/src/io/read/read.rs b/src/io/read/read.rs
index c46aff66a..0ba04e571 100644
--- a/src/io/read/read.rs
+++ b/src/io/read/read.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, Read};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs
index c970f431f..71cf004da 100644
--- a/src/io/read/read_exact.rs
+++ b/src/io/read/read_exact.rs
@@ -1,7 +1,7 @@
 use std::mem;
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, Read};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/read/read_to_end.rs b/src/io/read/read_to_end.rs
index d76ee8c42..c7c47b8f6 100644
--- a/src/io/read/read_to_end.rs
+++ b/src/io/read/read_to_end.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, Read};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs
index 60773e07a..5b74389e6 100644
--- a/src/io/read/read_to_string.rs
+++ b/src/io/read/read_to_string.rs
@@ -1,9 +1,9 @@
 use std::mem;
 use std::pin::Pin;
 use std::str;
+use std::future::Future;
 
 use super::read_to_end_internal;
-use crate::future::Future;
 use crate::io::{self, Read};
 use crate::task::{Context, Poll};
 
@@ -37,7 +37,11 @@ impl<T: Read + Unpin + ?Sized> Future for ReadToStringFuture<'_, T> {
                 ))
             }))
         } else {
-            debug_assert!(buf.is_empty());
+            #[allow(clippy::debug_assert_with_mut_call)]
+            {
+                debug_assert!(buf.is_empty());
+            }
+
             // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
             mem::swap(unsafe { buf.as_mut_vec() }, bytes);
             Poll::Ready(ret)
diff --git a/src/io/read/read_vectored.rs b/src/io/read/read_vectored.rs
index 8e52ba2dc..b4c61b8f6 100644
--- a/src/io/read/read_vectored.rs
+++ b/src/io/read/read_vectored.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, IoSliceMut, Read};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/read/take.rs b/src/io/read/take.rs
index def4e2405..ba9a9e318 100644
--- a/src/io/read/take.rs
+++ b/src/io/read/take.rs
@@ -1,19 +1,24 @@
 use std::cmp;
 use std::pin::Pin;
 
+use pin_project_lite::pin_project;
+
 use crate::io::{self, BufRead, Read};
 use crate::task::{Context, Poll};
 
-/// Reader adaptor which limits the bytes read from an underlying reader.
-///
-/// This struct is generally created by calling [`take`] on a reader.
-/// Please see the documentation of [`take`] for more details.
-///
-/// [`take`]: trait.Read.html#method.take
-#[derive(Debug)]
-pub struct Take<T> {
-    pub(crate) inner: T,
-    pub(crate) limit: u64,
+pin_project! {
+    /// Reader adaptor which limits the bytes read from an underlying reader.
+    ///
+    /// This struct is generally created by calling [`take`] on a reader.
+    /// Please see the documentation of [`take`] for more details.
+    ///
+    /// [`take`]: trait.Read.html#method.take
+    #[derive(Debug)]
+    pub struct Take<T> {
+        #[pin]
+        pub(crate) inner: T,
+        pub(crate) limit: u64,
+    }
 }
 
 impl<T> Take<T> {
@@ -152,15 +157,15 @@ impl<T> Take<T> {
     }
 }
 
-impl<T: Read + Unpin> Read for Take<T> {
+impl<T: Read> Read for Take<T> {
     /// Attempt to read from the `AsyncRead` into `buf`.
     fn poll_read(
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         buf: &mut [u8],
     ) -> Poll<io::Result<usize>> {
-        let Self { inner, limit } = &mut *self;
-        take_read_internal(Pin::new(inner), cx, buf, limit)
+        let this = self.project();
+        take_read_internal(this.inner, cx, buf, this.limit)
     }
 }
 
@@ -186,35 +191,34 @@ pub fn take_read_internal<R: Read + ?Sized>(
     }
 }
 
-impl<T: BufRead + Unpin> BufRead for Take<T> {
+impl<T: BufRead> BufRead for Take<T> {
     fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
-        let Self { inner, limit } = unsafe { self.get_unchecked_mut() };
-        let inner = unsafe { Pin::new_unchecked(inner) };
+        let this = self.project();
 
-        if *limit == 0 {
+        if *this.limit == 0 {
             return Poll::Ready(Ok(&[]));
         }
 
-        match futures_core::ready!(inner.poll_fill_buf(cx)) {
+        match futures_core::ready!(this.inner.poll_fill_buf(cx)) {
             Ok(buf) => {
-                let cap = cmp::min(buf.len() as u64, *limit) as usize;
+                let cap = cmp::min(buf.len() as u64, *this.limit) as usize;
                 Poll::Ready(Ok(&buf[..cap]))
             }
             Err(e) => Poll::Ready(Err(e)),
         }
     }
 
-    fn consume(mut self: Pin<&mut Self>, amt: usize) {
+    fn consume(self: Pin<&mut Self>, amt: usize) {
+        let this = self.project();
         // Don't let callers reset the limit by passing an overlarge value
-        let amt = cmp::min(amt as u64, self.limit) as usize;
-        self.limit -= amt as u64;
+        let amt = cmp::min(amt as u64, *this.limit) as usize;
+        *this.limit -= amt as u64;
 
-        let rd = Pin::new(&mut self.inner);
-        rd.consume(amt);
+        this.inner.consume(amt);
     }
 }
 
-#[cfg(test)]
+#[cfg(all(test, not(target_os = "unknown")))]
 mod tests {
     use crate::io;
     use crate::prelude::*;
diff --git a/src/io/repeat.rs b/src/io/repeat.rs
index a82e21be4..563681793 100644
--- a/src/io/repeat.rs
+++ b/src/io/repeat.rs
@@ -29,7 +29,8 @@ pub fn repeat(byte: u8) -> Repeat {
 
 /// A reader which yields one byte over and over and over and over and over and...
 ///
-/// This reader is constructed by the [`repeat`] function.
+/// This reader is created by the [`repeat`] function. See its
+/// documentation for more.
 ///
 /// [`repeat`]: fn.repeat.html
 pub struct Repeat {
diff --git a/src/io/seek.rs b/src/io/seek.rs
deleted file mode 100644
index 274eee736..000000000
--- a/src/io/seek.rs
+++ /dev/null
@@ -1,130 +0,0 @@
-use std::pin::Pin;
-
-use cfg_if::cfg_if;
-
-use crate::future::Future;
-use crate::io::{self, SeekFrom};
-use crate::task::{Context, Poll};
-use crate::utils::extension_trait;
-
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::ops::{Deref, DerefMut};
-    }
-}
-
-extension_trait! {
-    #[doc = r#"
-        Allows seeking through a byte stream.
-
-        This trait is a re-export of [`futures::io::AsyncSeek`] and is an async version of
-        [`std::io::Seek`].
-
-        The [provided methods] do not really exist in the trait itself, but they become
-        available when the prelude is imported:
-
-        ```
-        # #[allow(unused_imports)]
-        use async_std::prelude::*;
-        ```
-
-        [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
-        [`futures::io::AsyncSeek`]:
-        https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html
-        [provided methods]: #provided-methods
-    "#]
-    pub trait Seek [SeekExt: futures_io::AsyncSeek] {
-        #[doc = r#"
-            Attempt to seek to an offset, in bytes, in a stream.
-        "#]
-        fn poll_seek(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            pos: SeekFrom,
-        ) -> Poll<io::Result<u64>>;
-
-        #[doc = r#"
-            Seeks to a new position in a byte stream.
-
-            Returns the new position in the byte stream.
-
-            A seek beyond the end of stream is allowed, but behavior is defined by the
-            implementation.
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::io::SeekFrom;
-            use async_std::prelude::*;
-
-            let mut file = File::open("a.txt").await?;
-
-            let file_len = file.seek(SeekFrom::End(0)).await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn seek(
-            &mut self,
-            pos: SeekFrom,
-        ) -> impl Future<Output = io::Result<u64>> [SeekFuture<'_, Self>]
-        where
-            Self: Unpin,
-        {
-            SeekFuture { seeker: self, pos }
-        }
-    }
-
-    impl<T: Seek + Unpin + ?Sized> Seek for Box<T> {
-        fn poll_seek(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            pos: SeekFrom,
-        ) -> Poll<io::Result<u64>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-    }
-
-    impl<T: Seek + Unpin + ?Sized> Seek for &mut T {
-        fn poll_seek(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            pos: SeekFrom,
-        ) -> Poll<io::Result<u64>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-    }
-
-    impl<P> Seek for Pin<P>
-    where
-        P: DerefMut + Unpin,
-        <P as Deref>::Target: Seek,
-    {
-        fn poll_seek(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            pos: SeekFrom,
-        ) -> Poll<io::Result<u64>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-    }
-}
-
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct SeekFuture<'a, T: Unpin + ?Sized> {
-    seeker: &'a mut T,
-    pos: SeekFrom,
-}
-
-impl<T: SeekExt + Unpin + ?Sized> Future for SeekFuture<'_, T> {
-    type Output = io::Result<u64>;
-
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        let pos = self.pos;
-        Pin::new(&mut *self.seeker).poll_seek(cx, pos)
-    }
-}
diff --git a/src/io/seek/mod.rs b/src/io/seek/mod.rs
new file mode 100644
index 000000000..cb0b9e136
--- /dev/null
+++ b/src/io/seek/mod.rs
@@ -0,0 +1,50 @@
+mod seek;
+
+use seek::SeekFuture;
+
+use crate::io::SeekFrom;
+
+pub use futures_io::AsyncSeek as Seek;
+
+#[doc = r#"
+    Extension methods for [`Seek`].
+
+    [`Seek`]: ../trait.Seek.html
+"#]
+pub trait SeekExt: Seek {
+    #[doc = r#"
+        Seeks to a new position in a byte stream.
+
+        Returns the new position in the byte stream.
+
+        A seek beyond the end of stream is allowed, but behavior is defined by the
+        implementation.
+
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::io::SeekFrom;
+        use async_std::prelude::*;
+
+        let mut file = File::open("a.txt").await?;
+
+        let file_len = file.seek(SeekFrom::End(0)).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn seek(
+        &mut self,
+        pos: SeekFrom,
+    ) -> SeekFuture<'_, Self>
+    where
+        Self: Unpin,
+    {
+        SeekFuture { seeker: self, pos }
+    }
+}
+
+impl<T: Seek + ?Sized> SeekExt for T {}
diff --git a/src/io/seek/seek.rs b/src/io/seek/seek.rs
new file mode 100644
index 000000000..74aa93e5b
--- /dev/null
+++ b/src/io/seek/seek.rs
@@ -0,0 +1,21 @@
+use std::pin::Pin;
+use std::future::Future;
+
+use crate::io::{self, Seek, SeekFrom};
+use crate::task::{Context, Poll};
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+pub struct SeekFuture<'a, T: Unpin + ?Sized> {
+    pub(crate) seeker: &'a mut T,
+    pub(crate) pos: SeekFrom,
+}
+
+impl<T: Seek + Unpin + ?Sized> Future for SeekFuture<'_, T> {
+    type Output = io::Result<u64>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let pos = self.pos;
+        Pin::new(&mut *self.seeker).poll_seek(cx, pos)
+    }
+}
diff --git a/src/io/sink.rs b/src/io/sink.rs
index faa763c6a..86aeb0aec 100644
--- a/src/io/sink.rs
+++ b/src/io/sink.rs
@@ -25,7 +25,8 @@ pub fn sink() -> Sink {
 
 /// A writer that consumes and drops all data.
 ///
-/// This writer is constructed by the [`sink`] function.
+/// This writer is constructed by the [`sink`] function. See its documentation
+/// for more.
 ///
 /// [`sink`]: fn.sink.html
 pub struct Sink {
diff --git a/src/io/stderr.rs b/src/io/stderr.rs
index 64e0b1864..81cc197b9 100644
--- a/src/io/stderr.rs
+++ b/src/io/stderr.rs
@@ -1,11 +1,9 @@
 use std::pin::Pin;
 use std::sync::Mutex;
+use std::future::Future;
 
-use cfg_if::cfg_if;
-
-use crate::future::Future;
 use crate::io::{self, Write};
-use crate::task::{blocking, Context, Poll};
+use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
 
 /// Constructs a new handle to the standard error of the current process.
 ///
@@ -13,6 +11,12 @@ use crate::task::{blocking, Context, Poll};
 ///
 /// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
 ///
+/// ### Note: Windows Portability Consideration
+///
+/// When operating in a console, the Windows implementation of this stream does not support
+/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
+/// an error.
+///
 /// # Examples
 ///
 /// ```no_run
@@ -36,12 +40,16 @@ pub fn stderr() -> Stderr {
 
 /// A handle to the standard error of the current process.
 ///
-/// Created by the [`stderr`] function.
+/// This writer is created by the [`stderr`] function. See its documentation for
+/// more.
+///
+/// ### Note: Windows Portability Consideration
 ///
-/// This type is an async version of [`std::io::Stderr`].
+/// When operating in a console, the Windows implementation of this stream does not support
+/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
+/// an error.
 ///
 /// [`stderr`]: fn.stderr.html
-/// [`std::io::Stderr`]: https://doc.rust-lang.org/std/io/struct.Stderr.html
 #[derive(Debug)]
 pub struct Stderr(Mutex<State>);
 
@@ -56,7 +64,7 @@ enum State {
     /// The stderr is blocked on an asynchronous operation.
     ///
     /// Awaiting this operation will result in the new state of the stderr.
-    Busy(blocking::JoinHandle<State>),
+    Busy(JoinHandle<State>),
 }
 
 /// Inner representation of the asynchronous stderr.
@@ -81,11 +89,12 @@ enum Operation {
 
 impl Write for Stderr {
     fn poll_write(
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         buf: &[u8],
     ) -> Poll<io::Result<usize>> {
-        let state = &mut *self.0.lock().unwrap();
+        let mut state_guard = self.0.lock().unwrap();
+        let state = &mut *state_guard;
 
         loop {
             match state {
@@ -116,8 +125,8 @@ impl Write for Stderr {
                         inner.buf[..buf.len()].copy_from_slice(buf);
 
                         // Start the operation asynchronously.
-                        *state = State::Busy(blocking::spawn(async move {
-                            let res = std::io::Write::write(&mut inner.stderr, &mut inner.buf);
+                        *state = State::Busy(spawn_blocking(move || {
+                            let res = std::io::Write::write(&mut inner.stderr, &inner.buf);
                             inner.last_op = Some(Operation::Write(res));
                             State::Idle(Some(inner))
                         }));
@@ -129,8 +138,9 @@ impl Write for Stderr {
         }
     }
 
-    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-        let state = &mut *self.0.lock().unwrap();
+    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+        let mut state_guard = self.0.lock().unwrap();
+        let state = &mut *state_guard;
 
         loop {
             match state {
@@ -144,7 +154,7 @@ impl Write for Stderr {
                         let mut inner = opt.take().unwrap();
 
                         // Start the operation asynchronously.
-                        *state = State::Busy(blocking::spawn(async move {
+                        *state = State::Busy(spawn_blocking(move || {
                             let res = std::io::Write::flush(&mut inner.stderr);
                             inner.last_op = Some(Operation::Flush(res));
                             State::Idle(Some(inner))
@@ -162,34 +172,45 @@ impl Write for Stderr {
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, RawFd};
-        use crate::os::windows::io::{AsRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        use std::os::windows::io::{AsRawHandle, RawHandle};
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, RawFd};
+
+    impl AsRawFd for Stderr {
+        fn as_raw_fd(&self) -> RawFd {
+            std::io::stderr().as_raw_fd()
+        }
     }
-}
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for Stderr {
-            fn as_raw_fd(&self) -> RawFd {
-                std::io::stderr().as_raw_fd()
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd};
+
+        impl AsFd for Stderr {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                unsafe {
+                    BorrowedFd::borrow_raw(std::io::stderr().as_raw_fd())
+                }
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        impl AsRawHandle for Stderr {
-            fn as_raw_handle(&self) -> RawHandle {
-                std::io::stderr().as_raw_handle()
+cfg_windows! {
+    use crate::os::windows::io::{AsRawHandle, RawHandle};
+
+    impl AsRawHandle for Stderr {
+        fn as_raw_handle(&self) -> RawHandle {
+            std::io::stderr().as_raw_handle()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsHandle, BorrowedHandle};
+
+        impl AsHandle for Stderr {
+            fn as_handle(&self) -> BorrowedHandle<'_> {
+                unsafe {
+                    BorrowedHandle::borrow_raw(std::io::stderr().as_raw_handle())
+                }
             }
         }
     }
diff --git a/src/io/stdin.rs b/src/io/stdin.rs
index d2e9ec047..d8f96d49e 100644
--- a/src/io/stdin.rs
+++ b/src/io/stdin.rs
@@ -1,11 +1,11 @@
+use std::future::Future;
 use std::pin::Pin;
 use std::sync::Mutex;
 
-use cfg_if::cfg_if;
-
-use crate::future::{self, Future};
+use crate::future;
 use crate::io::{self, Read};
-use crate::task::{blocking, Context, Poll};
+use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
+use crate::utils::Context as _;
 
 /// Constructs a new handle to the standard input of the current process.
 ///
@@ -13,6 +13,12 @@ use crate::task::{blocking, Context, Poll};
 ///
 /// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html
 ///
+/// ### Note: Windows Portability Consideration
+///
+/// When operating in a console, the Windows implementation of this stream does not support
+/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return
+/// an error.
+///
 /// # Examples
 ///
 /// ```no_run
@@ -37,12 +43,16 @@ pub fn stdin() -> Stdin {
 
 /// A handle to the standard input of the current process.
 ///
-/// Created by the [`stdin`] function.
+/// This reader is created by the [`stdin`] function. See its documentation for
+/// more.
+///
+/// ### Note: Windows Portability Consideration
 ///
-/// This type is an async version of [`std::io::Stdin`].
+/// When operating in a console, the Windows implementation of this stream does not support
+/// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return
+/// an error.
 ///
 /// [`stdin`]: fn.stdin.html
-/// [`std::io::Stdin`]: https://doc.rust-lang.org/std/io/struct.Stdin.html
 #[derive(Debug)]
 pub struct Stdin(Mutex<State>);
 
@@ -57,7 +67,7 @@ enum State {
     /// The stdin is blocked on an asynchronous operation.
     ///
     /// Awaiting this operation will result in the new state of the stdin.
-    Busy(blocking::JoinHandle<State>),
+    Busy(JoinHandle<State>),
 }
 
 /// Inner representation of the asynchronous stdin.
@@ -69,7 +79,7 @@ struct Inner {
     /// The line buffer.
     line: String,
 
-    /// The write buffer.
+    /// The read buffer.
     buf: Vec<u8>,
 
     /// The result of the last asynchronous operation on the stdin.
@@ -119,7 +129,7 @@ impl Stdin {
                             let mut inner = opt.take().unwrap();
 
                             // Start the operation asynchronously.
-                            *state = State::Busy(blocking::spawn(async move {
+                            *state = State::Busy(spawn_blocking(move || {
                                 inner.line.clear();
                                 let res = inner.stdin.read_line(&mut inner.line);
                                 inner.last_op = Some(Operation::ReadLine(res));
@@ -133,16 +143,18 @@ impl Stdin {
             }
         })
         .await
+        .context(|| String::from("could not read line on stdin"))
     }
 }
 
 impl Read for Stdin {
     fn poll_read(
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         buf: &mut [u8],
     ) -> Poll<io::Result<usize>> {
-        let state = &mut *self.0.lock().unwrap();
+        let mut state_guard = self.0.lock().unwrap();
+        let state = &mut *state_guard;
 
         loop {
             match state {
@@ -172,7 +184,7 @@ impl Read for Stdin {
                         }
 
                         // Start the operation asynchronously.
-                        *state = State::Busy(blocking::spawn(async move {
+                        *state = State::Busy(spawn_blocking(move || {
                             let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf);
                             inner.last_op = Some(Operation::Read(res));
                             State::Idle(Some(inner))
@@ -186,34 +198,45 @@ impl Read for Stdin {
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, RawFd};
-        use crate::os::windows::io::{AsRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        use std::os::windows::io::{AsRawHandle, RawHandle};
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, RawFd};
+
+    impl AsRawFd for Stdin {
+        fn as_raw_fd(&self) -> RawFd {
+            std::io::stdin().as_raw_fd()
+        }
     }
-}
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for Stdin {
-            fn as_raw_fd(&self) -> RawFd {
-                std::io::stdin().as_raw_fd()
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd};
+
+        impl AsFd for Stdin {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                unsafe {
+                    BorrowedFd::borrow_raw(std::io::stdin().as_raw_fd())
+                }
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        impl AsRawHandle for Stdin {
-            fn as_raw_handle(&self) -> RawHandle {
-                std::io::stdin().as_raw_handle()
+cfg_windows! {
+    use crate::os::windows::io::{AsRawHandle, RawHandle};
+
+    impl AsRawHandle for Stdin {
+        fn as_raw_handle(&self) -> RawHandle {
+            std::io::stdin().as_raw_handle()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsHandle, BorrowedHandle};
+
+        impl AsHandle for Stdin {
+            fn as_handle(&self) -> BorrowedHandle<'_> {
+                unsafe {
+                    BorrowedHandle::borrow_raw(std::io::stdin().as_raw_handle())
+                }
             }
         }
     }
diff --git a/src/io/stdio.rs b/src/io/stdio.rs
new file mode 100644
index 000000000..0e11e1a99
--- /dev/null
+++ b/src/io/stdio.rs
@@ -0,0 +1,21 @@
+//! Internal types for stdio.
+//!
+//! This module is a port of `libstd/io/stdio.rs`,and contains internal types for `print`/`eprint`.
+
+use crate::io::{stderr, stdout};
+use crate::prelude::*;
+use std::fmt;
+
+#[doc(hidden)]
+pub async fn _print(args: fmt::Arguments<'_>) {
+    if let Err(e) = stdout().write_fmt(args).await {
+        panic!("failed printing to stdout: {}", e);
+    }
+}
+
+#[doc(hidden)]
+pub async fn _eprint(args: fmt::Arguments<'_>) {
+    if let Err(e) = stderr().write_fmt(args).await {
+        panic!("failed printing to stderr: {}", e);
+    }
+}
diff --git a/src/io/stdout.rs b/src/io/stdout.rs
index b7fee709a..3cc570dc8 100644
--- a/src/io/stdout.rs
+++ b/src/io/stdout.rs
@@ -1,11 +1,9 @@
 use std::pin::Pin;
 use std::sync::Mutex;
+use std::future::Future;
 
-use cfg_if::cfg_if;
-
-use crate::future::Future;
 use crate::io::{self, Write};
-use crate::task::{blocking, Context, Poll};
+use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
 
 /// Constructs a new handle to the standard output of the current process.
 ///
@@ -13,6 +11,12 @@ use crate::task::{blocking, Context, Poll};
 ///
 /// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html
 ///
+/// ### Note: Windows Portability Consideration
+///
+/// When operating in a console, the Windows implementation of this stream does not support
+/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
+/// an error.
+///
 /// # Examples
 ///
 /// ```no_run
@@ -36,12 +40,16 @@ pub fn stdout() -> Stdout {
 
 /// A handle to the standard output of the current process.
 ///
-/// Created by the [`stdout`] function.
+/// This writer is created by the [`stdout`] function. See its documentation
+/// for more.
+///
+/// ### Note: Windows Portability Consideration
 ///
-/// This type is an async version of [`std::io::Stdout`].
+/// When operating in a console, the Windows implementation of this stream does not support
+/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
+/// an error.
 ///
 /// [`stdout`]: fn.stdout.html
-/// [`std::io::Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html
 #[derive(Debug)]
 pub struct Stdout(Mutex<State>);
 
@@ -56,7 +64,7 @@ enum State {
     /// The stdout is blocked on an asynchronous operation.
     ///
     /// Awaiting this operation will result in the new state of the stdout.
-    Busy(blocking::JoinHandle<State>),
+    Busy(JoinHandle<State>),
 }
 
 /// Inner representation of the asynchronous stdout.
@@ -81,11 +89,12 @@ enum Operation {
 
 impl Write for Stdout {
     fn poll_write(
-        mut self: Pin<&mut Self>,
+        self: Pin<&mut Self>,
         cx: &mut Context<'_>,
         buf: &[u8],
     ) -> Poll<io::Result<usize>> {
-        let state = &mut *self.0.lock().unwrap();
+        let mut state_guard = self.0.lock().unwrap();
+        let state = &mut *state_guard;
 
         loop {
             match state {
@@ -116,8 +125,8 @@ impl Write for Stdout {
                         inner.buf[..buf.len()].copy_from_slice(buf);
 
                         // Start the operation asynchronously.
-                        *state = State::Busy(blocking::spawn(async move {
-                            let res = std::io::Write::write(&mut inner.stdout, &mut inner.buf);
+                        *state = State::Busy(spawn_blocking(move || {
+                            let res = std::io::Write::write(&mut inner.stdout, &inner.buf);
                             inner.last_op = Some(Operation::Write(res));
                             State::Idle(Some(inner))
                         }));
@@ -129,8 +138,9 @@ impl Write for Stdout {
         }
     }
 
-    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-        let state = &mut *self.0.lock().unwrap();
+    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+        let mut state_guard = self.0.lock().unwrap();
+        let state = &mut *state_guard;
 
         loop {
             match state {
@@ -144,7 +154,7 @@ impl Write for Stdout {
                         let mut inner = opt.take().unwrap();
 
                         // Start the operation asynchronously.
-                        *state = State::Busy(blocking::spawn(async move {
+                        *state = State::Busy(spawn_blocking(move || {
                             let res = std::io::Write::flush(&mut inner.stdout);
                             inner.last_op = Some(Operation::Flush(res));
                             State::Idle(Some(inner))
@@ -162,34 +172,45 @@ impl Write for Stdout {
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, RawFd};
-        use crate::os::windows::io::{AsRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        use std::os::windows::io::{AsRawHandle, RawHandle};
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, RawFd};
+
+    impl AsRawFd for Stdout {
+        fn as_raw_fd(&self) -> RawFd {
+            std::io::stdout().as_raw_fd()
+        }
     }
-}
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for Stdout {
-            fn as_raw_fd(&self) -> RawFd {
-                std::io::stdout().as_raw_fd()
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd};
+
+        impl AsFd for Stdout {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                unsafe {
+                    BorrowedFd::borrow_raw(std::io::stdout().as_raw_fd())
+                }
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        impl AsRawHandle for Stdout {
-            fn as_raw_handle(&self) -> RawHandle {
-                std::io::stdout().as_raw_handle()
+cfg_windows! {
+    use crate::os::windows::io::{AsRawHandle, RawHandle};
+
+    impl AsRawHandle for Stdout {
+        fn as_raw_handle(&self) -> RawHandle {
+            std::io::stdout().as_raw_handle()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsHandle, BorrowedHandle};
+
+        impl AsHandle for Stdout {
+            fn as_handle(&self) -> BorrowedHandle<'_> {
+                unsafe {
+                    BorrowedHandle::borrow_raw(std::io::stdout().as_raw_handle())
+                }
             }
         }
     }
diff --git a/src/io/timeout.rs b/src/io/timeout.rs
index 2d1b29e74..073c2f6e9 100644
--- a/src/io/timeout.rs
+++ b/src/io/timeout.rs
@@ -1,9 +1,12 @@
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
 use std::time::Duration;
 
-use futures_timer::TryFutureExt;
+use pin_project_lite::pin_project;
 
-use crate::future::Future;
 use crate::io;
+use crate::utils::{timer_after, Timer};
 
 /// Awaits an I/O future or times out after a duration of time.
 ///
@@ -33,5 +36,45 @@ pub async fn timeout<F, T>(dur: Duration, f: F) -> io::Result<T>
 where
     F: Future<Output = io::Result<T>>,
 {
-    f.timeout(dur).await
+    Timeout {
+        timeout: timer_after(dur),
+        future: f,
+    }
+    .await
+}
+
+pin_project! {
+    /// Future returned by the `FutureExt::timeout` method.
+    #[derive(Debug)]
+    pub struct Timeout<F, T>
+    where
+        F: Future<Output = io::Result<T>>,
+    {
+        #[pin]
+        future: F,
+        #[pin]
+        timeout: Timer,
+    }
+}
+
+impl<F, T> Future for Timeout<F, T>
+where
+    F: Future<Output = io::Result<T>>,
+{
+    type Output = io::Result<T>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        match this.future.poll(cx) {
+            Poll::Pending => {}
+            other => return other,
+        }
+
+        if this.timeout.poll(cx).is_ready() {
+            let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out"));
+            Poll::Ready(err)
+        } else {
+            Poll::Pending
+        }
+    }
 }
diff --git a/src/io/utils.rs b/src/io/utils.rs
new file mode 100644
index 000000000..0ba3ecb2d
--- /dev/null
+++ b/src/io/utils.rs
@@ -0,0 +1,42 @@
+use crate::utils::Context;
+
+use std::{error::Error as StdError, fmt, io};
+
+/// Wrap `std::io::Error` with additional message
+///
+/// Keeps the original error kind and stores the original I/O error as `source`.
+impl<T> Context for Result<T, std::io::Error> {
+    fn context(self, message: impl Fn() -> String) -> Self {
+        self.map_err(|e| VerboseError::wrap(e, message()))
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct VerboseError {
+    source: io::Error,
+    message: String,
+}
+
+impl VerboseError {
+    pub(crate) fn wrap(source: io::Error, message: impl Into<String>) -> io::Error {
+        io::Error::new(
+            source.kind(),
+            VerboseError {
+                source,
+                message: message.into(),
+            },
+        )
+    }
+}
+
+impl fmt::Display for VerboseError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.message)
+    }
+}
+
+impl StdError for VerboseError {
+    fn source(&self) -> Option<&(dyn StdError + 'static)> {
+        Some(&self.source)
+    }
+}
diff --git a/src/io/write/flush.rs b/src/io/write/flush.rs
index 08f2b5b4e..590c12e89 100644
--- a/src/io/write/flush.rs
+++ b/src/io/write/flush.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, Write};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs
index 5e1ecc8be..753e7e6aa 100644
--- a/src/io/write/mod.rs
+++ b/src/io/write/mod.rs
@@ -1,277 +1,187 @@
 mod flush;
 mod write;
 mod write_all;
+mod write_fmt;
 mod write_vectored;
 
 use flush::FlushFuture;
 use write::WriteFuture;
 use write_all::WriteAllFuture;
+use write_fmt::WriteFmtFuture;
 use write_vectored::WriteVectoredFuture;
 
-use cfg_if::cfg_if;
+use crate::io::{self, IoSlice};
 
-use crate::io::IoSlice;
-use crate::utils::extension_trait;
+pub use futures_io::AsyncWrite as Write;
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::pin::Pin;
-        use std::ops::{Deref, DerefMut};
+#[doc = r#"
+    Extension methods for [`Write`].
 
-        use crate::io;
-        use crate::task::{Context, Poll};
-    }
-}
-
-extension_trait! {
+    [`Write`]: ../trait.Write.html
+"#]
+pub trait WriteExt: Write {
     #[doc = r#"
-        Allows writing to a byte stream.
+        Writes some bytes into the byte stream.
 
-        This trait is a re-export of [`futures::io::AsyncWrite`] and is an async version of
-        [`std::io::Write`].
+        Returns the number of bytes written from the start of the buffer.
 
-        Methods other than [`poll_write`], [`poll_write_vectored`], [`poll_flush`], and
-        [`poll_close`] do not really exist in the trait itself, but they become available when
-        the prelude is imported:
+        If the return value is `Ok(n)` then it must be guaranteed that
+        `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying
+        object is no longer able to accept bytes and will likely not be able to in the
+        future as well, or that the buffer provided is empty.
 
-        ```
-        # #[allow(unused_imports)]
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
         use async_std::prelude::*;
-        ```
 
-        [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
-        [`futures::io::AsyncWrite`]:
-        https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html
-        [`poll_write`]: #tymethod.poll_write
-        [`poll_write_vectored`]: #method.poll_write_vectored
-        [`poll_flush`]: #tymethod.poll_flush
-        [`poll_close`]: #tymethod.poll_close
+        let mut file = File::create("a.txt").await?;
+
+        let n = file.write(b"hello world").await?;
+        #
+        # Ok(()) }) }
+        ```
     "#]
-    pub trait Write [WriteExt: futures_io::AsyncWrite] {
-        #[doc = r#"
-            Attempt to write bytes from `buf` into the object.
-        "#]
-        fn poll_write(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &[u8],
-        ) -> Poll<io::Result<usize>>;
-
-        #[doc = r#"
-            Attempt to write bytes from `bufs` into the object using vectored IO operations.
-        "#]
-        fn poll_write_vectored(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            bufs: &[IoSlice<'_>]
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        #[doc = r#"
-            Attempt to flush the object, ensuring that any buffered data reach
-            their destination.
-        "#]
-        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
-
-        #[doc = r#"
-            Attempt to close the object.
-        "#]
-        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
-
-        #[doc = r#"
-            Writes some bytes into the byte stream.
-
-            Returns the number of bytes written from the start of the buffer.
-
-            If the return value is `Ok(n)` then it must be guaranteed that
-            `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying
-            object is no longer able to accept bytes and will likely not be able to in the
-            future as well, or that the buffer provided is empty.
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
-
-            let mut file = File::create("a.txt").await?;
-
-            let n = file.write(b"hello world").await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn write<'a>(
-            &'a mut self,
-            buf: &'a [u8],
-        ) -> impl Future<Output = io::Result<usize>> + 'a [WriteFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            WriteFuture { writer: self, buf }
-        }
-
-        #[doc = r#"
-            Flushes the stream to ensure that all buffered contents reach their destination.
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
-
-            let mut file = File::create("a.txt").await?;
-
-            file.write_all(b"hello world").await?;
-            file.flush().await?;
-            #
-            # Ok(()) }) }
-            ```
-        "#]
-        fn flush(&mut self) -> impl Future<Output = io::Result<()>> + '_ [FlushFuture<'_, Self>]
-        where
-            Self: Unpin,
-        {
-            FlushFuture { writer: self }
-        }
-
-        #[doc = r#"
-            Like [`write`], except that it writes from a slice of buffers.
-
-            Data is copied from each buffer in order, with the final buffer read from possibly
-            being only partially consumed. This method must behave as a call to [`write`] with
-            the buffers concatenated would.
-
-            The default implementation calls [`write`] with either the first nonempty buffer
-            provided, or an empty one if none exists.
-
-            [`write`]: #tymethod.write
-        "#]
-        fn write_vectored<'a>(
-            &'a mut self,
-            bufs: &'a [IoSlice<'a>],
-        ) -> impl Future<Output = io::Result<usize>> + 'a [WriteVectoredFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            WriteVectoredFuture { writer: self, bufs }
-        }
-
-        #[doc = r#"
-            Writes an entire buffer into the byte stream.
-
-            This method will continuously call [`write`] until there is no more data to be
-            written or an error is returned. This method will not return until the entire
-            buffer has been successfully written or such an error occurs.
-
-            [`write`]: #tymethod.write
-
-            # Examples
-
-            ```no_run
-            # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-            #
-            use async_std::fs::File;
-            use async_std::prelude::*;
-
-            let mut file = File::create("a.txt").await?;
-
-            file.write_all(b"hello world").await?;
-            #
-            # Ok(()) }) }
-            ```
-
-            [`write`]: #tymethod.write
-        "#]
-        fn write_all<'a>(
-            &'a mut self,
-            buf: &'a [u8],
-        ) -> impl Future<Output = io::Result<()>> + 'a [WriteAllFuture<'a, Self>]
-        where
-            Self: Unpin,
-        {
-            WriteAllFuture { writer: self, buf }
-        }
+    fn write<'a>(
+        &'a mut self,
+        buf: &'a [u8],
+    ) -> WriteFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        WriteFuture { writer: self, buf }
     }
 
-    impl<T: Write + Unpin + ?Sized> Write for Box<T> {
-        fn poll_write(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &[u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    #[doc = r#"
+        Flushes the stream to ensure that all buffered contents reach their destination.
+
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::prelude::*;
+
+        let mut file = File::create("a.txt").await?;
+
+        file.write_all(b"hello world").await?;
+        file.flush().await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn flush(&mut self) -> FlushFuture<'_, Self>
+    where
+        Self: Unpin,
+    {
+        FlushFuture { writer: self }
     }
 
-    impl<T: Write + Unpin + ?Sized> Write for &mut T {
-        fn poll_write(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &[u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    #[doc = r#"
+        Like [`write`], except that it writes from a slice of buffers.
+
+        Data is copied from each buffer in order, with the final buffer read from possibly
+        being only partially consumed. This method must behave as a call to [`write`] with
+        the buffers concatenated would.
+
+        The default implementation calls [`write`] with either the first nonempty buffer
+        provided, or an empty one if none exists.
+
+        [`write`]: #tymethod.write
+    "#]
+    fn write_vectored<'a>(
+        &'a mut self,
+        bufs: &'a [IoSlice<'a>],
+    ) -> WriteVectoredFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        WriteVectoredFuture { writer: self, bufs }
     }
 
-    impl<P> Write for Pin<P>
+    #[doc = r#"
+        Writes an entire buffer into the byte stream.
+
+        This method will continuously call [`write`] until there is no more data to be
+        written or an error is returned. This method will not return until the entire
+        buffer has been successfully written or such an error occurs.
+
+        [`write`]: #tymethod.write
+
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::fs::File;
+        use async_std::prelude::*;
+
+        let mut file = File::create("a.txt").await?;
+
+        file.write_all(b"hello world").await?;
+        #
+        # Ok(()) }) }
+        ```
+
+        [`write`]: #tymethod.write
+    "#]
+    fn write_all<'a>(
+        &'a mut self,
+        buf: &'a [u8],
+    ) -> WriteAllFuture<'a, Self>
     where
-        P: DerefMut + Unpin,
-        <P as Deref>::Target: Write,
+        Self: Unpin,
     {
-        fn poll_write(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &[u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+        WriteAllFuture { writer: self, buf }
     }
 
-    impl Write for Vec<u8> {
-        fn poll_write(
-            self: Pin<&mut Self>,
-            cx: &mut Context<'_>,
-            buf: &[u8],
-        ) -> Poll<io::Result<usize>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
-
-        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    #[doc = r#"
+        Writes a formatted string into this writer, returning any error encountered.
+
+        This method will continuously call [`write`] until there is no more data to be
+        written or an error is returned. This future will not resolve until the entire
+        buffer has been successfully written or such an error occurs.
+
+        [`write`]: #tymethod.write
+
+        # Examples
+
+        ```no_run
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use async_std::io::prelude::*;
+        use async_std::fs::File;
+
+        let mut buffer = File::create("foo.txt").await?;
+
+        // this call
+        write!(buffer, "{:.*}", 2, 1.234567).await?;
+        // turns into this:
+        buffer.write_fmt(format_args!("{:.*}", 2, 1.234567)).await?;
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    fn write_fmt<'a>(
+        &'a mut self,
+        fmt: std::fmt::Arguments<'_>,
+    ) -> WriteFmtFuture<'a, Self>
+    where
+        Self: Unpin,
+    {
+        // In order to not have to implement an async version of `fmt` including private types
+        // and all, we convert `Arguments` to a `Result<Vec<u8>>` and pass that to the Future.
+        // Doing an owned conversion saves us from juggling references.
+        let mut string = String::new();
+        let res = std::fmt::write(&mut string, fmt)
+            .map(|_| string.into_bytes())
+            .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatter error"));
+        WriteFmtFuture { writer: self, res: Some(res), buffer: None, amt: 0 }
     }
 }
+
+impl<T: Write + ?Sized> WriteExt for T {}
diff --git a/src/io/write/write.rs b/src/io/write/write.rs
index da6e5c50d..8f13091dc 100644
--- a/src/io/write/write.rs
+++ b/src/io/write/write.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, Write};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/write/write_all.rs b/src/io/write/write_all.rs
index 5353f7ab8..f04c55d65 100644
--- a/src/io/write/write_all.rs
+++ b/src/io/write/write_all.rs
@@ -1,7 +1,7 @@
 use std::mem;
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, Write};
 use crate::task::{Context, Poll};
 
diff --git a/src/io/write/write_fmt.rs b/src/io/write/write_fmt.rs
new file mode 100644
index 000000000..a65e06a64
--- /dev/null
+++ b/src/io/write/write_fmt.rs
@@ -0,0 +1,51 @@
+use std::pin::Pin;
+use std::future::Future;
+
+use crate::io::{self, Write};
+use crate::task::{Context, Poll};
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+#[must_use]
+pub struct WriteFmtFuture<'a, T: Unpin + ?Sized> {
+    pub(crate) writer: &'a mut T,
+    pub(crate) res: Option<io::Result<Vec<u8>>>,
+    pub(crate) buffer: Option<Vec<u8>>,
+    pub(crate) amt: usize,
+}
+
+impl<T: Write + Unpin + ?Sized> Future for WriteFmtFuture<'_, T> {
+    type Output = io::Result<()>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        // Process the internal Result the first time we run.
+        if self.buffer.is_none() {
+            match self.res.take().unwrap() {
+                Err(err) => return Poll::Ready(Err(err)),
+                Ok(buffer) => self.buffer = Some(buffer),
+            };
+        }
+
+        // Get the types from the future.
+        let Self {
+            writer,
+            amt,
+            buffer,
+            ..
+        } = &mut *self;
+        let buffer = buffer.as_mut().unwrap();
+
+        // Copy the data from the buffer into the writer until it's done.
+        loop {
+            if *amt == buffer.len() {
+                futures_core::ready!(Pin::new(&mut **writer).poll_flush(cx))?;
+                return Poll::Ready(Ok(()));
+            }
+            let i = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, &buffer[*amt..]))?;
+            if i == 0 {
+                return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
+            }
+            *amt += i;
+        }
+    }
+}
diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs
index 5f8492b77..cdb49d429 100644
--- a/src/io/write/write_vectored.rs
+++ b/src/io/write/write_vectored.rs
@@ -1,6 +1,6 @@
 use std::pin::Pin;
+use std::future::Future;
 
-use crate::future::Future;
 use crate::io::{self, IoSlice, Write};
 use crate::task::{Context, Poll};
 
diff --git a/src/lib.rs b/src/lib.rs
index fa4e946f9..b807d448d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,30 +1,190 @@
-//! Async version of the Rust standard library.
+#![allow(rustdoc::invalid_html_tags)]
+//! # Async version of the Rust standard library
 //!
-//! Modules in this crate are organized in the same way as in the standard library, except blocking
+//! `async-std` is a foundation of portable Rust software, a set of minimal and battle-tested
+//! shared abstractions for the [broader Rust ecosystem][crates.io]. It offers std types, like
+//! [`Future`] and [`Stream`], library-defined [operations on language primitives](#primitives),
+//! [standard macros](#macros), [I/O] and [multithreading], among [many other things][other].
+//!
+//! `async-std` is available from [crates.io]. Once included, `async-std` can be accessed
+//! in [`use`] statements through the path `async_std`, as in [`use async_std::future`].
+//!
+//! [I/O]: io/index.html
+//! [multithreading]: task/index.html
+//! [other]: #what-is-in-the-standard-library-documentation
+//! [`use`]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
+//! [`use async_std::future`]: future/index.html
+//! [crates.io]: https://crates.io
+//! [`Future`]: future/trait.Future.html
+//! [`Stream`]: stream/trait.Stream.html
+//!
+//! # How to read this documentation
+//!
+//! If you already know the name of what you are looking for, the fastest way to
+//! find it is to use the <a href="#" onclick="focusSearchBar();">search
+//! bar</a> at the top of the page.
+//!
+//! Otherwise, you may want to jump to one of these useful sections:
+//!
+//! * [`async_std::*` modules](#modules)
+//! * [Async macros](#macros)
+//! * [The Async Prelude](prelude/index.html)
+//! * [Cargo.toml feature flags](#features)
+//! * [Examples](#examples)
+//!
+//! If this is your first time, the documentation for `async-std` is
+//! written to be casually perused. Clicking on interesting things should
+//! generally lead you to interesting places. Still, there are important bits
+//! you don't want to miss, so read on for a tour of the `async-std` and
+//! its documentation!
+//!
+//! Once you are familiar with the contents of `async-std` you may
+//! begin to find the verbosity of the prose distracting. At this stage in your
+//! development you may want to press the `[-]` button near the top of the
+//! page to collapse it into a more skimmable view.
+//!
+//! While you are looking at that `[-]` button also notice the `[src]`
+//! button. Rust's API documentation comes with the source code and you are
+//! encouraged to read it. The `async-std` source is generally high
+//! quality and a peek behind the curtains is often enlightening.
+//!
+//! Modules in this crate are organized in the same way as in `std`, except blocking
 //! functions have been replaced with async functions and threads have been replaced with
 //! lightweight tasks.
 //!
-//! More information, reading materials, and other resources:
+//! You can find more information, reading materials, and other resources here:
+//!
+//! * [The async-std website](https://async.rs/)
+//! * [The async-std book](https://book.async.rs)
+//! * [GitHub repository](https://github.com/async-rs/async-std)
+//! * [List of code examples](https://github.com/async-rs/async-std/tree/HEAD/examples)
+//! * [Discord chat](https://discord.gg/JvZeVNe)
+//!
+//! # What is in the `async-std` documentation?
+//!
+//! First, `async-std` is divided into a number of focused
+//! modules, [all listed further down this page](#modules). These modules are
+//! the bedrock upon which async Rust is forged, and they have mighty names
+//! like [`async_std::os`] and [`async_std::task`]. Modules' documentation
+//! typically includes an overview of the module along with examples, and are
+//! a smart place to start familiarizing yourself with the library.
+//!
+//! Second, `async-std` defines [The Async Prelude], a small collection
+//! of items - mostly traits - that should be imported into every module of
+//! every async crate. The traits in the prelude are pervasive, making the
+//! prelude documentation a good entry point to learning about the library.
+//!
+//! [The Async Prelude]: prelude/index.html
+//! [`async_std::os`]: os/index.html
+//! [`async_std::task`]: task/index.html
+//!
+//! And finally, `async-std` exports a number of async macros, and
+//! [lists them on this page](#macros).
+//!
+//! # Contributing changes to the documentation
+//!
+//! Check out `async-std`'s contribution guidelines [here](https://async.rs/contribute).
+//! The source for this documentation can be found on [GitHub](https://github.com/async-rs).
+//! To contribute changes, make sure you read the guidelines first, then submit
+//! pull requests for your suggested changes.
+//!
+//! Contributions are appreciated! If you see a part of the docs that can be
+//! improved, submit a PR, or chat with us first on
+//! [Discord](https://discord.gg/JvZeVNe).
+//!
+//! # A tour of `async-std`
+//!
+//! The rest of this crate documentation is dedicated to pointing out notable
+//! features of `async-std`.
+//!
+//! ## Platform abstractions and I/O
 //!
-//! * [🌐 The async-std website](https://async.rs/)
-//! * [📖 The async-std book](https://book.async.rs)
-//! * [🐙 GitHub repository](https://github.com/async-rs/async-std)
-//! * [📒 List of code examples](https://github.com/async-rs/async-std/tree/master/examples)
-//! * [💬 Discord chat](https://discord.gg/JvZeVNe)
+//! Besides basic data types, `async-std` is largely concerned with
+//! abstracting over differences in common platforms, most notably Windows and
+//! Unix derivatives.
+//!
+//! Common types of I/O, including [files], [TCP], [UDP], are defined in the
+//! [`io`], [`fs`], and [`net`] modules.
+//!
+//! The [`task`] module contains `async-std`'s task abstractions. [`sync`]
+//! contains further primitive shared memory types. [`channel`]  contains the channel types for message passing.
+//!
+//! [files]: fs/struct.File.html
+//! [TCP]: net/struct.TcpStream.html
+//! [UDP]: net/struct.UdpSocket.html
+//! [`io`]: io/index.html
+//! [`sync`]: sync/index.html
+//! [`channel`]: channel/index.html
+//!
+//! ## Timeouts, intervals, and delays
+//!
+//! `async-std` provides several methods to manipulate time:
+//!
+//! * [`task::sleep`] to wait for a duration to pass without blocking.
+//! * [`stream::interval`] for emitting an event at a set interval.
+//! * [`future::timeout`] to time-out futures if they don't resolve within a
+//!   set interval.
+//!
+//! [`task::sleep`]: task/fn.sleep.html
+//! [`stream::interval`]: stream/fn.interval.html
+//! [`future::timeout`]: future/fn.timeout.html
 //!
 //! # Examples
 //!
-//! Spawn a task and block the current thread on its result:
+//! All examples require the [`"attributes"` feature](#features) to be enabled.
+//! This feature is not enabled by default because it significantly impacts
+//! compile times. See [`task::block_on`] for an alternative way to start
+//! executing tasks.
+//!
+//! Call an async function from the main function:
+//!
+#![cfg_attr(feature = "attributes", doc = "```")]
+#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
+//! async fn say_hello() {
+//!     println!("Hello, world!");
+//! }
+//!
+//! #[async_std::main]
+//! async fn main() {
+//!     say_hello().await;
+//! }
+//! ```
+//!
+//! Await two futures concurrently, and return a tuple of their output:
 //!
+#![cfg_attr(feature = "attributes", doc = "```")]
+#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
+//! use async_std::prelude::*;
+//!
+//! #[async_std::main]
+//! async fn main() {
+//!     let a = async { 1u8 };
+//!     let b = async { 2u8 };
+//!     assert_eq!(a.join(b).await, (1u8, 2u8))
+//! }
 //! ```
-//! use async_std::task;
 //!
-//! fn main() {
-//!     task::block_on(async {
-//!         println!("Hello, world!");
-//!     })
+//! Create a UDP server that echoes back each received message to the sender:
+//!
+#![cfg_attr(feature = "attributes", doc = "```no_run")]
+#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
+//! use async_std::net::UdpSocket;
+//!
+//! #[async_std::main]
+//! async fn main() -> std::io::Result<()> {
+//!     let socket = UdpSocket::bind("127.0.0.1:8080").await?;
+//!     println!("Listening on {}", socket.local_addr()?);
+//!
+//!     let mut buf = vec![0u8; 1024];
+//!
+//!     loop {
+//!         let (recv, peer) = socket.recv_from(&mut buf).await?;
+//!         let sent = socket.send_to(&buf[..recv], &peer).await?;
+//!         println!("Sent {} out of {} bytes to {}", sent, recv, peer);
+//!     }
 //! }
 //! ```
+//! [`task::block_on`]: task/fn.block_on.html
 //!
 //! # Features
 //!
@@ -32,48 +192,147 @@
 //! <span
 //!   class="module-item stab portability"
 //!   style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"
-//! ><code>unstable</code></span>
+//! > <code>unstable</code> </span>
 //! are available only when the `unstable` Cargo feature is enabled:
 //!
 //! ```toml
 //! [dependencies.async-std]
-//! version = "0.99"
+//! version = "1.7.0"
 //! features = ["unstable"]
 //! ```
+//!
+//! Items marked with
+//! <span
+//!   class="module-item stab portability"
+//!   style="display: inline; border-radius: 3px; padding: 2px; font-size: 80%; line-height: 1.2;"
+//! > <code>attributes</code> </span>
+//! are available only when the `attributes` Cargo feature is enabled:
+//!
+//! ```toml
+//! [dependencies.async-std]
+//! version = "1.7.0"
+//! features = ["attributes"]
+//! ```
+//!
+//! Compatibility with the `tokio` 1.0 runtime is also simultaneously possible
+//! using the `tokio1` Cargo feature:
+//!
+//! ```toml
+//! [dependencies.async-std]
+//! version = "1.7.0"
+//! features = ["tokio1"]
+//! ```
+//!
+//! Compatibility with the `tokio` 0.2 runtime is possible using the `tokio02`
+//! Cargo feature:
+//!
+//! ```toml
+//! [dependencies.async-std]
+//! version = "1.7.0"
+//! features = ["tokio02"]
+//! ```
+//!
+//! Compatibility with the `tokio` 0.3 runtime is also simultaneously possible
+//! using the `tokio03` Cargo feature:
+//!
+//! ```toml
+//! [dependencies.async-std]
+//! version = "1.7.0"
+//! features = ["tokio03"]
+//! ```
+//!
+//! Additionally it's possible to only use the core traits and combinators by
+//! only enabling the `std` Cargo feature:
+//!
+//! ```toml
+//! [dependencies.async-std]
+//! version = "1.7.0"
+//! default-features = false
+//! features = ["std"]
+//! ```
+//!
+//! And to use async-std on `no_std` targets that only support `alloc` only
+//! enable the `alloc` Cargo feature:
+//!
+//! ```toml
+//! [dependencies.async-std]
+//! version = "1.7.0"
+//! default-features = false
+//! features = ["alloc"]
+//! ```
+//!
+//! # Runtime configuration
+//!
+//! Several environment variables are available to tune the async-std
+//! runtime:
+//!
+//! * `ASYNC_STD_THREAD_COUNT`: The number of threads that the
+//! async-std runtime will start. By default, this is one per logical
+//! cpu as determined by [async-global-executor](async_global_executor),
+//! which may be different than the number of physical cpus. Async-std
+//! _will panic_ if this is set to any value other than a positive
+//! integer.
+//! * `ASYNC_STD_THREAD_NAME`: The name that async-std's runtime
+//! threads report to the operating system. The default value is
+//! `"async-std/runtime"`.
+//!
 
+#![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(feature = "docs", feature(doc_cfg))]
 #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
-#![doc(test(attr(deny(rust_2018_idioms, warnings))))]
+#![allow(clippy::mutex_atomic, clippy::module_inception)]
+#![doc(test(attr(deny(rust_2018_idioms))))]
 #![doc(test(attr(allow(unused_extern_crates, unused_variables))))]
 #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")]
-#![recursion_limit = "1024"]
 
-use cfg_if::cfg_if;
+#[macro_use]
+mod utils;
 
-pub mod fs;
-pub mod future;
-pub mod io;
-pub mod net;
-pub mod os;
-pub mod prelude;
-pub mod stream;
-pub mod sync;
-pub mod task;
+#[cfg(feature = "attributes")]
+#[cfg_attr(feature = "docs", doc(cfg(attributes)))]
+#[doc(inline)]
+pub use async_attributes::{main, test};
 
-cfg_if! {
-    if #[cfg(any(feature = "unstable", feature = "docs"))] {
-        #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-        pub mod path;
-        #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-        pub mod pin;
+#[cfg(feature = "std")]
+mod macros;
 
-        mod unit;
-        mod vec;
-        mod result;
-        mod option;
-        mod string;
-        mod collections;
-    }
+cfg_alloc! {
+    pub mod task;
+    pub mod future;
+    pub mod stream;
 }
 
-pub(crate) mod utils;
+cfg_std! {
+    pub mod io;
+    pub mod os;
+    pub mod prelude;
+    pub mod sync;
+    pub mod channel;
+}
+
+cfg_default! {
+    #[cfg(not(target_os = "unknown"))]
+    pub mod fs;
+    pub mod path;
+    pub mod net;
+    #[cfg(not(target_os = "unknown"))]
+    pub(crate) mod rt;
+}
+
+cfg_unstable! {
+    pub mod pin;
+    #[cfg(all(not(target_os = "unknown"), feature = "std"))]
+    pub mod process;
+
+    mod unit;
+    mod vec;
+    mod result;
+    mod option;
+    mod string;
+    mod collections;
+}
+
+cfg_unstable_default! {
+    #[doc(inline)]
+    pub use std::{write, writeln};
+}
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 000000000..638a2348b
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,219 @@
+/// Prints to the standard output.
+///
+/// Equivalent to the [`println!`] macro except that a newline is not printed at
+/// the end of the message.
+///
+/// Note that stdout is frequently line-buffered by default so it may be
+/// necessary to use [`io::stdout().flush()`][flush] to ensure the output is emitted
+/// immediately.
+///
+/// Use `print!` only for the primary output of your program. Use
+/// [`eprint!`] instead to print error and progress messages.
+///
+/// [`println!`]: macro.println.html
+/// [flush]: io/trait.Write.html#tymethod.flush
+/// [`eprint!`]: macro.eprint.html
+///
+/// # Panics
+///
+/// Panics if writing to `io::stdout()` fails.
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::io;
+/// use async_std::prelude::*;
+/// use async_std::print;
+///
+/// print!("this ").await;
+/// print!("will ").await;
+/// print!("be ").await;
+/// print!("on ").await;
+/// print!("the ").await;
+/// print!("same ").await;
+/// print!("line ").await;
+///
+/// io::stdout().flush().await.unwrap();
+///
+/// print!("this string has a newline, why not choose println! instead?\n").await;
+///
+/// io::stdout().flush().await.unwrap();
+/// #
+/// # })
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[macro_export]
+macro_rules! print {
+    ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)))
+}
+
+/// Prints to the standard output, with a newline.
+///
+/// On all platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone
+/// (no additional CARRIAGE RETURN (`\r`/`U+000D`)).
+///
+/// Use the [`format!`] syntax to write data to the standard output.
+/// See [`std::fmt`] for more information.
+///
+/// Use `println!` only for the primary output of your program. Use
+/// [`eprintln!`] instead to print error and progress messages.
+///
+/// [`format!`]: macro.format.html
+/// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
+/// [`eprintln!`]: macro.eprintln.html
+/// # Panics
+///
+/// Panics if writing to `io::stdout` fails.
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::println;
+///
+/// println!().await; // prints just a newline
+/// println!("hello there!").await;
+/// println!("format {} arguments", "some").await;
+/// #
+/// # })
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[macro_export]
+macro_rules! println {
+    () => ($crate::print!("\n"));
+    ($($arg:tt)*) => (async {
+        $crate::io::_print(format_args!($($arg)*)).await;
+        $crate::io::_print(format_args!("\n")).await;
+    })
+}
+
+/// Prints to the standard error.
+///
+/// Equivalent to the [`print!`] macro, except that output goes to
+/// [`io::stderr`] instead of `io::stdout`. See [`print!`] for
+/// example usage.
+///
+/// Use `eprint!` only for error and progress messages. Use `print!`
+/// instead for the primary output of your program.
+///
+/// [`io::stderr`]: io/struct.Stderr.html
+/// [`print!`]: macro.print.html
+///
+/// # Panics
+///
+/// Panics if writing to `io::stderr` fails.
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::eprint;
+///
+/// eprint!("Error: Could not complete task").await;
+/// #
+/// # })
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[macro_export]
+macro_rules! eprint {
+    ($($arg:tt)*) => ($crate::io::_eprint(format_args!($($arg)*)))
+}
+
+/// Prints to the standard error, with a newline.
+///
+/// Equivalent to the [`println!`] macro, except that output goes to
+/// [`io::stderr`] instead of `io::stdout`. See [`println!`] for
+/// example usage.
+///
+/// Use `eprintln!` only for error and progress messages. Use `println!`
+/// instead for the primary output of your program.
+///
+/// [`io::stderr`]: io/struct.Stderr.html
+/// [`println!`]: macro.println.html
+///
+/// # Panics
+///
+/// Panics if writing to `io::stderr` fails.
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::eprintln;
+///
+/// eprintln!("Error: Could not complete task").await;
+/// #
+/// # })
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[macro_export]
+macro_rules! eprintln {
+    () => (async { $crate::eprint!("\n").await; });
+    ($($arg:tt)*) => (
+        async {
+            $crate::io::_eprint(format_args!($($arg)*)).await;
+            $crate::io::_eprint(format_args!("\n")).await;
+        }
+    );
+}
+
+/// Declares task-local values.
+///
+/// The macro wraps any number of static declarations and makes them task-local. Attributes and
+/// visibility modifiers are allowed.
+///
+/// Each declared value is of the accessor type [`LocalKey`].
+///
+/// [`LocalKey`]: task/struct.LocalKey.html
+///
+/// # Examples
+///
+/// ```
+/// #
+/// use std::cell::Cell;
+///
+/// use async_std::prelude::*;
+/// use async_std::task;
+///
+/// task_local! {
+///     static VAL: Cell<u32> = Cell::new(5);
+/// }
+///
+/// task::block_on(async {
+///     let v = VAL.with(|c| c.get());
+///     assert_eq!(v, 5);
+/// });
+/// ```
+#[cfg(feature = "default")]
+#[macro_export]
+macro_rules! task_local {
+    () => ();
+
+    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
+        $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = {
+            #[inline]
+            fn __init() -> $t {
+                $init
+            }
+
+            $crate::task::LocalKey {
+                __init,
+                __key: ::std::sync::atomic::AtomicU32::new(0),
+            }
+        };
+    );
+
+    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
+        $crate::task_local!($(#[$attr])* $vis static $name: $t = $init);
+        $crate::task_local!($($rest)*);
+    );
+}
diff --git a/src/net/addr.rs b/src/net/addr.rs
index 71f43a5c0..71988fb37 100644
--- a/src/net/addr.rs
+++ b/src/net/addr.rs
@@ -1,36 +1,47 @@
-use std::marker::PhantomData;
+use std::future::Future;
+use std::mem;
 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
 use std::pin::Pin;
 
-use cfg_if::cfg_if;
-
-use crate::future::Future;
 use crate::io;
-use crate::task::blocking;
-use crate::task::{Context, Poll};
+use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
+use crate::utils::Context as ErrorContext;
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        #[doc(hidden)]
-        pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>);
+cfg_not_docs! {
+    macro_rules! ret {
+        (impl Future<Output = $out:ty>, $fut:ty) => ($fut);
+    }
+}
 
-        macro_rules! ret {
-            ($a:lifetime, $f:tt, $i:ty) => (ImplFuture<$a, io::Result<$i>>);
-        }
-    } else {
-        macro_rules! ret {
-            ($a:lifetime, $f:tt, $i:ty) => ($f<$a, $i>);
-        }
+cfg_docs! {
+    #[doc(hidden)]
+    pub struct ImplFuture<T>(std::marker::PhantomData<T>);
+
+    macro_rules! ret {
+        (impl Future<Output = $out:ty>, $fut:ty) => (ImplFuture<$out>);
     }
 }
 
-/// A trait for objects which can be converted or resolved to one or more [`SocketAddr`] values.
+/// Converts or resolves addresses to [`SocketAddr`] values.
 ///
 /// This trait is an async version of [`std::net::ToSocketAddrs`].
 ///
 /// [`std::net::ToSocketAddrs`]: https://doc.rust-lang.org/std/net/trait.ToSocketAddrs.html
-/// [`SocketAddr`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html
+/// [`SocketAddr`]: enum.SocketAddr.html
+///
+/// # Examples
+///
+/// ```
+/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
+/// use async_std::net::ToSocketAddrs;
+///
+/// let addr = "localhost:8080".to_socket_addrs().await?.next().unwrap();
+/// println!("resolved: {:?}", addr);
+/// #
+/// # Ok(()) }) }
+/// ```
 pub trait ToSocketAddrs {
     /// Returned iterator over socket addresses which this type may correspond to.
     type Iter: Iterator<Item = SocketAddr>;
@@ -41,28 +52,54 @@ pub trait ToSocketAddrs {
     /// resolution performed.
     ///
     /// Note that this function may block a backend thread while resolution is performed.
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter);
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    );
 }
 
 #[doc(hidden)]
 #[allow(missing_debug_implementations)]
-pub enum ToSocketAddrsFuture<'a, I> {
-    Phantom(PhantomData<&'a ()>),
-    Join(blocking::JoinHandle<io::Result<I>>),
-    Ready(Option<io::Result<I>>),
+pub enum ToSocketAddrsFuture<I> {
+    Resolving(JoinHandle<io::Result<I>>),
+    Ready(io::Result<I>),
+    Done,
 }
 
-impl<I: Iterator<Item = SocketAddr>> Future for ToSocketAddrsFuture<'_, I> {
+// The field of `Self::Resolving` is `Unpin`, and the field of `Self::Ready` is never pinned.
+impl<I> Unpin for ToSocketAddrsFuture<I> {}
+
+/// Wrap `std::io::Error` with additional message
+///
+/// Keeps the original error kind and stores the original I/O error as `source`.
+impl<T> ErrorContext for ToSocketAddrsFuture<T> {
+    fn context(self, message: impl Fn() -> String) -> Self {
+        match self {
+            ToSocketAddrsFuture::Ready(res) => ToSocketAddrsFuture::Ready(res.context(message)),
+            x => x,
+        }
+    }
+}
+
+impl<I: Iterator<Item = SocketAddr>> Future for ToSocketAddrsFuture<I> {
     type Output = io::Result<I>;
 
     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match unsafe { self.get_unchecked_mut() } {
-            ToSocketAddrsFuture::Join(f) => Pin::new(&mut *f).poll(cx),
-            ToSocketAddrsFuture::Ready(res) => {
-                let res = res.take().expect("polled a completed future");
-                Poll::Ready(res)
+        let this = self.get_mut();
+        let state = mem::replace(this, ToSocketAddrsFuture::Done);
+
+        match state {
+            ToSocketAddrsFuture::Resolving(mut task) => {
+                let poll = Pin::new(&mut task).poll(cx);
+                if poll.is_pending() {
+                    *this = ToSocketAddrsFuture::Resolving(task);
+                }
+                poll
             }
-            _ => unreachable!(),
+            ToSocketAddrsFuture::Ready(res) => Poll::Ready(res),
+            ToSocketAddrsFuture::Done => panic!("polled a completed future"),
         }
     }
 }
@@ -70,87 +107,166 @@ impl<I: Iterator<Item = SocketAddr>> Future for ToSocketAddrsFuture<'_, I> {
 impl ToSocketAddrs for SocketAddr {
     type Iter = std::option::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        ToSocketAddrsFuture::Ready(Ok(Some(*self).into_iter()))
     }
 }
 
 impl ToSocketAddrs for SocketAddrV4 {
     type Iter = std::option::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        SocketAddr::V4(*self)
+            .to_socket_addrs()
+            .context(|| format!("could not resolve address `{}`", self))
     }
 }
 
 impl ToSocketAddrs for SocketAddrV6 {
     type Iter = std::option::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        SocketAddr::V6(*self)
+            .to_socket_addrs()
+            .context(|| format!("could not resolve address `{}`", self))
     }
 }
 
 impl ToSocketAddrs for (IpAddr, u16) {
     type Iter = std::option::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        let (ip, port) = *self;
+        match ip {
+            IpAddr::V4(a) => (a, port).to_socket_addrs(),
+            IpAddr::V6(a) => (a, port).to_socket_addrs(),
+        }
     }
 }
 
 impl ToSocketAddrs for (Ipv4Addr, u16) {
     type Iter = std::option::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        let (ip, port) = *self;
+        SocketAddrV4::new(ip, port).to_socket_addrs()
     }
 }
 
 impl ToSocketAddrs for (Ipv6Addr, u16) {
     type Iter = std::option::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        let (ip, port) = *self;
+        SocketAddrV6::new(ip, port, 0, 0).to_socket_addrs()
     }
 }
 
 impl ToSocketAddrs for (&str, u16) {
     type Iter = std::vec::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        let host = self.0.to_string();
-        let port = self.1;
-        let join = blocking::spawn(async move {
-            std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        let (host, port) = *self;
+
+        if let Ok(addr) = host.parse::<Ipv4Addr>() {
+            let addr = SocketAddrV4::new(addr, port);
+            return ToSocketAddrsFuture::Ready(Ok(vec![SocketAddr::V4(addr)].into_iter()));
+        }
+
+        if let Ok(addr) = host.parse::<Ipv6Addr>() {
+            let addr = SocketAddrV6::new(addr, port, 0, 0);
+            return ToSocketAddrsFuture::Ready(Ok(vec![SocketAddr::V6(addr)].into_iter()));
+        }
+
+        let host = host.to_string();
+        let task = spawn_blocking(move || {
+            let addr = (host.as_str(), port);
+            std::net::ToSocketAddrs::to_socket_addrs(&addr)
+                .context(|| format!("could not resolve address `{:?}`", addr))
         });
-        ToSocketAddrsFuture::Join(join)
+        ToSocketAddrsFuture::Resolving(task)
     }
 }
 
 impl ToSocketAddrs for str {
     type Iter = std::vec::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        let socket_addrs = self.to_string();
-        let join =
-            blocking::spawn(async move { std::net::ToSocketAddrs::to_socket_addrs(&socket_addrs) });
-        ToSocketAddrsFuture::Join(join)
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        if let Ok(addr) = self.parse() {
+            return ToSocketAddrsFuture::Ready(Ok(vec![addr].into_iter()));
+        }
+
+        let addr = self.to_string();
+        let task = spawn_blocking(move || {
+            std::net::ToSocketAddrs::to_socket_addrs(addr.as_str())
+                .context(|| format!("could not resolve address `{:?}`", addr))
+        });
+        ToSocketAddrsFuture::Resolving(task)
     }
 }
 
 impl<'a> ToSocketAddrs for &'a [SocketAddr] {
     type Iter = std::iter::Cloned<std::slice::Iter<'a, SocketAddr>>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self)))
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        ToSocketAddrsFuture::Ready(Ok(self.iter().cloned()))
     }
 }
 
 impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {
     type Iter = T::Iter;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
         (**self).to_socket_addrs()
     }
 }
@@ -158,7 +274,12 @@ impl<T: ToSocketAddrs + ?Sized> ToSocketAddrs for &T {
 impl ToSocketAddrs for String {
     type Iter = std::vec::IntoIter<SocketAddr>;
 
-    fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) {
-        ToSocketAddrs::to_socket_addrs(self.as_str())
+    fn to_socket_addrs(
+        &self,
+    ) -> ret!(
+        impl Future<Output = Self::Iter>,
+        ToSocketAddrsFuture<Self::Iter>
+    ) {
+        (&**self).to_socket_addrs()
     }
 }
diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs
deleted file mode 100644
index 806acdbe4..000000000
--- a/src/net/driver/mod.rs
+++ /dev/null
@@ -1,317 +0,0 @@
-use std::fmt;
-use std::sync::{Arc, Mutex};
-
-use lazy_static::lazy_static;
-use mio::{self, Evented};
-use slab::Slab;
-
-use crate::io;
-use crate::task::{Context, Poll, Waker};
-use crate::utils::abort_on_panic;
-
-/// Data associated with a registered I/O handle.
-#[derive(Debug)]
-struct Entry {
-    /// A unique identifier.
-    token: mio::Token,
-
-    /// Tasks that are blocked on reading from this I/O handle.
-    readers: Mutex<Vec<Waker>>,
-
-    /// Thasks that are blocked on writing to this I/O handle.
-    writers: Mutex<Vec<Waker>>,
-}
-
-/// The state of a networking driver.
-struct Reactor {
-    /// A mio instance that polls for new events.
-    poller: mio::Poll,
-
-    /// A collection of registered I/O handles.
-    entries: Mutex<Slab<Arc<Entry>>>,
-
-    /// Dummy I/O handle that is only used to wake up the polling thread.
-    notify_reg: (mio::Registration, mio::SetReadiness),
-
-    /// An identifier for the notification handle.
-    notify_token: mio::Token,
-}
-
-impl Reactor {
-    /// Creates a new reactor for polling I/O events.
-    fn new() -> io::Result<Reactor> {
-        let poller = mio::Poll::new()?;
-        let notify_reg = mio::Registration::new2();
-
-        let mut reactor = Reactor {
-            poller,
-            entries: Mutex::new(Slab::new()),
-            notify_reg,
-            notify_token: mio::Token(0),
-        };
-
-        // Register a dummy I/O handle for waking up the polling thread.
-        let entry = reactor.register(&reactor.notify_reg.0)?;
-        reactor.notify_token = entry.token;
-
-        Ok(reactor)
-    }
-
-    /// Registers an I/O event source and returns its associated entry.
-    fn register(&self, source: &dyn Evented) -> io::Result<Arc<Entry>> {
-        let mut entries = self.entries.lock().unwrap();
-
-        // Reserve a vacant spot in the slab and use its key as the token value.
-        let vacant = entries.vacant_entry();
-        let token = mio::Token(vacant.key());
-
-        // Allocate an entry and insert it into the slab.
-        let entry = Arc::new(Entry {
-            token,
-            readers: Mutex::new(Vec::new()),
-            writers: Mutex::new(Vec::new()),
-        });
-        vacant.insert(entry.clone());
-
-        // Register the I/O event source in the poller.
-        let interest = mio::Ready::all();
-        let opts = mio::PollOpt::edge();
-        self.poller.register(source, token, interest, opts)?;
-
-        Ok(entry)
-    }
-
-    /// Deregisters an I/O event source associated with an entry.
-    fn deregister(&self, source: &dyn Evented, entry: &Entry) -> io::Result<()> {
-        // Deregister the I/O object from the mio instance.
-        self.poller.deregister(source)?;
-
-        // Remove the entry associated with the I/O object.
-        self.entries.lock().unwrap().remove(entry.token.0);
-
-        Ok(())
-    }
-
-    // fn notify(&self) {
-    //     self.notify_reg
-    //         .1
-    //         .set_readiness(mio::Ready::readable())
-    //         .unwrap();
-    // }
-}
-
-lazy_static! {
-    /// The state of the global networking driver.
-    static ref REACTOR: Reactor = {
-        // Spawn a thread that waits on the poller for new events and wakes up tasks blocked on I/O
-        // handles.
-        std::thread::Builder::new()
-            .name("async-net-driver".to_string())
-            .spawn(move || {
-                // If the driver thread panics, there's not much we can do. It is not a
-                // recoverable error and there is no place to propagate it into so we just abort.
-                abort_on_panic(|| {
-                    main_loop().expect("async networking thread has panicked");
-                })
-            })
-            .expect("cannot start a thread driving blocking tasks");
-
-        Reactor::new().expect("cannot initialize reactor")
-    };
-}
-
-/// Waits on the poller for new events and wakes up tasks blocked on I/O handles.
-fn main_loop() -> io::Result<()> {
-    let reactor = &REACTOR;
-    let mut events = mio::Events::with_capacity(1000);
-
-    loop {
-        // Block on the poller until at least one new event comes in.
-        reactor.poller.poll(&mut events, None)?;
-
-        // Lock the entire entry table while we're processing new events.
-        let entries = reactor.entries.lock().unwrap();
-
-        for event in events.iter() {
-            let token = event.token();
-
-            if token == reactor.notify_token {
-                // If this is the notification token, we just need the notification state.
-                reactor.notify_reg.1.set_readiness(mio::Ready::empty())?;
-            } else {
-                // Otherwise, look for the entry associated with this token.
-                if let Some(entry) = entries.get(token.0) {
-                    // Set the readiness flags from this I/O event.
-                    let readiness = event.readiness();
-
-                    // Wake up reader tasks blocked on this I/O handle.
-                    if !(readiness & reader_interests()).is_empty() {
-                        for w in entry.readers.lock().unwrap().drain(..) {
-                            w.wake();
-                        }
-                    }
-
-                    // Wake up writer tasks blocked on this I/O handle.
-                    if !(readiness & writer_interests()).is_empty() {
-                        for w in entry.writers.lock().unwrap().drain(..) {
-                            w.wake();
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-/// An I/O handle powered by the networking driver.
-///
-/// This handle wraps an I/O event source and exposes a "futurized" interface on top of it,
-/// implementing traits `AsyncRead` and `AsyncWrite`.
-pub struct Watcher<T: Evented> {
-    /// Data associated with the I/O handle.
-    entry: Arc<Entry>,
-
-    /// The I/O event source.
-    source: Option<T>,
-}
-
-impl<T: Evented> Watcher<T> {
-    /// Creates a new I/O handle.
-    ///
-    /// The provided I/O event source will be kept registered inside the reactor's poller for the
-    /// lifetime of the returned I/O handle.
-    pub fn new(source: T) -> Watcher<T> {
-        Watcher {
-            entry: REACTOR
-                .register(&source)
-                .expect("cannot register an I/O event source"),
-            source: Some(source),
-        }
-    }
-
-    /// Returns a reference to the inner I/O event source.
-    pub fn get_ref(&self) -> &T {
-        self.source.as_ref().unwrap()
-    }
-
-    /// Polls the inner I/O source for a non-blocking read operation.
-    ///
-    /// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task
-    /// will be registered for wakeup when the I/O source becomes readable.
-    pub fn poll_read_with<'a, F, R>(&'a self, cx: &mut Context<'_>, mut f: F) -> Poll<io::Result<R>>
-    where
-        F: FnMut(&'a T) -> io::Result<R>,
-    {
-        // If the operation isn't blocked, return its result.
-        match f(self.source.as_ref().unwrap()) {
-            Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
-            res => return Poll::Ready(res),
-        }
-
-        // Lock the waker list.
-        let mut list = self.entry.readers.lock().unwrap();
-
-        // Try running the operation again.
-        match f(self.source.as_ref().unwrap()) {
-            Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
-            res => return Poll::Ready(res),
-        }
-
-        // Register the task if it isn't registered already.
-        if list.iter().all(|w| !w.will_wake(cx.waker())) {
-            list.push(cx.waker().clone());
-        }
-
-        Poll::Pending
-    }
-
-    /// Polls the inner I/O source for a non-blocking write operation.
-    ///
-    /// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task
-    /// will be registered for wakeup when the I/O source becomes writable.
-    pub fn poll_write_with<'a, F, R>(
-        &'a self,
-        cx: &mut Context<'_>,
-        mut f: F,
-    ) -> Poll<io::Result<R>>
-    where
-        F: FnMut(&'a T) -> io::Result<R>,
-    {
-        // If the operation isn't blocked, return its result.
-        match f(self.source.as_ref().unwrap()) {
-            Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
-            res => return Poll::Ready(res),
-        }
-
-        // Lock the waker list.
-        let mut list = self.entry.writers.lock().unwrap();
-
-        // Try running the operation again.
-        match f(self.source.as_ref().unwrap()) {
-            Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
-            res => return Poll::Ready(res),
-        }
-
-        // Register the task if it isn't registered already.
-        if list.iter().all(|w| !w.will_wake(cx.waker())) {
-            list.push(cx.waker().clone());
-        }
-
-        Poll::Pending
-    }
-
-    /// Deregisters and returns the inner I/O source.
-    ///
-    /// This method is typically used to convert `Watcher`s to raw file descriptors/handles.
-    #[allow(dead_code)]
-    pub fn into_inner(mut self) -> T {
-        let source = self.source.take().unwrap();
-        REACTOR
-            .deregister(&source, &self.entry)
-            .expect("cannot deregister I/O event source");
-        source
-    }
-}
-
-impl<T: Evented> Drop for Watcher<T> {
-    fn drop(&mut self) {
-        if let Some(ref source) = self.source {
-            REACTOR
-                .deregister(source, &self.entry)
-                .expect("cannot deregister I/O event source");
-        }
-    }
-}
-
-impl<T: Evented + fmt::Debug> fmt::Debug for Watcher<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Watcher")
-            .field("entry", &self.entry)
-            .field("source", &self.source)
-            .finish()
-    }
-}
-
-/// Returns a mask containing flags that interest tasks reading from I/O handles.
-#[inline]
-fn reader_interests() -> mio::Ready {
-    mio::Ready::all() - mio::Ready::writable()
-}
-
-/// Returns a mask containing flags that interest tasks writing into I/O handles.
-#[inline]
-fn writer_interests() -> mio::Ready {
-    mio::Ready::writable() | hup()
-}
-
-/// Returns a flag containing the hangup status.
-#[inline]
-fn hup() -> mio::Ready {
-    #[cfg(unix)]
-    let ready = mio::unix::UnixReady::hup().into();
-
-    #[cfg(not(unix))]
-    let ready = mio::Ready::empty();
-
-    ready
-}
diff --git a/src/net/mod.rs b/src/net/mod.rs
index 259dc1de5..181407357 100644
--- a/src/net/mod.rs
+++ b/src/net/mod.rs
@@ -1,14 +1,42 @@
 //! Networking primitives for TCP/UDP communication.
 //!
-//! For OS-specific networking primitives like Unix domain sockets, refer to the [`async_std::os`]
-//! module.
+//! This module provides networking functionality for the Transmission Control and User
+//! Datagram Protocols, as well as types for IP and socket addresses.
 //!
 //! This module is an async version of [`std::net`].
 //!
+//! # Organization
+//!
+//! * [`TcpListener`] and [`TcpStream`] provide functionality for communication over TCP
+//! * [`UdpSocket`] provides functionality for communication over UDP
+//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and
+//!   [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses
+//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`]
+//!   and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses
+//! * [`ToSocketAddrs`] is a trait that used for generic address resolution when interacting
+//!   with networking objects like [`TcpListener`], [`TcpStream`] or [`UdpSocket`]
+//! * Other types are return or parameter types for various methods in this module
+//!
+//! [`IpAddr`]: enum.IpAddr.html
+//! [`Ipv4Addr`]: struct.Ipv4Addr.html
+//! [`Ipv6Addr`]: struct.Ipv6Addr.html
+//! [`SocketAddr`]: enum.SocketAddr.html
+//! [`SocketAddrV4`]: struct.SocketAddrV4.html
+//! [`SocketAddrV6`]: struct.SocketAddrV6.html
+//! [`TcpListener`]: struct.TcpListener.html
+//! [`TcpStream`]: struct.TcpStream.html
+//! [`ToSocketAddrs`]: trait.ToSocketAddrs.html
+//! [`UdpSocket`]: struct.UdpSocket.html
+//!
+//! # Platform-specific extensions
+//!
+//! APIs such as Unix domain sockets are available on certain platforms only. You can find
+//! platform-specific extensions in the [`async_std::os`] module.
+//!
 //! [`async_std::os`]: ../os/index.html
 //! [`std::net`]: https://doc.rust-lang.org/std/net/index.html
 //!
-//! ## Examples
+//! # Examples
 //!
 //! A simple UDP echo server:
 //!
@@ -28,11 +56,21 @@
 //! # }) }
 //! ```
 
+pub use std::net::AddrParseError;
+pub use std::net::Shutdown;
+pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
+
+#[cfg(not(target_os = "unknown"))]
 pub use addr::ToSocketAddrs;
+#[cfg(not(target_os = "unknown"))]
 pub use tcp::{Incoming, TcpListener, TcpStream};
+#[cfg(not(target_os = "unknown"))]
 pub use udp::UdpSocket;
 
+#[cfg(not(target_os = "unknown"))]
 mod addr;
-pub(crate) mod driver;
+#[cfg(not(target_os = "unknown"))]
 mod tcp;
+#[cfg(not(target_os = "unknown"))]
 mod udp;
diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs
index 26e19d79f..d014f3387 100644
--- a/src/net/tcp/listener.rs
+++ b/src/net/tcp/listener.rs
@@ -1,15 +1,15 @@
+use std::fmt;
 use std::net::SocketAddr;
+use std::net::TcpStream as StdTcpStream;
 use std::pin::Pin;
 
-use cfg_if::cfg_if;
+use async_io::Async;
 
-use super::TcpStream;
-use crate::future::{self, Future};
 use crate::io;
-use crate::net::driver::Watcher;
-use crate::net::ToSocketAddrs;
+use crate::net::{TcpStream, ToSocketAddrs};
 use crate::stream::Stream;
-use crate::task::{Context, Poll};
+use crate::sync::Arc;
+use crate::task::{ready, Context, Poll};
 
 /// A TCP socket server, listening for connections.
 ///
@@ -50,7 +50,7 @@ use crate::task::{Context, Poll};
 /// ```
 #[derive(Debug)]
 pub struct TcpListener {
-    watcher: Watcher<mio::net::TcpListener>,
+    watcher: Async<std::net::TcpListener>,
 }
 
 impl TcpListener {
@@ -77,13 +77,12 @@ impl TcpListener {
     /// [`local_addr`]: #method.local_addr
     pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpListener> {
         let mut last_err = None;
+        let addrs = addrs.to_socket_addrs().await?;
 
-        for addr in addrs.to_socket_addrs().await? {
-            match mio::net::TcpListener::bind(&addr) {
-                Ok(mio_listener) => {
-                    return Ok(TcpListener {
-                        watcher: Watcher::new(mio_listener),
-                    });
+        for addr in addrs {
+            match Async::<std::net::TcpListener>::bind(addr) {
+                Ok(listener) => {
+                    return Ok(TcpListener { watcher: listener });
                 }
                 Err(err) => last_err = Some(err),
             }
@@ -114,13 +113,9 @@ impl TcpListener {
     /// # Ok(()) }) }
     /// ```
     pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
-        let (io, addr) =
-            future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.accept_std()))
-                .await?;
-
-        let mio_stream = mio::net::TcpStream::from_stream(io)?;
+        let (stream, addr) = self.watcher.accept().await?;
         let stream = TcpStream {
-            watcher: Watcher::new(mio_stream),
+            watcher: Arc::new(stream),
         };
         Ok((stream, addr))
     }
@@ -152,7 +147,48 @@ impl TcpListener {
     /// # Ok(()) }) }
     /// ```
     pub fn incoming(&self) -> Incoming<'_> {
-        Incoming(self)
+        Incoming {
+            incoming: Box::pin(self.watcher.incoming()),
+        }
+    }    
+
+    /// Turn this into a stream over the connections being received on this
+    /// listener.
+    ///
+    /// The returned stream is infinite and will also not yield
+    /// the peer's [`SocketAddr`] structure. Iterating over it is equivalent to
+    /// calling [`TcpListener::accept`] in a loop.
+    ///
+    /// ## Examples
+    ///
+    /// Merge the incoming connections of multiple sockets into one [`Stream`]:
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::net::TcpListener;
+    ///
+    /// // Our server listens on multiple ports for some reason
+    /// let listeners = vec![
+    ///     TcpListener::bind("[::0]:8080").await?,
+    ///     TcpListener::bind("[::0]:12345").await?,
+    ///     TcpListener::bind("[::0]:5678").await?,
+    /// ];
+    /// // Iterate over all incoming connections
+    /// let incoming = futures::stream::select_all(
+    ///     listeners.into_iter()
+    ///         .map(TcpListener::into_incoming)
+    ///         .map(Box::pin)
+    /// );
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    #[cfg(feature = "unstable")]
+    pub fn into_incoming(self) -> impl Stream<Item = io::Result<TcpStream>> + Send {
+        futures_lite::stream::unfold(self, |listener| async move {
+            let res = listener.accept().await.map(|(stream, _)| stream);
+            Some((res, listener))
+        })
     }
 
     /// Returns the local address that this listener is bound to.
@@ -188,84 +224,130 @@ impl TcpListener {
 /// [`incoming`]: struct.TcpListener.html#method.incoming
 /// [`TcpListener`]: struct.TcpListener.html
 /// [`std::net::Incoming`]: https://doc.rust-lang.org/std/net/struct.Incoming.html
-#[derive(Debug)]
-pub struct Incoming<'a>(&'a TcpListener);
+pub struct Incoming<'a> {
+    incoming: Pin<Box<dyn Stream<Item = io::Result<Async<StdTcpStream>>> + Send + Sync + 'a>>,
+}
 
-impl<'a> Stream for Incoming<'a> {
+impl Stream for Incoming<'_> {
     type Item = io::Result<TcpStream>;
 
-    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let future = self.0.accept();
-        pin_utils::pin_mut!(future);
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let res = ready!(Pin::new(&mut self.incoming).poll_next(cx));
+        Poll::Ready(res.map(|res| res.map(|stream| TcpStream { watcher: Arc::new(stream) })))
+    }
+}
 
-        let (socket, _) = futures_core::ready!(future.poll(cx))?;
-        Poll::Ready(Some(Ok(socket)))
+impl fmt::Debug for Incoming<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Incoming {{ ... }}")
     }
 }
 
 impl From<std::net::TcpListener> for TcpListener {
     /// Converts a `std::net::TcpListener` into its asynchronous equivalent.
     fn from(listener: std::net::TcpListener) -> TcpListener {
-        let mio_listener = mio::net::TcpListener::from_std(listener).unwrap();
         TcpListener {
-            watcher: Watcher::new(mio_listener),
+            watcher: Async::new(listener).expect("TcpListener is known to be good"),
         }
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-        // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+impl std::convert::TryFrom<TcpListener> for std::net::TcpListener {
+    type Error = io::Error;
+    /// Converts a `TcpListener` into its synchronous equivalent.
+    fn try_from(listener: TcpListener) -> io::Result<std::net::TcpListener> {
+        let inner = listener.watcher.into_inner()?;
+        inner.set_nonblocking(false)?;
+        Ok(inner)
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for TcpListener {
-            fn as_raw_fd(&self) -> RawFd {
-                self.watcher.get_ref().as_raw_fd()
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+
+    impl AsRawFd for TcpListener {
+        fn as_raw_fd(&self) -> RawFd {
+            self.watcher.get_ref().as_raw_fd()
+        }
+    }
+
+    impl FromRawFd for TcpListener {
+        unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
+            std::net::TcpListener::from_raw_fd(fd).into()
+        }
+    }
+
+    impl IntoRawFd for TcpListener {
+        fn into_raw_fd(self) -> RawFd {
+            self.watcher.into_inner().unwrap().into_raw_fd()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+        impl AsFd for TcpListener {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                self.watcher.get_ref().as_fd()
             }
         }
 
-        impl FromRawFd for TcpListener {
-            unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
-                std::net::TcpListener::from_raw_fd(fd).into()
+        impl From<OwnedFd> for TcpListener {
+            fn from(fd: OwnedFd) -> TcpListener {
+                std::net::TcpListener::from(fd).into()
             }
         }
 
-        impl IntoRawFd for TcpListener {
-            fn into_raw_fd(self) -> RawFd {
-                self.watcher.into_inner().into_raw_fd()
+        impl From<TcpListener> for OwnedFd {
+            fn from(listener: TcpListener) -> OwnedFd {
+                listener.watcher.into_inner().unwrap().into()
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(windows)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        // impl AsRawSocket for TcpListener {
-        //     fn as_raw_socket(&self) -> RawSocket {
-        //         self.raw_socket
-        //     }
-        // }
-        //
-        // impl FromRawSocket for TcpListener {
-        //     unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
-        //         net::TcpListener::from_raw_socket(handle).try_into().unwrap()
-        //     }
-        // }
-        //
-        // impl IntoRawSocket for TcpListener {
-        //     fn into_raw_socket(self) -> RawSocket {
-        //         self.raw_socket
-        //     }
-        // }
+cfg_windows! {
+    use crate::os::windows::io::{
+        AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket,
+    };
+
+    impl AsRawSocket for TcpListener {
+        fn as_raw_socket(&self) -> RawSocket {
+            self.watcher.as_raw_socket()
+        }
+    }
+
+    impl FromRawSocket for TcpListener {
+        unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
+            std::net::TcpListener::from_raw_socket(handle).into()
+        }
+    }
+
+    impl IntoRawSocket for TcpListener {
+        fn into_raw_socket(self) -> RawSocket {
+            self.watcher.into_inner().unwrap().into_raw_socket()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket};
+
+        impl AsSocket for TcpListener {
+            fn as_socket(&self) -> BorrowedSocket<'_> {
+                self.watcher.get_ref().as_socket()
+            }
+        }
+
+        impl From<OwnedSocket> for TcpListener {
+            fn from(fd: OwnedSocket) -> TcpListener {
+                std::net::TcpListener::from(fd).into()
+            }
+        }
+
+        impl From<TcpListener> for OwnedSocket {
+            fn from(listener: TcpListener) -> OwnedSocket {
+                listener.watcher.into_inner().unwrap().into()
+            }
+        }
     }
 }
diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs
index 10569434e..3311f904c 100644
--- a/src/net/tcp/stream.rs
+++ b/src/net/tcp/stream.rs
@@ -1,14 +1,12 @@
-use std::io::{IoSlice, IoSliceMut, Read as _, Write as _};
+use std::io::{IoSlice, IoSliceMut};
 use std::net::SocketAddr;
 use std::pin::Pin;
 
-use cfg_if::cfg_if;
+use async_io::Async;
 
-use crate::future;
 use crate::io::{self, Read, Write};
-use crate::net::driver::Watcher;
 use crate::net::ToSocketAddrs;
-use crate::task::blocking;
+use crate::sync::Arc;
 use crate::task::{Context, Poll};
 
 /// A TCP stream between a local and a remote socket.
@@ -25,9 +23,9 @@ use crate::task::{Context, Poll};
 /// [`connect`]: struct.TcpStream.html#method.connect
 /// [accepting]: struct.TcpListener.html#method.accept
 /// [listener]: struct.TcpListener.html
-/// [`AsyncRead`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html
-/// [`AsyncWrite`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html
-/// [`futures::io`]: https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/index.html
+/// [`AsyncRead`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncRead.html
+/// [`AsyncWrite`]: https://docs.rs/futures/0.3/futures/io/trait.AsyncWrite.html
+/// [`futures::io`]: https://docs.rs/futures/0.3/futures/io/index.html
 /// [`shutdown`]: struct.TcpStream.html#method.shutdown
 /// [`std::net::TcpStream`]: https://doc.rust-lang.org/std/net/struct.TcpStream.html
 ///
@@ -47,9 +45,9 @@ use crate::task::{Context, Poll};
 /// #
 /// # Ok(()) }) }
 /// ```
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct TcpStream {
-    pub(super) watcher: Watcher<mio::net::TcpStream>,
+    pub(super) watcher: Arc<Async<std::net::TcpStream>>,
 }
 
 impl TcpStream {
@@ -74,20 +72,19 @@ impl TcpStream {
     /// ```
     pub async fn connect<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpStream> {
         let mut last_err = None;
+        let addrs = addrs.to_socket_addrs().await?;
 
-        for addr in addrs.to_socket_addrs().await? {
-            let res = blocking::spawn(async move {
-                let std_stream = std::net::TcpStream::connect(addr)?;
-                let mio_stream = mio::net::TcpStream::from_stream(std_stream)?;
-                Ok(TcpStream {
-                    watcher: Watcher::new(mio_stream),
-                })
-            })
-            .await;
-
-            match res {
-                Ok(stream) => return Ok(stream),
-                Err(err) => last_err = Some(err),
+        for addr in addrs {
+            match Async::<std::net::TcpStream>::connect(addr).await {
+                Ok(stream) => {
+                    return Ok(TcpStream {
+                        watcher: Arc::new(stream),
+                    });
+                }
+                Err(e) => {
+                    last_err = Some(e);
+                    continue;
+                }
             }
         }
 
@@ -205,7 +202,7 @@ impl TcpStream {
     /// # Ok(()) }) }
     /// ```
     pub async fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
-        future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.peek(buf))).await
+        self.watcher.peek(buf).await
     }
 
     /// Gets the value of the `TCP_NODELAY` option on this socket.
@@ -308,7 +305,15 @@ impl Read for &TcpStream {
         cx: &mut Context<'_>,
         buf: &mut [u8],
     ) -> Poll<io::Result<usize>> {
-        self.watcher.poll_read_with(cx, |mut inner| inner.read(buf))
+        Pin::new(&mut &*self.watcher).poll_read(cx, buf)
+    }
+
+    fn poll_read_vectored(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        bufs: &mut [IoSliceMut<'_>],
+    ) -> Poll<io::Result<usize>> {
+        Pin::new(&mut &*self.watcher).poll_read_vectored(cx, bufs)
     }
 }
 
@@ -344,82 +349,142 @@ impl Write for &TcpStream {
         cx: &mut Context<'_>,
         buf: &[u8],
     ) -> Poll<io::Result<usize>> {
-        self.watcher
-            .poll_write_with(cx, |mut inner| inner.write(buf))
+        Pin::new(&mut &*self.watcher).poll_write(cx, buf)
+    }
+
+    fn poll_write_vectored(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        bufs: &[IoSlice<'_>],
+    ) -> Poll<io::Result<usize>> {
+        Pin::new(&mut &*self.watcher).poll_write_vectored(cx, bufs)
     }
 
     fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-        self.watcher.poll_write_with(cx, |mut inner| inner.flush())
+        Pin::new(&mut &*self.watcher).poll_flush(cx)
     }
 
-    fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
-        Poll::Ready(Ok(()))
+    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+        Pin::new(&mut &*self.watcher).poll_close(cx)
     }
 }
 
 impl From<std::net::TcpStream> for TcpStream {
     /// Converts a `std::net::TcpStream` into its asynchronous equivalent.
     fn from(stream: std::net::TcpStream) -> TcpStream {
-        let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap();
         TcpStream {
-            watcher: Watcher::new(mio_stream),
+            watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")),
         }
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-        // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+impl std::convert::TryFrom<TcpStream> for std::net::TcpStream {
+    type Error = io::Error;
+    /// Converts a `TcpStream` into its synchronous equivalent.
+    fn try_from(stream: TcpStream) -> io::Result<std::net::TcpStream> {
+        let inner = Arc::try_unwrap(stream.watcher)
+            .map_err(|_| io::Error::new(
+                io::ErrorKind::Other,
+                "Cannot convert TcpStream to synchronous: multiple references",
+            ))?
+            .into_inner()?;
+        inner.set_nonblocking(false)?;
+        Ok(inner)
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for TcpStream {
-            fn as_raw_fd(&self) -> RawFd {
-                self.watcher.get_ref().as_raw_fd()
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+
+    impl AsRawFd for TcpStream {
+        fn as_raw_fd(&self) -> RawFd {
+            self.watcher.get_ref().as_raw_fd()
+        }
+    }
+
+    impl FromRawFd for TcpStream {
+        unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
+            std::net::TcpStream::from_raw_fd(fd).into()
+        }
+    }
+
+    impl IntoRawFd for TcpStream {
+        fn into_raw_fd(self) -> RawFd {
+            // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file
+            // descriptor because it's possible that there are other clones of this `TcpStream`
+            // using it at the same time. We should probably document that behavior.
+            self.as_raw_fd()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+        impl AsFd for TcpStream {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                self.watcher.get_ref().as_fd()
             }
         }
 
-        impl FromRawFd for TcpStream {
-            unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
-                std::net::TcpStream::from_raw_fd(fd).into()
+        impl From<OwnedFd> for TcpStream {
+            fn from(fd: OwnedFd) -> TcpStream {
+                std::net::TcpStream::from(fd).into()
             }
         }
 
-        impl IntoRawFd for TcpStream {
-            fn into_raw_fd(self) -> RawFd {
-                self.watcher.into_inner().into_raw_fd()
+        impl From<TcpStream> for OwnedFd {
+            fn from(stream: TcpStream) -> OwnedFd {
+                stream.watcher.get_ref().try_clone().unwrap().into()
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(windows)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        // impl AsRawSocket for TcpStream {
-        //     fn as_raw_socket(&self) -> RawSocket {
-        //         self.raw_socket
-        //     }
-        // }
-        //
-        // impl FromRawSocket for TcpStream {
-        //     unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
-        //         net::TcpStream::from_raw_socket(handle).try_into().unwrap()
-        //     }
-        // }
-        //
-        // impl IntoRawSocket for TcpListener {
-        //     fn into_raw_socket(self) -> RawSocket {
-        //         self.raw_socket
-        //     }
-        // }
+cfg_windows! {
+    use crate::os::windows::io::{
+        RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket
+    };
+
+    impl AsRawSocket for TcpStream {
+        fn as_raw_socket(&self) -> RawSocket {
+            self.watcher.get_ref().as_raw_socket()
+        }
+    }
+
+    impl FromRawSocket for TcpStream {
+        unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
+            std::net::TcpStream::from_raw_socket(handle).into()
+        }
+    }
+
+    impl IntoRawSocket for TcpStream {
+        fn into_raw_socket(self) -> RawSocket {
+            // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file
+            // descriptor because it's possible that there are other clones of this `TcpStream`
+            // using it at the same time. We should probably document that behavior.
+            self.as_raw_socket()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket};
+
+        impl AsSocket for TcpStream {
+            fn as_socket(&self) -> BorrowedSocket<'_> {
+                self.watcher.get_ref().as_socket()
+            }
+        }
+
+        impl From<OwnedSocket> for TcpStream {
+            fn from(fd: OwnedSocket) -> TcpStream {
+                std::net::TcpStream::from(fd).into()
+            }
+        }
+
+        impl From<TcpStream> for OwnedSocket {
+            fn from(stream: TcpStream) -> OwnedSocket {
+                stream.watcher.get_ref().try_clone().unwrap().into()
+            }
+        }
     }
 }
diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs
index a750899d4..3bb2c6e9c 100644
--- a/src/net/udp/mod.rs
+++ b/src/net/udp/mod.rs
@@ -1,12 +1,11 @@
 use std::io;
 use std::net::SocketAddr;
-
-use cfg_if::cfg_if;
 use std::net::{Ipv4Addr, Ipv6Addr};
 
-use crate::future;
-use crate::net::driver::Watcher;
+use async_io::Async;
+
 use crate::net::ToSocketAddrs;
+use crate::utils::Context as _;
 
 /// A UDP socket.
 ///
@@ -46,7 +45,7 @@ use crate::net::ToSocketAddrs;
 /// ```
 #[derive(Debug)]
 pub struct UdpSocket {
-    watcher: Watcher<mio::net::UdpSocket>,
+    watcher: Async<std::net::UdpSocket>,
 }
 
 impl UdpSocket {
@@ -68,15 +67,14 @@ impl UdpSocket {
     /// #
     /// # Ok(()) }) }
     /// ```
-    pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> {
+    pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<UdpSocket> {
         let mut last_err = None;
+        let addrs = addrs.to_socket_addrs().await?;
 
-        for addr in addr.to_socket_addrs().await? {
-            match mio::net::UdpSocket::bind(&addr) {
-                Ok(mio_socket) => {
-                    return Ok(UdpSocket {
-                        watcher: Watcher::new(mio_socket),
-                    });
+        for addr in addrs {
+            match Async::<std::net::UdpSocket>::bind(addr) {
+                Ok(socket) => {
+                    return Ok(UdpSocket { watcher: socket });
                 }
                 Err(err) => last_err = Some(err),
             }
@@ -90,6 +88,32 @@ impl UdpSocket {
         }))
     }
 
+    /// Returns the peer address that this listener is connected to.
+    ///
+    /// This can be useful, for example, when connect to port 0 to figure out which port was
+    /// actually connected.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::net::UdpSocket;
+    ///
+    /// let socket1 = UdpSocket::bind("127.0.0.1:0").await?;
+    /// let socket2 = UdpSocket::bind("127.0.0.1:0").await?;
+    /// socket1.connect(socket2.local_addr()?).await?;
+    /// let addr = socket1.peer_addr()?;
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+        self.watcher
+            .get_ref()
+            .peer_addr()
+            .context(|| String::from("could not get peer address"))
+    }
+
     /// Returns the local address that this listener is bound to.
     ///
     /// This can be useful, for example, when binding to port 0 to figure out which port was
@@ -100,7 +124,7 @@ impl UdpSocket {
     /// ```no_run
     /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
     /// #
-    ///	use async_std::net::UdpSocket;
+    /// use async_std::net::UdpSocket;
     ///
     /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
     /// let addr = socket.local_addr()?;
@@ -108,7 +132,10 @@ impl UdpSocket {
     /// # Ok(()) }) }
     /// ```
     pub fn local_addr(&self) -> io::Result<SocketAddr> {
-        self.watcher.get_ref().local_addr()
+        self.watcher
+            .get_ref()
+            .local_addr()
+            .context(|| String::from("could not get local address"))
     }
 
     /// Sends data on the socket to the given address.
@@ -148,11 +175,10 @@ impl UdpSocket {
             }
         };
 
-        future::poll_fn(|cx| {
-            self.watcher
-                .poll_write_with(cx, |inner| inner.send_to(buf, &addr))
-        })
-        .await
+        self.watcher
+            .send_to(buf, addr)
+            .await
+            .context(|| format!("could not send packet to {}", addr))
     }
 
     /// Receives data from the socket.
@@ -175,11 +201,30 @@ impl UdpSocket {
     /// # Ok(()) }) }
     /// ```
     pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        future::poll_fn(|cx| {
-            self.watcher
-                .poll_read_with(cx, |inner| inner.recv_from(buf))
-        })
-        .await
+        self.watcher.recv_from(buf).await
+    }
+
+    /// Receives data from socket without removing it from the queue.
+    ///
+    /// On success, returns the number of bytes peeked and the origin.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::net::UdpSocket;
+    ///
+    /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
+    ///
+    /// let mut buf = vec![0; 1024];
+    /// let (n, peer) = socket.peek_from(&mut buf).await?;
+    /// println!("Peeked {} bytes from {}", n, peer);
+    /// #
+    /// # Ok (()) }) }
+    /// ```
+    pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.watcher.peek_from(buf).await
     }
 
     /// Connects the UDP socket to a remote address.
@@ -197,7 +242,7 @@ impl UdpSocket {
     /// ```no_run
     /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
     /// #
-    ///	use async_std::net::UdpSocket;
+    /// use async_std::net::UdpSocket;
     ///
     /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
     /// socket.connect("127.0.0.1:8080").await?;
@@ -206,8 +251,12 @@ impl UdpSocket {
     /// ```
     pub async fn connect<A: ToSocketAddrs>(&self, addrs: A) -> io::Result<()> {
         let mut last_err = None;
+        let addrs = addrs
+            .to_socket_addrs()
+            .await
+            .context(|| String::from("could not resolve addresses"))?;
 
-        for addr in addrs.to_socket_addrs().await? {
+        for addr in addrs {
             // TODO(stjepang): connect on the blocking pool
             match self.watcher.get_ref().connect(addr) {
                 Ok(()) => return Ok(()),
@@ -223,9 +272,12 @@ impl UdpSocket {
         }))
     }
 
-    /// Sends data on the socket to the given address.
+    /// Sends data on the socket to the remote address to which it is connected.
     ///
-    /// On success, returns the number of bytes written.
+    /// The [`connect`] method will connect this socket to a remote address.
+    /// This method will fail if the socket is not connected.
+    ///
+    /// [`connect`]: #method.connect
     ///
     /// # Examples
     ///
@@ -234,28 +286,21 @@ impl UdpSocket {
     /// #
     /// use async_std::net::UdpSocket;
     ///
-    /// const THE_MERCHANT_OF_VENICE: &[u8] = b"
-    ///     If you prick us, do we not bleed?
-    ///     If you tickle us, do we not laugh?
-    ///     If you poison us, do we not die?
-    ///     And if you wrong us, shall we not revenge?
-    /// ";
-    ///
-    /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
+    /// let socket = UdpSocket::bind("127.0.0.1:34254").await?;
+    /// socket.connect("127.0.0.1:8080").await?;
+    /// let bytes = socket.send(b"Hi there!").await?;
     ///
-    /// let addr = "127.0.0.1:7878";
-    /// let sent = socket.send_to(THE_MERCHANT_OF_VENICE, &addr).await?;
-    /// println!("Sent {} bytes to {}", sent, addr);
+    /// println!("Sent {} bytes", bytes);
     /// #
     /// # Ok(()) }) }
     /// ```
     pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
-        future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await
+        self.watcher.send(buf).await
     }
 
     /// Receives data from the socket.
     ///
-    /// On success, returns the number of bytes read and the origin.
+    /// On success, returns the number of bytes read.
     ///
     /// # Examples
     ///
@@ -265,15 +310,40 @@ impl UdpSocket {
     /// use async_std::net::UdpSocket;
     ///
     /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
+    /// socket.connect("127.0.0.1:8080").await?;
     ///
     /// let mut buf = vec![0; 1024];
-    /// let (n, peer) = socket.recv_from(&mut buf).await?;
-    /// println!("Received {} bytes from {}", n, peer);
+    /// let n = socket.recv(&mut buf).await?;
+    /// println!("Received {} bytes", n);
     /// #
     /// # Ok(()) }) }
     /// ```
     pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
-        future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await
+        self.watcher.recv(buf).await
+    }
+
+    /// Receives data from the socket without removing it from the queue.
+    ///
+    /// On success, returns the number of bytes peeked.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::net::UdpSocket;
+    ///
+    /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
+    /// socket.connect("127.0.0.1:8080").await?;
+    ///
+    /// let mut buf = vec![0; 1024];
+    /// let n = socket.peek(&mut buf).await?;
+    /// println!("Peeked {} bytes", n);
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    pub async fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.watcher.peek(buf).await
     }
 
     /// Gets the value of the `SO_BROADCAST` option for this socket.
@@ -391,14 +461,14 @@ impl UdpSocket {
     /// let mdns_addr = Ipv4Addr::new(224, 0, 0, 123);
     ///
     /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
-    /// socket.join_multicast_v4(&mdns_addr, &interface)?;
+    /// socket.join_multicast_v4(mdns_addr, interface)?;
     /// #
     /// # Ok(()) }) }
     /// ```
-    pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
+    pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
         self.watcher
             .get_ref()
-            .join_multicast_v4(multiaddr, interface)
+            .join_multicast_v4(&multiaddr, &interface)
     }
 
     /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type.
@@ -417,7 +487,7 @@ impl UdpSocket {
     /// use async_std::net::UdpSocket;
     ///
     /// let socket_addr = SocketAddr::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).into(), 0);
-    /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123) ;
+    /// let mdns_addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0123);
     /// let socket = UdpSocket::bind(&socket_addr).await?;
     ///
     /// socket.join_multicast_v6(&mdns_addr, 0)?;
@@ -435,10 +505,10 @@ impl UdpSocket {
     /// For more information about this option, see [`join_multicast_v4`].
     ///
     /// [`join_multicast_v4`]: #method.join_multicast_v4
-    pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> {
+    pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
         self.watcher
             .get_ref()
-            .leave_multicast_v4(multiaddr, interface)
+            .leave_multicast_v4(&multiaddr, &interface)
     }
 
     /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type.
@@ -456,68 +526,108 @@ impl UdpSocket {
 impl From<std::net::UdpSocket> for UdpSocket {
     /// Converts a `std::net::UdpSocket` into its asynchronous equivalent.
     fn from(socket: std::net::UdpSocket) -> UdpSocket {
-        let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap();
         UdpSocket {
-            watcher: Watcher::new(mio_socket),
+            watcher: Async::new(socket).expect("UdpSocket is known to be good"),
         }
     }
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-        // use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
-    } else if #[cfg(unix)] {
-        use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-    } else if #[cfg(windows)] {
-        // use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
+impl std::convert::TryFrom<UdpSocket> for std::net::UdpSocket {
+    type Error = io::Error;
+    /// Converts a `UdpSocket` into its synchronous equivalent.
+    fn try_from(listener: UdpSocket) -> io::Result<std::net::UdpSocket> {
+        let inner = listener.watcher.into_inner()?;
+        inner.set_nonblocking(false)?;
+        Ok(inner)
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-cfg_if! {
-    if #[cfg(any(unix, feature = "docs"))] {
-        impl AsRawFd for UdpSocket {
-            fn as_raw_fd(&self) -> RawFd {
-                self.watcher.get_ref().as_raw_fd()
+cfg_unix! {
+    use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+
+    impl AsRawFd for UdpSocket {
+        fn as_raw_fd(&self) -> RawFd {
+            self.watcher.get_ref().as_raw_fd()
+        }
+    }
+
+    impl FromRawFd for UdpSocket {
+        unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
+            std::net::UdpSocket::from_raw_fd(fd).into()
+        }
+    }
+
+    impl IntoRawFd for UdpSocket {
+        fn into_raw_fd(self) -> RawFd {
+            self.watcher.into_inner().unwrap().into_raw_fd()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+        impl AsFd for UdpSocket {
+            fn as_fd(&self) -> BorrowedFd<'_> {
+                self.watcher.get_ref().as_fd()
             }
         }
 
-        impl FromRawFd for UdpSocket {
-            unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
-                std::net::UdpSocket::from_raw_fd(fd).into()
+        impl From<OwnedFd> for UdpSocket {
+            fn from(fd: OwnedFd) -> UdpSocket {
+                std::net::UdpSocket::from(fd).into()
             }
         }
 
-        impl IntoRawFd for UdpSocket {
-            fn into_raw_fd(self) -> RawFd {
-                self.watcher.into_inner().into_raw_fd()
+        impl From<UdpSocket> for OwnedFd {
+            fn from(stream: UdpSocket) -> OwnedFd {
+                stream.watcher.into_inner().unwrap().into()
             }
         }
     }
 }
 
-#[cfg_attr(feature = "docs", doc(cfg(windows)))]
-cfg_if! {
-    if #[cfg(any(windows, feature = "docs"))] {
-        // use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
-        //
-        // impl AsRawSocket for UdpSocket {
-        //     fn as_raw_socket(&self) -> RawSocket {
-        //         self.raw_socket
-        //     }
-        // }
-        //
-        // impl FromRawSocket for UdpSocket {
-        //     unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
-        //         net::UdpSocket::from_raw_socket(handle).into()
-        //     }
-        // }
-        //
-        // impl IntoRawSocket for UdpSocket {
-        //     fn into_raw_socket(self) -> RawSocket {
-        //         self.raw_socket
-        //     }
-        // }
+cfg_windows! {
+    use crate::os::windows::io::{
+        RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket
+    };
+
+    impl AsRawSocket for UdpSocket {
+        fn as_raw_socket(&self) -> RawSocket {
+            self.watcher.get_ref().as_raw_socket()
+        }
+    }
+
+    impl FromRawSocket for UdpSocket {
+        unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
+            std::net::UdpSocket::from_raw_socket(handle).into()
+        }
+    }
+
+    impl IntoRawSocket for UdpSocket {
+        fn into_raw_socket(self) -> RawSocket {
+            self.watcher.into_inner().unwrap().into_raw_socket()
+        }
+    }
+
+    cfg_io_safety! {
+        use crate::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket};
+
+        impl AsSocket for UdpSocket {
+            fn as_socket(&self) -> BorrowedSocket<'_> {
+                self.watcher.get_ref().as_socket()
+            }
+        }
+
+        impl From<OwnedSocket> for UdpSocket {
+            fn from(fd: OwnedSocket) -> UdpSocket {
+                std::net::UdpSocket::from(fd).into()
+            }
+        }
+
+        impl From<UdpSocket> for OwnedSocket {
+            fn from(stream: UdpSocket) -> OwnedSocket {
+                stream.watcher.into_inner().unwrap().into()
+            }
+        }
     }
 }
diff --git a/src/option/from_stream.rs b/src/option/from_stream.rs
index e4da809eb..7931d97a6 100644
--- a/src/option/from_stream.rs
+++ b/src/option/from_stream.rs
@@ -2,8 +2,9 @@ use std::pin::Pin;
 
 use crate::prelude::*;
 use crate::stream::{FromStream, IntoStream};
+use std::convert::identity;
 
-impl<T, V> FromStream<Option<T>> for Option<V>
+impl<T: Send, V> FromStream<Option<T>> for Option<V>
 where
     V: FromStream<T>,
 {
@@ -11,35 +12,31 @@ where
     /// elements are taken, and `None` is returned. Should no `None`
     /// occur, a container with the values of each `Option` is returned.
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = Option<T>>>(
+    fn from_stream<'a, S: IntoStream<Item = Option<T>> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
-            // Using `scan` here because it is able to stop the stream early
+            // Using `take_while` here because it is able to stop the stream early
             // if a failure occurs
-            let mut found_error = false;
+            let mut found_none = false;
             let out: V = stream
-                .scan((), |_, elem| {
-                    match elem {
-                        Some(elem) => Some(elem),
-                        None => {
-                            found_error = true;
-                            // Stop processing the stream on error
-                            None
-                        }
+                .take_while(|elem| {
+                    elem.is_some() || {
+                        found_none = true;
+                        // Stop processing the stream on `None`
+                        false
                     }
                 })
+                .filter_map(identity)
                 .collect()
                 .await;
 
-            if found_error { None } else { Some(out) }
+            if found_none { None } else { Some(out) }
         })
     }
 }
diff --git a/src/option/mod.rs b/src/option/mod.rs
index afb29adc9..f0d67b77b 100644
--- a/src/option/mod.rs
+++ b/src/option/mod.rs
@@ -5,5 +5,11 @@
 
 mod from_stream;
 
+#[allow(unused)]
 #[doc(inline)]
 pub use std::option::Option;
+
+cfg_unstable! {
+    mod product;
+    mod sum;
+}
diff --git a/src/option/product.rs b/src/option/product.rs
new file mode 100644
index 000000000..b446c1ffe
--- /dev/null
+++ b/src/option/product.rs
@@ -0,0 +1,63 @@
+use std::pin::Pin;
+
+use crate::prelude::*;
+use crate::stream::{Product, Stream};
+use std::convert::identity;
+
+impl<T, U> Product<Option<U>> for Option<T>
+where
+    T: Product<U>,
+{
+    #[doc = r#"
+        Takes each element in the `Stream`: if it is a `None`, no further
+        elements are taken, and the `None` is returned. Should no `None` occur,
+        the product of all elements is returned.
+
+        # Examples
+
+        This multiplies every integer in a vector, rejecting the product if a negative element is
+        encountered:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let v = stream::from_iter(vec![1, 2, 4]);
+        let prod: Option<i32> = v.map(|x|
+            if x < 0 {
+                None
+            } else {
+                Some(x)
+            }).product().await;
+        assert_eq!(prod, Some(8));
+        #
+        # }) }
+        ```
+    "#]
+    fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Option<T>> + 'a>>
+    where
+        S: Stream<Item = Option<U>> + 'a,
+    {
+        Box::pin(async move {
+            // Using `take_while` here because it is able to stop the stream early
+            // if a failure occurs
+            let mut found_none = false;
+            let out = <T as Product<U>>::product(
+                stream
+                    .take_while(|elem| {
+                        elem.is_some() || {
+                            found_none = true;
+                            // Stop processing the stream on `None`
+                            false
+                        }
+                    })
+                    .filter_map(identity),
+            )
+            .await;
+
+            if found_none { None } else { Some(out) }
+        })
+    }
+}
diff --git a/src/option/sum.rs b/src/option/sum.rs
new file mode 100644
index 000000000..de404f42d
--- /dev/null
+++ b/src/option/sum.rs
@@ -0,0 +1,58 @@
+use std::pin::Pin;
+
+use crate::prelude::*;
+use crate::stream::{Stream, Sum};
+use std::convert::identity;
+
+impl<T, U> Sum<Option<U>> for Option<T>
+where
+    T: Sum<U>,
+{
+    #[doc = r#"
+        Takes each element in the `Iterator`: if it is a `None`, no further
+        elements are taken, and the `None` is returned. Should no `None` occur,
+        the sum of all elements is returned.
+
+        # Examples
+
+        This sums up the position of the character 'a' in a vector of strings,
+        if a word did not have the character 'a' the operation returns `None`:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let words = stream::from_iter(vec!["have", "a", "great", "day"]);
+        let total: Option<usize> = words.map(|w| w.find('a')).sum().await;
+        assert_eq!(total, Some(5));
+        #
+        # }) }
+        ```
+    "#]
+    fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Option<T>> + 'a>>
+    where
+        S: Stream<Item = Option<U>> + 'a,
+    {
+        Box::pin(async move {
+            // Using `take_while` here because it is able to stop the stream early
+            // if a failure occurs
+            let mut found_none = false;
+            let out = <T as Sum<U>>::sum(
+                stream
+                    .take_while(|elem| {
+                        elem.is_some() || {
+                            found_none = true;
+                            // Stop processing the stream on `None`
+                            false
+                        }
+                    })
+                    .filter_map(identity),
+            )
+            .await;
+
+            if found_none { None } else { Some(out) }
+        })
+    }
+}
diff --git a/src/os/mod.rs b/src/os/mod.rs
index fb9b94125..5b836aec9 100644
--- a/src/os/mod.rs
+++ b/src/os/mod.rs
@@ -1,9 +1,9 @@
 //! OS-specific extensions.
 
-#[cfg(any(unix, feature = "docs"))]
-#[cfg_attr(feature = "docs", doc(cfg(unix)))]
-pub mod unix;
+cfg_unix! {
+    pub mod unix;
+}
 
-#[cfg(any(windows, feature = "docs"))]
-#[cfg_attr(feature = "docs", doc(cfg(windows)))]
-pub mod windows;
+cfg_windows! {
+    pub mod windows;
+}
diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs
index be8932c06..498b3a97f 100644
--- a/src/os/unix/fs.rs
+++ b/src/os/unix/fs.rs
@@ -1,11 +1,8 @@
 //! Unix-specific filesystem extensions.
 
-use std::path::Path;
-
-use cfg_if::cfg_if;
-
 use crate::io;
-use crate::task::blocking;
+use crate::path::Path;
+use crate::task::spawn_blocking;
 
 /// Creates a new symbolic link on the filesystem.
 ///
@@ -29,46 +26,46 @@ use crate::task::blocking;
 pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
     let src = src.as_ref().to_owned();
     let dst = dst.as_ref().to_owned();
-    blocking::spawn(async move { std::os::unix::fs::symlink(&src, &dst) }).await
+    spawn_blocking(move || std::os::unix::fs::symlink(&src, &dst)).await
+}
+
+cfg_not_docs! {
+    pub use std::os::unix::fs::{DirBuilderExt, DirEntryExt, OpenOptionsExt};
 }
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        /// Unix-specific extensions to `DirBuilder`.
-        pub trait DirBuilderExt {
-            /// Sets the mode to create new directories with. This option defaults to
-            /// `0o777`.
-            fn mode(&mut self, mode: u32) -> &mut Self;
-        }
+cfg_docs! {
+    /// Unix-specific extensions to `DirBuilder`.
+    pub trait DirBuilderExt {
+        /// Sets the mode to create new directories with. This option defaults to
+        /// `0o777`.
+        fn mode(&mut self, mode: u32) -> &mut Self;
+    }
 
-        /// Unix-specific extension methods for `DirEntry`.
-        pub trait DirEntryExt {
-            /// Returns the underlying `d_ino` field in the contained `dirent`
-            /// structure.
-            fn ino(&self) -> u64;
-        }
+    /// Unix-specific extension methods for `DirEntry`.
+    pub trait DirEntryExt {
+        /// Returns the underlying `d_ino` field in the contained `dirent`
+        /// structure.
+        fn ino(&self) -> u64;
+    }
 
-        /// Unix-specific extensions to `OpenOptions`.
-        pub trait OpenOptionsExt {
-            /// Sets the mode bits that a new file will be created with.
-            ///
-            /// If a new file is created as part of a `File::open_opts` call then this
-            /// specified `mode` will be used as the permission bits for the new file.
-            /// If no `mode` is set, the default of `0o666` will be used.
-            /// The operating system masks out bits with the systems `umask`, to produce
-            /// the final permissions.
-            fn mode(&mut self, mode: u32) -> &mut Self;
+    /// Unix-specific extensions to `OpenOptions`.
+    pub trait OpenOptionsExt {
+        /// Sets the mode bits that a new file will be created with.
+        ///
+        /// If a new file is created as part of a `File::open_opts` call then this
+        /// specified `mode` will be used as the permission bits for the new file.
+        /// If no `mode` is set, the default of `0o666` will be used.
+        /// The operating system masks out bits with the systems `umask`, to produce
+        /// the final permissions.
+        fn mode(&mut self, mode: u32) -> &mut Self;
 
-            /// Pass custom flags to the `flags` argument of `open`.
-            ///
-            /// The bits that define the access mode are masked out with `O_ACCMODE`, to
-            /// ensure they do not interfere with the access mode set by Rusts options.
-            ///
-            /// Custom flags can only set flags, not remove flags set by Rusts options.
-            /// This options overwrites any previously set custom flags.
-            fn custom_flags(&mut self, flags: i32) -> &mut Self;
-        }
-    } else {
-        pub use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt};
+        /// Pass custom flags to the `flags` argument of `open`.
+        ///
+        /// The bits that define the access mode are masked out with `O_ACCMODE`, to
+        /// ensure they do not interfere with the access mode set by Rusts options.
+        ///
+        /// Custom flags can only set flags, not remove flags set by Rusts options.
+        /// This options overwrites any previously set custom flags.
+        fn custom_flags(&mut self, flags: i32) -> &mut Self;
     }
 }
diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs
index 820d509ca..9d1fbaede 100644
--- a/src/os/unix/io.rs
+++ b/src/os/unix/io.rs
@@ -1,56 +1,63 @@
 //! Unix-specific I/O extensions.
 
-use cfg_if::cfg_if;
+cfg_not_docs! {
+    pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+    
+    cfg_io_safety! {
+        pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+    }
+}
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        /// Raw file descriptors.
-        pub type RawFd = std::os::raw::c_int;
+cfg_docs! {
+    /// Raw file descriptors.
+    pub type RawFd = std::os::raw::c_int;
 
-        /// A trait to extract the raw unix file descriptor from an underlying
-        /// object.
+    /// A trait to extract the raw unix file descriptor from an underlying
+    /// object.
+    ///
+    /// This is only available on unix platforms and must be imported in order
+    /// to call the method. Windows platforms have a corresponding `AsRawHandle`
+    /// and `AsRawSocket` set of traits.
+    pub trait AsRawFd {
+        /// Extracts the raw file descriptor.
         ///
-        /// This is only available on unix platforms and must be imported in order
-        /// to call the method. Windows platforms have a corresponding `AsRawHandle`
-        /// and `AsRawSocket` set of traits.
-        pub trait AsRawFd {
-            /// Extracts the raw file descriptor.
-            ///
-            /// This method does **not** pass ownership of the raw file descriptor
-            /// to the caller. The descriptor is only guaranteed to be valid while
-            /// the original object has not yet been destroyed.
-            fn as_raw_fd(&self) -> RawFd;
-        }
+        /// This method does **not** pass ownership of the raw file descriptor
+        /// to the caller. The descriptor is only guaranteed to be valid while
+        /// the original object has not yet been destroyed.
+        fn as_raw_fd(&self) -> RawFd;
+    }
 
-        /// A trait to express the ability to construct an object from a raw file
+    /// A trait to express the ability to construct an object from a raw file
+    /// descriptor.
+    pub trait FromRawFd {
+        /// Constructs a new instance of `Self` from the given raw file
         /// descriptor.
-        pub trait FromRawFd {
-            /// Constructs a new instance of `Self` from the given raw file
-            /// descriptor.
-            ///
-            /// This function **consumes ownership** of the specified file
-            /// descriptor. The returned object will take responsibility for closing
-            /// it when the object goes out of scope.
-            ///
-            /// This function is also unsafe as the primitives currently returned
-            /// have the contract that they are the sole owner of the file
-            /// descriptor they are wrapping. Usage of this function could
-            /// accidentally allow violating this contract which can cause memory
-            /// unsafety in code that relies on it being true.
-            unsafe fn from_raw_fd(fd: RawFd) -> Self;
-        }
+        ///
+        /// This function **consumes ownership** of the specified file
+        /// descriptor. The returned object will take responsibility for closing
+        /// it when the object goes out of scope.
+        ///
+        /// This function is also unsafe as the primitives currently returned
+        /// have the contract that they are the sole owner of the file
+        /// descriptor they are wrapping. Usage of this function could
+        /// accidentally allow violating this contract which can cause memory
+        /// unsafety in code that relies on it being true.
+        unsafe fn from_raw_fd(fd: RawFd) -> Self;
+    }
+
+    /// A trait to express the ability to consume an object and acquire ownership of
+    /// its raw file descriptor.
+    pub trait IntoRawFd {
+        /// Consumes this object, returning the raw underlying file descriptor.
+        ///
+        /// This function **transfers ownership** of the underlying file descriptor
+        /// to the caller. Callers are then the unique owners of the file descriptor
+        /// and must close the descriptor once it's no longer needed.
+        fn into_raw_fd(self) -> RawFd;
+    }
 
-        /// A trait to express the ability to consume an object and acquire ownership of
-        /// its raw file descriptor.
-        pub trait IntoRawFd {
-            /// Consumes this object, returning the raw underlying file descriptor.
-            ///
-            /// This function **transfers ownership** of the underlying file descriptor
-            /// to the caller. Callers are then the unique owners of the file descriptor
-            /// and must close the descriptor once it's no longer needed.
-            fn into_raw_fd(self) -> RawFd;
-        }
-    } else {
-        pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+    cfg_io_safety! {
+        #[doc(inline)]
+        pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
     }
 }
diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs
index 722cfe6b8..36a97967c 100644
--- a/src/os/unix/mod.rs
+++ b/src/os/unix/mod.rs
@@ -1,5 +1,15 @@
 //! Platform-specific extensions for Unix platforms.
 
-pub mod fs;
-pub mod io;
-pub mod net;
+cfg_std! {
+    pub mod io;
+}
+
+cfg_default! {
+    pub mod fs;
+    pub mod net;
+}
+
+#[cfg(all(feature = "unstable", feature = "std"))]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::unix as process;
diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs
index 1f971f7f5..792860223 100644
--- a/src/os/unix/net/datagram.rs
+++ b/src/os/unix/net/datagram.rs
@@ -2,16 +2,14 @@
 
 use std::fmt;
 use std::net::Shutdown;
-use std::path::Path;
+use std::os::unix::net::UnixDatagram as StdUnixDatagram;
 
-use mio_uds;
+use async_io::Async;
 
 use super::SocketAddr;
-use crate::future;
 use crate::io;
-use crate::net::driver::Watcher;
 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-use crate::task::blocking;
+use crate::path::Path;
 
 /// A Unix datagram socket.
 ///
@@ -42,13 +40,13 @@ use crate::task::blocking;
 /// # Ok(()) }) }
 /// ```
 pub struct UnixDatagram {
-    watcher: Watcher<mio_uds::UnixDatagram>,
+    watcher: Async<StdUnixDatagram>,
 }
 
 impl UnixDatagram {
-    fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram {
+    fn new(socket: StdUnixDatagram) -> UnixDatagram {
         UnixDatagram {
-            watcher: Watcher::new(socket),
+            watcher: Async::new(socket).expect("UnixDatagram is known to be good"),
         }
     }
 
@@ -67,8 +65,8 @@ impl UnixDatagram {
     /// ```
     pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
         let path = path.as_ref().to_owned();
-        let socket = blocking::spawn(async move { mio_uds::UnixDatagram::bind(path) }).await?;
-        Ok(UnixDatagram::new(socket))
+        let socket = Async::<StdUnixDatagram>::bind(path)?;
+        Ok(UnixDatagram { watcher: socket })
     }
 
     /// Creates a Unix datagram which is not bound to any address.
@@ -85,7 +83,7 @@ impl UnixDatagram {
     /// # Ok(()) }) }
     /// ```
     pub fn unbound() -> io::Result<UnixDatagram> {
-        let socket = mio_uds::UnixDatagram::unbound()?;
+        let socket = StdUnixDatagram::unbound()?;
         Ok(UnixDatagram::new(socket))
     }
 
@@ -105,7 +103,7 @@ impl UnixDatagram {
     /// # Ok(()) }) }
     /// ```
     pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
-        let (a, b) = mio_uds::UnixDatagram::pair()?;
+        let (a, b) = StdUnixDatagram::pair()?;
         let a = UnixDatagram::new(a);
         let b = UnixDatagram::new(b);
         Ok((a, b))
@@ -197,11 +195,7 @@ impl UnixDatagram {
     /// # Ok(()) }) }
     /// ```
     pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        future::poll_fn(|cx| {
-            self.watcher
-                .poll_read_with(cx, |inner| inner.recv_from(buf))
-        })
-        .await
+        self.watcher.recv_from(buf).await
     }
 
     /// Receives data from the socket.
@@ -222,7 +216,7 @@ impl UnixDatagram {
     /// # Ok(()) }) }
     /// ```
     pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
-        future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await
+        self.watcher.recv(buf).await
     }
 
     /// Sends data on the socket to the specified address.
@@ -242,11 +236,7 @@ impl UnixDatagram {
     /// # Ok(()) }) }
     /// ```
     pub async fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> {
-        future::poll_fn(|cx| {
-            self.watcher
-                .poll_write_with(cx, |inner| inner.send_to(buf, path.as_ref()))
-        })
-        .await
+        self.watcher.send_to(buf, path.as_ref()).await
     }
 
     /// Sends data on the socket to the socket's peer.
@@ -267,7 +257,7 @@ impl UnixDatagram {
     /// # Ok(()) }) }
     /// ```
     pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
-        future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await
+        self.watcher.send(buf).await
     }
 
     /// Shut down the read, write, or both halves of this connection.
@@ -312,31 +302,63 @@ impl fmt::Debug for UnixDatagram {
     }
 }
 
-impl From<std::os::unix::net::UnixDatagram> for UnixDatagram {
+impl From<StdUnixDatagram> for UnixDatagram {
     /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent.
-    fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram {
-        let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap();
+    fn from(datagram: StdUnixDatagram) -> UnixDatagram {
         UnixDatagram {
-            watcher: Watcher::new(mio_datagram),
+            watcher: Async::new(datagram).expect("UnixDatagram is known to be good"),
         }
     }
 }
 
+impl std::convert::TryFrom<UnixDatagram> for StdUnixDatagram {
+    type Error = io::Error;
+    /// Converts a `UnixDatagram` into its synchronous equivalent.
+    fn try_from(listener: UnixDatagram) -> io::Result<StdUnixDatagram> {
+        let inner = listener.watcher.into_inner()?;
+        inner.set_nonblocking(false)?;
+        Ok(inner)
+    }
+}
+
 impl AsRawFd for UnixDatagram {
     fn as_raw_fd(&self) -> RawFd {
-        self.watcher.get_ref().as_raw_fd()
+        self.watcher.as_raw_fd()
     }
 }
 
 impl FromRawFd for UnixDatagram {
     unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
-        let datagram = std::os::unix::net::UnixDatagram::from_raw_fd(fd);
-        datagram.into()
+        let raw = StdUnixDatagram::from_raw_fd(fd);
+        let datagram = Async::<StdUnixDatagram>::new(raw).expect("invalid file descriptor");
+        UnixDatagram { watcher: datagram }
     }
 }
 
 impl IntoRawFd for UnixDatagram {
     fn into_raw_fd(self) -> RawFd {
-        self.watcher.into_inner().into_raw_fd()
+        self.watcher.into_inner().unwrap().into_raw_fd()
+    }
+}
+
+cfg_io_safety! {
+    use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+    impl AsFd for UnixDatagram {
+        fn as_fd(&self) -> BorrowedFd<'_> {
+            self.watcher.get_ref().as_fd()
+        }
+    }
+
+    impl From<OwnedFd> for UnixDatagram {
+        fn from(fd: OwnedFd) -> UnixDatagram {
+            StdUnixDatagram::from(fd).into()
+        }
+    }
+
+    impl From<UnixDatagram> for OwnedFd {
+        fn from(stream: UnixDatagram) -> OwnedFd {
+            stream.watcher.into_inner().unwrap().into()
+        }
     }
 }
diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs
index ed4f1f4cc..b87b781f2 100644
--- a/src/os/unix/net/listener.rs
+++ b/src/os/unix/net/listener.rs
@@ -1,19 +1,20 @@
 //! Unix-specific networking extensions.
 
 use std::fmt;
-use std::path::Path;
+use std::os::unix::net::UnixListener as StdUnixListener;
+use std::os::unix::net::UnixStream as StdUnixStream;
 use std::pin::Pin;
 
-use mio_uds;
+use async_io::Async;
 
 use super::SocketAddr;
 use super::UnixStream;
-use crate::future::{self, Future};
 use crate::io;
-use crate::net::driver::Watcher;
 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::path::Path;
 use crate::stream::Stream;
-use crate::task::{blocking, Context, Poll};
+use crate::sync::Arc;
+use crate::task::{ready, Context, Poll};
 
 /// A Unix domain socket server, listening for connections.
 ///
@@ -49,7 +50,7 @@ use crate::task::{blocking, Context, Poll};
 /// # Ok(()) }) }
 /// ```
 pub struct UnixListener {
-    watcher: Watcher<mio_uds::UnixListener>,
+    watcher: Async<StdUnixListener>,
 }
 
 impl UnixListener {
@@ -68,11 +69,9 @@ impl UnixListener {
     /// ```
     pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
         let path = path.as_ref().to_owned();
-        let listener = blocking::spawn(async move { mio_uds::UnixListener::bind(path) }).await?;
+        let listener = Async::<StdUnixListener>::bind(path)?;
 
-        Ok(UnixListener {
-            watcher: Watcher::new(listener),
-        })
+        Ok(UnixListener { watcher: listener })
     }
 
     /// Accepts a new incoming connection to this listener.
@@ -92,22 +91,14 @@ impl UnixListener {
     /// # Ok(()) }) }
     /// ```
     pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
-        future::poll_fn(|cx| {
-            let res =
-                futures_core::ready!(self.watcher.poll_read_with(cx, |inner| inner.accept_std()));
-
-            match res? {
-                None => Poll::Pending,
-                Some((io, addr)) => {
-                    let mio_stream = mio_uds::UnixStream::from_stream(io)?;
-                    let stream = UnixStream {
-                        watcher: Watcher::new(mio_stream),
-                    };
-                    Poll::Ready(Ok((stream, addr)))
-                }
-            }
-        })
-        .await
+        let (stream, addr) = self.watcher.accept().await?;
+
+        Ok((
+            UnixStream {
+                watcher: Arc::new(stream),
+            },
+            addr,
+        ))
     }
 
     /// Returns a stream of incoming connections.
@@ -137,7 +128,9 @@ impl UnixListener {
     /// # Ok(()) }) }
     /// ```
     pub fn incoming(&self) -> Incoming<'_> {
-        Incoming(self)
+        Incoming {
+            incoming: Box::pin(self.watcher.incoming()),
+        }
     }
 
     /// Returns the local socket address of this listener.
@@ -183,34 +176,48 @@ impl fmt::Debug for UnixListener {
 /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
 /// [`incoming`]: struct.UnixListener.html#method.incoming
 /// [`UnixListener`]: struct.UnixListener.html
-#[derive(Debug)]
-pub struct Incoming<'a>(&'a UnixListener);
+pub struct Incoming<'a> {
+    incoming: Pin<Box<dyn Stream<Item = io::Result<Async<StdUnixStream>>> + Send + Sync + 'a>>,
+}
 
 impl Stream for Incoming<'_> {
     type Item = io::Result<UnixStream>;
 
-    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let future = self.0.accept();
-        pin_utils::pin_mut!(future);
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let res = ready!(Pin::new(&mut self.incoming).poll_next(cx));
+        Poll::Ready(res.map(|res| res.map(|stream| UnixStream { watcher: Arc::new(stream) })))
+    }
+}
 
-        let (socket, _) = futures_core::ready!(future.poll(cx))?;
-        Poll::Ready(Some(Ok(socket)))
+impl fmt::Debug for Incoming<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Incoming")
+            .finish()
     }
 }
 
-impl From<std::os::unix::net::UnixListener> for UnixListener {
+impl From<StdUnixListener> for UnixListener {
     /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent.
-    fn from(listener: std::os::unix::net::UnixListener) -> UnixListener {
-        let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap();
+    fn from(listener: StdUnixListener) -> UnixListener {
         UnixListener {
-            watcher: Watcher::new(mio_listener),
+            watcher: Async::new(listener).expect("UnixListener is known to be good"),
         }
     }
 }
 
+impl std::convert::TryFrom<UnixListener> for StdUnixListener {
+    type Error = io::Error;
+    /// Converts a `UnixListener` into its synchronous equivalent.
+    fn try_from(listener: UnixListener) -> io::Result<StdUnixListener> {
+        let inner = listener.watcher.into_inner()?;
+        inner.set_nonblocking(false)?;
+        Ok(inner)
+    }
+}
+
 impl AsRawFd for UnixListener {
     fn as_raw_fd(&self) -> RawFd {
-        self.watcher.get_ref().as_raw_fd()
+        self.watcher.as_raw_fd()
     }
 }
 
@@ -223,6 +230,28 @@ impl FromRawFd for UnixListener {
 
 impl IntoRawFd for UnixListener {
     fn into_raw_fd(self) -> RawFd {
-        self.watcher.into_inner().into_raw_fd()
+        self.watcher.into_inner().unwrap().into_raw_fd()
+    }
+}
+
+cfg_io_safety! {
+    use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+    impl AsFd for UnixListener {
+        fn as_fd(&self) -> BorrowedFd<'_> {
+            self.watcher.get_ref().as_fd()
+        }
+    }
+
+    impl From<OwnedFd> for UnixListener {
+        fn from(fd: OwnedFd) -> UnixListener {
+            std::os::unix::net::UnixListener::from(fd).into()
+        }
+    }
+
+    impl From<UnixListener> for OwnedFd {
+        fn from(stream: UnixListener) -> OwnedFd {
+            stream.watcher.into_inner().unwrap().into()
+        }
     }
 }
diff --git a/src/os/unix/net/mod.rs b/src/os/unix/net/mod.rs
index a719a4845..e0a3b9b63 100644
--- a/src/os/unix/net/mod.rs
+++ b/src/os/unix/net/mod.rs
@@ -1,7 +1,5 @@
 //! Unix-specific networking extensions.
 
-use cfg_if::cfg_if;
-
 pub use datagram::UnixDatagram;
 pub use listener::{Incoming, UnixListener};
 pub use stream::UnixStream;
@@ -10,90 +8,90 @@ mod datagram;
 mod listener;
 mod stream;
 
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::fmt;
-        use std::path::Path;
+cfg_not_docs! {
+    pub use std::os::unix::net::SocketAddr;
+}
+
+cfg_docs! {
+    use std::fmt;
+
+    use crate::path::Path;
 
-        /// An address associated with a Unix socket.
+    /// An address associated with a Unix socket.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::os::unix::net::UnixListener;
+    ///
+    /// let socket = UnixListener::bind("/tmp/socket").await?;
+    /// let addr = socket.local_addr()?;
+    /// ```
+    #[derive(Clone)]
+    pub struct SocketAddr {
+        _private: (),
+    }
+
+    impl SocketAddr {
+        /// Returns `true` if the address is unnamed.
         ///
         /// # Examples
         ///
-        /// ```
+        /// A named address:
+        ///
+        /// ```no_run
         /// use async_std::os::unix::net::UnixListener;
         ///
         /// let socket = UnixListener::bind("/tmp/socket").await?;
         /// let addr = socket.local_addr()?;
+        /// assert_eq!(addr.is_unnamed(), false);
         /// ```
-        #[derive(Clone)]
-        pub struct SocketAddr {
-            _private: (),
+        ///
+        /// An unnamed address:
+        ///
+        /// ```no_run
+        /// use async_std::os::unix::net::UnixDatagram;
+        ///
+        /// let socket = UnixDatagram::unbound().await?;
+        /// let addr = socket.local_addr()?;
+        /// assert_eq!(addr.is_unnamed(), true);
+        /// ```
+        pub fn is_unnamed(&self) -> bool {
+            unreachable!("this impl only appears in the rendered docs")
         }
 
-        impl SocketAddr {
-            /// Returns `true` if the address is unnamed.
-            ///
-            /// # Examples
-            ///
-            /// A named address:
-            ///
-            /// ```no_run
-            /// use async_std::os::unix::net::UnixListener;
-            ///
-            /// let socket = UnixListener::bind("/tmp/socket").await?;
-            /// let addr = socket.local_addr()?;
-            /// assert_eq!(addr.is_unnamed(), false);
-            /// ```
-            ///
-            /// An unnamed address:
-            ///
-            /// ```no_run
-            /// use async_std::os::unix::net::UnixDatagram;
-            ///
-            /// let socket = UnixDatagram::unbound().await?;
-            /// let addr = socket.local_addr()?;
-            /// assert_eq!(addr.is_unnamed(), true);
-            /// ```
-            pub fn is_unnamed(&self) -> bool {
-                unreachable!("this impl only appears in the rendered docs")
-            }
-
-            /// Returns the contents of this address if it is a `pathname` address.
-            ///
-            /// # Examples
-            ///
-            /// With a pathname:
-            ///
-            /// ```no_run
-            /// use std::path::Path;
-            ///
-            /// use async_std::os::unix::net::UnixListener;
-            ///
-            /// let socket = UnixListener::bind("/tmp/socket").await?;
-            /// let addr = socket.local_addr()?;
-            /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket")));
-            /// ```
-            ///
-            /// Without a pathname:
-            ///
-            /// ```
-            /// use async_std::os::unix::net::UnixDatagram;
-            ///
-            /// let socket = UnixDatagram::unbound()?;
-            /// let addr = socket.local_addr()?;
-            /// assert_eq!(addr.as_pathname(), None);
-            /// ```
-            pub fn as_pathname(&self) -> Option<&Path> {
-                unreachable!("this impl only appears in the rendered docs")
-            }
+        /// Returns the contents of this address if it is a `pathname` address.
+        ///
+        /// # Examples
+        ///
+        /// With a pathname:
+        ///
+        /// ```no_run
+        /// use async_std::os::unix::net::UnixListener;
+        /// use async_std::path::Path;
+        ///
+        /// let socket = UnixListener::bind("/tmp/socket").await?;
+        /// let addr = socket.local_addr()?;
+        /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/socket")));
+        /// ```
+        ///
+        /// Without a pathname:
+        ///
+        /// ```
+        /// use async_std::os::unix::net::UnixDatagram;
+        ///
+        /// let socket = UnixDatagram::unbound()?;
+        /// let addr = socket.local_addr()?;
+        /// assert_eq!(addr.as_pathname(), None);
+        /// ```
+        pub fn as_pathname(&self) -> Option<&Path> {
+            unreachable!("this impl only appears in the rendered docs")
         }
+    }
 
-        impl fmt::Debug for SocketAddr {
-            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-                unreachable!("this impl only appears in the rendered docs")
-            }
+    impl fmt::Debug for SocketAddr {
+        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+            unreachable!("this impl only appears in the rendered docs")
         }
-    } else {
-        pub use std::os::unix::net::SocketAddr;
     }
 }
diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs
index ae30b5bf9..4a4f45ad6 100644
--- a/src/os/unix/net/stream.rs
+++ b/src/os/unix/net/stream.rs
@@ -1,18 +1,18 @@
 //! Unix-specific networking extensions.
 
 use std::fmt;
-use std::io::{Read as _, Write as _};
 use std::net::Shutdown;
-use std::path::Path;
+use std::os::unix::net::UnixStream as StdUnixStream;
 use std::pin::Pin;
 
-use mio_uds;
+use async_io::Async;
 
 use super::SocketAddr;
 use crate::io::{self, Read, Write};
-use crate::net::driver::Watcher;
 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
-use crate::task::{blocking, Context, Poll};
+use crate::path::Path;
+use crate::sync::Arc;
+use crate::task::{Context, Poll};
 
 /// A Unix stream socket.
 ///
@@ -37,8 +37,9 @@ use crate::task::{blocking, Context, Poll};
 /// #
 /// # Ok(()) }) }
 /// ```
+#[derive(Clone)]
 pub struct UnixStream {
-    pub(super) watcher: Watcher<mio_uds::UnixStream>,
+    pub(super) watcher: Arc<Async<StdUnixStream>>,
 }
 
 impl UnixStream {
@@ -57,15 +58,9 @@ impl UnixStream {
     /// ```
     pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
         let path = path.as_ref().to_owned();
+        let stream = Arc::new(Async::<StdUnixStream>::connect(path).await?);
 
-        blocking::spawn(async move {
-            let std_stream = std::os::unix::net::UnixStream::connect(path)?;
-            let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?;
-            Ok(UnixStream {
-                watcher: Watcher::new(mio_stream),
-            })
-        })
-        .await
+        Ok(UnixStream { watcher: stream })
     }
 
     /// Creates an unnamed pair of connected sockets.
@@ -84,12 +79,12 @@ impl UnixStream {
     /// # Ok(()) }) }
     /// ```
     pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
-        let (a, b) = mio_uds::UnixStream::pair()?;
+        let (a, b) = Async::<StdUnixStream>::pair()?;
         let a = UnixStream {
-            watcher: Watcher::new(a),
+            watcher: Arc::new(a),
         };
         let b = UnixStream {
-            watcher: Watcher::new(b),
+            watcher: Arc::new(b),
         };
         Ok((a, b))
     }
@@ -169,7 +164,7 @@ impl Read for &UnixStream {
         cx: &mut Context<'_>,
         buf: &mut [u8],
     ) -> Poll<io::Result<usize>> {
-        self.watcher.poll_read_with(cx, |mut inner| inner.read(buf))
+        Pin::new(&mut &*self.watcher).poll_read(cx, buf)
     }
 }
 
@@ -197,16 +192,15 @@ impl Write for &UnixStream {
         cx: &mut Context<'_>,
         buf: &[u8],
     ) -> Poll<io::Result<usize>> {
-        self.watcher
-            .poll_write_with(cx, |mut inner| inner.write(buf))
+        Pin::new(&mut &*self.watcher).poll_write(cx, buf)
     }
 
     fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
-        self.watcher.poll_write_with(cx, |mut inner| inner.flush())
+        Pin::new(&mut &*self.watcher).poll_flush(cx)
     }
 
-    fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
-        Poll::Ready(Ok(()))
+    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
+        Pin::new(&mut &*self.watcher).poll_close(cx)
     }
 }
 
@@ -227,19 +221,34 @@ impl fmt::Debug for UnixStream {
     }
 }
 
-impl From<std::os::unix::net::UnixStream> for UnixStream {
+impl From<StdUnixStream> for UnixStream {
     /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent.
-    fn from(stream: std::os::unix::net::UnixStream) -> UnixStream {
-        let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap();
+    fn from(stream: StdUnixStream) -> UnixStream {
+        let stream = Async::new(stream).expect("UnixStream is known to be good");
         UnixStream {
-            watcher: Watcher::new(mio_stream),
+            watcher: Arc::new(stream),
         }
     }
 }
 
+impl std::convert::TryFrom<UnixStream> for StdUnixStream {
+    type Error = io::Error;
+    /// Converts a `UnixStream` into its synchronous equivalent.
+    fn try_from(stream: UnixStream) -> io::Result<StdUnixStream> {
+        let inner = Arc::try_unwrap(stream.watcher)
+            .map_err(|_| io::Error::new(
+                io::ErrorKind::Other,
+                "Cannot convert UnixStream to synchronous: multiple references",
+            ))?
+            .into_inner()?;
+        inner.set_nonblocking(false)?;
+        Ok(inner)
+    }
+}
+
 impl AsRawFd for UnixStream {
     fn as_raw_fd(&self) -> RawFd {
-        self.watcher.get_ref().as_raw_fd()
+        self.watcher.as_raw_fd()
     }
 }
 
@@ -252,6 +261,28 @@ impl FromRawFd for UnixStream {
 
 impl IntoRawFd for UnixStream {
     fn into_raw_fd(self) -> RawFd {
-        self.watcher.into_inner().into_raw_fd()
+        (*self.watcher).get_ref().try_clone().unwrap().into_raw_fd()
+    }
+}
+
+cfg_io_safety! {
+    use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
+
+    impl AsFd for UnixStream {
+        fn as_fd(&self) -> BorrowedFd<'_> {
+            self.watcher.get_ref().as_fd()
+        }
+    }
+
+    impl From<OwnedFd> for UnixStream {
+        fn from(fd: OwnedFd) -> UnixStream {
+            std::os::unix::net::UnixStream::from(fd).into()
+        }
+    }
+
+    impl From<UnixStream> for OwnedFd {
+        fn from(stream: UnixStream) -> OwnedFd {
+            stream.watcher.get_ref().try_clone().unwrap().into()
+        }
     }
 }
diff --git a/src/os/windows/fs.rs b/src/os/windows/fs.rs
new file mode 100644
index 000000000..243f3819d
--- /dev/null
+++ b/src/os/windows/fs.rs
@@ -0,0 +1,55 @@
+//! Windows-specific filesystem extensions.
+
+use crate::io;
+use crate::path::Path;
+use crate::task::spawn_blocking;
+
+/// Creates a new directory symbolic link on the filesystem.
+///
+/// The `dst` path will be a directory symbolic link pointing to the `src` path.
+///
+/// This function is an async version of [`std::os::windows::fs::symlink_dir`].
+///
+/// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
+///
+/// # Examples
+///
+/// ```no_run
+/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
+/// use async_std::os::windows::fs::symlink_dir;
+///
+/// symlink_dir("a", "b").await?;
+/// #
+/// # Ok(()) }) }
+/// ```
+pub async fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
+    let src = src.as_ref().to_owned();
+    let dst = dst.as_ref().to_owned();
+    spawn_blocking(move || std::os::windows::fs::symlink_dir(&src, &dst)).await
+}
+
+/// Creates a new file symbolic link on the filesystem.
+///
+/// The `dst` path will be a file symbolic link pointing to the `src` path.
+///
+/// This function is an async version of [`std::os::windows::fs::symlink_file`].
+///
+/// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
+///
+/// # Examples
+///
+/// ```no_run
+/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
+/// use async_std::os::windows::fs::symlink_file;
+///
+/// symlink_file("a.txt", "b.txt").await?;
+/// #
+/// # Ok(()) }) }
+/// ```
+pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
+    let src = src.as_ref().to_owned();
+    let dst = dst.as_ref().to_owned();
+    spawn_blocking(move || std::os::windows::fs::symlink_file(&src, &dst)).await
+}
diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs
index 20f87d294..caffc6fc6 100644
--- a/src/os/windows/io.rs
+++ b/src/os/windows/io.rs
@@ -1,50 +1,93 @@
 //! Windows-specific I/O extensions.
 
-use cfg_if::cfg_if;
-
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        /// Raw HANDLEs.
-        pub type RawHandle = *mut std::os::raw::c_void;
-
-        /// Raw SOCKETs.
-        pub type RawSocket = u64;
-
-        /// Extracts raw handles.
-        pub trait AsRawHandle {
-            /// Extracts the raw handle, without taking any ownership.
-            fn as_raw_handle(&self) -> RawHandle;
-        }
-
-        /// Construct I/O objects from raw handles.
-        pub trait FromRawHandle {
-            /// Constructs a new I/O object from the specified raw handle.
-            ///
-            /// This function will **consume ownership** of the handle given,
-            /// passing responsibility for closing the handle to the returned
-            /// object.
-            ///
-            /// This function is also unsafe as the primitives currently returned
-            /// have the contract that they are the sole owner of the file
-            /// descriptor they are wrapping. Usage of this function could
-            /// accidentally allow violating this contract which can cause memory
-            /// unsafety in code that relies on it being true.
-            unsafe fn from_raw_handle(handle: RawHandle) -> Self;
-        }
-
-        /// A trait to express the ability to consume an object and acquire ownership of
-        /// its raw `HANDLE`.
-        pub trait IntoRawHandle {
-            /// Consumes this object, returning the raw underlying handle.
-            ///
-            /// This function **transfers ownership** of the underlying handle to the
-            /// caller. Callers are then the unique owners of the handle and must close
-            /// it once it's no longer needed.
-            fn into_raw_handle(self) -> RawHandle;
-        }
-    } else {
+cfg_not_docs! {
+    pub use std::os::windows::io::{
+        AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle,
+        AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket,
+    };
+
+    cfg_io_safety! {
+        pub use std::os::windows::io::{
+            AsHandle, BorrowedHandle, OwnedHandle,
+            AsSocket, BorrowedSocket, OwnedSocket,
+        };
+    }
+}
+
+cfg_docs! {
+    /// Raw HANDLEs.
+    pub type RawHandle = *mut std::os::raw::c_void;
+
+    /// Raw SOCKETs.
+    pub type RawSocket = u64;
+
+    /// Extracts raw handles.
+    pub trait AsRawHandle {
+        /// Extracts the raw handle, without taking any ownership.
+        fn as_raw_handle(&self) -> RawHandle;
+    }
+
+    /// Construct I/O objects from raw handles.
+    pub trait FromRawHandle {
+        /// Constructs a new I/O object from the specified raw handle.
+        ///
+        /// This function will **consume ownership** of the handle given,
+        /// passing responsibility for closing the handle to the returned
+        /// object.
+        ///
+        /// This function is also unsafe as the primitives currently returned
+        /// have the contract that they are the sole owner of the file
+        /// descriptor they are wrapping. Usage of this function could
+        /// accidentally allow violating this contract which can cause memory
+        /// unsafety in code that relies on it being true.
+        unsafe fn from_raw_handle(handle: RawHandle) -> Self;
+    }
+
+    /// A trait to express the ability to consume an object and acquire ownership of
+    /// its raw `HANDLE`.
+    pub trait IntoRawHandle {
+        /// Consumes this object, returning the raw underlying handle.
+        ///
+        /// This function **transfers ownership** of the underlying handle to the
+        /// caller. Callers are then the unique owners of the handle and must close
+        /// it once it's no longer needed.
+        fn into_raw_handle(self) -> RawHandle;
+    }
+
+    /// Creates I/O objects from raw sockets.
+    pub trait FromRawSocket {
+        /// Creates a new I/O object from the given raw socket.
+        ///
+        /// This function will consume ownership of the socket provided and it will be closed when the returned object goes out of scope.
+        ///
+        /// This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the
+        /// file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause
+        /// memory unsafety in code that relies on it being true.
+        unsafe fn from_raw_socket(sock: RawSocket) -> Self;
+    }
+
+    /// Extracts raw sockets.
+    pub trait AsRawSocket {
+        /// Extracts the underlying raw socket from this object.
+        fn as_raw_socket(&self) -> RawSocket;
+    }
+
+    /// A trait to express the ability to consume an object and acquire ownership of
+    /// its raw `SOCKET`.
+    pub trait IntoRawSocket {
+        /// Consumes this object, returning the raw underlying socket.
+        ///
+        /// This function **transfers ownership** of the underlying socket to the
+        /// caller. Callers are then the unique owners of the socket and must close
+        /// it once it's no longer needed.
+        fn into_raw_socket(self) -> RawSocket;
+    }
+
+    cfg_io_safety! {
+        #[doc(inline)]
         pub use std::os::windows::io::{
-            AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket,
+            AsHandle, BorrowedHandle, OwnedHandle,
+            AsSocket, BorrowedSocket, OwnedSocket,
         };
     }
 }
diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs
index 30218f0ef..0d45a52e4 100644
--- a/src/os/windows/mod.rs
+++ b/src/os/windows/mod.rs
@@ -1,3 +1,13 @@
 //! Platform-specific extensions for Windows.
 
-pub mod io;
+cfg_std! {
+    pub mod io;
+}
+
+cfg_unstable! {
+    #[cfg(feature = "default")]
+    pub mod fs;
+    #[cfg(feature = "std")]
+    #[cfg(windows)]
+    pub use async_process::windows as process;
+}
diff --git a/src/path/ancestors.rs b/src/path/ancestors.rs
new file mode 100644
index 000000000..c7237ffd8
--- /dev/null
+++ b/src/path/ancestors.rs
@@ -0,0 +1,39 @@
+use std::iter::FusedIterator;
+
+use crate::path::Path;
+
+/// An iterator over [`Path`] and its ancestors.
+///
+/// This `struct` is created by the [`ancestors`] method on [`Path`].
+/// See its documentation for more.
+///
+/// # Examples
+///
+/// ```
+/// use async_std::path::Path;
+///
+/// let path = Path::new("/foo/bar");
+///
+/// for ancestor in path.ancestors() {
+///     println!("{}", ancestor.display());
+/// }
+/// ```
+///
+/// [`ancestors`]: struct.Path.html#method.ancestors
+/// [`Path`]: struct.Path.html
+#[derive(Copy, Clone, Debug)]
+pub struct Ancestors<'a> {
+    pub(crate) next: Option<&'a Path>,
+}
+
+impl<'a> Iterator for Ancestors<'a> {
+    type Item = &'a Path;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let next = self.next;
+        self.next = next.and_then(Path::parent);
+        next
+    }
+}
+
+impl FusedIterator for Ancestors<'_> {}
diff --git a/src/path/components.rs b/src/path/components.rs
new file mode 100644
index 000000000..51649c55c
--- /dev/null
+++ b/src/path/components.rs
@@ -0,0 +1,82 @@
+use std::ffi::OsStr;
+use std::iter::FusedIterator;
+
+use crate::path::{Component, Path};
+
+/// An iterator over the [`Component`]s of a [`Path`].
+///
+/// This `struct` is created by the [`components`] method on [`Path`].
+/// See its documentation for more.
+///
+/// # Examples
+///
+/// ```
+/// use async_std::path::Path;
+///
+/// let path = Path::new("/tmp/foo/bar.txt");
+///
+/// for component in path.components() {
+///     println!("{:?}", component);
+/// }
+/// ```
+///
+/// [`Component`]: enum.Component.html
+/// [`components`]: struct.Path.html#method.components
+/// [`Path`]: struct.Path.html
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Components<'a> {
+    pub(crate) inner: std::path::Components<'a>,
+}
+
+impl<'a> Components<'a> {
+    /// Extracts a slice corresponding to the portion of the path remaining for iteration.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let mut components = Path::new("/tmp/foo/bar.txt").components();
+    /// components.next();
+    /// components.next();
+    ///
+    /// assert_eq!(Path::new("foo/bar.txt"), components.as_path());
+    /// ```
+    pub fn as_path(&self) -> &'a Path {
+        self.inner.as_path().into()
+    }
+}
+
+impl AsRef<Path> for Components<'_> {
+    fn as_ref(&self) -> &Path {
+        self.as_path()
+    }
+}
+
+impl AsRef<OsStr> for Components<'_> {
+    fn as_ref(&self) -> &OsStr {
+        self.as_path().as_os_str()
+    }
+}
+
+impl<'a> Iterator for Components<'a> {
+    type Item = Component<'a>;
+
+    fn next(&mut self) -> Option<Component<'a>> {
+        self.inner.next()
+    }
+}
+
+impl<'a> DoubleEndedIterator for Components<'a> {
+    fn next_back(&mut self) -> Option<Component<'a>> {
+        self.inner.next_back()
+    }
+}
+
+impl FusedIterator for Components<'_> {}
+
+impl AsRef<Path> for Component<'_> {
+    fn as_ref(&self) -> &Path {
+        self.as_os_str().as_ref()
+    }
+}
diff --git a/src/path/iter.rs b/src/path/iter.rs
new file mode 100644
index 000000000..b4061003b
--- /dev/null
+++ b/src/path/iter.rs
@@ -0,0 +1,82 @@
+use std::ffi::OsStr;
+use std::fmt;
+use std::iter::FusedIterator;
+
+use crate::path::{Component, Components, Path};
+
+/// An iterator over the [`Component`]s of a [`Path`], as [`OsStr`] slices.
+///
+/// This `struct` is created by the [`iter`] method on [`Path`].
+/// See its documentation for more.
+///
+/// [`Component`]: enum.Component.html
+/// [`iter`]: struct.Path.html#method.iter
+/// [`OsStr`]: ../../std/ffi/struct.OsStr.html
+/// [`Path`]: struct.Path.html
+#[derive(Clone)]
+pub struct Iter<'a> {
+    pub(crate) inner: Components<'a>,
+}
+
+impl<'a> Iter<'a> {
+    /// Extracts a slice corresponding to the portion of the path remaining for iteration.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let mut iter = Path::new("/tmp/foo/bar.txt").iter();
+    /// iter.next();
+    /// iter.next();
+    ///
+    /// assert_eq!(Path::new("foo/bar.txt"), iter.as_path());
+    /// ```
+    pub fn as_path(&self) -> &'a Path {
+        self.inner.as_path()
+    }
+}
+
+impl<'a> Iterator for Iter<'a> {
+    type Item = &'a OsStr;
+
+    fn next(&mut self) -> Option<&'a OsStr> {
+        self.inner.next().map(Component::as_os_str)
+    }
+}
+
+impl fmt::Debug for Iter<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        struct DebugHelper<'a>(&'a Path);
+
+        impl fmt::Debug for DebugHelper<'_> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.debug_list().entries(self.0.iter()).finish()
+            }
+        }
+
+        f.debug_tuple("Iter")
+            .field(&DebugHelper(self.as_path()))
+            .finish()
+    }
+}
+
+impl AsRef<Path> for Iter<'_> {
+    fn as_ref(&self) -> &Path {
+        self.as_path()
+    }
+}
+
+impl AsRef<OsStr> for Iter<'_> {
+    fn as_ref(&self) -> &OsStr {
+        self.as_path().as_os_str()
+    }
+}
+
+impl<'a> DoubleEndedIterator for Iter<'a> {
+    fn next_back(&mut self) -> Option<&'a OsStr> {
+        self.inner.next_back().map(Component::as_os_str)
+    }
+}
+
+impl FusedIterator for Iter<'_> {}
diff --git a/src/path/mod.rs b/src/path/mod.rs
index 915324391..7ce9b62d6 100644
--- a/src/path/mod.rs
+++ b/src/path/mod.rs
@@ -2,20 +2,86 @@
 //!
 //! This module is an async version of [`std::path`].
 //!
+//! This module provides two types, [`PathBuf`] and [`Path`][`Path`] (akin to [`String`]
+//! and [`str`]), for working with paths abstractly. These types are thin wrappers
+//! around [`OsString`] and [`OsStr`] respectively, meaning that they work directly
+//! on strings according to the local platform's path syntax.
+//!
+//! Paths can be parsed into [`Component`]s by iterating over the structure
+//! returned by the [`components`] method on [`Path`]. [`Component`]s roughly
+//! correspond to the substrings between path separators (`/` or `\`). You can
+//! reconstruct an equivalent path from components with the [`push`] method on
+//! [`PathBuf`]; note that the paths may differ syntactically by the
+//! normalization described in the documentation for the [`components`] method.
+//!
 //! [`std::path`]: https://doc.rust-lang.org/std/path/index.html
+//!
+//! ## Simple usage
+//!
+//! Path manipulation includes both parsing components from slices and building
+//! new owned paths.
+//!
+//! To parse a path, you can create a [`Path`] slice from a [`str`]
+//! slice and start asking questions:
+//!
+//! ```
+//! use async_std::path::Path;
+//! use std::ffi::OsStr;
+//!
+//! let path = Path::new("/tmp/foo/bar.txt");
+//!
+//! let parent = path.parent();
+//! assert_eq!(parent, Some(Path::new("/tmp/foo")));
+//!
+//! let file_stem = path.file_stem();
+//! assert_eq!(file_stem, Some(OsStr::new("bar")));
+//!
+//! let extension = path.extension();
+//! assert_eq!(extension, Some(OsStr::new("txt")));
+//! ```
+//!
+//! To build or modify paths, use [`PathBuf`]:
+//!
+//! ```
+//! use async_std::path::PathBuf;
+//!
+//! // This way works...
+//! let mut path = PathBuf::from("c:\\");
+//!
+//! path.push("windows");
+//! path.push("system32");
+//!
+//! path.set_extension("dll");
+//!
+//! // ... but push is best used if you don't know everything up
+//! // front. If you do, this way is better:
+//! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect();
+//! ```
+//!
+//! [`Component`]: enum.Component.html
+//! [`components`]: struct.Path.html#method.components
+//! [`PathBuf`]: struct.PathBuf.html
+//! [`Path`]: struct.Path.html
+//! [`push`]: struct.PathBuf.html#method.push
+//! [`String`]: https://doc.rust-lang.org/std/string/struct.String.html
+//!
+//! [`str`]: https://doc.rust-lang.org/std/primitive.str.html
+//! [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html
+//! [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
 
-// Structs re-export
-#[doc(inline)]
-pub use std::path::{Ancestors, Components, Display, Iter, PrefixComponent, StripPrefixError};
-
-// Enums re-export
-#[doc(inline)]
-pub use std::path::{Component, Prefix};
+mod ancestors;
+mod components;
+mod iter;
+mod path;
+mod pathbuf;
 
-// Constants re-export
 #[doc(inline)]
-pub use std::path::MAIN_SEPARATOR;
+pub use std::path::{
+    is_separator, Component, Display, Prefix, PrefixComponent, StripPrefixError, MAIN_SEPARATOR,
+};
 
-// Functions re-export
-#[doc(inline)]
-pub use std::path::is_separator;
+pub use ancestors::Ancestors;
+pub use components::Components;
+pub use iter::Iter;
+pub use path::Path;
+pub use pathbuf::PathBuf;
diff --git a/src/path/path.rs b/src/path/path.rs
new file mode 100644
index 000000000..185bfaff0
--- /dev/null
+++ b/src/path/path.rs
@@ -0,0 +1,1041 @@
+use std::borrow::{Cow, ToOwned};
+use std::cmp::Ordering;
+use std::ffi::{OsStr, OsString};
+use std::rc::Rc;
+use std::sync::Arc;
+
+use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError};
+#[cfg(not(target_os = "unknown"))]
+use crate::{fs, io};
+
+/// A slice of a path.
+///
+/// This struct is an async version of [`std::path::Path`].
+///
+/// This type supports a number of operations for inspecting a path, including
+/// breaking the path into its components (separated by `/` on Unix and by either
+/// `/` or `\` on Windows), extracting the file name, determining whether the path
+/// is absolute, and so on.
+///
+/// This is an *unsized* type, meaning that it must always be used behind a
+/// pointer like `&` or `Box`. For an owned version of this type,
+/// see [`PathBuf`].
+///
+/// [`PathBuf`]: struct.PathBuf.html
+/// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
+///
+/// More details about the overall approach can be found in
+/// the [module documentation](index.html).
+///
+/// # Examples
+///
+/// ```
+/// use std::path::Path;
+/// use std::ffi::OsStr;
+///
+/// // Note: this example does work on Windows
+/// let path = Path::new("./foo/bar.txt");
+///
+/// let parent = path.parent();
+/// assert_eq!(parent, Some(Path::new("./foo")));
+///
+/// let file_stem = path.file_stem();
+/// assert_eq!(file_stem, Some(OsStr::new("bar")));
+///
+/// let extension = path.extension();
+/// assert_eq!(extension, Some(OsStr::new("txt")));
+/// ```
+#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Path {
+    inner: std::path::Path,
+}
+
+impl Path {
+    /// Directly wraps a string slice as a `Path` slice.
+    ///
+    /// This is a cost-free conversion.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// Path::new("foo.txt");
+    /// ```
+    ///
+    /// You can create `Path`s from `String`s, or even other `Path`s:
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let string = String::from("foo.txt");
+    /// let from_string = Path::new(&string);
+    /// let from_path = Path::new(&from_string);
+    /// assert_eq!(from_string, from_path);
+    /// ```
+    pub fn new<S: AsRef<OsStr> + ?Sized>(s: &S) -> &Path {
+        unsafe { &*(std::path::Path::new(s) as *const std::path::Path as *const Path) }
+    }
+
+    /// Returns the underlying [`OsStr`] slice.
+    ///
+    /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::ffi::OsStr;
+    ///
+    /// use async_std::path::Path;
+    ///
+    /// let os_str = Path::new("foo.txt").as_os_str();
+    /// assert_eq!(os_str, OsStr::new("foo.txt"));
+    /// ```
+    pub fn as_os_str(&self) -> &OsStr {
+        self.inner.as_os_str()
+    }
+
+    /// Returns a [`&str`] slice if the `Path` is valid unicode.
+    ///
+    /// This conversion may entail doing a check for UTF-8 validity.
+    /// Note that validation is performed because non-UTF-8 strings are
+    /// perfectly valid for some OS.
+    ///
+    /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("foo.txt");
+    /// assert_eq!(path.to_str(), Some("foo.txt"));
+    /// ```
+    pub fn to_str(&self) -> Option<&str> {
+        self.inner.to_str()
+    }
+
+    /// Converts a `Path` to a [`Cow<str>`].
+    ///
+    /// Any non-Unicode sequences are replaced with
+    /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
+    ///
+    /// [`Cow<str>`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html
+    /// [U+FFFD]: https://doc.rust-lang.org/std/char/constant.REPLACEMENT_CHARACTER.html
+    ///
+    /// # Examples
+    ///
+    /// Calling `to_string_lossy` on a `Path` with valid unicode:
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("foo.txt");
+    /// assert_eq!(path.to_string_lossy(), "foo.txt");
+    /// ```
+    ///
+    /// Had `path` contained invalid unicode, the `to_string_lossy` call might
+    /// have returned `"fo�.txt"`.
+    pub fn to_string_lossy(&self) -> Cow<'_, str> {
+        self.inner.to_string_lossy()
+    }
+
+    /// Converts a `Path` to an owned [`PathBuf`].
+    ///
+    /// [`PathBuf`]: struct.PathBuf.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let path_buf = Path::new("foo.txt").to_path_buf();
+    /// assert_eq!(path_buf, PathBuf::from("foo.txt"));
+    /// ```
+    pub fn to_path_buf(&self) -> PathBuf {
+        PathBuf::from(self.inner.to_path_buf())
+    }
+
+    /// Returns `true` if the `Path` is absolute, i.e. if it is independent of
+    /// the current directory.
+    ///
+    /// * On Unix, a path is absolute if it starts with the root, so
+    ///   `is_absolute` and [`has_root`] are equivalent.
+    ///
+    /// * On Windows, a path is absolute if it has a prefix and starts with the
+    ///   root: `c:\windows` is absolute, while `c:temp` and `\temp` are not.
+    ///
+    /// [`has_root`]: #method.has_root
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// assert!(!Path::new("foo.txt").is_absolute());
+    /// ```
+    pub fn is_absolute(&self) -> bool {
+        self.inner.is_absolute()
+    }
+
+    /// Returns `true` if the `Path` is relative, i.e. not absolute.
+    ///
+    /// See [`is_absolute`]'s documentation for more details.
+    ///
+    /// [`is_absolute`]: #method.is_absolute
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// assert!(Path::new("foo.txt").is_relative());
+    /// ```
+    pub fn is_relative(&self) -> bool {
+        self.inner.is_relative()
+    }
+
+    /// Returns `true` if the `Path` has a root.
+    ///
+    /// * On Unix, a path has a root if it begins with `/`.
+    ///
+    /// * On Windows, a path has a root if it:
+    ///     * has no prefix and begins with a separator, e.g. `\windows`
+    ///     * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows`
+    ///     * has any non-disk prefix, e.g. `\\server\share`
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// assert!(Path::new("/etc/passwd").has_root());
+    /// ```
+    pub fn has_root(&self) -> bool {
+        self.inner.has_root()
+    }
+
+    /// Returns the `Path` without its final component, if there is one.
+    ///
+    /// Returns [`None`] if the path terminates in a root or prefix.
+    ///
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/foo/bar");
+    /// let parent = path.parent().unwrap();
+    /// assert_eq!(parent, Path::new("/foo"));
+    ///
+    /// let grand_parent = parent.parent().unwrap();
+    /// assert_eq!(grand_parent, Path::new("/"));
+    /// assert_eq!(grand_parent.parent(), None);
+    /// ```
+    pub fn parent(&self) -> Option<&Path> {
+        self.inner.parent().map(|p| p.into())
+    }
+
+    /// Produces an iterator over `Path` and its ancestors.
+    ///
+    /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero
+    /// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`,
+    /// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns
+    /// [`None`], the iterator will do likewise. The iterator will always yield at least one value,
+    /// namely `&self`.
+    ///
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html
+    /// [`parent`]: struct.Path.html#method.parent
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let mut ancestors = Path::new("/foo/bar").ancestors();
+    /// assert_eq!(ancestors.next(), Some(Path::new("/foo/bar").into()));
+    /// assert_eq!(ancestors.next(), Some(Path::new("/foo").into()));
+    /// assert_eq!(ancestors.next(), Some(Path::new("/").into()));
+    /// assert_eq!(ancestors.next(), None);
+    /// ```
+    pub fn ancestors(&self) -> Ancestors<'_> {
+        Ancestors { next: Some(&self) }
+    }
+
+    /// Returns the final component of the `Path`, if there is one.
+    ///
+    /// If the path is a normal file, this is the file name. If it's the path of a directory, this
+    /// is the directory name.
+    ///
+    /// Returns [`None`] if the path terminates in `..`.
+    ///
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::ffi::OsStr;
+    ///
+    /// use async_std::path::Path;
+    ///
+    /// assert_eq!(Some(OsStr::new("bin")), Path::new("/usr/bin/").file_name());
+    /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("tmp/foo.txt").file_name());
+    /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name());
+    /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name());
+    /// assert_eq!(None, Path::new("foo.txt/..").file_name());
+    /// assert_eq!(None, Path::new("/").file_name());
+    /// ```
+    pub fn file_name(&self) -> Option<&OsStr> {
+        self.inner.file_name()
+    }
+
+    /// Returns a path that becomes `self` when joined onto `base`.
+    ///
+    /// # Errors
+    ///
+    /// If `base` is not a prefix of `self` (i.e., [`starts_with`]
+    /// returns `false`), returns [`Err`].
+    ///
+    /// [`starts_with`]: #method.starts_with
+    /// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let path = Path::new("/test/haha/foo.txt");
+    ///
+    /// assert_eq!(path.strip_prefix("/"), Ok(Path::new("test/haha/foo.txt")));
+    /// assert_eq!(path.strip_prefix("/test"), Ok(Path::new("haha/foo.txt")));
+    /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt")));
+    /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new("")));
+    /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new("")));
+    /// assert_eq!(path.strip_prefix("test").is_ok(), false);
+    /// assert_eq!(path.strip_prefix("/haha").is_ok(), false);
+    ///
+    /// let prefix = PathBuf::from("/test/");
+    /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt")));
+    /// ```
+    pub fn strip_prefix<P>(&self, base: P) -> Result<&Path, StripPrefixError>
+    where
+        P: AsRef<Path>,
+    {
+        Ok(self.inner.strip_prefix(base.as_ref())?.into())
+    }
+
+    /// Determines whether `base` is a prefix of `self`.
+    ///
+    /// Only considers whole path components to match.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/etc/passwd");
+    ///
+    /// assert!(path.starts_with("/etc"));
+    /// assert!(path.starts_with("/etc/"));
+    /// assert!(path.starts_with("/etc/passwd"));
+    /// assert!(path.starts_with("/etc/passwd/"));
+    ///
+    /// assert!(!path.starts_with("/e"));
+    /// ```
+    pub fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
+        self.inner.starts_with(base.as_ref())
+    }
+
+    /// Determines whether `child` is a suffix of `self`.
+    ///
+    /// Only considers whole path components to match.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/etc/passwd");
+    ///
+    /// assert!(path.ends_with("passwd"));
+    /// ```
+    pub fn ends_with<P: AsRef<Path>>(&self, child: P) -> bool {
+        self.inner.ends_with(child.as_ref())
+    }
+
+    /// Extracts the stem (non-extension) portion of [`file_name`].
+    ///
+    /// [`file_name`]: struct.Path.html#method.file_name
+    ///
+    /// The stem is:
+    ///
+    /// * [`None`], if there is no file name
+    /// * The entire file name if there is no embedded `.`
+    /// * The entire file name if the file name begins with `.` and has no other `.`s within
+    /// * Otherwise, the portion of the file name before the final `.`
+    ///
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("foo.rs");
+    ///
+    /// assert_eq!("foo", path.file_stem().unwrap());
+    /// ```
+    pub fn file_stem(&self) -> Option<&OsStr> {
+        self.inner.file_stem()
+    }
+
+    /// Extracts the extension of [`file_name`], if possible.
+    ///
+    /// The extension is:
+    ///
+    /// * [`None`], if there is no file name
+    /// * [`None`], if there is no embedded `.`
+    /// * [`None`], if the file name begins with `.` and has no other `.`s within
+    /// * Otherwise, the portion of the file name after the final `.`
+    ///
+    /// [`file_name`]: struct.Path.html#method.file_name
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("foo.rs");
+    ///
+    /// assert_eq!("rs", path.extension().unwrap());
+    /// ```
+    pub fn extension(&self) -> Option<&OsStr> {
+        self.inner.extension()
+    }
+
+    /// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
+    ///
+    /// See [`PathBuf::push`] for more details on what it means to adjoin a path.
+    ///
+    /// [`PathBuf`]: struct.PathBuf.html
+    /// [`PathBuf::push`]: struct.PathBuf.html#method.push
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd"));
+    /// ```
+    pub fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf {
+        self.inner.join(path.as_ref()).into()
+    }
+
+    /// Creates an owned [`PathBuf`] like `self` but with the given file name.
+    ///
+    /// See [`PathBuf::set_file_name`] for more details.
+    ///
+    /// [`PathBuf`]: struct.PathBuf.html
+    /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let path = Path::new("/tmp/foo.txt");
+    /// assert_eq!(path.with_file_name("bar.txt"), PathBuf::from("/tmp/bar.txt"));
+    ///
+    /// let path = Path::new("/tmp");
+    /// assert_eq!(path.with_file_name("var"), PathBuf::from("/var"));
+    /// ```
+    pub fn with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> PathBuf {
+        self.inner.with_file_name(file_name).into()
+    }
+
+    /// Creates an owned [`PathBuf`] like `self` but with the given extension.
+    ///
+    /// See [`PathBuf::set_extension`] for more details.
+    ///
+    /// [`PathBuf`]: struct.PathBuf.html
+    /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let path = Path::new("foo.rs");
+    /// assert_eq!(path.with_extension("txt"), PathBuf::from("foo.txt"));
+    /// ```
+    pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
+        self.inner.with_extension(extension).into()
+    }
+
+    /// Produces an iterator over the [`Component`]s of the path.
+    ///
+    /// When parsing the path, there is a small amount of normalization:
+    ///
+    /// * Repeated separators are ignored, so `a/b` and `a//b` both have
+    ///   `a` and `b` as components.
+    ///
+    /// * Occurrences of `.` are normalized away, except if they are at the
+    ///   beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and
+    ///   `a/b` all have `a` and `b` as components, but `./a/b` starts with
+    ///   an additional [`CurDir`] component.
+    ///
+    /// * A trailing slash is normalized away, `/a/b` and `/a/b/` are equivalent.
+    ///
+    /// Note that no other normalization takes place; in particular, `a/c`
+    /// and `a/b/../c` are distinct, to account for the possibility that `b`
+    /// is a symbolic link (so its parent isn't `a`).
+    ///
+    /// [`Component`]: enum.Component.html
+    /// [`CurDir`]: enum.Component.html#variant.CurDir
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::ffi::OsStr;
+    ///
+    /// use async_std::path::{Path, Component};
+    ///
+    /// let mut components = Path::new("/tmp/foo.txt").components();
+    ///
+    /// assert_eq!(components.next(), Some(Component::RootDir));
+    /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("tmp"))));
+    /// assert_eq!(components.next(), Some(Component::Normal(OsStr::new("foo.txt"))));
+    /// assert_eq!(components.next(), None);
+    /// ```
+    pub fn components(&self) -> Components<'_> {
+        Components {
+            inner: self.inner.components(),
+        }
+    }
+
+    /// Produces an iterator over the path's components viewed as [`OsStr`]
+    /// slices.
+    ///
+    /// For more information about the particulars of how the path is separated
+    /// into components, see [`components`].
+    ///
+    /// [`components`]: #method.components
+    /// [`OsStr`]: https://doc.rust-lang.org/std/ffi/struct.OsStr.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::ffi::OsStr;
+    ///
+    /// use async_std::path::{self, Path};
+    ///
+    /// let mut it = Path::new("/tmp/foo.txt").iter();
+    /// assert_eq!(it.next(), Some(OsStr::new(&path::MAIN_SEPARATOR.to_string())));
+    /// assert_eq!(it.next(), Some(OsStr::new("tmp")));
+    /// assert_eq!(it.next(), Some(OsStr::new("foo.txt")));
+    /// assert_eq!(it.next(), None)
+    /// ```
+    pub fn iter(&self) -> Iter<'_> {
+        Iter {
+            inner: self.components(),
+        }
+    }
+
+    /// Returns an object that implements [`Display`] for safely printing paths
+    /// that may contain non-Unicode data.
+    ///
+    /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/tmp/foo.rs");
+    ///
+    /// println!("{}", path.display());
+    /// ```
+    pub fn display(&self) -> Display<'_> {
+        self.inner.display()
+    }
+
+    /// Reads the metadata of a file or directory.
+    ///
+    /// This function will traverse symbolic links to query information about the
+    /// destination file.
+    ///
+    /// This is an alias to [`fs::metadata`].
+    ///
+    /// [`fs::metadata`]: ../fs/fn.metadata.html
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/Minas/tirith");
+    /// let metadata = path.metadata().await?;
+    /// println!("{:?}", metadata.file_type());
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn metadata(&self) -> io::Result<fs::Metadata> {
+        fs::metadata(self).await
+    }
+
+    /// Reads the metadata of a file or directory without following symbolic links.
+    ///
+    /// This is an alias to [`fs::symlink_metadata`].
+    ///
+    /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/Minas/tirith");
+    /// let metadata = path.symlink_metadata().await?;
+    /// println!("{:?}", metadata.file_type());
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
+        fs::symlink_metadata(self).await
+    }
+
+    /// Returns the canonical form of a path.
+    ///
+    /// The returned path is in absolute form with all intermediate components normalized and
+    /// symbolic links resolved.
+    ///
+    /// This is an alias to [`fs::canonicalize`].
+    ///
+    /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let path = Path::new("/foo/test/../test/bar.rs");
+    /// assert_eq!(path.canonicalize().await?, PathBuf::from("/foo/test/bar.rs"));
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn canonicalize(&self) -> io::Result<PathBuf> {
+        fs::canonicalize(self).await
+    }
+
+    /// Reads a symbolic link, returning the file that the link points to.
+    ///
+    /// This is an alias to [`fs::read_link`].
+    ///
+    /// [`fs::read_link`]: ../fs/fn.read_link.html
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::Path;
+    ///
+    /// let path = Path::new("/laputa/sky_castle.rs");
+    /// let path_link = path.read_link().await?;
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn read_link(&self) -> io::Result<PathBuf> {
+        fs::read_link(self).await
+    }
+
+    /// Returns a stream over the entries within a directory.
+    ///
+    /// The stream will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New
+    /// errors may be encountered after an iterator is initially constructed.
+    ///
+    /// This is an alias to [`fs::read_dir`].
+    ///
+    /// [`io::Result`]: ../io/type.Result.html
+    /// [`DirEntry`]: ../fs/struct.DirEntry.html
+    /// [`fs::read_dir`]: ../fs/fn.read_dir.html
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::fs;
+    /// use async_std::path::Path;
+    /// use async_std::prelude::*;
+    ///
+    /// let path = Path::new("/laputa");
+    /// let mut dir = fs::read_dir(&path).await?;
+    ///
+    /// while let Some(res) = dir.next().await {
+    ///     let entry = res?;
+    ///     println!("{}", entry.file_name().to_string_lossy());
+    /// }
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn read_dir(&self) -> io::Result<fs::ReadDir> {
+        fs::read_dir(self).await
+    }
+
+    /// Returns `true` if the path points at an existing entity.
+    ///
+    /// This function will traverse symbolic links to query information about the
+    /// destination file. In case of broken symbolic links this will return `false`.
+    ///
+    /// If you cannot access the directory containing the file, e.g., because of a
+    /// permission error, this will return `false`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::Path;
+    /// assert_eq!(Path::new("does_not_exist.txt").exists().await, false);
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    ///
+    /// # See Also
+    ///
+    /// This is a convenience function that coerces errors to false. If you want to
+    /// check errors, call [fs::metadata].
+    ///
+    /// [fs::metadata]: ../fs/fn.metadata.html
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn exists(&self) -> bool {
+        fs::metadata(self).await.is_ok()
+    }
+
+    /// Returns `true` if the path exists on disk and is pointing at a regular file.
+    ///
+    /// This function will traverse symbolic links to query information about the
+    /// destination file. In case of broken symbolic links this will return `false`.
+    ///
+    /// If you cannot access the directory containing the file, e.g., because of a
+    /// permission error, this will return `false`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::Path;
+    /// assert_eq!(Path::new("./is_a_directory/").is_file().await, false);
+    /// assert_eq!(Path::new("a_file.txt").is_file().await, true);
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    ///
+    /// # See Also
+    ///
+    /// This is a convenience function that coerces errors to false. If you want to
+    /// check errors, call [fs::metadata] and handle its Result. Then call
+    /// [fs::Metadata::is_file] if it was Ok.
+    ///
+    /// [fs::metadata]: ../fs/fn.metadata.html
+    /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn is_file(&self) -> bool {
+        fs::metadata(self)
+            .await
+            .map(|m| m.is_file())
+            .unwrap_or(false)
+    }
+
+    /// Returns `true` if the path exists on disk and is pointing at a directory.
+    ///
+    /// This function will traverse symbolic links to query information about the
+    /// destination file. In case of broken symbolic links this will return `false`.
+    ///
+    /// If you cannot access the directory containing the file, e.g., because of a
+    /// permission error, this will return `false`.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+    /// #
+    /// use async_std::path::Path;
+    ///
+    /// assert_eq!(Path::new("./is_a_directory/").is_dir().await, true);
+    /// assert_eq!(Path::new("a_file.txt").is_dir().await, false);
+    /// #
+    /// # Ok(()) }) }
+    /// ```
+    ///
+    /// # See Also
+    ///
+    /// This is a convenience function that coerces errors to false. If you want to
+    /// check errors, call [fs::metadata] and handle its Result. Then call
+    /// [fs::Metadata::is_dir] if it was Ok.
+    ///
+    /// [fs::metadata]: ../fs/fn.metadata.html
+    /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn is_dir(&self) -> bool {
+        fs::metadata(self)
+            .await
+            .map(|m| m.is_dir())
+            .unwrap_or(false)
+    }
+
+    /// Converts a [`Box<Path>`][`Box`] into a [`PathBuf`] without copying or
+    /// allocating.
+    ///
+    /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
+    /// [`PathBuf`]: struct.PathBuf.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::Path;
+    ///
+    /// let path: Box<Path> = Path::new("foo.txt").into();
+    /// let path_buf = path.into_path_buf();
+    /// ```
+    pub fn into_path_buf(self: Box<Path>) -> PathBuf {
+        let rw = Box::into_raw(self) as *mut std::path::Path;
+        let inner = unsafe { Box::from_raw(rw) };
+        inner.into_path_buf().into()
+    }
+}
+
+impl From<&Path> for Box<Path> {
+    fn from(path: &Path) -> Box<Path> {
+        let boxed: Box<std::path::Path> = path.inner.into();
+        let rw = Box::into_raw(boxed) as *mut Path;
+        unsafe { Box::from_raw(rw) }
+    }
+}
+
+impl From<&Path> for Arc<Path> {
+    /// Converts a Path into a Rc by copying the Path data into a new Rc buffer.
+    #[inline]
+    fn from(s: &Path) -> Arc<Path> {
+        let arc: Arc<OsStr> = Arc::from(s.as_os_str());
+        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) }
+    }
+}
+
+impl From<&Path> for Rc<Path> {
+    #[inline]
+    fn from(s: &Path) -> Rc<Path> {
+        let rc: Rc<OsStr> = Rc::from(s.as_os_str());
+        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) }
+    }
+}
+
+impl ToOwned for Path {
+    type Owned = PathBuf;
+
+    fn to_owned(&self) -> PathBuf {
+        self.to_path_buf()
+    }
+}
+
+impl AsRef<OsStr> for Path {
+    fn as_ref(&self) -> &OsStr {
+        self.inner.as_ref()
+    }
+}
+
+impl AsRef<Path> for Path {
+    fn as_ref(&self) -> &Path {
+        self
+    }
+}
+
+impl AsRef<Path> for OsStr {
+    fn as_ref(&self) -> &Path {
+        Path::new(self)
+    }
+}
+
+impl<'a> From<&'a Path> for Cow<'a, Path> {
+    #[inline]
+    fn from(s: &'a Path) -> Cow<'a, Path> {
+        Cow::Borrowed(s)
+    }
+}
+
+impl AsRef<Path> for Cow<'_, OsStr> {
+    fn as_ref(&self) -> &Path {
+        Path::new(self)
+    }
+}
+
+impl AsRef<Path> for OsString {
+    fn as_ref(&self) -> &Path {
+        Path::new(self)
+    }
+}
+
+impl AsRef<Path> for str {
+    fn as_ref(&self) -> &Path {
+        Path::new(self)
+    }
+}
+
+impl AsRef<Path> for String {
+    fn as_ref(&self) -> &Path {
+        Path::new(self)
+    }
+}
+
+impl AsRef<Path> for PathBuf {
+    fn as_ref(&self) -> &Path {
+        self
+    }
+}
+
+impl<'a> IntoIterator for &'a PathBuf {
+    type Item = &'a OsStr;
+    type IntoIter = Iter<'a>;
+
+    fn into_iter(self) -> Iter<'a> {
+        self.iter()
+    }
+}
+
+impl<'a> IntoIterator for &'a Path {
+    type Item = &'a OsStr;
+    type IntoIter = Iter<'a>;
+
+    fn into_iter(self) -> Iter<'a> {
+        self.iter()
+    }
+}
+
+macro_rules! impl_cmp {
+    ($lhs:ty, $rhs: ty) => {
+        impl<'a, 'b> PartialEq<$rhs> for $lhs {
+            #[inline]
+            fn eq(&self, other: &$rhs) -> bool {
+                <Path as PartialEq>::eq(self, other)
+            }
+        }
+
+        impl<'a, 'b> PartialEq<$lhs> for $rhs {
+            #[inline]
+            fn eq(&self, other: &$lhs) -> bool {
+                <Path as PartialEq>::eq(self, other)
+            }
+        }
+
+        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
+            #[inline]
+            fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
+                <Path as PartialOrd>::partial_cmp(self, other)
+            }
+        }
+
+        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
+            #[inline]
+            fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
+                <Path as PartialOrd>::partial_cmp(self, other)
+            }
+        }
+    };
+}
+
+impl_cmp!(PathBuf, Path);
+impl_cmp!(PathBuf, &'a Path);
+impl_cmp!(Cow<'a, Path>, Path);
+impl_cmp!(Cow<'a, Path>, &'b Path);
+impl_cmp!(Cow<'a, Path>, PathBuf);
+
+macro_rules! impl_cmp_os_str {
+    ($lhs:ty, $rhs: ty) => {
+        impl<'a, 'b> PartialEq<$rhs> for $lhs {
+            #[inline]
+            fn eq(&self, other: &$rhs) -> bool {
+                <Path as PartialEq>::eq(self, other.as_ref())
+            }
+        }
+
+        impl<'a, 'b> PartialEq<$lhs> for $rhs {
+            #[inline]
+            fn eq(&self, other: &$lhs) -> bool {
+                <Path as PartialEq>::eq(self.as_ref(), other)
+            }
+        }
+
+        impl<'a, 'b> PartialOrd<$rhs> for $lhs {
+            #[inline]
+            fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
+                <Path as PartialOrd>::partial_cmp(self, other.as_ref())
+            }
+        }
+
+        impl<'a, 'b> PartialOrd<$lhs> for $rhs {
+            #[inline]
+            fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
+                <Path as PartialOrd>::partial_cmp(self.as_ref(), other)
+            }
+        }
+    };
+}
+
+impl_cmp_os_str!(PathBuf, OsStr);
+impl_cmp_os_str!(PathBuf, &'a OsStr);
+impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>);
+impl_cmp_os_str!(PathBuf, OsString);
+impl_cmp_os_str!(Path, OsStr);
+impl_cmp_os_str!(Path, &'a OsStr);
+impl_cmp_os_str!(Path, Cow<'a, OsStr>);
+impl_cmp_os_str!(Path, OsString);
+impl_cmp_os_str!(&'a Path, OsStr);
+impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>);
+impl_cmp_os_str!(&'a Path, OsString);
+
+impl<'a> From<&'a std::path::Path> for &'a Path {
+    fn from(path: &'a std::path::Path) -> &'a Path {
+        &Path::new(path.as_os_str())
+    }
+}
+
+impl<'a> Into<&'a std::path::Path> for &'a Path {
+    fn into(self) -> &'a std::path::Path {
+        std::path::Path::new(&self.inner)
+    }
+}
+
+impl AsRef<std::path::Path> for Path {
+    fn as_ref(&self) -> &std::path::Path {
+        self.into()
+    }
+}
+
+impl AsRef<Path> for std::path::Path {
+    fn as_ref(&self) -> &Path {
+        self.into()
+    }
+}
+
+impl AsRef<Path> for std::path::PathBuf {
+    fn as_ref(&self) -> &Path {
+        let p: &std::path::Path = self.as_ref();
+        p.into()
+    }
+}
diff --git a/src/path/pathbuf.rs b/src/path/pathbuf.rs
new file mode 100644
index 000000000..f9370cbab
--- /dev/null
+++ b/src/path/pathbuf.rs
@@ -0,0 +1,377 @@
+use std::borrow::{Borrow, Cow};
+use std::ffi::{OsStr, OsString};
+use std::iter::{self, FromIterator};
+use std::ops::Deref;
+#[cfg(feature = "unstable")]
+use std::pin::Pin;
+use std::rc::Rc;
+use std::str::FromStr;
+use std::sync::Arc;
+
+use crate::path::Path;
+#[cfg(feature = "unstable")]
+use crate::prelude::*;
+#[cfg(feature = "unstable")]
+use crate::stream::{self, FromStream, IntoStream};
+
+/// This struct is an async version of [`std::path::PathBuf`].
+///
+/// [`std::path::Path`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html
+#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct PathBuf {
+    inner: std::path::PathBuf,
+}
+
+impl PathBuf {
+    /// Allocates an empty `PathBuf`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::PathBuf;
+    ///
+    /// let path = PathBuf::new();
+    /// ```
+    pub fn new() -> PathBuf {
+        std::path::PathBuf::new().into()
+    }
+
+    /// Coerces to a [`Path`] slice.
+    ///
+    /// [`Path`]: struct.Path.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let p = PathBuf::from("/test");
+    /// assert_eq!(Path::new("/test"), p.as_path());
+    /// ```
+    pub fn as_path(&self) -> &Path {
+        self.inner.as_path().into()
+    }
+
+    /// Extends `self` with `path`.
+    ///
+    /// If `path` is absolute, it replaces the current path.
+    ///
+    /// On Windows:
+    ///
+    /// * if `path` has a root but no prefix (e.g., `\windows`), it
+    ///   replaces everything except for the prefix (if any) of `self`.
+    /// * if `path` has a prefix but no root, it replaces `self`.
+    ///
+    /// # Examples
+    ///
+    /// Pushing a relative path extends the existing path:
+    ///
+    /// ```
+    /// use async_std::path::PathBuf;
+    ///
+    /// let mut path = PathBuf::from("/tmp");
+    /// path.push("file.bk");
+    /// assert_eq!(path, PathBuf::from("/tmp/file.bk"));
+    /// ```
+    ///
+    /// Pushing an absolute path replaces the existing path:
+    ///
+    /// ```
+    /// use async_std::path::PathBuf;
+    ///
+    /// let mut path = PathBuf::from("/tmp");
+    /// path.push("/etc");
+    /// assert_eq!(path, PathBuf::from("/etc"));
+    /// ```
+    pub fn push<P: AsRef<Path>>(&mut self, path: P) {
+        self.inner.push(path.as_ref())
+    }
+
+    /// Truncates `self` to [`self.parent`].
+    ///
+    /// Returns `false` and does nothing if [`self.parent`] is [`None`].
+    /// Otherwise, returns `true`.
+    ///
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    /// [`self.parent`]: struct.PathBuf.html#method.parent
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let mut p = PathBuf::from("/test/test.rs");
+    ///
+    /// p.pop();
+    /// assert_eq!(Path::new("/test"), p);
+    /// p.pop();
+    /// assert_eq!(Path::new("/"), p);
+    /// ```
+    pub fn pop(&mut self) -> bool {
+        self.inner.pop()
+    }
+
+    /// Updates [`self.file_name`] to `file_name`.
+    ///
+    /// If [`self.file_name`] was [`None`], this is equivalent to pushing
+    /// `file_name`.
+    ///
+    /// Otherwise it is equivalent to calling [`pop`] and then pushing
+    /// `file_name`. The new path will be a sibling of the original path.
+    /// (That is, it will have the same parent.)
+    ///
+    /// [`self.file_name`]: struct.PathBuf.html#method.file_name
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    /// [`pop`]: struct.PathBuf.html#method.pop
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::PathBuf;
+    ///
+    /// let mut buf = PathBuf::from("/");
+    /// assert!(buf.file_name() == None);
+    /// buf.set_file_name("bar");
+    /// assert!(buf == PathBuf::from("/bar"));
+    /// assert!(buf.file_name().is_some());
+    /// buf.set_file_name("baz.txt");
+    /// assert!(buf == PathBuf::from("/baz.txt"));
+    /// ```
+    pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S) {
+        self.inner.set_file_name(file_name)
+    }
+
+    /// Updates [`self.extension`] to `extension`.
+    ///
+    /// Returns `false` and does nothing if [`self.file_name`] is [`None`],
+    /// returns `true` and updates the extension otherwise.
+    ///
+    /// If [`self.extension`] is [`None`], the extension is added; otherwise
+    /// it is replaced.
+    ///
+    /// [`self.file_name`]: struct.PathBuf.html#method.file_name
+    /// [`self.extension`]: struct.PathBuf.html#method.extension
+    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::{Path, PathBuf};
+    ///
+    /// let mut p = PathBuf::from("/feel/the");
+    ///
+    /// p.set_extension("force");
+    /// assert_eq!(Path::new("/feel/the.force"), p.as_path());
+    ///
+    /// p.set_extension("dark_side");
+    /// assert_eq!(Path::new("/feel/the.dark_side"), p.as_path());
+    /// ```
+    pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
+        self.inner.set_extension(extension)
+    }
+
+    /// Consumes the `PathBuf`, returning its internal [`OsString`] storage.
+    ///
+    /// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::path::PathBuf;
+    ///
+    /// let p = PathBuf::from("/the/head");
+    /// let os_str = p.into_os_string();
+    /// ```
+    pub fn into_os_string(self) -> OsString {
+        self.inner.into_os_string()
+    }
+
+    /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`].
+    ///
+    /// [`Box`]: https://doc.rust-lang.org/std/boxed/struct.Box.html
+    /// [`Path`]: struct.Path.html
+    pub fn into_boxed_path(self) -> Box<Path> {
+        let rw = Box::into_raw(self.inner.into_boxed_path()) as *mut Path;
+        unsafe { Box::from_raw(rw) }
+    }
+}
+
+impl From<Box<Path>> for PathBuf {
+    fn from(boxed: Box<Path>) -> PathBuf {
+        boxed.into_path_buf()
+    }
+}
+
+impl From<PathBuf> for Box<Path> {
+    fn from(p: PathBuf) -> Box<Path> {
+        p.into_boxed_path()
+    }
+}
+
+impl Clone for Box<Path> {
+    #[inline]
+    fn clone(&self) -> Self {
+        self.to_path_buf().into_boxed_path()
+    }
+}
+
+impl<T: ?Sized + AsRef<OsStr>> From<&T> for PathBuf {
+    fn from(s: &T) -> PathBuf {
+        PathBuf::from(s.as_ref().to_os_string())
+    }
+}
+
+impl From<OsString> for PathBuf {
+    fn from(s: OsString) -> PathBuf {
+        PathBuf { inner: s.into() }
+    }
+}
+
+impl From<PathBuf> for OsString {
+    fn from(path_buf: PathBuf) -> OsString {
+        path_buf.inner.into()
+    }
+}
+
+impl From<String> for PathBuf {
+    fn from(s: String) -> PathBuf {
+        PathBuf::from(OsString::from(s))
+    }
+}
+
+impl FromStr for PathBuf {
+    type Err = core::convert::Infallible;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(PathBuf::from(s))
+    }
+}
+
+impl<P: AsRef<Path>> FromIterator<P> for PathBuf {
+    fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> PathBuf {
+        let mut buf = PathBuf::new();
+        buf.extend(iter);
+        buf
+    }
+}
+
+impl<P: AsRef<Path>> iter::Extend<P> for PathBuf {
+    fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
+        iter.into_iter().for_each(move |p| self.push(p.as_ref()));
+    }
+}
+
+impl Deref for PathBuf {
+    type Target = Path;
+
+    fn deref(&self) -> &Path {
+        Path::new(&self.inner)
+    }
+}
+
+impl Borrow<Path> for PathBuf {
+    fn borrow(&self) -> &Path {
+        self.deref()
+    }
+}
+
+impl<'a> From<PathBuf> for Cow<'a, Path> {
+    #[inline]
+    fn from(s: PathBuf) -> Cow<'a, Path> {
+        Cow::Owned(s)
+    }
+}
+
+impl<'a> From<&'a PathBuf> for Cow<'a, Path> {
+    #[inline]
+    fn from(p: &'a PathBuf) -> Cow<'a, Path> {
+        Cow::Borrowed(p.as_path())
+    }
+}
+
+impl<'a> From<Cow<'a, Path>> for PathBuf {
+    #[inline]
+    fn from(p: Cow<'a, Path>) -> Self {
+        p.into_owned()
+    }
+}
+
+impl From<PathBuf> for Arc<Path> {
+    #[inline]
+    fn from(s: PathBuf) -> Arc<Path> {
+        let arc: Arc<OsStr> = Arc::from(s.into_os_string());
+        unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Path) }
+    }
+}
+
+impl From<PathBuf> for Rc<Path> {
+    #[inline]
+    fn from(s: PathBuf) -> Rc<Path> {
+        let rc: Rc<OsStr> = Rc::from(s.into_os_string());
+        unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Path) }
+    }
+}
+
+impl AsRef<OsStr> for PathBuf {
+    fn as_ref(&self) -> &OsStr {
+        self.inner.as_ref()
+    }
+}
+
+#[cfg(feature = "unstable")]
+impl<P: AsRef<Path>> stream::Extend<P> for PathBuf {
+    fn extend<'a, S: IntoStream<Item = P> + 'a>(
+        &'a mut self,
+        stream: S,
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
+
+            while let Some(item) = stream.next().await {
+                self.push(item.as_ref());
+            }
+        })
+    }
+}
+
+#[cfg(feature = "unstable")]
+impl<'b, P: AsRef<Path> + 'b + Send> FromStream<P> for PathBuf {
+    #[inline]
+    fn from_stream<'a, S: IntoStream<Item = P> + 'a>(
+        stream: S,
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            let mut out = Self::new();
+            stream::extend(&mut out, stream).await;
+            out
+        })
+    }
+}
+
+impl From<std::path::PathBuf> for PathBuf {
+    fn from(path: std::path::PathBuf) -> PathBuf {
+        PathBuf { inner: path }
+    }
+}
+
+impl Into<std::path::PathBuf> for PathBuf {
+    fn into(self) -> std::path::PathBuf {
+        self.inner
+    }
+}
+
+impl AsRef<std::path::Path> for PathBuf {
+    fn as_ref(&self) -> &std::path::Path {
+        self.inner.as_ref()
+    }
+}
diff --git a/src/pin.rs b/src/pin/mod.rs
similarity index 100%
rename from src/pin.rs
rename to src/pin/mod.rs
diff --git a/src/prelude.rs b/src/prelude.rs
index 4c26a28af..a2a14a182 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -11,28 +11,43 @@
 //! use async_std::prelude::*;
 //! ```
 
-#[doc(no_inline)]
-pub use crate::future::Future;
-#[doc(no_inline)]
-pub use crate::io::BufRead as _;
-#[doc(no_inline)]
-pub use crate::io::Read as _;
-#[doc(no_inline)]
-pub use crate::io::Seek as _;
-#[doc(no_inline)]
-pub use crate::io::Write as _;
-#[doc(hidden)]
-pub use crate::stream::Stream;
-#[doc(no_inline)]
-pub use crate::task_local;
+cfg_std! {
+    #[doc(no_inline)]
+    pub use std::future::Future;
+    #[doc(no_inline)]
+    pub use crate::stream::Stream;
 
-#[doc(hidden)]
-pub use crate::io::buf_read::BufReadExt as _;
-#[doc(hidden)]
-pub use crate::io::read::ReadExt as _;
-#[doc(hidden)]
-pub use crate::io::seek::SeekExt as _;
-#[doc(hidden)]
-pub use crate::io::write::WriteExt as _;
-#[doc(hidden)]
-pub use crate::stream::stream::StreamExt as _;
+    #[doc(inline)]
+    pub use crate::future::future::FutureExt;
+    #[doc(inline)]
+    pub use crate::stream::stream::StreamExt;
+    #[doc(no_inline)]
+    pub use crate::io::BufRead as _;
+    #[doc(no_inline)]
+    pub use crate::io::Read as _;
+    #[doc(no_inline)]
+    pub use crate::io::Seek as _;
+    #[doc(no_inline)]
+    pub use crate::io::Write as _;
+
+    #[doc(no_inline)]
+    pub use crate::io::prelude::BufReadExt as _;
+    #[doc(no_inline)]
+    pub use crate::io::prelude::ReadExt as _;
+    #[doc(no_inline)]
+    pub use crate::io::prelude::SeekExt as _;
+    #[doc(no_inline)]
+    pub use crate::io::prelude::WriteExt as _;
+}
+
+cfg_default! {
+    #[doc(no_inline)]
+    pub use crate::task_local;
+}
+
+cfg_unstable! {
+    #[doc(no_inline)]
+    pub use crate::stream::DoubleEndedStream;
+    #[doc(no_inline)]
+    pub use crate::stream::ExactSizeStream;
+}
diff --git a/src/process.rs b/src/process.rs
new file mode 100644
index 000000000..8ba803da5
--- /dev/null
+++ b/src/process.rs
@@ -0,0 +1,43 @@
+//! A module for working with processes.
+//!
+//! This module is mostly concerned with spawning and interacting with child processes, but it also
+//! provides abort and exit for terminating the current process.
+//!
+//! This is an async version of [`std::process`].
+//!
+//! [`std::process`]: https://doc.rust-lang.org/std/process/index.html
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::ExitStatus;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::Output;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::Stdio;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::Child;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::ChildStderr;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::ChildStdin;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::ChildStdout;
+
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[doc(inline)]
+pub use async_process::Command;
+
+// Re-export functions.
+pub use std::process::{abort, exit, id};
diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs
index 6033eb973..68fa535fa 100644
--- a/src/result/from_stream.rs
+++ b/src/result/from_stream.rs
@@ -5,43 +5,68 @@ use crate::stream::{FromStream, IntoStream};
 
 impl<T, E, V> FromStream<Result<T, E>> for Result<V, E>
 where
+    T: Send,
+    E: Send,
     V: FromStream<T>,
 {
     /// Takes each element in the stream: if it is an `Err`, no further
     /// elements are taken, and the `Err` is returned. Should no `Err`
     /// occur, a container with the values of each `Result` is returned.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # fn main() { async_std::task::block_on(async {
+    /// #
+    /// use async_std::prelude::*;
+    /// use async_std::stream;
+    ///
+    /// let v = stream::from_iter(vec![1, 2]);
+    /// let res: Result<Vec<u32>, &'static str> = v.map(|x: u32|
+    ///     x.checked_add(1).ok_or("Overflow!")
+    /// ).collect().await;
+    /// assert_eq!(res, Ok(vec![2, 3]));
+    /// #
+    /// # }) }
+    /// ```
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = Result<T, E>>>(
+    fn from_stream<'a, S: IntoStream<Item = Result<T, E>> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
-            // Using `scan` here because it is able to stop the stream early
+            // Using `take_while` here because it is able to stop the stream early
             // if a failure occurs
+            let mut is_error = false;
             let mut found_error = None;
             let out: V = stream
-                .scan((), |_, elem| {
-                    match elem {
-                        Ok(elem) => Some(elem),
-                        Err(err) => {
-                            found_error = Some(err);
-                            // Stop processing the stream on error
-                            None
-                        }
+                .take_while(|elem| {
+                    // Stop processing the stream on `Err`
+                    !is_error
+                        && (elem.is_ok() || {
+                            is_error = true;
+                            // Capture first `Err`
+                            true
+                        })
+                })
+                .filter_map(|elem| match elem {
+                    Ok(value) => Some(value),
+                    Err(err) => {
+                        found_error = Some(err);
+                        None
                     }
                 })
                 .collect()
                 .await;
 
-            match found_error {
-                Some(err) => Err(err),
-                None => Ok(out),
+            if is_error {
+                Err(found_error.unwrap())
+            } else {
+                Ok(out)
             }
         })
     }
diff --git a/src/result/mod.rs b/src/result/mod.rs
index 908f9c4dc..fc263318b 100644
--- a/src/result/mod.rs
+++ b/src/result/mod.rs
@@ -5,5 +5,11 @@
 
 mod from_stream;
 
+#[allow(unused)]
 #[doc(inline)]
 pub use std::result::Result;
+
+cfg_unstable! {
+    mod product;
+    mod sum;
+}
diff --git a/src/result/product.rs b/src/result/product.rs
new file mode 100644
index 000000000..45782ff70
--- /dev/null
+++ b/src/result/product.rs
@@ -0,0 +1,75 @@
+use std::pin::Pin;
+
+use crate::prelude::*;
+use crate::stream::{Product, Stream};
+
+impl<T, U, E> Product<Result<U, E>> for Result<T, E>
+where
+    T: Product<U>,
+{
+    #[doc = r#"
+        Takes each element in the `Stream`: if it is an `Err`, no further
+        elements are taken, and the `Err` is returned. Should no `Err` occur,
+        the product of all elements is returned.
+
+        # Examples
+
+        This multiplies every integer in a vector, rejecting the product if a negative element is
+        encountered:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let v = stream::from_iter(vec![1, 2, 4]);
+        let res: Result<i32, &'static str> = v.map(|x|
+            if x < 0 {
+                Err("Negative element found")
+            } else {
+                Ok(x)
+            }).product().await;
+        assert_eq!(res, Ok(8));
+        #
+        # }) }
+        ```
+    "#]
+    fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>
+    where
+        S: Stream<Item = Result<U, E>> + 'a,
+    {
+        Box::pin(async move {
+            // Using `take_while` here because it is able to stop the stream early
+            // if a failure occurs
+            let mut is_error = false;
+            let mut found_error = None;
+            let out = <T as Product<U>>::product(
+                stream
+                    .take_while(|elem| {
+                        // Stop processing the stream on `Err`
+                        !is_error
+                            && (elem.is_ok() || {
+                                is_error = true;
+                                // Capture first `Err`
+                                true
+                            })
+                    })
+                    .filter_map(|elem| match elem {
+                        Ok(value) => Some(value),
+                        Err(err) => {
+                            found_error = Some(err);
+                            None
+                        }
+                    }),
+            )
+            .await;
+
+            if is_error {
+                Err(found_error.unwrap())
+            } else {
+                Ok(out)
+            }
+        })
+    }
+}
diff --git a/src/result/sum.rs b/src/result/sum.rs
new file mode 100644
index 000000000..b6d84a0c4
--- /dev/null
+++ b/src/result/sum.rs
@@ -0,0 +1,75 @@
+use std::pin::Pin;
+
+use crate::prelude::*;
+use crate::stream::{Stream, Sum};
+
+impl<T, U, E> Sum<Result<U, E>> for Result<T, E>
+where
+    T: Sum<U>,
+{
+    #[doc = r#"
+        Takes each element in the `Stream`: if it is an `Err`, no further
+        elements are taken, and the `Err` is returned. Should no `Err` occur,
+        the sum of all elements is returned.
+
+        # Examples
+
+        This sums up every integer in a vector, rejecting the sum if a negative
+        element is encountered:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let v = stream::from_iter(vec![1, 2]);
+        let res: Result<i32, &'static str> = v.map(|x|
+            if x < 0 {
+                Err("Negative element found")
+            } else {
+                Ok(x)
+            }).sum().await;
+        assert_eq!(res, Ok(3));
+        #
+        # }) }
+        ```
+    "#]
+    fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>
+    where
+        S: Stream<Item = Result<U, E>> + 'a,
+    {
+        Box::pin(async move {
+            // Using `take_while` here because it is able to stop the stream early
+            // if a failure occurs
+            let mut is_error = false;
+            let mut found_error = None;
+            let out = <T as Sum<U>>::sum(
+                stream
+                    .take_while(|elem| {
+                        // Stop processing the stream on `Err`
+                        !is_error
+                            && (elem.is_ok() || {
+                                is_error = true;
+                                // Capture first `Err`
+                                true
+                            })
+                    })
+                    .filter_map(|elem| match elem {
+                        Ok(value) => Some(value),
+                        Err(err) => {
+                            found_error = Some(err);
+                            None
+                        }
+                    }),
+            )
+            .await;
+
+            if is_error {
+                Err(found_error.unwrap())
+            } else {
+                Ok(out)
+            }
+        })
+    }
+}
diff --git a/src/rt/mod.rs b/src/rt/mod.rs
new file mode 100644
index 000000000..80f1c4e6b
--- /dev/null
+++ b/src/rt/mod.rs
@@ -0,0 +1,18 @@
+//! The runtime.
+
+use std::env;
+
+use once_cell::sync::Lazy;
+
+/// Dummy runtime struct.
+pub struct Runtime {}
+
+/// The global runtime.
+pub static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
+    // Create an executor thread pool.
+
+    let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or_else(|_| "async-std/runtime".to_string());
+    async_global_executor::init_with_config(async_global_executor::GlobalExecutorConfig::default().with_env_var("ASYNC_STD_THREAD_COUNT").with_thread_name_fn(move || thread_name.clone()));
+
+    Runtime {}
+});
diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs
deleted file mode 100644
index 6fab77c22..000000000
--- a/src/stream/double_ended_stream.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use crate::stream::Stream;
-
-use std::pin::Pin;
-use std::task::{Context, Poll};
-
-/// A stream able to yield elements from both ends.
-///
-/// Something that implements `DoubleEndedStream` has one extra capability
-/// over something that implements [`Stream`]: the ability to also take
-/// `Item`s from the back, as well as the front.
-///
-/// [`Stream`]: trait.Stream.html
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[cfg(any(feature = "unstable", feature = "docs"))]
-pub trait DoubleEndedStream: Stream {
-    /// Removes and returns an element from the end of the stream.
-    ///
-    /// Returns `None` when there are no more elements.
-    ///
-    /// The [trait-level] docs contain more details.
-    ///
-    /// [trait-level]: trait.DoubleEndedStream.html
-    fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
-}
diff --git a/src/stream/double_ended_stream/mod.rs b/src/stream/double_ended_stream/mod.rs
new file mode 100644
index 000000000..0cd705418
--- /dev/null
+++ b/src/stream/double_ended_stream/mod.rs
@@ -0,0 +1,246 @@
+use crate::stream::Stream;
+
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+mod next_back;
+mod nth_back;
+mod rfind;
+mod rfold;
+mod try_rfold;
+
+use next_back::NextBackFuture;
+use nth_back::NthBackFuture;
+use rfind::RFindFuture;
+use rfold::RFoldFuture;
+use try_rfold::TryRFoldFuture;
+
+/// A stream able to yield elements from both ends.
+///
+/// Something that implements `DoubleEndedStream` has one extra capability
+/// over something that implements [`Stream`]: the ability to also take
+/// `Item`s from the back, as well as the front.
+///
+/// [`Stream`]: trait.Stream.html
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub trait DoubleEndedStream: Stream {
+    #[doc = r#"
+        Attempts to receive the next item from the back of the stream.
+
+        There are several possible return values:
+
+        * `Poll::Pending` means this stream's next_back value is not ready yet.
+        * `Poll::Ready(None)` means this stream has been exhausted.
+        * `Poll::Ready(Some(item))` means `item` was received out of the stream.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use std::pin::Pin;
+
+        use async_std::prelude::*;
+        use async_std::stream;
+        use async_std::task::{Context, Poll};
+
+        fn increment(
+            s: impl DoubleEndedStream<Item = i32> + Unpin,
+        ) -> impl DoubleEndedStream<Item = i32> + Unpin {
+            struct Increment<S>(S);
+
+            impl<S: DoubleEndedStream<Item = i32> + Unpin> Stream for Increment<S> {
+                type Item = S::Item;
+
+                fn poll_next(
+                    mut self: Pin<&mut Self>,
+                    cx: &mut Context<'_>,
+                ) -> Poll<Option<Self::Item>> {
+                    match Pin::new(&mut self.0).poll_next(cx) {
+                        Poll::Pending => Poll::Pending,
+                        Poll::Ready(None) => Poll::Ready(None),
+                        Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)),
+                    }
+                }
+            }
+
+            impl<S: DoubleEndedStream<Item = i32> + Unpin> DoubleEndedStream for Increment<S> {
+                fn poll_next_back(
+                    mut self: Pin<&mut Self>,
+                    cx: &mut Context<'_>,
+                ) -> Poll<Option<Self::Item>> {
+                    match Pin::new(&mut self.0).poll_next_back(cx) {
+                        Poll::Pending => Poll::Pending,
+                        Poll::Ready(None) => Poll::Ready(None),
+                        Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)),
+                    }
+                }
+            }
+
+            Increment(s)
+        }
+
+        let mut s = increment(stream::once(7));
+
+        assert_eq!(s.next_back().await, Some(8));
+        assert_eq!(s.next_back().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+
+    #[doc = r#"
+            Advances the stream and returns the next value.
+
+            Returns [`None`] when iteration is finished. Individual stream implementations may
+            choose to resume iteration, and so calling `next()` again may or may not eventually
+            start returning more values.
+
+            [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+
+            # Examples
+
+            ```
+            # fn main() { async_std::task::block_on(async {
+            #
+            use async_std::prelude::*;
+            use async_std::stream;
+
+            let mut s = stream::from_iter(vec![7u8]);
+
+            assert_eq!(s.next_back().await, Some(7));
+            assert_eq!(s.next_back().await, None);
+            #
+            # }) }
+            ```
+        "#]
+    fn next_back(&mut self) -> NextBackFuture<'_, Self>
+    where
+        Self: Unpin,
+    {
+        NextBackFuture { stream: self }
+    }
+
+    #[doc = r#"
+            Returns the nth element from the back of the stream.
+
+            # Examples
+
+            Basic usage:
+
+            ```
+            # fn main() { async_std::task::block_on(async {
+            #
+            use async_std::prelude::*;
+            use async_std::stream;
+
+            let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
+
+            let second = s.nth_back(1).await;
+            assert_eq!(second, Some(4));
+            #
+            # }) }
+            ```
+        "#]
+    fn nth_back(&mut self, n: usize) -> NthBackFuture<'_, Self>
+    where
+        Self: Unpin + Sized,
+    {
+        NthBackFuture::new(self, n)
+    }
+
+    #[doc = r#"
+            Returns the first element from the right that matches the predicate.
+
+            # Examples
+
+            Basic usage:
+
+            ```
+            # fn main() { async_std::task::block_on(async {
+            #
+            use async_std::prelude::*;
+            use async_std::stream;
+
+            let mut s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
+
+            let second = s.rfind(|v| v % 2 == 0).await;
+            assert_eq!(second, Some(4));
+            #
+            # }) }
+            ```
+        "#]
+    fn rfind<P>(&mut self, p: P) -> RFindFuture<'_, Self, P>
+    where
+        Self: Unpin + Sized,
+        P: FnMut(&Self::Item) -> bool,
+    {
+        RFindFuture::new(self, p)
+    }
+
+    #[doc = r#"
+            # Examples
+
+            Basic usage:
+
+            ```
+            # fn main() { async_std::task::block_on(async {
+            #
+            use async_std::prelude::*;
+            use async_std::stream;
+
+            let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
+
+            let second = s.rfold(0, |acc, v| v + acc).await;
+
+            assert_eq!(second, 15);
+            #
+            # }) }
+            ```
+        "#]
+    fn rfold<B, F>(self, accum: B, f: F) -> RFoldFuture<Self, F, B>
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        RFoldFuture::new(self, accum, f)
+    }
+
+    #[doc = r#"
+            A combinator that applies a function as long as it returns successfully, producing a single, final value.
+            Immediately returns the error when the function returns unsuccessfully.
+
+            # Examples
+
+            Basic usage:
+
+            ```
+            # fn main() { async_std::task::block_on(async {
+            #
+            use async_std::prelude::*;
+            use async_std::stream;
+
+            let s = stream::from_iter(vec![1u8, 2, 3, 4, 5]);
+            let sum = s.try_rfold(0, |acc, v| {
+                if (acc+v) % 2 == 1 {
+                    Ok(v+3)
+                } else {
+                    Err("fail")
+                }
+            }).await;
+
+            assert_eq!(sum, Err("fail"));
+            #
+            # }) }
+            ```
+        "#]
+    fn try_rfold<B, F, E>(self, accum: B, f: F) -> TryRFoldFuture<Self, F, B>
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> Result<B, E>,
+    {
+        TryRFoldFuture::new(self, accum, f)
+    }
+}
diff --git a/src/stream/double_ended_stream/next_back.rs b/src/stream/double_ended_stream/next_back.rs
new file mode 100644
index 000000000..9fb52b7b6
--- /dev/null
+++ b/src/stream/double_ended_stream/next_back.rs
@@ -0,0 +1,19 @@
+use core::pin::Pin;
+use core::future::Future;
+
+use crate::stream::DoubleEndedStream;
+use crate::task::{Context, Poll};
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+pub struct NextBackFuture<'a, T: Unpin + ?Sized> {
+    pub(crate) stream: &'a mut T,
+}
+
+impl<T: DoubleEndedStream + Unpin + ?Sized> Future for NextBackFuture<'_, T> {
+    type Output = Option<T::Item>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        Pin::new(&mut *self.stream).poll_next_back(cx)
+    }
+}
diff --git a/src/stream/double_ended_stream/nth_back.rs b/src/stream/double_ended_stream/nth_back.rs
new file mode 100644
index 000000000..2701e9b69
--- /dev/null
+++ b/src/stream/double_ended_stream/nth_back.rs
@@ -0,0 +1,41 @@
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+use crate::stream::DoubleEndedStream;
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+pub struct NthBackFuture<'a, S> {
+    stream: &'a mut S,
+    n: usize,
+}
+
+impl<'a, S> NthBackFuture<'a, S> {
+    pub(crate) fn new(stream: &'a mut S, n: usize) -> Self {
+        NthBackFuture { stream, n }
+    }
+}
+
+impl<'a, S> Future for NthBackFuture<'a, S>
+where
+    S: DoubleEndedStream + Sized + Unpin,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let next = futures_core::ready!(Pin::new(&mut *self.stream).poll_next_back(cx));
+        match next {
+            Some(v) => match self.n {
+                0 => Poll::Ready(Some(v)),
+                _ => {
+                    self.n -= 1;
+                    cx.waker().wake_by_ref();
+                    Poll::Pending
+                }
+            },
+            None => Poll::Ready(None),
+        }
+    }
+}
+
diff --git a/src/stream/double_ended_stream/rfind.rs b/src/stream/double_ended_stream/rfind.rs
new file mode 100644
index 000000000..366655169
--- /dev/null
+++ b/src/stream/double_ended_stream/rfind.rs
@@ -0,0 +1,41 @@
+use core::task::{Context, Poll};
+use core::future::Future;
+use core::pin::Pin;
+
+use crate::stream::DoubleEndedStream;
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+pub struct RFindFuture<'a, S, P> {
+    stream: &'a mut S,
+    p: P,
+}
+
+impl<'a, S, P> RFindFuture<'a, S, P> {
+    pub(super) fn new(stream: &'a mut S, p: P) -> Self {
+        RFindFuture { stream, p }
+    }
+}
+
+impl<S: Unpin, P> Unpin for RFindFuture<'_, S, P> {}
+
+impl<'a, S, P> Future for RFindFuture<'a, S, P>
+where
+    S: DoubleEndedStream + Unpin + Sized,
+    P: FnMut(&S::Item) -> bool,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let item = futures_core::ready!(Pin::new(&mut *self.stream).poll_next_back(cx));
+
+        match item {
+            Some(v) if (&mut self.p)(&v) => Poll::Ready(Some(v)),
+            Some(_) => {
+                cx.waker().wake_by_ref();
+                Poll::Pending
+            }
+            None => Poll::Ready(None),
+        }
+    }
+}
diff --git a/src/stream/double_ended_stream/rfold.rs b/src/stream/double_ended_stream/rfold.rs
new file mode 100644
index 000000000..9bc18783f
--- /dev/null
+++ b/src/stream/double_ended_stream/rfold.rs
@@ -0,0 +1,52 @@
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+
+use crate::stream::DoubleEndedStream;
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct RFoldFuture<S, F, B> {
+        #[pin]
+        stream: S,
+        f: F,
+        acc: Option<B>,
+    }
+}
+
+impl<S, F, B> RFoldFuture<S, F, B> {
+    pub(super) fn new(stream: S, init: B, f: F) -> Self {
+        RFoldFuture {
+            stream,
+            f,
+            acc: Some(init),
+        }
+    }
+}
+
+impl<S, F, B> Future for RFoldFuture<S, F, B>
+where
+    S: DoubleEndedStream + Sized,
+    F: FnMut(B, S::Item) -> B,
+{
+    type Output = B;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+        loop {
+            let next = futures_core::ready!(this.stream.as_mut().poll_next_back(cx));
+
+            match next {
+                Some(v) => {
+                    let old = this.acc.take().unwrap();
+                    let new = (this.f)(old, v);
+                    *this.acc = Some(new);
+                }
+                None => return Poll::Ready(this.acc.take().unwrap()),
+            }
+        }
+    }
+}
diff --git a/src/stream/double_ended_stream/try_rfold.rs b/src/stream/double_ended_stream/try_rfold.rs
new file mode 100644
index 000000000..d67b6ecf8
--- /dev/null
+++ b/src/stream/double_ended_stream/try_rfold.rs
@@ -0,0 +1,56 @@
+use crate::future::Future;
+use core::pin::Pin;
+use crate::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+
+use crate::stream::DoubleEndedStream;
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct TryRFoldFuture<S, F, T> {
+        #[pin]
+        stream: S,
+        f: F,
+        acc: Option<T>,
+    }
+}
+
+impl<S, F, T> TryRFoldFuture<S, F, T> {
+    pub(super) fn new(stream: S, init: T, f: F) -> Self {
+        TryRFoldFuture {
+            stream,
+            f,
+            acc: Some(init),
+        }
+    }
+}
+
+impl<S, F, T, E> Future for TryRFoldFuture<S, F, T>
+where
+    S: DoubleEndedStream + Unpin,
+    F: FnMut(T, S::Item) -> Result<T, E>,
+{
+    type Output = Result<T, E>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+        loop {
+            let next = futures_core::ready!(this.stream.as_mut().poll_next_back(cx));
+
+            match next {
+                Some(v) => {
+                    let old = this.acc.take().unwrap();
+                    let new = (this.f)(old, v);
+
+                    match new {
+                        Ok(o) => *this.acc = Some(o),
+                        Err(e) => return Poll::Ready(Err(e)),
+                    }
+                }
+                None => return Poll::Ready(Ok(this.acc.take().unwrap())),
+            }
+        }
+    }
+}
diff --git a/src/stream/empty.rs b/src/stream/empty.rs
index c9deea867..a11337af3 100644
--- a/src/stream/empty.rs
+++ b/src/stream/empty.rs
@@ -1,15 +1,20 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::marker::PhantomData;
+use core::pin::Pin;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
 /// Creates a stream that doesn't yield any items.
 ///
+/// This `struct` is created by the [`empty`] function. See its
+/// documentation for more.
+///
+/// [`empty`]: fn.empty.html
+///
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use async_std::prelude::*;
 /// use async_std::stream;
@@ -18,7 +23,7 @@ use crate::task::{Context, Poll};
 ///
 /// assert_eq!(s.next().await, None);
 /// #
-/// # }) }
+/// # })
 /// ```
 pub fn empty<T>() -> Empty<T> {
     Empty {
diff --git a/src/stream/exact_size_stream.rs b/src/stream/exact_size_stream.rs
new file mode 100644
index 000000000..8b6ba97da
--- /dev/null
+++ b/src/stream/exact_size_stream.rs
@@ -0,0 +1,120 @@
+pub use crate::stream::Stream;
+
+/// A stream that knows its exact length.
+///
+/// Many [`Stream`]s don't know how many times they will iterate, but some do.
+/// If a stream knows how many times it can iterate, providing access to
+/// that information can be useful. For example, if you want to iterate
+/// backwards, a good start is to know where the end is.
+///
+/// When implementing an `ExactSizeStream`, you must also implement
+/// [`Stream`]. When doing so, the implementation of [`size_hint`] *must*
+/// return the exact size of the stream.
+///
+/// [`Stream`]: trait.Stream.html
+/// [`size_hint`]: trait.Stream.html#method.size_hint
+///
+/// The [`len`] method has a default implementation, so you usually shouldn't
+/// implement it. However, you may be able to provide a more performant
+/// implementation than the default, so overriding it in this case makes sense.
+///
+/// [`len`]: #method.len
+///
+/// # Examples
+///
+/// Basic usage:
+///
+/// ```
+/// // a finite range knows exactly how many times it will iterate
+/// let five = 0..5;
+///
+/// assert_eq!(5, five.len());
+/// ```
+///
+/// In the [module level docs][moddocs], we implemented an [`Stream`],
+/// `Counter`. Let's implement `ExactSizeStream` for it as well:
+///
+/// [moddocs]: index.html
+///
+/// ```
+/// # use std::task::{Context, Poll};
+/// # use std::pin::Pin;
+/// # use async_std::prelude::*;
+/// # struct Counter {
+/// #     count: usize,
+/// # }
+/// # impl Counter {
+/// #     fn new() -> Counter {
+/// #         Counter { count: 0 }
+/// #     }
+/// # }
+/// # impl Stream for Counter {
+/// #     type Item = usize;
+/// #     fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+/// #         self.count += 1;
+/// #         if self.count < 6 {
+/// #             Poll::Ready(Some(self.count))
+/// #         } else {
+/// #             Poll::Ready(None)
+/// #         }
+/// #     }
+/// # }
+/// # async_std::task::block_on(async {
+/// #
+/// impl ExactSizeStream for Counter {
+///     // We can easily calculate the remaining number of iterations.
+///     fn len(&self) -> usize {
+///         5 - self.count
+///     }
+/// }
+///
+/// // And now we can use it!
+///
+/// let counter = Counter::new();
+///
+/// assert_eq!(5, counter.len());
+/// # });
+/// ```
+#[allow(clippy::len_without_is_empty)] // ExactSizeIterator::is_empty is unstable
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub trait ExactSizeStream: Stream {
+    /// Returns the exact number of times the stream will iterate.
+    ///
+    /// This method has a default implementation, so you usually should not
+    /// implement it directly. However, if you can provide a more efficient
+    /// implementation, you can do so. See the [trait-level] docs for an
+    /// example.
+    ///
+    /// This function has the same safety guarantees as the [`size_hint`]
+    /// function.
+    ///
+    /// [trait-level]: trait.ExactSizeStream.html
+    /// [`size_hint`]: trait.Stream.html#method.size_hint
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// // a finite range knows exactly how many times it will iterate
+    /// let five = 0..5;
+    ///
+    /// assert_eq!(5, five.len());
+    /// ```
+    fn len(&self) -> usize {
+        let (lower, upper) = self.size_hint();
+        // Note: This assertion is overly defensive, but it checks the invariant
+        // guaranteed by the trait. If this trait were rust-internal,
+        // we could use debug_assert!; assert_eq! will check all Rust user
+        // implementations too.
+        assert_eq!(upper, Some(lower));
+        lower
+    }
+}
+
+impl<I: ExactSizeStream + ?Sized + Unpin> ExactSizeStream for &mut I {
+    fn len(&self) -> usize {
+        (**self).len()
+    }
+}
diff --git a/src/stream/extend.rs b/src/stream/extend.rs
index 27efd8b70..0c7d41049 100644
--- a/src/stream/extend.rs
+++ b/src/stream/extend.rs
@@ -1,9 +1,9 @@
-use std::pin::Pin;
+use core::pin::Pin;
+use core::future::Future;
 
-use crate::prelude::*;
 use crate::stream::IntoStream;
 
-/// Extend a collection with the contents of a stream.
+/// Extends a collection with the contents of a stream.
 ///
 /// Streams produce a series of values asynchronously, and collections can also be thought of as a
 /// series of values. The `Extend` trait bridges this gap, allowing you to extend a collection
@@ -14,39 +14,64 @@ use crate::stream::IntoStream;
 /// ## Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use async_std::prelude::*;
-/// use async_std::stream::{self, Extend};
+/// use async_std::stream;
 ///
 /// let mut v: Vec<usize> = vec![1, 2];
 /// let s = stream::repeat(3usize).take(3);
-/// v.stream_extend(s).await;
+/// stream::Extend::extend(&mut v, s).await;
 ///
 /// assert_eq!(v, vec![1, 2, 3, 3, 3]);
 /// #
-/// # }) }
+/// # })
 /// ```
+#[cfg(feature = "unstable")]
 #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
 pub trait Extend<A> {
     /// Extends a collection with the contents of a stream.
-    fn stream_extend<'a, T: IntoStream<Item = A> + 'a>(
+    fn extend<'a, T: IntoStream<Item = A> + 'a>(
         &'a mut self,
         stream: T,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
     where
-        A: 'a;
+        <T as IntoStream>::IntoStream: Send;
 }
 
-impl Extend<()> for () {
-    fn stream_extend<'a, T: IntoStream<Item = ()> + 'a>(
-        &'a mut self,
-        stream: T,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
-        let stream = stream.into_stream();
-        Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-            while let Some(_) = stream.next().await {}
-        })
-    }
+/// Extends a collection with the contents of a stream.
+///
+/// Streams produce a series of values asynchronously, and collections can also be thought of as a
+/// series of values. The [`Extend`] trait bridges this gap, allowing you to extend a collection
+/// asynchronously by including the contents of that stream. When extending a collection with an
+/// already existing key, that entry is updated or, in the case of collections that permit multiple
+/// entries with equal keys, that entry is inserted.
+///
+/// [`Extend`]: trait.Extend.html
+///
+/// ## Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let mut v: Vec<usize> = vec![1, 2];
+/// let s = stream::repeat(3usize).take(3);
+/// stream::extend(&mut v, s).await;
+///
+/// assert_eq!(v, vec![1, 2, 3, 3, 3]);
+/// #
+/// # })
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub async fn extend<'a, C, T, S>(collection: &mut C, stream: S)
+where
+    C: Extend<T>,
+    S: IntoStream<Item = T> + 'a,
+    <S as IntoStream>::IntoStream: Send,
+{
+    Extend::extend(collection, stream).await
 }
diff --git a/src/stream/from_fn.rs b/src/stream/from_fn.rs
new file mode 100644
index 000000000..d3736454a
--- /dev/null
+++ b/src/stream/from_fn.rs
@@ -0,0 +1,68 @@
+use core::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+/// A stream that yields elements by calling a closure.
+///
+/// This stream is created by the [`from_fn`] function. See its
+/// documentation for more.
+///
+/// [`from_fn`]: fn.from_fn.html
+#[derive(Clone, Debug)]
+pub struct FromFn<F> {
+    f: F,
+}
+
+impl<F> Unpin for FromFn<F> {}
+
+/// Creates a new stream where to produce each new element a provided closure is called.
+///
+/// This allows creating a custom stream with any behaviour without using the more verbose
+/// syntax of creating a dedicated type and implementing a `Stream` trait for it.
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let mut count = 0u8;
+/// let s = stream::from_fn(|| {
+///     count += 1;
+///     if count > 3 {
+///         None
+///     } else {
+///         Some(count)
+///     }
+/// });
+///
+/// pin_utils::pin_mut!(s);
+///
+/// assert_eq!(s.next().await, Some(1));
+/// assert_eq!(s.next().await, Some(2));
+/// assert_eq!(s.next().await, Some(3));
+/// assert_eq!(s.next().await, None);
+/// #
+/// # })
+/// ```
+pub fn from_fn<T, F>(f: F) -> FromFn<F>
+where
+    F: FnMut() -> Option<T>,
+{
+    FromFn { f }
+}
+
+impl<T, F> Stream for FromFn<F>
+where
+    F: FnMut() -> Option<T>,
+{
+    type Item = T;
+
+    fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let item = (&mut self.f)();
+        Poll::Ready(item)
+    }
+}
diff --git a/src/stream/from_iter.rs b/src/stream/from_iter.rs
new file mode 100644
index 000000000..ea2ed8efb
--- /dev/null
+++ b/src/stream/from_iter.rs
@@ -0,0 +1,62 @@
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+#[cfg(feature = "unstable")]
+use crate::stream::double_ended_stream::DoubleEndedStream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// A stream that was created from iterator.
+    ///
+    /// This stream is created by the [`from_iter`] function.
+    /// See it documentation for more.
+    ///
+    /// [`from_iter`]: fn.from_iter.html
+    #[derive(Clone, Debug)]
+    pub struct FromIter<I> {
+        iter: I,
+    }
+}
+
+/// Converts an iterator into a stream.
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let mut s = stream::from_iter(vec![0, 1, 2, 3]);
+///
+/// assert_eq!(s.next().await, Some(0));
+/// assert_eq!(s.next().await, Some(1));
+/// assert_eq!(s.next().await, Some(2));
+/// assert_eq!(s.next().await, Some(3));
+/// assert_eq!(s.next().await, None);
+/// #
+/// # })
+/// ```
+pub fn from_iter<I: IntoIterator>(iter: I) -> FromIter<I::IntoIter> {
+    FromIter {
+        iter: iter.into_iter(),
+    }
+}
+
+impl<I: Iterator> Stream for FromIter<I> {
+    type Item = I::Item;
+
+    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        Poll::Ready(self.iter.next())
+    }
+}
+
+#[cfg(feature = "unstable")]
+impl<T: DoubleEndedIterator> DoubleEndedStream for FromIter<T> {
+    fn poll_next_back(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<T::Item>> {
+        Poll::Ready(self.iter.next_back())
+    }
+}
diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs
index 047dab8fb..c4001917c 100644
--- a/src/stream/from_stream.rs
+++ b/src/stream/from_stream.rs
@@ -1,6 +1,7 @@
-use super::IntoStream;
+use core::future::Future;
+use core::pin::Pin;
 
-use std::pin::Pin;
+use crate::stream::IntoStream;
 
 /// Conversion from a `Stream`.
 ///
@@ -15,22 +16,24 @@ use std::pin::Pin;
 ///
 /// ```
 /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-///  use crate::async_std::stream::FromStream;
-///  use async_std::prelude::*;
-///  use async_std::stream;
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream::{self, FromStream};
 ///
-///  let five_fives = stream::repeat(5).take(5);
+/// let five_fives = stream::repeat(5).take(5);
 ///
-///  let v = Vec::from_stream(five_fives).await;
+/// let v = Vec::from_stream(five_fives).await;
 ///
-///  assert_eq!(v, vec![5, 5, 5, 5, 5]);
+/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+/// #
 /// # Ok(()) }) }
 /// ```
 ///
 /// Using `collect` to  implicitly use `FromStream`
 ///
-///```
+/// ```
 /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
 /// use async_std::prelude::*;
 /// use async_std::stream;
 /// let five_fives = stream::repeat(5).take(5);
@@ -40,14 +43,13 @@ use std::pin::Pin;
 /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 /// #
 /// # Ok(()) }) }
-///```
+/// ```
 ///
 /// Implementing `FromStream` for your type:
 ///
 /// ```
 /// use async_std::prelude::*;
-/// use async_std::stream::{Extend, FromStream, IntoStream};
-/// use async_std::stream;
+/// use async_std::stream::{self, FromStream, IntoStream};
 /// use std::pin::Pin;
 ///
 /// // A sample collection, that's just a wrapper over Vec<T>
@@ -70,14 +72,17 @@ use std::pin::Pin;
 /// impl FromStream<i32> for MyCollection {
 ///     fn from_stream<'a, S: IntoStream<Item = i32> + 'a>(
 ///         stream: S,
-///     ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> {
+///     ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
+///    where
+///        <S as IntoStream>::IntoStream: Send,
+///    {
 ///         let stream = stream.into_stream();
 ///
 ///         Box::pin(async move {
 ///             let mut c = MyCollection::new();
 ///
 ///             let mut v = vec![];
-///             v.stream_extend(stream).await;
+///             stream::extend(&mut v, stream).await;
 ///
 ///             for i in v {
 ///                 c.add(i);
@@ -88,6 +93,7 @@ use std::pin::Pin;
 /// }
 ///
 /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
 /// // Now we can make a new stream...
 /// let stream = stream::repeat(5).take(5);
 ///
@@ -102,13 +108,14 @@ use std::pin::Pin;
 /// let c: MyCollection = stream.collect().await;
 ///
 /// assert_eq!(c.0, vec![5, 5, 5, 5, 5]);
+/// #
 /// # Ok(()) }) }
-///```
+/// ```
 ///
 /// [`IntoStream`]: trait.IntoStream.html
+#[cfg(feature = "unstable")]
 #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[cfg(any(feature = "unstable", feature = "docs"))]
-pub trait FromStream<T> {
+pub trait FromStream<T: Send> {
     /// Creates a value from a stream.
     ///
     /// # Examples
@@ -117,18 +124,21 @@ pub trait FromStream<T> {
     ///
     /// ```
     /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
-    ///  use crate::async_std::stream::FromStream;
-    ///  use async_std::prelude::*;
-    ///  use async_std::stream;
+    /// #
+    /// use async_std::prelude::*;
+    /// use async_std::stream::{self, FromStream};
     ///
-    ///  let five_fives = stream::repeat(5).take(5);
+    /// let five_fives = stream::repeat(5).take(5);
     ///
-    ///  let v = Vec::from_stream(five_fives).await;
+    /// let v = Vec::from_stream(five_fives).await;
     ///
-    ///  assert_eq!(v, vec![5, 5, 5, 5, 5]);
+    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
+    /// #
     /// # Ok(()) }) }
     /// ```
     fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>;
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send;
 }
diff --git a/src/stream/fused_stream.rs b/src/stream/fused_stream.rs
new file mode 100644
index 000000000..e14ab5b1f
--- /dev/null
+++ b/src/stream/fused_stream.rs
@@ -0,0 +1,21 @@
+use crate::stream::Stream;
+
+/// A stream that always continues to yield `None` when exhausted.
+///
+/// Calling next on a fused stream that has returned `None` once is guaranteed
+/// to return [`None`] again. This trait should be implemented by all streams
+/// that behave this way because it allows optimizing [`Stream::fuse`].
+///
+/// Note: In general, you should not use `FusedStream` in generic bounds if
+/// you need a fused stream. Instead, you should just call [`Stream::fuse`]
+/// on the stream. If the stream is already fused, the additional [`Fuse`]
+/// wrapper will be a no-op with no performance penalty.
+///
+/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+/// [`Stream::fuse`]: trait.Stream.html#method.fuse
+/// [`Fuse`]: struct.Fuse.html
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub trait FusedStream: Stream {}
+
+impl<S: FusedStream + ?Sized + Unpin> FusedStream for &mut S {}
diff --git a/src/stream/interval.rs b/src/stream/interval.rs
new file mode 100644
index 000000000..0a7eb4807
--- /dev/null
+++ b/src/stream/interval.rs
@@ -0,0 +1,78 @@
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+use std::time::Duration;
+
+use crate::stream::Stream;
+use crate::utils::{timer_after, Timer};
+
+/// Creates a new stream that yields at a set interval.
+///
+/// The stream first yields after `dur`, and continues to yield every
+/// `dur` after that. The stream accounts for time elapsed between calls, and
+/// will adjust accordingly to prevent time skews.
+///
+/// Each interval may be slightly longer than the specified duration, but never
+/// less.
+///
+/// Note that intervals are not intended for high resolution timers, but rather
+/// they will likely fire some granularity after the exact instant that they're
+/// otherwise indicated to fire at.
+///
+/// See also: [`task::sleep`].
+///
+/// [`task::sleep`]: ../task/fn.sleep.html
+///
+/// # Examples
+///
+/// Basic example:
+///
+/// ```no_run
+/// use async_std::prelude::*;
+/// use async_std::stream;
+/// use std::time::Duration;
+///
+/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+/// #
+/// let mut interval = stream::interval(Duration::from_secs(4));
+/// while let Some(_) = interval.next().await {
+///     println!("prints every four seconds");
+/// }
+/// #
+/// # Ok(()) }) }
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub fn interval(dur: Duration) -> Interval {
+    Interval {
+        delay: timer_after(dur),
+        interval: dur,
+    }
+}
+
+/// A stream representing notifications at fixed interval
+///
+/// This stream is created by the [`interval`] function. See its
+/// documentation for more.
+///
+/// [`interval`]: fn.interval.html
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[derive(Debug)]
+pub struct Interval {
+    delay: Timer,
+    interval: Duration,
+}
+
+impl Stream for Interval {
+    type Item = ();
+
+    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        if Pin::new(&mut self.delay).poll(cx).is_pending() {
+            return Poll::Pending;
+        }
+        let interval = self.interval;
+        let _ = std::mem::replace(&mut self.delay, timer_after(interval));
+        Poll::Ready(Some(()))
+    }
+}
diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs
index 923318146..f96dc59b3 100644
--- a/src/stream/into_stream.rs
+++ b/src/stream/into_stream.rs
@@ -13,8 +13,8 @@ use crate::stream::Stream;
 /// See also: [`FromStream`].
 ///
 /// [`FromStream`]: trait.FromStream.html
+#[cfg(feature = "unstable")]
 #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[cfg(any(feature = "unstable", feature = "docs"))]
 pub trait IntoStream {
     /// The type of the elements being iterated over.
     type Item;
diff --git a/src/stream/mod.rs b/src/stream/mod.rs
index 8aa12a2f1..8dd7d6339 100644
--- a/src/stream/mod.rs
+++ b/src/stream/mod.rs
@@ -1,53 +1,351 @@
-//! Asynchronous iteration.
+//! Composable asynchronous iteration.
 //!
 //! This module is an async version of [`std::iter`].
 //!
-//! [`std::iter`]: https://doc.rust-lang.org/std/iter/index.html
+//! If you've found yourself with an asynchronous collection of some kind,
+//! and needed to perform an operation on the elements of said collection,
+//! you'll quickly run into 'streams'. Streams are heavily used in idiomatic
+//! asynchronous Rust code, so it's worth becoming familiar with them.
+//!
+//! Before explaining more, let's talk about how this module is structured:
+//!
+//! # Organization
+//!
+//! This module is largely organized by type:
+//!
+//! * [Traits] are the core portion: these traits define what kind of streams
+//!   exist and what you can do with them. The methods of these traits are worth
+//!   putting some extra study time into.
+//! * [Functions] provide some helpful ways to create some basic streams.
+//! * [Structs] are often the return types of the various methods on this
+//!   module's traits. You'll usually want to look at the method that creates
+//!   the `struct`, rather than the `struct` itself. For more detail about why,
+//!   see '[Implementing Stream](#implementing-stream)'.
+//!
+//! [Traits]: #traits
+//! [Functions]: #functions
+//! [Structs]: #structs
+//!
+//! That's it! Let's dig into streams.
 //!
-//! # Examples
+//! # Stream
 //!
+//! The heart and soul of this module is the [`Stream`] trait. The core of
+//! [`Stream`] looks like this:
+//!
+//! ```
+//! #![allow(dead_code)]
+//! # use async_std::task::{Context, Poll};
+//! # use std::pin::Pin;
+//! pub trait Stream {
+//!     type Item;
+//!     fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+//! }
+//! # impl Stream for () {
+//! #   type Item = ();
+//! #   fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { Poll::Pending }
+//! # }
 //! ```
-//! # fn main() { async_std::task::block_on(async {
+//!
+//! A stream has a method, [`next`], which when called, returns an
+//! [`Poll`]<[`Option`]`<Item>>`. [`next`] will return `Ready(Some(Item))`
+//! as long as there are elements, and once they've all been exhausted, will
+//! return `None` to indicate that iteration is finished. If we're waiting on
+//! something asynchronous to resolve `Pending` is returned.
+//!
+//! Individual streams may choose to resume iteration, and so calling
+//! [`next`] again may or may not eventually start returning `Ready(Some(Item))`
+//! again at some point.
+//!
+//! [`Stream`]'s full definition includes a number of other methods as well,
+//! but they are default methods, built on top of [`next`], and so you get
+//! them for free.
+//!
+//! Streams are also composable, and it's common to chain them together to do
+//! more complex forms of processing. See the [Adapters](#adapters) section
+//! below for more details.
+//!
+//! [`Poll`]: ../task/enum.Poll.html
+//! [`Stream`]: trait.Stream.html
+//! [`next`]: trait.Stream.html#tymethod.next
+//! [`Option`]: ../../std/option/enum.Option.html
+//!
+//! # The three forms of streaming
+//!
+//! There are three common methods which can create streams from a collection:
+//!
+//! * `stream()`, which iterates over `&T`.
+//! * `stream_mut()`, which iterates over `&mut T`.
+//! * `into_stream()`, which iterates over `T`.
+//!
+//! Various things in async-std may implement one or more of the
+//! three, where appropriate.
+//!
+//! # Implementing Stream
+//!
+//! Creating a stream of your own involves two steps: creating a `struct` to
+//! hold the stream's state, and then `impl`ementing [`Stream`] for that
+//! `struct`. This is why there are so many `struct`s in this module: there is
+//! one for each stream and iterator adapter.
+//!
+//! Let's make a stream named `Counter` which counts from `1` to `5`:
+//!
+//! ```
+//! # use async_std::prelude::*;
+//! # use async_std::task::{Context, Poll};
+//! # use std::pin::Pin;
+//! // First, the struct:
+//!
+//! /// A stream which counts from one to five
+//! struct Counter {
+//!     count: usize,
+//! }
+//!
+//! // we want our count to start at one, so let's add a new() method to help.
+//! // This isn't strictly necessary, but is convenient. Note that we start
+//! // `count` at zero, we'll see why in `next()`'s implementation below.
+//! impl Counter {
+//!     fn new() -> Counter {
+//!         Counter { count: 0 }
+//!     }
+//! }
+//!
+//! // Then, we implement `Stream` for our `Counter`:
+//!
+//! impl Stream for Counter {
+//!     // we will be counting with usize
+//!     type Item = usize;
+//!
+//!     // poll_next() is the only required method
+//!     fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+//!         // Increment our count. This is why we started at zero.
+//!         self.count += 1;
+//!
+//!         // Check to see if we've finished counting or not.
+//!         if self.count < 6 {
+//!             Poll::Ready(Some(self.count))
+//!         } else {
+//!             Poll::Ready(None)
+//!         }
+//!     }
+//! }
+//!
+//! // And now we can use it!
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! let mut counter = Counter::new();
+//!
+//! let x = counter.next().await.unwrap();
+//! println!("{}", x);
+//!
+//! let x = counter.next().await.unwrap();
+//! println!("{}", x);
+//!
+//! let x = counter.next().await.unwrap();
+//! println!("{}", x);
+//!
+//! let x = counter.next().await.unwrap();
+//! println!("{}", x);
+//!
+//! let x = counter.next().await.unwrap();
+//! println!("{}", x);
 //! #
-//! use async_std::prelude::*;
-//! use async_std::stream;
+//! # Ok(()) }) }
+//! ```
+//!
+//! This will print `1` through `5`, each on their own line.
+//!
+//! Calling `next().await` this way gets repetitive. Rust has a construct which
+//! can call `next()` on your stream, until it reaches `None`. Let's go over
+//! that next.
 //!
-//! let mut s = stream::repeat(9).take(3);
+//! # while let Loops and IntoStream
 //!
-//! while let Some(v) = s.next().await {
-//!     assert_eq!(v, 9);
+//! Rust's `while let` loop syntax is an idiomatic way to iterate over streams. Here's a basic
+//! example of `while let`:
+//!
+//! ```
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! # use async_std::prelude::*;
+//! # use async_std::stream;
+//! let mut values = stream::from_iter(1u8..6);
+//!
+//! while let Some(x) = values.next().await {
+//!     println!("{}", x);
 //! }
 //! #
-//! # }) }
+//! # Ok(()) }) }
 //! ```
-
-use cfg_if::cfg_if;
+//!
+//! This will print the numbers one through five, each on their own line. But
+//! you'll notice something here: we never called anything on our vector to
+//! produce a stream. What gives?
+//!
+//! There's a trait in the standard library for converting something into an
+//! stream: [`IntoStream`]. This trait has one method, [`into_stream`],
+//! which converts the thing implementing [`IntoStream`] into a stream.
+//!
+//! Unlike `std::iter::IntoIterator`, `IntoStream` does not have compiler
+//! support yet. This means that automatic conversions like with `for` loops
+//! doesn't occur yet, and `into_stream` or `from_iter` as above will always
+//! have to be called manually.
+//!
+//! [`IntoStream`]: trait.IntoStream.html
+//! [`into_stream`]: trait.IntoStream.html#tymethod.into_stream
+//!
+//! # Adapters
+//!
+//! Functions which take an [`Stream`] and return another [`Stream`] are
+//! often called 'stream adapters', as they are a form of the 'adapter
+//! pattern'.
+//!
+//! Common stream adapters include [`map`], [`take`], and [`filter`].
+//! For more, see their documentation.
+//!
+//! [`map`]: trait.Stream.html#method.map
+//! [`take`]: trait.Stream.html#method.take
+//! [`filter`]: trait.Stream.html#method.filter
+//!
+//! # Laziness
+//!
+//! Streams (and stream [adapters](#adapters)) are *lazy*. This means that
+//! just creating a stream doesn't _do_ a whole lot. Nothing really happens
+//! until you call [`next`]. This is sometimes a source of confusion when
+//! creating a stream solely for its side effects. For example, the [`map`]
+//! method calls a closure on each element it iterates over:
+//!
+//! ```
+//! # #![allow(unused_must_use)]
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! # use async_std::prelude::*;
+//! # use async_std::stream;
+//! let v = stream::repeat(1u8).take(5);
+//! v.map(|x| println!("{}", x));
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! This will not print any values, as we only created a stream, rather than
+//! using it. The compiler will warn us about this kind of behavior:
+//!
+//! ```text
+//! warning: unused result that must be used: streams are lazy and
+//! do nothing unless consumed
+//! ```
+//!
+//! The idiomatic way to write a [`map`] for its side effects is to use a
+//! `while let` loop instead:
+//!
+//! ```
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! # use async_std::prelude::*;
+//! # use async_std::stream;
+//! let mut v = stream::repeat(1u8).take(5);
+//!
+//! while let Some(x) = &v.next().await {
+//!     println!("{}", x);
+//! }
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! [`map`]: trait.Stream.html#method.map
+//!
+//! The two most common ways to evaluate a stream are to use a `while let` loop
+//! like this, or using the [`collect`] method to produce a new collection.
+//!
+//! [`collect`]: trait.Stream.html#method.collect
+//!
+//! # Infinity
+//!
+//! Streams do not have to be finite. As an example, a repeat stream is
+//! an infinite stream:
+//!
+//! ```
+//! # use async_std::stream;
+//! let numbers = stream::repeat(1u8);
+//! ```
+//!
+//! It is common to use the [`take`] stream adapter to turn an infinite
+//! stream into a finite one:
+//!
+//! ```
+//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+//! #
+//! # use async_std::prelude::*;
+//! # use async_std::stream;
+//! let numbers = stream::from_iter(0u8..);
+//! let mut five_numbers = numbers.take(5);
+//!
+//! while let Some(number) = five_numbers.next().await {
+//!     println!("{}", number);
+//! }
+//! #
+//! # Ok(()) }) }
+//! ```
+//!
+//! This will print the numbers `0` through `4`, each on their own line.
+//!
+//! Bear in mind that methods on infinite streams, even those for which a
+//! result can be determined mathematically in finite time, may not terminate.
+//! Specifically, methods such as [`min`], which in the general case require
+//! traversing every element in the stream, are likely not to return
+//! successfully for any infinite streams.
+//!
+//! ```ignore
+//! let ones = async_std::stream::repeat(1);
+//! let least = ones.min().await.unwrap(); // Oh no! An infinite loop!
+//! // `ones.min()` causes an infinite loop, so we won't reach this point!
+//! println!("The smallest number one is {}.", least);
+//! ```
+//!
+//! [`std::iter`]: https://doc.rust-lang.org/std/iter/index.html
+//! [`take`]: trait.Stream.html#method.take
+//! [`min`]: trait.Stream.html#method.min
 
 pub use empty::{empty, Empty};
+pub use from_fn::{from_fn, FromFn};
+pub use from_iter::{from_iter, FromIter};
 pub use once::{once, Once};
 pub use repeat::{repeat, Repeat};
-pub use stream::{Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, Zip};
+pub use repeat_with::{repeat_with, RepeatWith};
+pub use stream::*;
 
 pub(crate) mod stream;
 
 mod empty;
+mod from_fn;
+mod from_iter;
 mod once;
 mod repeat;
+mod repeat_with;
 
-cfg_if! {
-    if #[cfg(any(feature = "unstable", feature = "docs"))] {
-        mod double_ended_stream;
-        mod extend;
-        mod from_stream;
-        mod into_stream;
-
-        pub use double_ended_stream::DoubleEndedStream;
-        pub use extend::Extend;
-        pub use from_stream::FromStream;
-        pub use into_stream::IntoStream;
+cfg_unstable! {
+    mod double_ended_stream;
+    mod exact_size_stream;
+    mod extend;
+    mod from_stream;
+    mod fused_stream;
+    mod interval;
+    mod into_stream;
+    mod pending;
+    mod product;
+    mod successors;
+    mod sum;
 
-        #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-        #[doc(inline)]
-        pub use async_macros::{join_stream as join, JoinStream as Join};
-    }
+    pub use double_ended_stream::DoubleEndedStream;
+    pub use exact_size_stream::ExactSizeStream;
+    pub use extend::{extend, Extend};
+    pub use from_stream::FromStream;
+    pub use fused_stream::FusedStream;
+    pub use interval::{interval, Interval};
+    pub use into_stream::IntoStream;
+    pub use pending::{pending, Pending};
+    pub use product::Product;
+    pub use stream::Merge;
+    pub use successors::{successors, Successors};
+    pub use sum::Sum;
 }
diff --git a/src/stream/once.rs b/src/stream/once.rs
index 133a155c9..b86f181d9 100644
--- a/src/stream/once.rs
+++ b/src/stream/once.rs
@@ -1,14 +1,19 @@
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
+#[cfg(feature = "unstable")]
+use crate::stream::DoubleEndedStream;
+
 /// Creates a stream that yields a single item.
 ///
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use async_std::prelude::*;
 /// use async_std::stream;
@@ -18,26 +23,36 @@ use crate::task::{Context, Poll};
 /// assert_eq!(s.next().await, Some(7));
 /// assert_eq!(s.next().await, None);
 /// #
-/// # }) }
+/// # })
 /// ```
 pub fn once<T>(t: T) -> Once<T> {
     Once { value: Some(t) }
 }
 
-/// A stream that yields a single item.
-///
-/// This stream is constructed by the [`once`] function.
-///
-/// [`once`]: fn.once.html
-#[derive(Debug)]
-pub struct Once<T> {
-    value: Option<T>,
+pin_project! {
+    /// A stream that yields a single item.
+    ///
+    /// This stream is created by the [`once`] function. See its
+    /// documentation for more.
+    ///
+    /// [`once`]: fn.once.html
+    #[derive(Clone, Debug)]
+    pub struct Once<T> {
+        value: Option<T>,
+    }
 }
 
-impl<T: Unpin> Stream for Once<T> {
+impl<T> Stream for Once<T> {
     type Item = T;
 
-    fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
-        Poll::Ready(self.value.take())
+    fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
+        Poll::Ready(self.project().value.take())
+    }
+}
+
+#[cfg(feature = "unstable")]
+impl <T> DoubleEndedStream for Once<T> {
+    fn poll_next_back(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        Poll::Ready(self.project().value.take())
     }
 }
diff --git a/src/stream/pending.rs b/src/stream/pending.rs
new file mode 100644
index 000000000..edb6be4b1
--- /dev/null
+++ b/src/stream/pending.rs
@@ -0,0 +1,68 @@
+use core::marker::PhantomData;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+use crate::stream::{DoubleEndedStream, ExactSizeStream, FusedStream, Stream};
+
+/// A stream that never returns any items.
+///
+/// This stream is created by the [`pending`] function. See its
+/// documentation for more.
+///
+/// [`pending`]: fn.pending.html
+#[derive(Debug)]
+pub struct Pending<T> {
+    _marker: PhantomData<T>,
+}
+
+/// Creates a stream that never returns any items.
+///
+/// The returned stream will always return `Pending` when polled.
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use std::time::Duration;
+///
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let dur = Duration::from_millis(100);
+/// let mut s = stream::pending::<()>().timeout(dur);
+///
+/// let item = s.next().await;
+///
+/// assert!(item.is_some());
+/// assert!(item.unwrap().is_err());
+///
+/// #
+/// # })
+/// ```
+pub fn pending<T>() -> Pending<T> {
+    Pending {
+        _marker: PhantomData,
+    }
+}
+
+impl<T> Stream for Pending<T> {
+    type Item = T;
+
+    fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
+        Poll::Pending
+    }
+}
+
+impl<T> DoubleEndedStream for Pending<T> {
+    fn poll_next_back(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
+        Poll::Pending
+    }
+}
+
+impl<T> FusedStream for Pending<T> {}
+
+impl<T> ExactSizeStream for Pending<T> {
+    fn len(&self) -> usize {
+        0
+    }
+}
diff --git a/src/stream/product.rs b/src/stream/product.rs
new file mode 100644
index 000000000..991727d29
--- /dev/null
+++ b/src/stream/product.rs
@@ -0,0 +1,65 @@
+use core::pin::Pin;
+use core::future::Future;
+
+use crate::stream::Stream;
+
+/// Trait to represent types that can be created by multiplying the elements of a stream.
+///
+/// This trait is used to implement the [`product`] method on streams. Types which
+/// implement the trait can be generated by the [`product`] method. Like
+/// [`FromStream`] this trait should rarely be called directly and instead
+/// interacted with through [`Stream::product`].
+///
+/// [`product`]: trait.Product.html#tymethod.product
+/// [`FromStream`]: trait.FromStream.html
+/// [`Stream::product`]: trait.Stream.html#method.product
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub trait Product<A = Self>: Sized {
+    /// Method which takes a stream and generates `Self` from the elements by
+    /// multiplying the items.
+    fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Self> + 'a>>
+    where
+        S: Stream<Item = A> + 'a;
+}
+
+use core::ops::Mul;
+use core::num::Wrapping;
+use crate::stream::stream::StreamExt;
+
+macro_rules! num_product {
+    ($one:expr, $($a:ty)*) => ($(
+        impl Product for $a {
+            fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Self>+ 'a>>
+            where
+                S: Stream<Item = $a> + 'a,
+            {
+                Box::pin(async move { stream.fold($one, Mul::mul).await } )
+            }
+        }
+        impl<'a> Product<&'a $a> for $a {
+            fn product<'b, S>(stream: S) -> Pin<Box<dyn Future<Output = Self> + 'b>>
+            where
+                S: Stream<Item = &'a $a> + 'b,
+            {
+                Box::pin(async move { stream.fold($one, Mul::mul).await } )
+            }
+        }
+    )*);
+}
+
+macro_rules! integer_product {
+    ($($a:ty)*) => (
+        num_product!(1, $($a)*);
+        num_product!(Wrapping(1), $(Wrapping<$a>)*);
+    );
+}
+
+macro_rules! float_product {
+    ($($a:ty)*) => (
+        num_product!(1.0, $($a)*);
+    );
+}
+
+integer_product!{ i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
+float_product!{ f32 f64 }
diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs
index 1a6da4116..e73a2f829 100644
--- a/src/stream/repeat.rs
+++ b/src/stream/repeat.rs
@@ -1,4 +1,4 @@
-use std::pin::Pin;
+use core::pin::Pin;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
@@ -8,7 +8,7 @@ use crate::task::{Context, Poll};
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use async_std::prelude::*;
 /// use async_std::stream;
@@ -18,7 +18,7 @@ use crate::task::{Context, Poll};
 /// assert_eq!(s.next().await, Some(7));
 /// assert_eq!(s.next().await, Some(7));
 /// #
-/// # }) }
+/// # })
 /// ```
 pub fn repeat<T>(item: T) -> Repeat<T>
 where
@@ -29,10 +29,11 @@ where
 
 /// A stream that yields the same item repeatedly.
 ///
-/// This stream is constructed by the [`repeat`] function.
+/// This stream is created by the [`repeat`] function. See its
+/// documentation for more.
 ///
 /// [`repeat`]: fn.repeat.html
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub struct Repeat<T> {
     item: T,
 }
diff --git a/src/stream/repeat_with.rs b/src/stream/repeat_with.rs
new file mode 100644
index 000000000..39984a3a2
--- /dev/null
+++ b/src/stream/repeat_with.rs
@@ -0,0 +1,84 @@
+use core::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+/// A stream that repeats elements of type `T` endlessly by applying a provided closure.
+///
+/// This stream is created by the [`repeat_with`] function. See its
+/// documentation for more.
+///
+/// [`repeat_with`]: fn.repeat_with.html
+#[derive(Clone, Debug)]
+pub struct RepeatWith<F> {
+    f: F,
+}
+
+impl<F> Unpin for RepeatWith<F> {}
+
+/// Creates a new stream that repeats elements of type `A` endlessly by applying the provided closure.
+///
+/// # Examples
+///
+/// Basic usage:
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let s = stream::repeat_with(|| 1);
+///
+/// pin_utils::pin_mut!(s);
+///
+/// assert_eq!(s.next().await, Some(1));
+/// assert_eq!(s.next().await, Some(1));
+/// assert_eq!(s.next().await, Some(1));
+/// assert_eq!(s.next().await, Some(1));
+/// # })
+/// ```
+///
+/// Going finite:
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let mut n = 1;
+/// let s = stream::repeat_with(|| {
+///     let item = n;
+///     n *= 2;
+///     item
+/// })
+/// .take(4);
+///
+/// pin_utils::pin_mut!(s);
+///
+/// assert_eq!(s.next().await, Some(1));
+/// assert_eq!(s.next().await, Some(2));
+/// assert_eq!(s.next().await, Some(4));
+/// assert_eq!(s.next().await, Some(8));
+/// assert_eq!(s.next().await, None);
+/// # })
+/// ```
+pub fn repeat_with<T, F>(repeater: F) -> RepeatWith<F>
+where
+    F: FnMut() -> T,
+{
+    RepeatWith { f: repeater }
+}
+
+impl<T, F> Stream for RepeatWith<F>
+where
+    F: FnMut() -> T,
+{
+    type Item = T;
+
+    fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let item = (&mut self.f)();
+        Poll::Ready(Some(item))
+    }
+}
diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs
index 3b65fc764..06f4d7f80 100644
--- a/src/stream/stream/all.rs
+++ b/src/stream/stream/all.rs
@@ -1,7 +1,7 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::marker::PhantomData;
+use core::pin::Pin;
+use core::future::Future;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
@@ -10,10 +10,19 @@ use crate::task::{Context, Poll};
 pub struct AllFuture<'a, S, F, T> {
     pub(crate) stream: &'a mut S,
     pub(crate) f: F,
-    pub(crate) result: bool,
     pub(crate) _marker: PhantomData<T>,
 }
 
+impl<'a, S, F, T> AllFuture<'a, S, F, T> {
+    pub(crate) fn new(stream: &'a mut S, f: F) -> Self {
+        Self {
+            stream,
+            f,
+            _marker: PhantomData,
+        }
+    }
+}
+
 impl<S: Unpin, F, T> Unpin for AllFuture<'_, S, F, T> {}
 
 impl<S, F> Future for AllFuture<'_, S, F, S::Item>
@@ -29,7 +38,6 @@ where
         match next {
             Some(v) => {
                 let result = (&mut self.f)(v);
-                self.result = result;
 
                 if result {
                     // don't forget to wake this task again to pull the next item from stream
@@ -39,7 +47,7 @@ where
                     Poll::Ready(false)
                 }
             }
-            None => Poll::Ready(self.result),
+            None => Poll::Ready(true),
         }
     }
 }
diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs
index a23adf4bf..15154c506 100644
--- a/src/stream/stream/any.rs
+++ b/src/stream/stream/any.rs
@@ -1,7 +1,7 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::marker::PhantomData;
+use core::pin::Pin;
+use core::future::Future;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
@@ -10,10 +10,19 @@ use crate::task::{Context, Poll};
 pub struct AnyFuture<'a, S, F, T> {
     pub(crate) stream: &'a mut S,
     pub(crate) f: F,
-    pub(crate) result: bool,
     pub(crate) _marker: PhantomData<T>,
 }
 
+impl<'a, S, F, T> AnyFuture<'a, S, F, T> {
+    pub(crate) fn new(stream: &'a mut S, f: F) -> Self {
+        Self {
+            stream,
+            f,
+            _marker: PhantomData,
+        }
+    }
+}
+
 impl<S: Unpin, F, T> Unpin for AnyFuture<'_, S, F, T> {}
 
 impl<S, F> Future for AnyFuture<'_, S, F, S::Item>
@@ -29,7 +38,6 @@ where
         match next {
             Some(v) => {
                 let result = (&mut self.f)(v);
-                self.result = result;
 
                 if result {
                     Poll::Ready(true)
@@ -39,7 +47,7 @@ where
                     Poll::Pending
                 }
             }
-            None => Poll::Ready(self.result),
+            None => Poll::Ready(false),
         }
     }
 }
diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs
index 2693382ee..c034a53e4 100644
--- a/src/stream/stream/chain.rs
+++ b/src/stream/stream/chain.rs
@@ -1,22 +1,32 @@
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use super::fuse::Fuse;
-use crate::prelude::*;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// Chains two streams one after another.
-#[derive(Debug)]
-pub struct Chain<S, U> {
-    first: Fuse<S>,
-    second: Fuse<U>,
+pin_project! {
+    /// A stream that chains two streams one after another.
+    ///
+    /// This `struct` is created by the [`chain`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`chain`]: trait.Stream.html#method.chain
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct Chain<S, U> {
+        #[pin]
+        first: Fuse<S>,
+        #[pin]
+        second: Fuse<U>,
+    }
 }
 
 impl<S: Stream, U: Stream> Chain<S, U> {
-    pin_utils::unsafe_pinned!(first: Fuse<S>);
-    pin_utils::unsafe_pinned!(second: Fuse<U>);
-
     pub(super) fn new(first: S, second: U) -> Self {
-        Chain {
+        Self {
             first: first.fuse(),
             second: second.fuse(),
         }
@@ -26,22 +36,23 @@ impl<S: Stream, U: Stream> Chain<S, U> {
 impl<S: Stream, U: Stream<Item = S::Item>> Stream for Chain<S, U> {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        if !self.first.done {
-            let next = futures_core::ready!(self.as_mut().first().poll_next(cx));
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+        if !this.first.done {
+            let next = futures_core::ready!(this.first.as_mut().poll_next(cx));
             if let Some(next) = next {
                 return Poll::Ready(Some(next));
             }
         }
 
-        if !self.second.done {
-            let next = futures_core::ready!(self.as_mut().second().poll_next(cx));
+        if !this.second.done {
+            let next = futures_core::ready!(this.second.as_mut().poll_next(cx));
             if let Some(next) = next {
                 return Poll::Ready(Some(next));
             }
         }
 
-        if self.first.done && self.second.done {
+        if this.first.done && this.second.done {
             return Poll::Ready(None);
         }
 
diff --git a/src/stream/stream/cloned.rs b/src/stream/stream/cloned.rs
new file mode 100644
index 000000000..eac992dd0
--- /dev/null
+++ b/src/stream/stream/cloned.rs
@@ -0,0 +1,33 @@
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+use pin_project_lite::pin_project;
+use core::pin::Pin;
+
+pin_project! {
+    /// A stream that clones the elements of an underlying stream.
+    #[derive(Debug)]
+    pub struct Cloned<S> {
+        #[pin]
+        stream: S,
+    }
+}
+
+impl<S> Cloned<S> {
+    pub(super) fn new(stream: S) -> Self {
+        Self { stream }
+    }
+}
+
+impl<'a, S, T: 'a> Stream for Cloned<S>
+where
+    S: Stream<Item = &'a T>,
+    T: Clone,
+{
+    type Item = T;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+        Poll::Ready(next.cloned())
+    }
+}
diff --git a/src/stream/stream/cmp.rs b/src/stream/stream/cmp.rs
new file mode 100644
index 000000000..bf93408a6
--- /dev/null
+++ b/src/stream/stream/cmp.rs
@@ -0,0 +1,93 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::fuse::Fuse;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Lexicographically compares the elements of this `Stream` with those
+    // of another using `Ord`.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct CmpFuture<L: Stream, R: Stream> {
+        #[pin]
+        l: Fuse<L>,
+        #[pin]
+        r: Fuse<R>,
+        l_cache: Option<L::Item>,
+        r_cache: Option<R::Item>,
+    }
+}
+
+impl<L: Stream, R: Stream> CmpFuture<L, R> {
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            l: l.fuse(),
+            r: r.fuse(),
+            l_cache: None,
+            r_cache: None,
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for CmpFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream<Item = L::Item> + Sized,
+    L::Item: Ord,
+{
+    type Output = Ordering;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+        loop {
+            // Stream that completes earliest can be considered Less, etc
+            let l_complete = this.l.done && this.l_cache.is_none();
+            let r_complete = this.r.done && this.r_cache.is_none();
+
+            if l_complete && r_complete {
+                return Poll::Ready(Ordering::Equal);
+            } else if l_complete {
+                return Poll::Ready(Ordering::Less);
+            } else if r_complete {
+                return Poll::Ready(Ordering::Greater);
+            }
+
+            // Get next value if possible and necessary
+            if !this.l.done && this.l_cache.is_none() {
+                let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx));
+                if let Some(item) = l_next {
+                    *this.l_cache = Some(item);
+                }
+            }
+
+            if !this.r.done && this.r_cache.is_none() {
+                let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx));
+                if let Some(item) = r_next {
+                    *this.r_cache = Some(item);
+                }
+            }
+
+            // Compare if both values are available.
+            if this.l_cache.is_some() && this.r_cache.is_some() {
+                let l_value = this.l_cache.take().unwrap();
+                let r_value = this.r_cache.take().unwrap();
+                let result = l_value.cmp(&r_value);
+
+                if let Ordering::Equal = result {
+                    // Reset cache to prepare for next comparison
+                    *this.l_cache = None;
+                    *this.r_cache = None;
+                } else {
+                    // Return non equal value
+                    return Poll::Ready(result);
+                }
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/copied.rs b/src/stream/stream/copied.rs
new file mode 100644
index 000000000..19296f5b9
--- /dev/null
+++ b/src/stream/stream/copied.rs
@@ -0,0 +1,33 @@
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+use pin_project_lite::pin_project;
+use core::pin::Pin;
+
+pin_project! {
+    /// A stream that copies the elements of an underlying stream.
+    #[derive(Debug)]
+    pub struct Copied<S> {
+        #[pin]
+        stream: S,
+    }
+}
+
+impl<S> Copied<S> {
+    pub(super) fn new(stream: S) -> Self {
+        Self { stream }
+    }
+}
+
+impl<'a, S, T: 'a> Stream for Copied<S>
+where
+    S: Stream<Item = &'a T>,
+    T: Copy,
+{
+    type Item = T;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+        Poll::Ready(next.copied())
+    }
+}
diff --git a/src/stream/stream/count.rs b/src/stream/stream/count.rs
new file mode 100644
index 000000000..63e044977
--- /dev/null
+++ b/src/stream/stream/count.rs
@@ -0,0 +1,46 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    pub struct CountFuture<S> {
+        #[pin]
+        stream: S,
+        count: usize,
+    }
+}
+
+impl<S> CountFuture<S> {
+    pub(crate) fn new(stream: S) -> Self {
+        Self { stream, count: 0 }
+    }
+}
+
+impl<S> Future for CountFuture<S>
+where
+    S: Stream,
+{
+    type Output = usize;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(_) => {
+                cx.waker().wake_by_ref();
+                *this.count += 1;
+                Poll::Pending
+            }
+            None => Poll::Ready(*this.count),
+        }
+    }
+}
diff --git a/src/stream/stream/cycle.rs b/src/stream/stream/cycle.rs
new file mode 100644
index 000000000..dc4c3a177
--- /dev/null
+++ b/src/stream/stream/cycle.rs
@@ -0,0 +1,48 @@
+use core::pin::Pin;
+
+use futures_core::ready;
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// A stream that will repeatedly yield the same list of elements.
+    #[derive(Debug)]
+    pub struct Cycle<S> {
+        orig: S,
+        #[pin]
+        source: S,
+    }
+}
+
+impl<S> Cycle<S>
+where
+    S: Stream + Clone,
+{
+    pub(crate) fn new(source: S) -> Self {
+        Self {
+            orig: source.clone(),
+            source,
+        }
+    }
+}
+
+impl<S> Stream for Cycle<S>
+where
+    S: Stream + Clone,
+{
+    type Item = S::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+
+        match ready!(this.source.as_mut().poll_next(cx)) {
+            None => {
+                this.source.set(this.orig.clone());
+                this.source.poll_next(cx)
+            }
+            item => Poll::Ready(item),
+        }
+    }
+}
diff --git a/src/stream/stream/delay.rs b/src/stream/stream/delay.rs
new file mode 100644
index 000000000..9a7f947c6
--- /dev/null
+++ b/src/stream/stream/delay.rs
@@ -0,0 +1,49 @@
+use core::future::Future;
+use core::pin::Pin;
+use core::time::Duration;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+use crate::utils::{timer_after, Timer};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct Delay<S> {
+        #[pin]
+        stream: S,
+        #[pin]
+        delay: Timer,
+        delay_done: bool,
+    }
+}
+
+impl<S> Delay<S> {
+    pub(super) fn new(stream: S, dur: Duration) -> Self {
+        Delay {
+            stream,
+            delay: timer_after(dur),
+            delay_done: false,
+        }
+    }
+}
+
+impl<S> Stream for Delay<S>
+where
+    S: Stream,
+{
+    type Item = S::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+
+        if !*this.delay_done {
+            futures_core::ready!(this.delay.poll(cx));
+            *this.delay_done = true;
+        }
+
+        this.stream.poll_next(cx)
+    }
+}
diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs
index 7d5a3d68e..093fefbda 100644
--- a/src/stream/stream/enumerate.rs
+++ b/src/stream/stream/enumerate.rs
@@ -1,21 +1,22 @@
-use crate::task::{Context, Poll};
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
+use crate::task::{Context, Poll};
 
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct Enumerate<S> {
-    stream: S,
-    i: usize,
+pin_project! {
+    #[derive(Debug)]
+    pub struct Enumerate<S> {
+        #[pin]
+        stream: S,
+        i: usize,
+    }
 }
 
 impl<S> Enumerate<S> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(i: usize);
-
     pub(super) fn new(stream: S) -> Self {
-        Enumerate { stream, i: 0 }
+        Self { stream, i: 0 }
     }
 }
 
@@ -25,13 +26,14 @@ where
 {
     type Item = (usize, S::Item);
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
 
         match next {
             Some(v) => {
-                let ret = (self.i, v);
-                *self.as_mut().i() += 1;
+                let ret = (*this.i, v);
+                *this.i += 1;
                 Poll::Ready(Some(ret))
             }
             None => Poll::Ready(None),
diff --git a/src/stream/stream/eq.rs b/src/stream/stream/eq.rs
new file mode 100644
index 000000000..3d8307b0e
--- /dev/null
+++ b/src/stream/stream/eq.rs
@@ -0,0 +1,63 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::fuse::Fuse;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Lexicographically compares the elements of this `Stream` with those
+    // of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct EqFuture<L: Stream, R: Stream> {
+        #[pin]
+        l: Fuse<L>,
+        #[pin]
+        r: Fuse<R>,
+    }
+}
+
+impl<L: Stream, R: Stream> EqFuture<L, R>
+where
+    L::Item: PartialEq<R::Item>,
+{
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            l: l.fuse(),
+            r: r.fuse(),
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for EqFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream + Sized,
+    L::Item: PartialEq<R::Item>,
+{
+    type Output = bool;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+
+        loop {
+            let l_val = futures_core::ready!(this.l.as_mut().poll_next(cx));
+            let r_val = futures_core::ready!(this.r.as_mut().poll_next(cx));
+
+            if this.l.done && this.r.done {
+                return Poll::Ready(true);
+            }
+
+            match (l_val, r_val) {
+                (Some(l), Some(r)) if l != r => {
+                    return Poll::Ready(false);
+                }
+                _ => {}
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs
index 3fd54539a..2dc7dd486 100644
--- a/src/stream/stream/filter.rs
+++ b/src/stream/stream/filter.rs
@@ -1,48 +1,52 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream to filter elements of another stream with a predicate.
-#[derive(Debug)]
-pub struct Filter<S, P, T> {
-    stream: S,
-    predicate: P,
-    __t: PhantomData<T>,
+pin_project! {
+    /// A stream to filter elements of another stream with a predicate.
+    ///
+    /// This `struct` is created by the [`filter`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`filter`]: trait.Stream.html#method.filter
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct Filter<S, P> {
+        #[pin]
+        stream: S,
+        predicate: P,
+    }
 }
 
-impl<S, P, T> Filter<S, P, T> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(predicate: P);
-
+impl<S, P> Filter<S, P> {
     pub(super) fn new(stream: S, predicate: P) -> Self {
-        Filter {
+        Self {
             stream,
             predicate,
-            __t: PhantomData,
         }
     }
 }
 
-impl<S, P> Stream for Filter<S, P, S::Item>
+impl<S, P> Stream for Filter<S, P>
 where
     S: Stream,
     P: FnMut(&S::Item) -> bool,
 {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
 
         match next {
-            Some(v) => match (self.as_mut().predicate())(&v) {
-                true => Poll::Ready(Some(v)),
-                false => {
-                    cx.waker().wake_by_ref();
-                    Poll::Pending
-                }
-            },
+            Some(v) if (this.predicate)(&v) => Poll::Ready(Some(v)),
+            Some(_) => {
+                cx.waker().wake_by_ref();
+                Poll::Pending
+            }
             None => Poll::Ready(None),
         }
     }
diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs
index 756efff18..e43e8f09f 100644
--- a/src/stream/stream/filter_map.rs
+++ b/src/stream/stream/filter_map.rs
@@ -1,43 +1,37 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct FilterMap<S, F, T, B> {
-    stream: S,
-    f: F,
-    __from: PhantomData<T>,
-    __to: PhantomData<B>,
+pin_project! {
+    #[derive(Debug)]
+    pub struct FilterMap<S, F> {
+        #[pin]
+        stream: S,
+        f: F,
+    }
 }
 
-impl<S, F, T, B> FilterMap<S, F, T, B> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(f: F);
-
+impl<S, F> FilterMap<S, F> {
     pub(crate) fn new(stream: S, f: F) -> Self {
-        FilterMap {
-            stream,
-            f,
-            __from: PhantomData,
-            __to: PhantomData,
-        }
+        Self { stream, f }
     }
 }
 
-impl<S, F, B> Stream for FilterMap<S, F, S::Item, B>
+impl<S, F, B> Stream for FilterMap<S, F>
 where
     S: Stream,
     F: FnMut(S::Item) -> Option<B>,
 {
     type Item = B;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
         match next {
-            Some(v) => match (self.as_mut().f())(v) {
+            Some(v) => match (this.f)(v) {
                 Some(b) => Poll::Ready(Some(b)),
                 None => {
                     cx.waker().wake_by_ref();
diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs
index 014c8b780..8652ac095 100644
--- a/src/stream/stream/find.rs
+++ b/src/stream/stream/find.rs
@@ -1,31 +1,25 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::future::Future;
+use core::pin::Pin;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
 #[doc(hidden)]
 #[allow(missing_debug_implementations)]
-pub struct FindFuture<'a, S, P, T> {
+pub struct FindFuture<'a, S, P> {
     stream: &'a mut S,
     p: P,
-    __t: PhantomData<T>,
 }
 
-impl<'a, S, P, T> FindFuture<'a, S, P, T> {
+impl<'a, S, P> FindFuture<'a, S, P> {
     pub(super) fn new(stream: &'a mut S, p: P) -> Self {
-        FindFuture {
-            stream,
-            p,
-            __t: PhantomData,
-        }
+        Self { stream, p }
     }
 }
 
-impl<S: Unpin, P, T> Unpin for FindFuture<'_, S, P, T> {}
+impl<S: Unpin, P> Unpin for FindFuture<'_, S, P> {}
 
-impl<'a, S, P> Future for FindFuture<'a, S, P, S::Item>
+impl<'a, S, P> Future for FindFuture<'a, S, P>
 where
     S: Stream + Unpin + Sized,
     P: FnMut(&S::Item) -> bool,
@@ -36,13 +30,11 @@ where
         let item = futures_core::ready!(Pin::new(&mut *self.stream).poll_next(cx));
 
         match item {
-            Some(v) => match (&mut self.p)(&v) {
-                true => Poll::Ready(Some(v)),
-                false => {
-                    cx.waker().wake_by_ref();
-                    Poll::Pending
-                }
-            },
+            Some(v) if (&mut self.p)(&v) => Poll::Ready(Some(v)),
+            Some(_) => {
+                cx.waker().wake_by_ref();
+                Poll::Pending
+            }
             None => Poll::Ready(None),
         }
     }
diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs
index dfcf92d66..f7e3c1e04 100644
--- a/src/stream/stream/find_map.rs
+++ b/src/stream/stream/find_map.rs
@@ -1,33 +1,25 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
 
-use crate::future::Future;
 use crate::stream::Stream;
 
 #[doc(hidden)]
 #[allow(missing_debug_implementations)]
-pub struct FindMapFuture<'a, S, F, T, B> {
+pub struct FindMapFuture<'a, S, F> {
     stream: &'a mut S,
     f: F,
-    __b: PhantomData<B>,
-    __t: PhantomData<T>,
 }
 
-impl<'a, S, B, F, T> FindMapFuture<'a, S, F, T, B> {
+impl<'a, S, F> FindMapFuture<'a, S, F> {
     pub(super) fn new(stream: &'a mut S, f: F) -> Self {
-        FindMapFuture {
-            stream,
-            f,
-            __b: PhantomData,
-            __t: PhantomData,
-        }
+        Self { stream, f }
     }
 }
 
-impl<S: Unpin, F, T, B> Unpin for FindMapFuture<'_, S, F, T, B> {}
+impl<S: Unpin, F> Unpin for FindMapFuture<'_, S, F> {}
 
-impl<'a, S, B, F> Future for FindMapFuture<'a, S, F, S::Item, B>
+impl<'a, S, B, F> Future for FindMapFuture<'a, S, F>
 where
     S: Stream + Unpin + Sized,
     F: FnMut(S::Item) -> Option<B>,
diff --git a/src/stream/stream/flat_map.rs b/src/stream/stream/flat_map.rs
new file mode 100644
index 000000000..d0cc73d32
--- /dev/null
+++ b/src/stream/stream/flat_map.rs
@@ -0,0 +1,65 @@
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::stream::map::Map;
+use crate::stream::stream::StreamExt;
+use crate::stream::{IntoStream, Stream};
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// A stream that maps each element to a stream, and yields the elements of the produced
+    /// streams.
+    ///
+    /// This `struct` is created by the [`flat_map`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`flat_map`]: trait.Stream.html#method.flat_map
+    /// [`Stream`]: trait.Stream.html
+    pub struct FlatMap<S, U, F> {
+        #[pin]
+        stream: Map<S, F>,
+        #[pin]
+        inner_stream: Option<U>,
+    }
+}
+
+impl<S, U, F> FlatMap<S, U, F>
+where
+    S: Stream,
+    U: IntoStream,
+    F: FnMut(S::Item) -> U,
+{
+    pub(super) fn new(stream: S, f: F) -> Self {
+        Self {
+            stream: stream.map(f),
+            inner_stream: None,
+        }
+    }
+}
+
+impl<S, U, F> Stream for FlatMap<S, U, F>
+where
+    S: Stream,
+    U: Stream,
+    F: FnMut(S::Item) -> U,
+{
+    type Item = U::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+        loop {
+            if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() {
+                match futures_core::ready!(inner.poll_next(cx)) {
+                    item @ Some(_) => return Poll::Ready(item),
+                    None => this.inner_stream.set(None),
+                }
+            }
+
+            match futures_core::ready!(this.stream.as_mut().poll_next(cx)) {
+                inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)),
+                None => return Poll::Ready(None),
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/flatten.rs b/src/stream/stream/flatten.rs
new file mode 100644
index 000000000..e7a498dc2
--- /dev/null
+++ b/src/stream/stream/flatten.rs
@@ -0,0 +1,80 @@
+use core::fmt;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::{IntoStream, Stream};
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// A stream that flattens one level of nesting in an stream of things that can be turned into
+    /// streams.
+    ///
+    /// This `struct` is created by the [`flatten`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`flatten`]: trait.Stream.html#method.flatten
+    /// [`Stream`]: trait.Stream.html
+    pub struct Flatten<S>
+    where
+        S: Stream,
+        S::Item: IntoStream,
+    {
+        #[pin]
+        stream: S,
+        #[pin]
+        inner_stream: Option<<S::Item as IntoStream>::IntoStream>,
+    }
+}
+
+impl<S> Flatten<S>
+where
+    S: Stream,
+    S::Item: IntoStream,
+{
+    pub(super) fn new(stream: S) -> Self {
+        Self {
+            stream,
+            inner_stream: None,
+        }
+    }
+}
+
+impl<S, U> Stream for Flatten<S>
+where
+    S: Stream,
+    S::Item: IntoStream<IntoStream = U, Item = U::Item>,
+    U: Stream,
+{
+    type Item = U::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+        loop {
+            if let Some(inner) = this.inner_stream.as_mut().as_pin_mut() {
+                match futures_core::ready!(inner.poll_next(cx)) {
+                    item @ Some(_) => return Poll::Ready(item),
+                    None => this.inner_stream.set(None),
+                }
+            }
+
+            match futures_core::ready!(this.stream.as_mut().poll_next(cx)) {
+                inner @ Some(_) => this.inner_stream.set(inner.map(IntoStream::into_stream)),
+                None => return Poll::Ready(None),
+            }
+        }
+    }
+}
+
+impl<S, U> fmt::Debug for Flatten<S>
+where
+    S: fmt::Debug + Stream,
+    S::Item: IntoStream<IntoStream = U, Item = U::Item>,
+    U: fmt::Debug + Stream,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Flatten")
+            .field("inner", &self.stream)
+            .finish()
+    }
+}
diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs
index 18ddcd815..3938a3739 100644
--- a/src/stream/stream/fold.rs
+++ b/src/stream/stream/fold.rs
@@ -1,52 +1,50 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct FoldFuture<S, F, T, B> {
-    stream: S,
-    f: F,
-    acc: Option<B>,
-    __t: PhantomData<T>,
+pin_project! {
+    #[derive(Debug)]
+    pub struct FoldFuture<S, F, B> {
+        #[pin]
+        stream: S,
+        f: F,
+        acc: Option<B>,
+    }
 }
 
-impl<S, F, T, B> FoldFuture<S, F, T, B> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(f: F);
-    pin_utils::unsafe_unpinned!(acc: Option<B>);
-
+impl<S, F, B> FoldFuture<S, F, B> {
     pub(super) fn new(stream: S, init: B, f: F) -> Self {
-        FoldFuture {
+        Self {
             stream,
             f,
             acc: Some(init),
-            __t: PhantomData,
         }
     }
 }
 
-impl<S, F, B> Future for FoldFuture<S, F, S::Item, B>
+impl<S, F, B> Future for FoldFuture<S, F, B>
 where
     S: Stream + Sized,
     F: FnMut(B, S::Item) -> B,
 {
     type Output = B;
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
         loop {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
 
             match next {
                 Some(v) => {
-                    let old = self.as_mut().acc().take().unwrap();
-                    let new = (self.as_mut().f())(old, v);
-                    *self.as_mut().acc() = Some(new);
+                    let old = this.acc.take().unwrap();
+                    let new = (this.f)(old, v);
+                    *this.acc = Some(new);
                 }
-                None => return Poll::Ready(self.as_mut().acc().take().unwrap()),
+                None => return Poll::Ready(this.acc.take().unwrap()),
             }
         }
     }
diff --git a/src/stream/stream/for_each.rs b/src/stream/stream/for_each.rs
index 0406a5075..dbada101c 100644
--- a/src/stream/stream/for_each.rs
+++ b/src/stream/stream/for_each.rs
@@ -1,44 +1,44 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::pin::Pin;
+use core::future::Future;
+
+use pin_project_lite::pin_project;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct ForEachFuture<S, F, T> {
-    stream: S,
-    f: F,
-    __t: PhantomData<T>,
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct ForEachFuture<S, F> {
+        #[pin]
+        stream: S,
+        f: F,
+    }
 }
 
-impl<S, F, T> ForEachFuture<S, F, T> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(f: F);
-
+impl<S, F> ForEachFuture<S, F> {
     pub(super) fn new(stream: S, f: F) -> Self {
-        ForEachFuture {
+        Self {
             stream,
             f,
-            __t: PhantomData,
         }
     }
 }
 
-impl<S, F> Future for ForEachFuture<S, F, S::Item>
+impl<S, F> Future for ForEachFuture<S, F>
 where
     S: Stream + Sized,
     F: FnMut(S::Item),
 {
     type Output = ();
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
         loop {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
 
             match next {
-                Some(v) => (self.as_mut().f())(v),
+                Some(v) => (this.f)(v),
                 None => return Poll::Ready(()),
             }
         }
diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs
index ff5bdab1f..f3a059626 100644
--- a/src/stream/stream/fuse.rs
+++ b/src/stream/stream/fuse.rs
@@ -1,33 +1,46 @@
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A `Stream` that is permanently closed once a single call to `poll` results in
-/// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`.
-#[derive(Clone, Debug)]
-pub struct Fuse<S> {
-    pub(crate) stream: S,
-    pub(crate) done: bool,
+pin_project! {
+    /// A stream that yields `None` forever after the underlying stream yields `None` once.
+    ///
+    /// This `struct` is created by the [`fuse`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`fuse`]: trait.Stream.html#method.fuse
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Clone, Debug)]
+    pub struct Fuse<S> {
+        #[pin]
+        pub(crate) stream: S,
+        pub(crate) done: bool,
+    }
 }
 
-impl<S: Unpin> Unpin for Fuse<S> {}
-
-impl<S: Stream> Fuse<S> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(done: bool);
+impl<S> Fuse<S> {
+    pub(super) fn new(stream: S) -> Self {
+        Self {
+            stream,
+            done: false,
+        }
+    }
 }
 
 impl<S: Stream> Stream for Fuse<S> {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
-        if self.done {
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
+        let this = self.project();
+        if *this.done {
             Poll::Ready(None)
         } else {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.poll_next(cx));
             if next.is_none() {
-                *self.as_mut().done() = true;
+                *this.done = true;
             }
             Poll::Ready(next)
         }
diff --git a/src/stream/stream/ge.rs b/src/stream/stream/ge.rs
new file mode 100644
index 000000000..1e1b70df5
--- /dev/null
+++ b/src/stream/stream/ge.rs
@@ -0,0 +1,50 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::partial_cmp::PartialCmpFuture;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Determines if the elements of this `Stream` are lexicographically
+    // greater than or equal to those of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct GeFuture<L: Stream, R: Stream> {
+        #[pin]
+        partial_cmp: PartialCmpFuture<L, R>,
+    }
+}
+
+impl<L: Stream, R: Stream> GeFuture<L, R>
+where
+    L::Item: PartialOrd<R::Item>,
+{
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            partial_cmp: l.partial_cmp(r),
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for GeFuture<L, R>
+where
+    L: Stream,
+    R: Stream,
+    L::Item: PartialOrd<R::Item>,
+{
+    type Output = bool;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
+
+        match result {
+            Some(Ordering::Greater) | Some(Ordering::Equal) => Poll::Ready(true),
+            _ => Poll::Ready(false),
+        }
+    }
+}
diff --git a/src/stream/stream/gt.rs b/src/stream/stream/gt.rs
new file mode 100644
index 000000000..d58e7e9e6
--- /dev/null
+++ b/src/stream/stream/gt.rs
@@ -0,0 +1,50 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::partial_cmp::PartialCmpFuture;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Determines if the elements of this `Stream` are lexicographically
+    // greater than those of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct GtFuture<L: Stream, R: Stream> {
+        #[pin]
+        partial_cmp: PartialCmpFuture<L, R>,
+    }
+}
+
+impl<L: Stream, R: Stream> GtFuture<L, R>
+where
+    L::Item: PartialOrd<R::Item>,
+{
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            partial_cmp: l.partial_cmp(r),
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for GtFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream + Sized,
+    L::Item: PartialOrd<R::Item>,
+{
+    type Output = bool;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
+
+        match result {
+            Some(Ordering::Greater) => Poll::Ready(true),
+            _ => Poll::Ready(false),
+        }
+    }
+}
diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs
index e63b58492..d2f6cf395 100644
--- a/src/stream/stream/inspect.rs
+++ b/src/stream/stream/inspect.rs
@@ -1,43 +1,49 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream that does something with each element of another stream.
-#[derive(Debug)]
-pub struct Inspect<S, F, T> {
-    stream: S,
-    f: F,
-    __t: PhantomData<T>,
+pin_project! {
+    /// A stream that does something with each element of another stream.
+    ///
+    /// This `struct` is created by the [`inspect`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`inspect`]: trait.Stream.html#method.inspect
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct Inspect<S, F> {
+        #[pin]
+        stream: S,
+        f: F,
+    }
 }
 
-impl<S, F, T> Inspect<S, F, T> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(f: F);
-
+impl<S, F> Inspect<S, F> {
     pub(super) fn new(stream: S, f: F) -> Self {
-        Inspect {
+        Self {
             stream,
             f,
-            __t: PhantomData,
         }
     }
 }
 
-impl<S, F> Stream for Inspect<S, F, S::Item>
+impl<S, F> Stream for Inspect<S, F>
 where
     S: Stream,
     F: FnMut(&S::Item),
 {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+        let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
 
-        Poll::Ready(next.and_then(|x| {
-            (self.as_mut().f())(&x);
-            Some(x)
+        Poll::Ready(next.map(|x| {
+            (this.f)(&x);
+            x
         }))
     }
 }
diff --git a/src/stream/stream/last.rs b/src/stream/stream/last.rs
new file mode 100644
index 000000000..ebf1a484a
--- /dev/null
+++ b/src/stream/stream/last.rs
@@ -0,0 +1,45 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct LastFuture<S, T> {
+        #[pin]
+        stream: S,
+        last: Option<T>,
+    }
+}
+
+impl<S, T> LastFuture<S, T> {
+    pub(crate) fn new(stream: S) -> Self {
+        Self { stream, last: None }
+    }
+}
+
+impl<S> Future for LastFuture<S, S::Item>
+where
+    S: Stream + Unpin + Sized,
+    S::Item: Copy,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(new) => {
+                cx.waker().wake_by_ref();
+                *this.last = Some(new);
+                Poll::Pending
+            }
+            None => Poll::Ready(*this.last),
+        }
+    }
+}
diff --git a/src/stream/stream/le.rs b/src/stream/stream/le.rs
new file mode 100644
index 000000000..169f9eced
--- /dev/null
+++ b/src/stream/stream/le.rs
@@ -0,0 +1,50 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::partial_cmp::PartialCmpFuture;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// Determines if the elements of this `Stream` are lexicographically
+    /// less or equal to those of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct LeFuture<L: Stream, R: Stream> {
+        #[pin]
+        partial_cmp: PartialCmpFuture<L, R>,
+    }
+}
+
+impl<L: Stream, R: Stream> LeFuture<L, R>
+where
+    L::Item: PartialOrd<R::Item>,
+{
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            partial_cmp: l.partial_cmp(r),
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for LeFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream + Sized,
+    L::Item: PartialOrd<R::Item>,
+{
+    type Output = bool;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
+
+        match result {
+            Some(Ordering::Less) | Some(Ordering::Equal) => Poll::Ready(true),
+            _ => Poll::Ready(false),
+        }
+    }
+}
diff --git a/src/stream/stream/lt.rs b/src/stream/stream/lt.rs
new file mode 100644
index 000000000..1851b8e4c
--- /dev/null
+++ b/src/stream/stream/lt.rs
@@ -0,0 +1,50 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::partial_cmp::PartialCmpFuture;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Determines if the elements of this `Stream` are lexicographically
+    // less than those of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct LtFuture<L: Stream, R: Stream> {
+        #[pin]
+        partial_cmp: PartialCmpFuture<L, R>,
+    }
+}
+
+impl<L: Stream, R: Stream> LtFuture<L, R>
+where
+    L::Item: PartialOrd<R::Item>,
+{
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            partial_cmp: l.partial_cmp(r),
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for LtFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream + Sized,
+    L::Item: PartialOrd<R::Item>,
+{
+    type Output = bool;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let result = futures_core::ready!(self.project().partial_cmp.poll(cx));
+
+        match result {
+            Some(Ordering::Less) => Poll::Ready(true),
+            _ => Poll::Ready(false),
+        }
+    }
+}
diff --git a/src/stream/stream/map.rs b/src/stream/stream/map.rs
index 4bc2e366a..0eab3ce2b 100644
--- a/src/stream/stream/map.rs
+++ b/src/stream/stream/map.rs
@@ -1,41 +1,39 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct Map<S, F, T, B> {
-    stream: S,
-    f: F,
-    __from: PhantomData<T>,
-    __to: PhantomData<B>,
+pin_project! {
+    /// A stream that maps value of another stream with a function.
+    #[derive(Debug)]
+    pub struct Map<S, F> {
+        #[pin]
+        stream: S,
+        f: F,
+    }
 }
 
-impl<S, F, T, B> Map<S, F, T, B> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(f: F);
-
+impl<S, F> Map<S, F> {
     pub(crate) fn new(stream: S, f: F) -> Self {
-        Map {
+        Self {
             stream,
             f,
-            __from: PhantomData,
-            __to: PhantomData,
         }
     }
 }
 
-impl<S, F, B> Stream for Map<S, F, S::Item, B>
+impl<S, F, B> Stream for Map<S, F>
 where
     S: Stream,
     F: FnMut(S::Item) -> B,
 {
     type Item = B;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
-        Poll::Ready(next.map(self.as_mut().f()))
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+        Poll::Ready(next.map(this.f))
     }
 }
diff --git a/src/stream/stream/max.rs b/src/stream/stream/max.rs
new file mode 100644
index 000000000..03fe63595
--- /dev/null
+++ b/src/stream/stream/max.rs
@@ -0,0 +1,53 @@
+use core::cmp::{Ord, Ordering};
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct MaxFuture<S, T> {
+        #[pin]
+        stream: S,
+        max: Option<T>,
+    }
+}
+
+impl<S, T> MaxFuture<S, T> {
+    pub(super) fn new(stream: S) -> Self {
+        Self { stream, max: None }
+    }
+}
+
+impl<S> Future for MaxFuture<S, S::Item>
+where
+    S: Stream,
+    S::Item: Ord,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(new) => {
+                cx.waker().wake_by_ref();
+                match this.max.take() {
+                    None => *this.max = Some(new),
+
+                    Some(old) => match new.cmp(&old) {
+                        Ordering::Greater => *this.max = Some(new),
+                        _ => *this.max = Some(old),
+                    },
+                }
+                Poll::Pending
+            }
+            None => Poll::Ready(this.max.take()),
+        }
+    }
+}
diff --git a/src/stream/stream/max_by.rs b/src/stream/stream/max_by.rs
new file mode 100644
index 000000000..8f986452d
--- /dev/null
+++ b/src/stream/stream/max_by.rs
@@ -0,0 +1,57 @@
+use core::cmp::Ordering;
+use core::pin::Pin;
+use core::future::Future;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct MaxByFuture<S, F, T> {
+        #[pin]
+        stream: S,
+        compare: F,
+        max: Option<T>,
+    }
+}
+
+impl<S, F, T> MaxByFuture<S, F, T> {
+    pub(super) fn new(stream: S, compare: F) -> Self {
+        Self {
+            stream,
+            compare,
+            max: None,
+        }
+    }
+}
+
+impl<S, F> Future for MaxByFuture<S, F, S::Item>
+where
+    S: Stream,
+    F: FnMut(&S::Item, &S::Item) -> Ordering,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(new) => {
+                cx.waker().wake_by_ref();
+                match this.max.take() {
+                    None => *this.max = Some(new),
+                    Some(old) => match (this.compare)(&new, &old) {
+                        Ordering::Greater => *this.max = Some(new),
+                        _ => *this.max = Some(old),
+                    },
+                }
+                Poll::Pending
+            }
+            None => Poll::Ready(this.max.take()),
+        }
+    }
+}
diff --git a/src/stream/stream/max_by_key.rs b/src/stream/stream/max_by_key.rs
new file mode 100644
index 000000000..8fa91ab98
--- /dev/null
+++ b/src/stream/stream/max_by_key.rs
@@ -0,0 +1,65 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct MaxByKeyFuture<S, T, K> {
+        #[pin]
+        stream: S,
+        max: Option<(T, T)>,
+        key_by: K,
+    }
+}
+
+impl<S, T, K> MaxByKeyFuture<S, T, K> {
+    pub(super) fn new(stream: S, key_by: K) -> Self {
+        Self {
+            stream,
+            max: None,
+            key_by,
+        }
+    }
+}
+
+impl<S, K> Future for MaxByKeyFuture<S, S::Item, K>
+where
+    S: Stream,
+    K: FnMut(&S::Item) -> S::Item,
+    S::Item: Ord,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        fn key<B, T>(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) {
+            move |x| (f(&x), x)
+        }
+
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(new) => {
+                let (key, value) = key(this.key_by)(new);
+                cx.waker().wake_by_ref();
+
+                match this.max.take() {
+                    None => *this.max = Some((key, value)),
+
+                    Some(old) => match key.cmp(&old.0) {
+                        Ordering::Greater => *this.max = Some((key, value)),
+                        _ => *this.max = Some(old),
+                    },
+                }
+                Poll::Pending
+            }
+            None => Poll::Ready(this.max.take().map(|max| max.1)),
+        }
+    }
+}
diff --git a/src/stream/stream/merge.rs b/src/stream/stream/merge.rs
new file mode 100644
index 000000000..232097292
--- /dev/null
+++ b/src/stream/stream/merge.rs
@@ -0,0 +1,73 @@
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+
+use crate::stream::stream::StreamExt;
+use crate::stream::Fuse;
+use crate::stream::Stream;
+use crate::utils;
+
+pin_project! {
+    /// A stream that merges two other streams into a single stream.
+    ///
+    /// This `struct` is created by the [`merge`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`merge`]: trait.Stream.html#method.merge
+    /// [`Stream`]: trait.Stream.html
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    #[derive(Debug)]
+    pub struct Merge<L, R> {
+        #[pin]
+        left: Fuse<L>,
+        #[pin]
+        right: Fuse<R>,
+    }
+}
+
+impl<L: Stream, R: Stream> Merge<L, R> {
+    pub(crate) fn new(left: L, right: R) -> Self {
+        Self {
+            left: left.fuse(),
+            right: right.fuse(),
+        }
+    }
+}
+
+impl<L, R, T> Stream for Merge<L, R>
+where
+    L: Stream<Item = T>,
+    R: Stream<Item = T>,
+{
+    type Item = T;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        if utils::random(2) == 0 {
+            poll_next_in_order(this.left, this.right, cx)
+        } else {
+            poll_next_in_order(this.right, this.left, cx)
+        }
+    }
+}
+
+fn poll_next_in_order<F, S, I>(
+    first: Pin<&mut F>,
+    second: Pin<&mut S>,
+    cx: &mut Context<'_>,
+) -> Poll<Option<I>>
+where
+    F: Stream<Item = I>,
+    S: Stream<Item = I>,
+{
+    match first.poll_next(cx) {
+        Poll::Ready(None) => second.poll_next(cx),
+        Poll::Ready(item) => Poll::Ready(item),
+        Poll::Pending => match second.poll_next(cx) {
+            Poll::Ready(None) | Poll::Pending => Poll::Pending,
+            Poll::Ready(item) => Poll::Ready(item),
+        },
+    }
+}
diff --git a/src/stream/stream/min.rs b/src/stream/stream/min.rs
new file mode 100644
index 000000000..8430b943a
--- /dev/null
+++ b/src/stream/stream/min.rs
@@ -0,0 +1,53 @@
+use core::cmp::{Ord, Ordering};
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct MinFuture<S, T> {
+        #[pin]
+        stream: S,
+        min: Option<T>,
+    }
+}
+
+impl<S, T> MinFuture<S, T> {
+    pub(super) fn new(stream: S) -> Self {
+        Self { stream, min: None }
+    }
+}
+
+impl<S> Future for MinFuture<S, S::Item>
+where
+    S: Stream,
+    S::Item: Ord,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(new) => {
+                cx.waker().wake_by_ref();
+                match this.min.take() {
+                    None => *this.min = Some(new),
+
+                    Some(old) => match new.cmp(&old) {
+                        Ordering::Less => *this.min = Some(new),
+                        _ => *this.min = Some(old),
+                    },
+                }
+                Poll::Pending
+            }
+            None => Poll::Ready(this.min.take()),
+        }
+    }
+}
diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs
index a68cf3130..fe1d40ea8 100644
--- a/src/stream/stream/min_by.rs
+++ b/src/stream/stream/min_by.rs
@@ -1,25 +1,26 @@
-use std::cmp::Ordering;
-use std::pin::Pin;
+use core::cmp::Ordering;
+use core::pin::Pin;
+use core::future::Future;
+
+use pin_project_lite::pin_project;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-#[doc(hidden)]
-#[allow(missing_debug_implementations)]
-pub struct MinByFuture<S, F, T> {
-    stream: S,
-    compare: F,
-    min: Option<T>,
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct MinByFuture<S, F, T> {
+        #[pin]
+        stream: S,
+        compare: F,
+        min: Option<T>,
+    }
 }
 
 impl<S, F, T> MinByFuture<S, F, T> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(compare: F);
-    pin_utils::unsafe_unpinned!(min: Option<T>);
-
     pub(super) fn new(stream: S, compare: F) -> Self {
-        MinByFuture {
+        Self {
             stream,
             compare,
             min: None,
@@ -35,22 +36,23 @@ where
 {
     type Output = Option<S::Item>;
 
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
 
         match next {
             Some(new) => {
                 cx.waker().wake_by_ref();
-                match self.as_mut().min().take() {
-                    None => *self.as_mut().min() = Some(new),
-                    Some(old) => match (&mut self.as_mut().compare())(&new, &old) {
-                        Ordering::Less => *self.as_mut().min() = Some(new),
-                        _ => *self.as_mut().min() = Some(old),
+                match this.min.take() {
+                    None => *this.min = Some(new),
+                    Some(old) => match (this.compare)(&new, &old) {
+                        Ordering::Less => *this.min = Some(new),
+                        _ => *this.min = Some(old),
                     },
                 }
                 Poll::Pending
             }
-            None => Poll::Ready(self.min),
+            None => Poll::Ready(*this.min),
         }
     }
 }
diff --git a/src/stream/stream/min_by_key.rs b/src/stream/stream/min_by_key.rs
new file mode 100644
index 000000000..549b7983b
--- /dev/null
+++ b/src/stream/stream/min_by_key.rs
@@ -0,0 +1,65 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct MinByKeyFuture<S, T, K> {
+        #[pin]
+        stream: S,
+        min: Option<(T, T)>,
+        key_by: K,
+    }
+}
+
+impl<S, T, K> MinByKeyFuture<S, T, K> {
+    pub(super) fn new(stream: S, key_by: K) -> Self {
+        Self {
+            stream,
+            min: None,
+            key_by,
+        }
+    }
+}
+
+impl<S, K> Future for MinByKeyFuture<S, S::Item, K>
+where
+    S: Stream,
+    K: FnMut(&S::Item) -> S::Item,
+    S::Item: Ord,
+{
+    type Output = Option<S::Item>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        fn key<B, T>(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) {
+            move |x| (f(&x), x)
+        }
+
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(new) => {
+                let (key, value) = key(this.key_by)(new);
+                cx.waker().wake_by_ref();
+
+                match this.min.take() {
+                    None => *this.min = Some((key, value)),
+
+                    Some(old) => match key.cmp(&old.0) {
+                        Ordering::Less => *this.min = Some((key, value)),
+                        _ => *this.min = Some(old),
+                    },
+                }
+                Poll::Pending
+            }
+            None => Poll::Ready(this.min.take().map(|min| min.1)),
+        }
+    }
+}
diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs
index 8849605ca..144194d24 100644
--- a/src/stream/stream/mod.rs
+++ b/src/stream/stream/mod.rs
@@ -7,7 +7,7 @@
 //! # Examples
 //!
 //! ```
-//! # fn main() { async_std::task::block_on(async {
+//! # async_std::task::block_on(async {
 //! #
 //! use async_std::prelude::*;
 //! use async_std::stream;
@@ -18,13 +18,18 @@
 //!     assert_eq!(v, 9);
 //! }
 //! #
-//! # }) }
+//! # })
 //! ```
 
 mod all;
 mod any;
 mod chain;
+mod cloned;
+mod cmp;
+mod copied;
+mod cycle;
 mod enumerate;
+mod eq;
 mod filter;
 mod filter_map;
 mod find;
@@ -32,33 +37,67 @@ mod find_map;
 mod fold;
 mod for_each;
 mod fuse;
+mod ge;
+mod gt;
 mod inspect;
+mod last;
+mod le;
+mod lt;
 mod map;
+mod max;
+mod max_by;
+mod max_by_key;
+mod min;
 mod min_by;
+mod min_by_key;
+mod ne;
 mod next;
 mod nth;
+mod partial_cmp;
+mod position;
 mod scan;
 mod skip;
 mod skip_while;
 mod step_by;
 mod take;
+mod take_while;
+mod try_fold;
 mod try_for_each;
 mod zip;
 
 use all::AllFuture;
 use any::AnyFuture;
+use cmp::CmpFuture;
+use cycle::Cycle;
 use enumerate::Enumerate;
+use eq::EqFuture;
 use filter_map::FilterMap;
 use find::FindFuture;
 use find_map::FindMapFuture;
 use fold::FoldFuture;
 use for_each::ForEachFuture;
+use ge::GeFuture;
+use gt::GtFuture;
+use last::LastFuture;
+use le::LeFuture;
+use lt::LtFuture;
+use max::MaxFuture;
+use max_by::MaxByFuture;
+use max_by_key::MaxByKeyFuture;
+use min::MinFuture;
 use min_by::MinByFuture;
+use min_by_key::MinByKeyFuture;
+use ne::NeFuture;
 use next::NextFuture;
 use nth::NthFuture;
-use try_for_each::TryForEeachFuture;
+use partial_cmp::PartialCmpFuture;
+use position::PositionFuture;
+use try_fold::TryFoldFuture;
+use try_for_each::TryForEachFuture;
 
 pub use chain::Chain;
+pub use cloned::Cloned;
+pub use copied::Copied;
 pub use filter::Filter;
 pub use fuse::Fuse;
 pub use inspect::Inspect;
@@ -68,1128 +107,2204 @@ pub use skip::Skip;
 pub use skip_while::SkipWhile;
 pub use step_by::StepBy;
 pub use take::Take;
+pub use take_while::TakeWhile;
 pub use zip::Zip;
 
-use std::cmp::Ordering;
-use std::marker::PhantomData;
-
-use cfg_if::cfg_if;
-
-use crate::utils::extension_trait;
-
-cfg_if! {
-    if #[cfg(feature = "docs")] {
-        use std::ops::{Deref, DerefMut};
-
-        use crate::task::{Context, Poll};
-    }
+use core::cmp::Ordering;
+
+cfg_unstable! {
+    use core::future::Future;
+    use core::pin::Pin;
+    use core::time::Duration;
+
+    use crate::stream::into_stream::IntoStream;
+    use crate::stream::{FromStream, Product, Sum};
+    use crate::stream::Extend;
+
+    use count::CountFuture;
+    use partition::PartitionFuture;
+    use unzip::UnzipFuture;
+
+    pub use merge::Merge;
+    pub use flatten::Flatten;
+    pub use flat_map::FlatMap;
+    pub use timeout::{TimeoutError, Timeout};
+    pub use throttle::Throttle;
+    pub use delay::Delay;
+
+    mod count;
+    mod merge;
+    mod flatten;
+    mod flat_map;
+    mod partition;
+    mod timeout;
+    mod throttle;
+    mod delay;
+    mod unzip;
 }
 
-cfg_if! {
-    if #[cfg(any(feature = "unstable", feature = "docs"))] {
-        use std::pin::Pin;
+pub use futures_core::stream::Stream as Stream;
 
-        use crate::future::Future;
-        use crate::stream::FromStream;
-    }
-}
+#[doc = r#"
+    Extension methods for [`Stream`].
 
-extension_trait! {
+    [`Stream`]: ../stream/trait.Stream.html
+"#]
+pub trait StreamExt: Stream {
     #[doc = r#"
-        An asynchronous stream of values.
+        Advances the stream and returns the next value.
+
+        Returns [`None`] when iteration is finished. Individual stream implementations may
+        choose to resume iteration, and so calling `next()` again may or may not eventually
+        start returning more values.
 
-        This trait is a re-export of [`futures::stream::Stream`] and is an async version of
-        [`std::iter::Iterator`].
+        [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
 
-        The [provided methods] do not really exist in the trait itself, but they become
-        available when the prelude is imported:
+        # Examples
 
         ```
-        # #[allow(unused_imports)]
+        # fn main() { async_std::task::block_on(async {
+        #
         use async_std::prelude::*;
-        ```
-
-        [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
-        [`futures::stream::Stream`]:
-        https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html
-        [provided methods]: #provided-methods
-    "#]
-    pub trait Stream [StreamExt: futures_core::stream::Stream] {
-        #[doc = r#"
-            The type of items yielded by this stream.
-        "#]
-        type Item;
+        use async_std::stream;
 
-        #[doc = r#"
-            Attempts to receive the next item from the stream.
+        let mut s = stream::once(7);
 
-            There are several possible return values:
+        assert_eq!(s.next().await, Some(7));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn next(&mut self) -> NextFuture<'_, Self>
+    where
+        Self: Unpin,
+    {
+        NextFuture { stream: self }
+    }
 
-            * `Poll::Pending` means this stream's next value is not ready yet.
-            * `Poll::Ready(None)` means this stream has been exhausted.
-            * `Poll::Ready(Some(item))` means `item` was received out of the stream.
+    #[doc = r#"
+        Creates a stream that yields its first `n` elements.
 
-            # Examples
+        # Examples
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::pin::Pin;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            use async_std::prelude::*;
-            use async_std::stream;
-            use async_std::task::{Context, Poll};
-
-            fn increment(
-                s: impl Stream<Item = i32> + Unpin,
-            ) -> impl Stream<Item = i32> + Unpin {
-                struct Increment<S>(S);
-
-                impl<S: Stream<Item = i32> + Unpin> Stream for Increment<S> {
-                    type Item = S::Item;
-
-                    fn poll_next(
-                        mut self: Pin<&mut Self>,
-                        cx: &mut Context<'_>,
-                    ) -> Poll<Option<Self::Item>> {
-                        match Pin::new(&mut self.0).poll_next(cx) {
-                            Poll::Pending => Poll::Pending,
-                            Poll::Ready(None) => Poll::Ready(None),
-                            Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)),
-                        }
-                    }
-                }
-
-                Increment(s)
-            }
+        let mut s = stream::repeat(9).take(3);
 
-            let mut s = increment(stream::once(7));
+        while let Some(v) = s.next().await {
+            assert_eq!(v, 9);
+        }
+        #
+        # }) }
+        ```
+    "#]
+    fn take(self, n: usize) -> Take<Self>
+    where
+        Self: Sized,
+    {
+        Take::new(self, n)
+    }
 
-            assert_eq!(s.next().await, Some(8));
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+    #[doc = r#"
+        Creates a stream that yields elements based on a predicate.
 
-        #[doc = r#"
-            Advances the stream and returns the next value.
+        # Examples
 
-            Returns [`None`] when iteration is finished. Individual stream implementations may
-            choose to resume iteration, and so calling `next()` again may or may not eventually
-            start returning more values.
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+        let s = stream::from_iter(vec![1, 2, 3, 4]);
+        let mut s = s.take_while(|x| x < &3 );
 
-            # Examples
+        assert_eq!(s.next().await, Some(1));
+        assert_eq!(s.next().await, Some(2));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
+    where
+        Self: Sized,
+        P: FnMut(&Self::Item) -> bool,
+    {
+        TakeWhile::new(self, predicate)
+    }
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+    #[doc = r#"
+        Limit the amount of items yielded per timeslice in a stream.
 
-            let mut s = stream::once(7);
+        This stream does not drop any items, but will only limit the rate at which items pass through.
+        # Examples
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+        use std::time::{Duration, Instant};
 
-            assert_eq!(s.next().await, Some(7));
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn next(&mut self) -> impl Future<Output = Option<Self::Item>> + '_ [NextFuture<'_, Self>]
-        where
-            Self: Unpin,
-        {
-            NextFuture { stream: self }
-        }
+        let start = Instant::now();
 
-        #[doc = r#"
-            Creates a stream that yields its first `n` elements.
+        // emit value every 5 milliseconds
+        let s = stream::interval(Duration::from_millis(5)).take(2);
 
-            # Examples
+        // throttle for 10 milliseconds
+        let mut s = s.throttle(Duration::from_millis(10));
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+        s.next().await;
+        assert!(start.elapsed().as_millis() >= 5);
 
-            let mut s = stream::repeat(9).take(3);
+        s.next().await;
+        assert!(start.elapsed().as_millis() >= 15);
 
-            while let Some(v) = s.next().await {
-                assert_eq!(v, 9);
-            }
-            #
-            # }) }
-            ```
-        "#]
-        fn take(self, n: usize) -> Take<Self>
-        where
-            Self: Sized,
-        {
-            Take {
-                stream: self,
-                remaining: n,
-            }
-        }
+        s.next().await;
+        assert!(start.elapsed().as_millis() >= 25);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn throttle(self, d: Duration) -> Throttle<Self>
+    where
+        Self: Sized,
+    {
+        Throttle::new(self, d)
+    }
 
-        #[doc = r#"
-            Creates a stream that yields each `step`th element.
+    #[doc = r#"
+        Creates a stream that yields each `step`th element.
 
-            # Panics
+        # Panics
 
-            This method will panic if the given step is `0`.
+        This method will panic if the given step is `0`.
 
-            # Examples
+        # Examples
 
-            Basic usage:
+        Basic usage:
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect();
-            let mut stepped = s.step_by(2);
+        let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]);
+        let mut stepped = s.step_by(2);
 
-            assert_eq!(stepped.next().await, Some(0));
-            assert_eq!(stepped.next().await, Some(2));
-            assert_eq!(stepped.next().await, Some(4));
-            assert_eq!(stepped.next().await, None);
+        assert_eq!(stepped.next().await, Some(0));
+        assert_eq!(stepped.next().await, Some(2));
+        assert_eq!(stepped.next().await, Some(4));
+        assert_eq!(stepped.next().await, None);
 
-            #
-            # }) }
-            ```
-        "#]
-        fn step_by(self, step: usize) -> StepBy<Self>
-        where
-            Self: Sized,
-        {
-            StepBy::new(self, step)
-        }
+        #
+        # }) }
+        ```
+    "#]
+    fn step_by(self, step: usize) -> StepBy<Self>
+    where
+        Self: Sized,
+    {
+        StepBy::new(self, step)
+    }
 
-        #[doc = r#"
-            Takes two streams and creates a new stream over both in sequence.
+    #[doc = r#"
+        Takes two streams and creates a new stream over both in sequence.
 
-            # Examples
+        # Examples
 
-            Basic usage:
+        Basic usage:
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
-
-            let first: VecDeque<_> = vec![0u8, 1].into_iter().collect();
-            let second: VecDeque<_> = vec![2, 3].into_iter().collect();
-            let mut c = first.chain(second);
-
-            assert_eq!(c.next().await, Some(0));
-            assert_eq!(c.next().await, Some(1));
-            assert_eq!(c.next().await, Some(2));
-            assert_eq!(c.next().await, Some(3));
-            assert_eq!(c.next().await, None);
-
-            #
-            # }) }
-            ```
-        "#]
-        fn chain<U>(self, other: U) -> Chain<Self, U>
-        where
-            Self: Sized,
-            U: Stream<Item = Self::Item> + Sized,
-        {
-            Chain::new(self, other)
-        }
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-        #[doc = r#"
-            Creates a stream that gives the current element's count as well as the next value.
+        let first = stream::from_iter(vec![0u8, 1]);
+        let second = stream::from_iter(vec![2, 3]);
+        let mut c = first.chain(second);
 
-            # Overflow behaviour.
+        assert_eq!(c.next().await, Some(0));
+        assert_eq!(c.next().await, Some(1));
+        assert_eq!(c.next().await, Some(2));
+        assert_eq!(c.next().await, Some(3));
+        assert_eq!(c.next().await, None);
 
-            This combinator does no guarding against overflows.
+        #
+        # }) }
+        ```
+    "#]
+    fn chain<U>(self, other: U) -> Chain<Self, U>
+    where
+        Self: Sized,
+        U: Stream<Item = Self::Item> + Sized,
+    {
+        Chain::new(self, other)
+    }
 
-            # Examples
+        #[doc = r#"
+        Creates an stream which copies all of its elements.
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
+        # Examples
 
-            let s: VecDeque<_> = vec!['a', 'b', 'c'].into_iter().collect();
-            let mut s = s.enumerate();
+        Basic usage:
 
-            assert_eq!(s.next().await, Some((0, 'a')));
-            assert_eq!(s.next().await, Some((1, 'b')));
-            assert_eq!(s.next().await, Some((2, 'c')));
-            assert_eq!(s.next().await, None);
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            #
-            # }) }
-            ```
-        "#]
-        fn enumerate(self) -> Enumerate<Self>
-        where
-            Self: Sized,
-        {
-            Enumerate::new(self)
-        }
+        let v = stream::from_iter(vec![&1, &2, &3]);
 
-        #[doc = r#"
-            Takes a closure and creates a stream that calls that closure on every element of this stream.
+        let mut v_cloned = v.cloned();
 
-            # Examples
+        assert_eq!(v_cloned.next().await, Some(1));
+        assert_eq!(v_cloned.next().await, Some(2));
+        assert_eq!(v_cloned.next().await, Some(3));
+        assert_eq!(v_cloned.next().await, None);
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
+        #
+        # }) }
+        ```
+    "#]
+    fn cloned<'a, T>(self) -> Cloned<Self>
+    where
+        Self: Sized + Stream<Item = &'a T>,
+        T: Clone + 'a,
+    {
+        Cloned::new(self)
+    }
 
-            let s: VecDeque<_> = vec![1, 2, 3].into_iter().collect();
-            let mut s = s.map(|x| 2 * x);
 
-            assert_eq!(s.next().await, Some(2));
-            assert_eq!(s.next().await, Some(4));
-            assert_eq!(s.next().await, Some(6));
-            assert_eq!(s.next().await, None);
+    #[doc = r#"
+        Creates an stream which copies all of its elements.
 
-            #
-            # }) }
-            ```
-        "#]
-        fn map<B, F>(self, f: F) -> Map<Self, F, Self::Item, B>
-        where
-            Self: Sized,
-            F: FnMut(Self::Item) -> B,
-        {
-            Map::new(self, f)
-        }
+        # Examples
 
-        #[doc = r#"
-            A combinator that does something with each element in the stream, passing the value
-            on.
+        Basic usage:
 
-            # Examples
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            Basic usage:
+        let s = stream::from_iter(vec![&1, &2, &3]);
+        let mut s_copied  = s.copied();
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
-
-            let a: VecDeque<_> = vec![1u8, 2, 3, 4, 5].into_iter().collect();
-            let sum = a
-                    .inspect(|x| println!("about to filter {}", x))
-                    .filter(|x| x % 2 == 0)
-                    .inspect(|x| println!("made it through filter: {}", x))
-                    .fold(0, |sum, i| sum + i).await;
-
-            assert_eq!(sum, 6);
-            #
-            # }) }
-            ```
-        "#]
-        fn inspect<F>(self, f: F) -> Inspect<Self, F, Self::Item>
-        where
-            Self: Sized,
-            F: FnMut(&Self::Item),
-        {
-            Inspect::new(self, f)
-        }
+        assert_eq!(s_copied.next().await, Some(1));
+        assert_eq!(s_copied.next().await, Some(2));
+        assert_eq!(s_copied.next().await, Some(3));
+        assert_eq!(s_copied.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn copied<'a, T>(self) -> Copied<Self>
+    where
+        Self: Sized + Stream<Item = &'a T>,
+        T: Copy + 'a,
+    {
+        Copied::new(self)
+    }
 
-        #[doc = r#"
-            Transforms this `Stream` into a "fused" `Stream` such that after the first time
-            `poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return
-            `Poll::Ready(None)`.
+    #[doc = r#"
+        Creates a stream that yields the provided values infinitely and in order.
 
-            # Examples
+        # Examples
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+        Basic usage:
 
-            let mut s = stream::once(1).fuse();
-            assert_eq!(s.next().await, Some(1));
-            assert_eq!(s.next().await, None);
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn fuse(self) -> Fuse<Self>
-        where
-            Self: Sized,
-        {
-            Fuse {
-                stream: self,
-                done: false,
-            }
-        }
+        ```
+        # async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-        #[doc = r#"
-            Creates a stream that uses a predicate to determine if an element should be yielded.
+        let mut s = stream::once(7).cycle();
 
-            # Examples
+        assert_eq!(s.next().await, Some(7));
+        assert_eq!(s.next().await, Some(7));
+        assert_eq!(s.next().await, Some(7));
+        assert_eq!(s.next().await, Some(7));
+        assert_eq!(s.next().await, Some(7));
+        #
+        # })
+        ```
+    "#]
+    fn cycle(self) -> Cycle<Self>
+    where
+        Self: Clone + Sized,
+    {
+        Cycle::new(self)
+    }
 
-            Basic usage:
+    #[doc = r#"
+        Creates a stream that gives the current element's count as well as the next value.
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        # Overflow behaviour.
 
-            use async_std::prelude::*;
+        This combinator does no guarding against overflows.
 
-            let s: VecDeque<usize> = vec![1, 2, 3, 4].into_iter().collect();
-            let mut s = s.filter(|i| i % 2 == 0);
-
-            assert_eq!(s.next().await, Some(2));
-            assert_eq!(s.next().await, Some(4));
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn filter<P>(self, predicate: P) -> Filter<Self, P, Self::Item>
-        where
-            Self: Sized,
-            P: FnMut(&Self::Item) -> bool,
-        {
-            Filter::new(self, predicate)
-        }
+        # Examples
 
-        #[doc = r#"
-            Both filters and maps a stream.
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            # Examples
+        let s = stream::from_iter(vec!['a', 'b', 'c']);
+        let mut s = s.enumerate();
 
-            Basic usage:
+        assert_eq!(s.next().await, Some((0, 'a')));
+        assert_eq!(s.next().await, Some((1, 'b')));
+        assert_eq!(s.next().await, Some((2, 'c')));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn enumerate(self) -> Enumerate<Self>
+    where
+        Self: Sized,
+    {
+        Enumerate::new(self)
+    }
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+    #[doc = r#"
+        Creates a stream that is delayed before it starts yielding items.
 
-            use async_std::prelude::*;
+        # Examples
 
-            let s: VecDeque<&str> = vec!["1", "lol", "3", "NaN", "5"].into_iter().collect();
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+        use std::time::{Duration, Instant};
 
-            let mut parsed = s.filter_map(|a| a.parse::<u32>().ok());
+        let start = Instant::now();
+        let mut s = stream::from_iter(vec![0u8, 1, 2]).delay(Duration::from_millis(200));
 
-            let one = parsed.next().await;
-            assert_eq!(one, Some(1));
+        assert_eq!(s.next().await, Some(0));
+        // The first time will take more than 200ms due to delay.
+        assert!(start.elapsed().as_millis() >= 200);
 
-            let three = parsed.next().await;
-            assert_eq!(three, Some(3));
+        assert_eq!(s.next().await, Some(1));
+        // There will be no delay after the first time.
+        assert!(start.elapsed().as_millis() < 400);
 
-            let five = parsed.next().await;
-            assert_eq!(five, Some(5));
+        assert_eq!(s.next().await, Some(2));
+        assert!(start.elapsed().as_millis() < 400);
 
-            let end = parsed.next().await;
-            assert_eq!(end, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F, Self::Item, B>
-        where
-            Self: Sized,
-            F: FnMut(Self::Item) -> Option<B>,
-        {
-            FilterMap::new(self, f)
-        }
+        assert_eq!(s.next().await, None);
+        assert!(start.elapsed().as_millis() < 400);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(any(feature = "unstable", feature = "docs"))]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn delay(self, dur: std::time::Duration) -> Delay<Self>
+    where
+        Self: Sized,
+    {
+        Delay::new(self, dur)
+    }
 
-        #[doc = r#"
-            Returns the element that gives the minimum value with respect to the
-            specified comparison function. If several elements are equally minimum,
-            the first element is returned. If the stream is empty, `None` is returned.
+    #[doc = r#"
+        Takes a closure and creates a stream that calls that closure on every element of this stream.
 
-            # Examples
+        # Examples
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            use async_std::prelude::*;
+        let s = stream::from_iter(vec![1, 2, 3]);
+        let mut s = s.map(|x| 2 * x);
 
-            let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
+        assert_eq!(s.next().await, Some(2));
+        assert_eq!(s.next().await, Some(4));
+        assert_eq!(s.next().await, Some(6));
+        assert_eq!(s.next().await, None);
 
-            let min = s.clone().min_by(|x, y| x.cmp(y)).await;
-            assert_eq!(min, Some(1));
+        #
+        # }) }
+        ```
+    "#]
+    fn map<B, F>(self, f: F) -> Map<Self, F>
+    where
+        Self: Sized,
+        F: FnMut(Self::Item) -> B,
+    {
+        Map::new(self, f)
+    }
 
-            let min = s.min_by(|x, y| y.cmp(x)).await;
-            assert_eq!(min, Some(3));
+    #[doc = r#"
+        A combinator that does something with each element in the stream, passing the value
+        on.
 
-            let min = VecDeque::<usize>::new().min_by(|x, y| x.cmp(y)).await;
-            assert_eq!(min, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn min_by<F>(
-            self,
-            compare: F,
-        ) -> impl Future<Output = Option<Self::Item>> [MinByFuture<Self, F, Self::Item>]
-        where
-            Self: Sized,
-            F: FnMut(&Self::Item, &Self::Item) -> Ordering,
-        {
-            MinByFuture::new(self, compare)
-        }
+        # Examples
 
-        #[doc = r#"
-            Returns the nth element of the stream.
+        Basic usage:
 
-            # Examples
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            Basic usage:
+        let s = stream::from_iter(vec![1, 2, 3, 4, 5]);
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        let sum = s
+           .inspect(|x| println!("about to filter {}", x))
+           .filter(|x| x % 2 == 0)
+           .inspect(|x| println!("made it through filter: {}", x))
+           .fold(0, |sum, i| sum + i)
+           .await;
 
-            use async_std::prelude::*;
+        assert_eq!(sum, 6);
+        #
+        # }) }
+        ```
+    "#]
+    fn inspect<F>(self, f: F) -> Inspect<Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item),
+    {
+        Inspect::new(self, f)
+    }
 
-            let mut s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
+    #[doc = r#"
+        Returns the last element of the stream.
 
-            let second = s.nth(1).await;
-            assert_eq!(second, Some(2));
-            #
-            # }) }
-            ```
-            Calling `nth()` multiple times:
+        # Examples
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        Basic usage:
 
-            use async_std::prelude::*;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            let mut s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
+        let s = stream::from_iter(vec![1, 2, 3]);
 
-            let second = s.nth(0).await;
-            assert_eq!(second, Some(1));
+        let last  = s.last().await;
+        assert_eq!(last, Some(3));
+        #
+        # }) }
+        ```
 
-            let second = s.nth(0).await;
-            assert_eq!(second, Some(2));
-            #
-            # }) }
-            ```
-            Returning `None` if the stream finished before returning `n` elements:
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        An empty stream will return `None`:
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::stream;
+        use crate::async_std::prelude::*;
 
-            use async_std::prelude::*;
+        let s = stream::empty::<()>();
 
-            let mut s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-
-            let fourth = s.nth(4).await;
-            assert_eq!(fourth, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn nth(
-            &mut self,
-            n: usize,
-        ) -> impl Future<Output = Option<Self::Item>> + '_ [NthFuture<'_, Self>]
-        where
-            Self: Sized,
-        {
-            NthFuture::new(self, n)
-        }
+        let last  = s.last().await;
+        assert_eq!(last, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn last(
+        self,
+    ) -> LastFuture<Self, Self::Item>
+    where
+        Self: Sized,
+    {
+        LastFuture::new(self)
+    }
 
-        #[doc = r#"
-            Tests if every element of the stream matches a predicate.
+    #[doc = r#"
+        Creates a stream which ends after the first `None`.
 
-            `all()` takes a closure that returns `true` or `false`. It applies
-            this closure to each element of the stream, and if they all return
-            `true`, then so does `all()`. If any of them return `false`, it
-            returns `false`.
+        After a stream returns `None`, future calls may or may not yield `Some(T)` again.
+        `fuse()` adapts an iterator, ensuring that after a `None` is given, it will always
+        return `None` forever.
 
-            `all()` is short-circuiting; in other words, it will stop processing
-            as soon as it finds a `false`, given that no matter what else happens,
-            the result will also be `false`.
+        # Examples
 
-            An empty stream returns `true`.
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::once(1).fuse();
+        assert_eq!(s.next().await, Some(1));
+        assert_eq!(s.next().await, None);
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn fuse(self) -> Fuse<Self>
+    where
+        Self: Sized,
+    {
+        Fuse::new(self)
+    }
 
-            # Examples
+    #[doc = r#"
+        Creates a stream that uses a predicate to determine if an element should be yielded.
 
-            Basic usage:
+        # Examples
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+        Basic usage:
 
-            let mut s = stream::repeat::<u32>(42).take(3);
-            assert!(s.all(|x| x ==  42).await);
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            #
-            # }) }
-            ```
+        let s = stream::from_iter(vec![1, 2, 3, 4]);
+        let mut s = s.filter(|i| i % 2 == 0);
 
-            Empty stream:
+        assert_eq!(s.next().await, Some(2));
+        assert_eq!(s.next().await, Some(4));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn filter<P>(self, predicate: P) -> Filter<Self, P>
+    where
+        Self: Sized,
+        P: FnMut(&Self::Item) -> bool,
+    {
+        Filter::new(self, predicate)
+    }
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+    #[doc= r#"
+        Creates an stream that works like map, but flattens nested structure.
 
-            let mut s = stream::empty::<u32>();
-            assert!(s.all(|_| false).await);
-            #
-            # }) }
-            ```
-        "#]
-        #[inline]
-        fn all<F>(
-            &mut self,
-            f: F,
-        ) -> impl Future<Output = bool> + '_ [AllFuture<'_, Self, F, Self::Item>]
-        where
-            Self: Unpin + Sized,
-            F: FnMut(Self::Item) -> bool,
-        {
-            AllFuture {
-                stream: self,
-                result: true, // the default if the empty stream
-                _marker: PhantomData,
-                f,
-            }
-        }
+        # Examples
 
-        #[doc = r#"
-            Searches for an element in a stream that satisfies a predicate.
+        Basic usage:
 
-            # Examples
+        ```
+        # async_std::task::block_on(async {
 
-            Basic usage:
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
+        let words = stream::from_iter(&["alpha", "beta", "gamma"]);
 
-            let mut s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-            let res = s.find(|x| *x == 2).await;
-            assert_eq!(res, Some(2));
-            #
-            # }) }
-            ```
+        let merged: String = words
+            .flat_map(|s| stream::from_iter(s.chars()))
+            .collect().await;
+            assert_eq!(merged, "alphabetagamma");
 
-            Resuming after a first find:
+        let d3 = stream::from_iter(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]);
+        let d1: Vec<_> = d3
+            .flat_map(|item| stream::from_iter(item))
+            .flat_map(|item| stream::from_iter(item))
+            .collect().await;
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
-
-            let mut s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-            let res = s.find(|x| *x == 2).await;
-            assert_eq!(res, Some(2));
-
-            let next = s.next().await;
-            assert_eq!(next, Some(3));
-            #
-            # }) }
-            ```
-        "#]
-        fn find<P>(
-            &mut self,
-            p: P,
-        ) -> impl Future<Output = Option<Self::Item>> + '_ [FindFuture<'_, Self, P, Self::Item>]
+        assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]);
+        # });
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
         where
             Self: Sized,
-            P: FnMut(&Self::Item) -> bool,
-        {
-            FindFuture::new(self, p)
-        }
+            U: IntoStream,
+            F: FnMut(Self::Item) -> U,
+    {
+        FlatMap::new(self, f)
+    }
 
-        #[doc = r#"
-            Applies function to the elements of stream and returns the first non-none result.
+    #[doc = r#"
+        Creates an stream that flattens nested structure.
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
-
-            let mut s: VecDeque<&str> = vec!["lol", "NaN", "2", "5"].into_iter().collect();
-            let first_number = s.find_map(|s| s.parse().ok()).await;
-
-            assert_eq!(first_number, Some(2));
-            #
-            # }) }
-            ```
-        "#]
-        fn find_map<F, B>(
-            &mut self,
-            f: F,
-        ) -> impl Future<Output = Option<B>> + '_ [FindMapFuture<'_, Self, F, Self::Item, B>]
-        where
-            Self: Sized,
-            F: FnMut(Self::Item) -> Option<B>,
-        {
-            FindMapFuture::new(self, f)
-        }
+        # Examples
 
-        #[doc = r#"
-            A combinator that applies a function to every element in a stream
-            producing a single, final value.
+        Basic usage:
 
-            # Examples
+        ```
+        # async_std::task::block_on(async {
 
-            Basic usage:
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
-
-            let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-            let sum = s.fold(0, |acc, x| acc + x).await;
-
-            assert_eq!(sum, 6);
-            #
-            # }) }
-            ```
-        "#]
-        fn fold<B, F>(
-            self,
-            init: B,
-            f: F,
-        ) -> impl Future<Output = B> [FoldFuture<Self, F, Self::Item, B>]
-        where
-            Self: Sized,
-            F: FnMut(B, Self::Item) -> B,
-        {
-            FoldFuture::new(self, init, f)
-        }
+        let inner1 = stream::from_iter(vec![1u8,2,3]);
+        let inner2 = stream::from_iter(vec![4u8,5,6]);
+        let s  = stream::from_iter(vec![inner1, inner2]);
 
-        #[doc = r#"
-            Call a closure on each element of the stream.
+        let v: Vec<_> = s.flatten().collect().await;
 
-            # Examples
+        assert_eq!(v, vec![1,2,3,4,5,6]);
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use std::collections::VecDeque;
-            use std::sync::mpsc::channel;
+        # });
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn flatten(self) -> Flatten<Self>
+    where
+        Self: Sized,
+        Self::Item: IntoStream,
+    {
+        Flatten::new(self)
+    }
 
-            let (tx, rx) = channel();
+    #[doc = r#"
+        Both filters and maps a stream.
 
-            let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-            let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await;
+        # Examples
 
-            let v: Vec<_> = rx.iter().collect();
+        Basic usage:
 
-            assert_eq!(v, vec![1, 2, 3]);
-            #
-            # }) }
-            ```
-        "#]
-        fn for_each<F>(
-            self,
-            f: F,
-        ) -> impl Future<Output = ()> [ForEachFuture<Self, F, Self::Item>]
-        where
-            Self: Sized,
-            F: FnMut(Self::Item),
-        {
-            ForEachFuture::new(self, f)
-        }
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
 
-        #[doc = r#"
-            Tests if any element of the stream matches a predicate.
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            `any()` takes a closure that returns `true` or `false`. It applies
-            this closure to each element of the stream, and if any of them return
-            `true`, then so does `any()`. If they all return `false`, it
-            returns `false`.
+        let s = stream::from_iter(vec!["1", "lol", "3", "NaN", "5"]);
 
-            `any()` is short-circuiting; in other words, it will stop processing
-            as soon as it finds a `true`, given that no matter what else happens,
-            the result will also be `true`.
+        let mut parsed = s.filter_map(|a| a.parse::<u32>().ok());
 
-            An empty stream returns `false`.
+        let one = parsed.next().await;
+        assert_eq!(one, Some(1));
 
-            # Examples
+        let three = parsed.next().await;
+        assert_eq!(three, Some(3));
 
-            Basic usage:
+        let five = parsed.next().await;
+        assert_eq!(five, Some(5));
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+        let end = parsed.next().await;
+        assert_eq!(end, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
+    where
+        Self: Sized,
+        F: FnMut(Self::Item) -> Option<B>,
+    {
+        FilterMap::new(self, f)
+    }
+
+    #[doc = r#"
+        Returns the element that gives the minimum value with respect to the
+        specified key function. If several elements are equally minimum,
+        the first element is returned. If the stream is empty, `None` is returned.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![-1isize, 2, -3]);
+
+        let min = s.clone().min_by_key(|x| x.abs()).await;
+        assert_eq!(min, Some(-1));
+
+        let min = stream::empty::<isize>().min_by_key(|x| x.abs()).await;
+        assert_eq!(min, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn min_by_key<B, F>(
+        self,
+        key_by: F,
+    ) -> MinByKeyFuture<Self, Self::Item, F>
+    where
+        Self: Sized,
+        B: Ord,
+        F: FnMut(&Self::Item) -> B,
+    {
+        MinByKeyFuture::new(self, key_by)
+    }
+
+    #[doc = r#"
+        Returns the element that gives the maximum value with respect to the
+        specified key function. If several elements are equally maximum,
+        the first element is returned. If the stream is empty, `None` is returned.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![-3_i32, 0, 1, 5, -10]);
+
+        let max = s.clone().max_by_key(|x| x.abs()).await;
+        assert_eq!(max, Some(-10));
+
+        let max = stream::empty::<isize>().max_by_key(|x| x.abs()).await;
+        assert_eq!(max, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn max_by_key<B, F>(
+        self,
+        key_by: F,
+    ) -> MaxByKeyFuture<Self, Self::Item, F>
+    where
+        Self: Sized,
+        B: Ord,
+        F: FnMut(&Self::Item) -> B,
+    {
+        MaxByKeyFuture::new(self, key_by)
+    }
+
+    #[doc = r#"
+        Returns the element that gives the minimum value with respect to the
+        specified comparison function. If several elements are equally minimum,
+        the first element is returned. If the stream is empty, `None` is returned.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1u8, 2, 3]);
+
+        let min = s.clone().min_by(|x, y| x.cmp(y)).await;
+        assert_eq!(min, Some(1));
+
+        let min = s.min_by(|x, y| y.cmp(x)).await;
+        assert_eq!(min, Some(3));
+
+        let min = stream::empty::<u8>().min_by(|x, y| x.cmp(y)).await;
+        assert_eq!(min, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn min_by<F>(
+        self,
+        compare: F,
+    ) -> MinByFuture<Self, F, Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        MinByFuture::new(self, compare)
+    }
+
+    #[doc = r#"
+        Returns the element that gives the maximum value. If several elements are equally maximum,
+        the first element is returned. If the stream is empty, `None` is returned.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1usize, 2, 3]);
+
+        let max = s.clone().max().await;
+        assert_eq!(max, Some(3));
+
+        let max = stream::empty::<usize>().max().await;
+        assert_eq!(max, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn max(
+        self,
+    ) -> MaxFuture<Self, Self::Item>
+    where
+        Self: Sized,
+        Self::Item: Ord,
+    {
+        MaxFuture::new(self)
+    }
+
+            #[doc = r#"
+        Returns the element that gives the minimum value. If several elements are equally minimum,
+        the first element is returned. If the stream is empty, `None` is returned.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1usize, 2, 3]);
+
+        let min = s.clone().min().await;
+        assert_eq!(min, Some(1));
+
+        let min = stream::empty::<usize>().min().await;
+        assert_eq!(min, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn min(
+        self,
+    ) -> MinFuture<Self, Self::Item>
+    where
+        Self: Sized,
+        Self::Item: Ord,
+    {
+        MinFuture::new(self)
+    }
+
+     #[doc = r#"
+        Returns the element that gives the maximum value with respect to the
+        specified comparison function. If several elements are equally maximum,
+        the first element is returned. If the stream is empty, `None` is returned.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1u8, 2, 3]);
+
+        let max = s.clone().max_by(|x, y| x.cmp(y)).await;
+        assert_eq!(max, Some(3));
+
+        let max = s.max_by(|x, y| y.cmp(x)).await;
+        assert_eq!(max, Some(1));
+
+        let max = stream::empty::<usize>().max_by(|x, y| x.cmp(y)).await;
+        assert_eq!(max, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn max_by<F>(
+        self,
+        compare: F,
+    ) -> MaxByFuture<Self, F, Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        MaxByFuture::new(self, compare)
+    }
+
+    #[doc = r#"
+        Returns the nth element of the stream.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::from_iter(vec![1u8, 2, 3]);
+
+        let second = s.nth(1).await;
+        assert_eq!(second, Some(2));
+        #
+        # }) }
+        ```
+        Calling `nth()` multiple times:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::stream;
+        use async_std::prelude::*;
+
+        let mut s = stream::from_iter(vec![1u8, 2, 3]);
+
+        let second = s.nth(0).await;
+        assert_eq!(second, Some(1));
+
+        let second = s.nth(0).await;
+        assert_eq!(second, Some(2));
+        #
+        # }) }
+        ```
+        Returning `None` if the stream finished before returning `n` elements:
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s  = stream::from_iter(vec![1u8, 2, 3]);
+
+        let fourth = s.nth(4).await;
+        assert_eq!(fourth, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn nth(
+        &mut self,
+        n: usize,
+    ) -> NthFuture<'_, Self>
+    where
+        Self: Unpin + Sized,
+    {
+        NthFuture::new(self, n)
+    }
+
+    #[doc = r#"
+        Tests if every element of the stream matches a predicate.
+
+        `all()` takes a closure that returns `true` or `false`. It applies
+        this closure to each element of the stream, and if they all return
+        `true`, then so does `all()`. If any of them return `false`, it
+        returns `false`.
+
+        `all()` is short-circuiting; in other words, it will stop processing
+        as soon as it finds a `false`, given that no matter what else happens,
+        the result will also be `false`.
+
+        An empty stream returns `true`.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::repeat::<u32>(42).take(3);
+        assert!(s.all(|x| x ==  42).await);
+
+        #
+        # }) }
+        ```
+
+        Empty stream:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::empty::<u32>();
+        assert!(s.all(|_| false).await);
+        #
+        # }) }
+        ```
+    "#]
+    #[inline]
+    fn all<F>(
+        &mut self,
+        f: F,
+    ) -> AllFuture<'_, Self, F, Self::Item>
+    where
+        Self: Unpin + Sized,
+        F: FnMut(Self::Item) -> bool,
+    {
+        AllFuture::new(self, f)
+    }
+
+    #[doc = r#"
+        Searches for an element in a stream that satisfies a predicate.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::from_iter(vec![1u8, 2, 3]);
+        let res = s.find(|x| *x == 2).await;
+        assert_eq!(res, Some(2));
+        #
+        # }) }
+        ```
+
+        Resuming after a first find:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s= stream::from_iter(vec![1, 2, 3]);
+        let res = s.find(|x| *x == 2).await;
+        assert_eq!(res, Some(2));
+
+        let next = s.next().await;
+        assert_eq!(next, Some(3));
+        #
+        # }) }
+        ```
+    "#]
+    fn find<P>(
+        &mut self,
+        p: P,
+    ) -> FindFuture<'_, Self, P>
+    where
+        Self: Unpin + Sized,
+        P: FnMut(&Self::Item) -> bool,
+    {
+        FindFuture::new(self, p)
+    }
+
+    #[doc = r#"
+        Applies function to the elements of stream and returns the first non-none result.
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::from_iter(vec!["lol", "NaN", "2", "5"]);
+        let first_number = s.find_map(|s| s.parse().ok()).await;
+
+        assert_eq!(first_number, Some(2));
+        #
+        # }) }
+        ```
+    "#]
+    fn find_map<F, B>(
+        &mut self,
+        f: F,
+    ) -> FindMapFuture<'_, Self, F>
+    where
+        Self: Unpin + Sized,
+        F: FnMut(Self::Item) -> Option<B>,
+    {
+        FindMapFuture::new(self, f)
+    }
+
+    #[doc = r#"
+        A combinator that applies a function to every element in a stream
+        producing a single, final value.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1u8, 2, 3]);
+        let sum = s.fold(0, |acc, x| acc + x).await;
+
+        assert_eq!(sum, 6);
+        #
+        # }) }
+        ```
+    "#]
+    fn fold<B, F>(
+        self,
+        init: B,
+        f: F,
+    ) -> FoldFuture<Self, F, B>
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        FoldFuture::new(self, init, f)
+    }
+
+    #[doc = r#"
+        A combinator that applies a function to every element in a stream
+        creating two collections from it.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let (even, odd): (Vec<i32>, Vec<i32>) = stream::from_iter(vec![1, 2, 3])
+            .partition(|&n| n % 2 == 0).await;
+
+        assert_eq!(even, vec![2]);
+        assert_eq!(odd, vec![1, 3]);
+
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn partition<B, F>(
+        self,
+        f: F,
+    ) -> PartitionFuture<Self, F, B>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> bool,
+        B: Default + Extend<Self::Item>,
+    {
+        PartitionFuture::new(self, f)
+    }
+
+    #[doc = r#"
+        Call a closure on each element of the stream.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+        use std::sync::mpsc::channel;
+
+        let (tx, rx) = channel();
+
+        let s = stream::from_iter(vec![1usize, 2, 3]);
+        let sum = s.for_each(move |x| tx.clone().send(x).unwrap()).await;
+
+        let v: Vec<_> = rx.iter().collect();
+
+        assert_eq!(v, vec![1, 2, 3]);
+        #
+        # }) }
+        ```
+    "#]
+    fn for_each<F>(
+        self,
+        f: F,
+    ) -> ForEachFuture<Self, F>
+    where
+        Self: Sized,
+        F: FnMut(Self::Item),
+    {
+        ForEachFuture::new(self, f)
+    }
+
+    #[doc = r#"
+        Tests if any element of the stream matches a predicate.
+
+        `any()` takes a closure that returns `true` or `false`. It applies
+        this closure to each element of the stream, and if any of them return
+        `true`, then so does `any()`. If they all return `false`, it
+        returns `false`.
+
+        `any()` is short-circuiting; in other words, it will stop processing
+        as soon as it finds a `true`, given that no matter what else happens,
+        the result will also be `true`.
+
+        An empty stream returns `false`.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::repeat::<u32>(42).take(3);
+        assert!(s.any(|x| x ==  42).await);
+        #
+        # }) }
+        ```
+
+        Empty stream:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::empty::<u32>();
+        assert!(!s.any(|_| false).await);
+        #
+        # }) }
+        ```
+    "#]
+    #[inline]
+    fn any<F>(
+        &mut self,
+        f: F,
+    ) -> AnyFuture<'_, Self, F, Self::Item>
+    where
+        Self: Unpin + Sized,
+        F: FnMut(Self::Item) -> bool,
+    {
+        AnyFuture::new(self, f)
+    }
+
+    #[doc = r#"
+        Borrows an stream, rather than consuming it.
+
+        This is useful to allow applying stream adaptors while still retaining ownership of the original stream.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let a = vec![1isize, 2, 3];
+
+        let stream = stream::from_iter(a);
+
+        let sum: isize = stream.take(5).sum().await;
+
+        assert_eq!(sum, 6);
+
+        // if we try to use stream again, it won't work. The following line
+        // gives error: use of moved value: `stream`
+        // assert_eq!(stream.next(), None);
+
+        // let's try that again
+        let a = vec![1isize, 2, 3];
+
+        let mut stream = stream::from_iter(a);
+
+        // instead, we add in a .by_ref()
+        let sum: isize = stream.by_ref().take(2).sum().await;
+
+        assert_eq!(sum, 3);
+
+        // now this is just fine:
+        assert_eq!(stream.next().await, Some(3));
+        assert_eq!(stream.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn by_ref(&mut self) -> &mut Self {
+        self
+    }
+
+    #[doc = r#"
+        A stream adaptor similar to [`fold`] that holds internal state and produces a new
+        stream.
+
+        [`fold`]: #method.fold
+
+        `scan()` takes two arguments: an initial value which seeds the internal state, and
+        a closure with two arguments, the first being a mutable reference to the internal
+        state and the second a stream element. The closure can assign to the internal state
+        to share state between iterations.
+
+        On iteration, the closure will be applied to each element of the stream and the
+        return value from the closure, an `Option`, is yielded by the stream.
+
+        ## Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1isize, 2, 3]);
+        let mut s = s.scan(1, |state, x| {
+            *state = *state * x;
+            Some(-*state)
+        });
+
+        assert_eq!(s.next().await, Some(-1));
+        assert_eq!(s.next().await, Some(-2));
+        assert_eq!(s.next().await, Some(-6));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    #[inline]
+    fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
+    where
+        Self: Sized,
+        F: FnMut(&mut St, Self::Item) -> Option<B>,
+    {
+        Scan::new(self, initial_state, f)
+    }
+
+    #[doc = r#"
+        Combinator that `skip`s elements based on a predicate.
+
+        Takes a closure argument. It will call this closure on every element in
+        the stream and ignore elements until it returns `false`.
+
+        After `false` is returned, `SkipWhile`'s job is over and all further
+        elements in the stream are yielded.
+
+        ## Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let a = stream::from_iter(vec![-1i32, 0, 1]);
+        let mut s = a.skip_while(|x| x.is_negative());
+
+        assert_eq!(s.next().await, Some(0));
+        assert_eq!(s.next().await, Some(1));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
+    where
+        Self: Sized,
+        P: FnMut(&Self::Item) -> bool,
+    {
+        SkipWhile::new(self, predicate)
+    }
 
-            let mut s = stream::repeat::<u32>(42).take(3);
-            assert!(s.any(|x| x ==  42).await);
-            #
-            # }) }
-            ```
+    #[doc = r#"
+        Creates a combinator that skips the first `n` elements.
 
-            Empty stream:
+        ## Examples
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            let mut s = stream::empty::<u32>();
-            assert!(!s.any(|_| false).await);
-            #
-            # }) }
-            ```
-        "#]
-        #[inline]
-        fn any<F>(
-            &mut self,
-            f: F,
-        ) -> impl Future<Output = bool> + '_ [AnyFuture<'_, Self, F, Self::Item>]
-        where
-            Self: Unpin + Sized,
-            F: FnMut(Self::Item) -> bool,
-        {
-            AnyFuture {
-                stream: self,
-                result: false, // the default if the empty stream
-                _marker: PhantomData,
-                f,
-            }
-        }
+        let s = stream::from_iter(vec![1u8, 2, 3]);
+        let mut skipped = s.skip(2);
 
-        #[doc = r#"
-            A stream adaptor similar to [`fold`] that holds internal state and produces a new
-            stream.
+        assert_eq!(skipped.next().await, Some(3));
+        assert_eq!(skipped.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn skip(self, n: usize) -> Skip<Self>
+    where
+        Self: Sized,
+    {
+        Skip::new(self, n)
+    }
 
-            [`fold`]: #method.fold
+    #[doc=r#"
+        Await a stream or times out after a duration of time.
 
-            `scan()` takes two arguments: an initial value which seeds the internal state, and
-            a closure with two arguments, the first being a mutable reference to the internal
-            state and the second a stream element. The closure can assign to the internal state
-            to share state between iterations.
+        If you want to await an I/O future consider using
+        [`io::timeout`](../io/fn.timeout.html) instead.
 
-            On iteration, the closure will be applied to each element of the stream and the
-            return value from the closure, an `Option`, is yielded by the stream.
+        # Examples
 
-            ## Examples
+        ```
+        # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
+        #
+        use std::time::Duration;
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        use async_std::stream;
+        use async_std::prelude::*;
 
-            use async_std::prelude::*;
+        let mut s = stream::repeat(1).take(3).timeout(Duration::from_secs(1));
 
-            let s: VecDeque<isize> = vec![1, 2, 3].into_iter().collect();
-            let mut s = s.scan(1, |state, x| {
-                *state = *state * x;
-                Some(-*state)
-            });
-
-            assert_eq!(s.next().await, Some(-1));
-            assert_eq!(s.next().await, Some(-2));
-            assert_eq!(s.next().await, Some(-6));
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        #[inline]
-        fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
-        where
-            Self: Sized,
-            F: FnMut(&mut St, Self::Item) -> Option<B>,
-        {
-            Scan::new(self, initial_state, f)
+        while let Some(v) = s.next().await {
+            assert_eq!(v, Ok(1));
         }
 
-        #[doc = r#"
-            Combinator that `skip`s elements based on a predicate.
+        // when timeout
+        let mut s = stream::pending::<()>().timeout(Duration::from_millis(10));
+        match s.next().await {
+            Some(item) => assert!(item.is_err()),
+            None => panic!()
+        };
+        #
+        # Ok(()) }) }
+        ```
+    "#]
+    #[cfg(any(feature = "unstable", feature = "docs"))]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn timeout(self, dur: Duration) -> Timeout<Self>
+    where
+        Self: Stream + Sized,
+    {
+        Timeout::new(self, dur)
+    }
 
-            Takes a closure argument. It will call this closure on every element in
-            the stream and ignore elements until it returns `false`.
+    #[doc = r#"
+        A combinator that applies a function as long as it returns successfully, producing a single, final value.
+        Immediately returns the error when the function returns unsuccessfully.
 
-            After `false` is returned, `SkipWhile`'s job is over and all further
-            elements in the strem are yielded.
+        # Examples
 
-            ## Examples
+        Basic usage:
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let mut s = stream::from_iter(vec![1usize, 2, 3]);
+        let sum = s.try_fold(0, |acc, v| {
+            if (acc+v) % 2 == 1 {
+                Ok(v+3)
+            } else {
+                Err("fail")
+            }
+        }).await;
 
-            use async_std::prelude::*;
+        assert_eq!(sum, Err("fail"));
+        #
+        # }) }
+        ```
+    "#]
+    fn try_fold<B, F, T, E>(
+        &mut self,
+        init: T,
+        f: F,
+    ) -> TryFoldFuture<'_, Self, F, T>
+    where
+        Self: Unpin + Sized,
+        F: FnMut(B, Self::Item) -> Result<T, E>,
+    {
+        TryFoldFuture::new(self, init, f)
+    }
 
-            let a: VecDeque<_> = vec![-1i32, 0, 1].into_iter().collect();
-            let mut s = a.skip_while(|x| x.is_negative());
-
-            assert_eq!(s.next().await, Some(0));
-            assert_eq!(s.next().await, Some(1));
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P, Self::Item>
-        where
-            Self: Sized,
-            P: FnMut(&Self::Item) -> bool,
-        {
-            SkipWhile::new(self, predicate)
-        }
+    #[doc = r#"
+        Applies a falliable function to each element in a stream, stopping at first error and returning it.
 
-        #[doc = r#"
-            Creates a combinator that skips the first `n` elements.
+        # Examples
 
-            ## Examples
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use std::sync::mpsc::channel;
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        let (tx, rx) = channel();
 
-            use async_std::prelude::*;
+        let mut s = stream::from_iter(vec![1u8, 2, 3]);
+        let s = s.try_for_each(|v| {
+            if v % 2 == 1 {
+                tx.clone().send(v).unwrap();
+                Ok(())
+            } else {
+                Err("even")
+            }
+        });
 
-            let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-            let mut skipped = s.skip(2);
+        let res = s.await;
+        drop(tx);
+        let values: Vec<_> = rx.iter().collect();
 
-            assert_eq!(skipped.next().await, Some(3));
-            assert_eq!(skipped.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        fn skip(self, n: usize) -> Skip<Self>
-        where
-            Self: Sized,
-        {
-            Skip::new(self, n)
-        }
+        assert_eq!(values, vec![1]);
+        assert_eq!(res, Err("even"));
+        #
+        # }) }
+        ```
+    "#]
+    fn try_for_each<F, E>(
+        &mut self,
+        f: F,
+    ) -> TryForEachFuture<'_, Self, F>
+    where
+        Self: Unpin + Sized,
+        F: FnMut(Self::Item) -> Result<(), E>,
+    {
+        TryForEachFuture::new(self, f)
+    }
 
-        #[doc = r#"
-            Applies a falliable function to each element in a stream, stopping at first error and returning it.
+    #[doc = r#"
+        'Zips up' two streams into a single stream of pairs.
 
-            # Examples
+        `zip()` returns a new stream that will iterate over two other streams, returning a
+        tuple where the first element comes from the first stream, and the second element
+        comes from the second stream.
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
-            use std::sync::mpsc::channel;
-            use async_std::prelude::*;
+        In other words, it zips two streams together, into a single one.
 
-            let (tx, rx) = channel();
-
-            let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();
-            let s = s.try_for_each(|v| {
-                if v % 2 == 1 {
-                    tx.clone().send(v).unwrap();
-                    Ok(())
-                } else {
-                    Err("even")
-                }
-            });
-
-            let res = s.await;
-            drop(tx);
-            let values: Vec<_> = rx.iter().collect();
-
-            assert_eq!(values, vec![1]);
-            assert_eq!(res, Err("even"));
-            #
-            # }) }
-            ```
-        "#]
-        fn try_for_each<F, E>(
-            self,
-            f: F,
-        ) -> impl Future<Output = E> [TryForEeachFuture<Self, F, Self::Item, E>]
-        where
-            Self: Sized,
-            F: FnMut(Self::Item) -> Result<(), E>,
-        {
-            TryForEeachFuture::new(self, f)
-        }
+        If either stream returns [`None`], [`poll_next`] from the zipped stream will return
+        [`None`]. If the first stream returns [`None`], `zip` will short-circuit and
+        `poll_next` will not be called on the second stream.
 
-        #[doc = r#"
-            'Zips up' two streams into a single stream of pairs.
+        [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
+        [`poll_next`]: #tymethod.poll_next
+
+        ## Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let l = stream::from_iter(vec![1u8, 2, 3]);
+        let r = stream::from_iter(vec![4u8, 5, 6, 7]);
+        let mut s = l.zip(r);
+
+        assert_eq!(s.next().await, Some((1, 4)));
+        assert_eq!(s.next().await, Some((2, 5)));
+        assert_eq!(s.next().await, Some((3, 6)));
+        assert_eq!(s.next().await, None);
+        #
+        # }) }
+        ```
+    "#]
+    #[inline]
+    fn zip<U>(self, other: U) -> Zip<Self, U>
+    where
+        Self: Sized,
+        U: Stream,
+    {
+        Zip::new(self, other)
+    }
 
-            `zip()` returns a new stream that will iterate over two other streams, returning a
-            tuple where the first element comes from the first stream, and the second element
-            comes from the second stream.
+    #[doc = r#"
+        Converts an stream of pairs into a pair of containers.
 
-            In other words, it zips two streams together, into a single one.
+        `unzip()` consumes an entire stream of pairs, producing two collections: one from the left elements of the pairs, and one from the right elements.
 
-            If either stream returns [`None`], [`poll_next`] from the zipped stream will return
-            [`None`]. If the first stream returns [`None`], `zip` will short-circuit and
-            `poll_next` will not be called on the second stream.
+        This function is, in some sense, the opposite of [`zip`].
 
-            [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
-            [`poll_next`]: #tymethod.poll_next
+        [`zip`]: trait.Stream.html#method.zip
 
-            ## Examples
+        # Example
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use std::collections::VecDeque;
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
 
-            use async_std::prelude::*;
+        let s  = stream::from_iter(vec![(1,2), (3,4)]);
 
-            let l: VecDeque<isize> = vec![1, 2, 3].into_iter().collect();
-            let r: VecDeque<isize> = vec![4, 5, 6, 7].into_iter().collect();
-            let mut s = l.zip(r);
-
-            assert_eq!(s.next().await, Some((1, 4)));
-            assert_eq!(s.next().await, Some((2, 5)));
-            assert_eq!(s.next().await, Some((3, 6)));
-            assert_eq!(s.next().await, None);
-            #
-            # }) }
-            ```
-        "#]
-        #[inline]
-        fn zip<U>(self, other: U) -> Zip<Self, U>
-        where
-            Self: Sized + Stream,
-            U: Stream,
-        {
-            Zip::new(self, other)
-        }
+        let (left, right): (Vec<_>, Vec<_>) = s.unzip().await;
 
-        #[doc = r#"
-            Transforms a stream into a collection.
+        assert_eq!(left, [1, 3]);
+        assert_eq!(right, [2, 4]);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB>
+    where
+    FromA: Default + Extend<A>,
+    FromB: Default + Extend<B>,
+    Self: Stream<Item = (A, B)> + Sized,
+    {
+        UnzipFuture::new(self)
+    }
 
-            `collect()` can take anything streamable, and turn it into a relevant
-            collection. This is one of the more powerful methods in the async
-            standard library, used in a variety of contexts.
+    #[doc = r#"
+        Transforms a stream into a collection.
 
-            The most basic pattern in which `collect()` is used is to turn one
-            collection into another. You take a collection, call [`stream`] on it,
-            do a bunch of transformations, and then `collect()` at the end.
+        `collect()` can take anything streamable, and turn it into a relevant
+        collection. This is one of the more powerful methods in the async
+        standard library, used in a variety of contexts.
 
-            Because `collect()` is so general, it can cause problems with type
-            inference. As such, `collect()` is one of the few times you'll see
-            the syntax affectionately known as the 'turbofish': `::<>`. This
-            helps the inference algorithm understand specifically which collection
-            you're trying to collect into.
+        The most basic pattern in which `collect()` is used is to turn one
+        collection into another. You take a collection, call [`into_stream`] on it,
+        do a bunch of transformations, and then `collect()` at the end.
 
-            # Examples
+        Because `collect()` is so general, it can cause problems with type
+        inference. As such, `collect()` is one of the few times you'll see
+        the syntax affectionately known as the 'turbofish': `::<>`. This
+        helps the inference algorithm understand specifically which collection
+        you're trying to collect into.
 
-            ```
-            # fn main() { async_std::task::block_on(async {
-            #
-            use async_std::prelude::*;
-            use async_std::stream;
+        # Examples
 
-            let s = stream::repeat(9u8).take(3);
-            let buf: Vec<u8> = s.collect().await;
-
-            assert_eq!(buf, vec![9; 3]);
-
-            // You can also collect streams of Result values
-            // into any collection that implements FromStream
-            let s = stream::repeat(Ok(9)).take(3);
-            // We are using Vec here, but other collections
-            // are supported as well
-            let buf: Result<Vec<u8>, ()> = s.collect().await;
-
-            assert_eq!(buf, Ok(vec![9; 3]));
-
-            // The stream will stop on the first Err and
-            // return that instead
-            let s = stream::repeat(Err(5)).take(3);
-            let buf: Result<Vec<u8>, u8> = s.collect().await;
-
-            assert_eq!(buf, Err(5));
-            #
-            # }) }
-            ```
-
-            [`stream`]: trait.Stream.html#tymethod.next
-        "#]
-        #[cfg(any(feature = "unstable", feature = "docs"))]
-        #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-        #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"]
-        fn collect<'a, B>(
-            self,
-        ) -> impl Future<Output = B> + 'a [Pin<Box<dyn Future<Output = B> + 'a>>]
-        where
-            Self: Sized + 'a,
-            B: FromStream<Self::Item>,
-        {
-            FromStream::from_stream(self)
-        }
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::repeat(9u8).take(3);
+        let buf: Vec<u8> = s.collect().await;
+
+        assert_eq!(buf, vec![9; 3]);
+
+        // You can also collect streams of Result values
+        // into any collection that implements FromStream
+        let s = stream::repeat(Ok(9)).take(3);
+        // We are using Vec here, but other collections
+        // are supported as well
+        let buf: Result<Vec<u8>, ()> = s.collect().await;
+
+        assert_eq!(buf, Ok(vec![9; 3]));
+
+        // The stream will stop on the first Err and
+        // return that instead
+        let s = stream::repeat(Err(5)).take(3);
+        let buf: Result<Vec<u8>, u8> = s.collect().await;
+
+        assert_eq!(buf, Err(5));
+        #
+        # }) }
+        ```
+
+        [`into_stream`]: trait.IntoStream.html#tymethod.into_stream
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn collect<'a, B>(
+        self,
+    ) -> Pin<Box<dyn Future<Output = B> + 'a + Send>>
+    where
+        Self: Sized + 'a + Send,
+        B: FromStream<Self::Item>,
+        Self::Item: Send,
+    {
+        FromStream::from_stream(self)
     }
 
-    impl<S: Stream + Unpin + ?Sized> Stream for Box<S> {
-        type Item = S::Item;
+    #[doc = r#"
+        Combines multiple streams into a single stream of all their outputs.
 
-        fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+        Items are yielded as soon as they're received, and the stream continues yield until
+        both streams have been exhausted. The output ordering between streams is not guaranteed.
+
+        # Examples
+
+        ```
+        # async_std::task::block_on(async {
+        use async_std::prelude::*;
+        use async_std::stream::{self, FromStream};
+
+        let a = stream::once(1u8);
+        let b = stream::once(2u8);
+        let c = stream::once(3u8);
+
+        let s = a.merge(b).merge(c);
+        let mut lst = Vec::from_stream(s).await;
+
+        lst.sort_unstable();
+        assert_eq!(&lst, &[1u8, 2u8, 3u8]);
+        # });
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn merge<U>(self, other: U) -> Merge<Self, U>
+    where
+        Self: Sized,
+        U: Stream<Item = Self::Item> + Sized,
+    {
+        Merge::new(self, other)
     }
 
-    impl<S: Stream + Unpin + ?Sized> Stream for &mut S {
-        type Item = S::Item;
+    #[doc = r#"
+        Lexicographically compares the elements of this `Stream` with those
+        of another.
+
+        # Examples
 
-        fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        use std::cmp::Ordering;
+
+        let s1 = stream::from_iter(vec![1]);
+        let s2 = stream::from_iter(vec![1, 2]);
+        let s3 = stream::from_iter(vec![1, 2, 3]);
+        let s4 = stream::from_iter(vec![1, 2, 4]);
+        assert_eq!(s1.clone().partial_cmp(s1.clone()).await, Some(Ordering::Equal));
+        assert_eq!(s1.clone().partial_cmp(s2.clone()).await, Some(Ordering::Less));
+        assert_eq!(s2.clone().partial_cmp(s1.clone()).await, Some(Ordering::Greater));
+        assert_eq!(s3.clone().partial_cmp(s4.clone()).await, Some(Ordering::Less));
+        assert_eq!(s4.clone().partial_cmp(s3.clone()).await, Some(Ordering::Greater));
+        #
+        # }) }
+        ```
+    "#]
+    fn partial_cmp<S>(
+       self,
+       other: S
+    ) -> PartialCmpFuture<Self, S>
+    where
+        Self: Sized + Stream,
+        S: Stream,
+        <Self as Stream>::Item: PartialOrd<S::Item>,
+    {
+        PartialCmpFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Searches for an element in a Stream that satisfies a predicate, returning
+        its index.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![1usize, 2, 3]);
+        let res = s.clone().position(|x| x == 1).await;
+        assert_eq!(res, Some(0));
+
+        let res = s.clone().position(|x| x == 2).await;
+        assert_eq!(res, Some(1));
+
+        let res = s.clone().position(|x| x == 3).await;
+        assert_eq!(res, Some(2));
+
+        let res = s.clone().position(|x| x == 4).await;
+        assert_eq!(res, None);
+        #
+        # }) }
+        ```
+    "#]
+    fn position<P>(
+       &mut self,
+       predicate: P,
+    ) -> PositionFuture<'_, Self, P>
+    where
+        Self: Unpin + Sized,
+        P: FnMut(Self::Item) -> bool,
+    {
+        PositionFuture::new(self, predicate)
     }
 
-    impl<P> Stream for Pin<P>
+    #[doc = r#"
+        Lexicographically compares the elements of this `Stream` with those
+        of another using 'Ord'.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+        use std::cmp::Ordering;
+
+        let s1 = stream::from_iter(vec![1]);
+        let s2 = stream::from_iter(vec![1, 2]);
+        let s3 = stream::from_iter(vec![1, 2, 3]);
+        let s4 = stream::from_iter(vec![1, 2, 4]);
+
+        assert_eq!(s1.clone().cmp(s1.clone()).await, Ordering::Equal);
+        assert_eq!(s1.clone().cmp(s2.clone()).await, Ordering::Less);
+        assert_eq!(s2.clone().cmp(s1.clone()).await, Ordering::Greater);
+        assert_eq!(s3.clone().cmp(s4.clone()).await, Ordering::Less);
+        assert_eq!(s4.clone().cmp(s3.clone()).await, Ordering::Greater);
+        #
+        # }) }
+        ```
+    "#]
+    fn cmp<S>(
+       self,
+       other: S
+    ) -> CmpFuture<Self, S>
     where
-        P: DerefMut + Unpin,
-        <P as Deref>::Target: Stream,
+        Self: Sized + Stream,
+        S: Stream,
+        <Self as Stream>::Item: Ord
     {
-        type Item = <<P as Deref>::Target as Stream>::Item;
+        CmpFuture::new(self, other)
+    }
 
-        fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+    #[doc = r#"
+        Counts the number of elements in the stream.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s1 = stream::from_iter(vec![0]);
+        let s2 = stream::from_iter(vec![1, 2, 3]);
+
+        assert_eq!(s1.count().await, 1);
+        assert_eq!(s2.count().await, 3);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn count(self) -> CountFuture<Self>
+    where
+        Self: Sized,
+    {
+        CountFuture::new(self)
     }
 
-    impl<T: Unpin> Stream for std::collections::VecDeque<T> {
-        type Item = T;
+    #[doc = r#"
+        Determines if the elements of this `Stream` are lexicographically
+        not equal to those of another.
+
+        # Examples
 
-        fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-            unreachable!("this impl only appears in the rendered docs")
-        }
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let single     = stream::from_iter(vec![1usize]);
+        let single_ne  = stream::from_iter(vec![10usize]);
+        let multi      = stream::from_iter(vec![1usize,2]);
+        let multi_ne   = stream::from_iter(vec![1usize,5]);
+
+        assert_eq!(single.clone().ne(single.clone()).await, false);
+        assert_eq!(single_ne.clone().ne(single.clone()).await, true);
+        assert_eq!(multi.clone().ne(single_ne.clone()).await, true);
+        assert_eq!(multi_ne.clone().ne(multi.clone()).await, true);
+        #
+        # }) }
+        ```
+    "#]
+    fn ne<S>(
+       self,
+       other: S
+    ) -> NeFuture<Self, S>
+    where
+        Self: Sized,
+        S: Sized + Stream,
+        <Self as Stream>::Item: PartialEq<S::Item>,
+    {
+        NeFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Determines if the elements of this `Stream` are lexicographically
+        greater than or equal to those of another.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let single    = stream::from_iter(vec![1]);
+        let single_gt = stream::from_iter(vec![10]);
+        let multi     = stream::from_iter(vec![1,2]);
+        let multi_gt  = stream::from_iter(vec![1,5]);
+
+        assert_eq!(single.clone().ge(single.clone()).await, true);
+        assert_eq!(single_gt.clone().ge(single.clone()).await, true);
+        assert_eq!(multi.clone().ge(single_gt.clone()).await, false);
+        assert_eq!(multi_gt.clone().ge(multi.clone()).await, true);
+        #
+        # }) }
+        ```
+    "#]
+    fn ge<S>(
+       self,
+       other: S
+    ) -> GeFuture<Self, S>
+    where
+        Self: Sized + Stream,
+        S: Stream,
+        <Self as Stream>::Item: PartialOrd<S::Item>,
+    {
+        GeFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Determines if the elements of this `Stream` are lexicographically
+        equal to those of another.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let single     = stream::from_iter(vec![1]);
+        let single_eq  = stream::from_iter(vec![10]);
+        let multi      = stream::from_iter(vec![1,2]);
+        let multi_eq   = stream::from_iter(vec![1,5]);
+
+        assert_eq!(single.clone().eq(single.clone()).await, true);
+        assert_eq!(single_eq.clone().eq(single.clone()).await, false);
+        assert_eq!(multi.clone().eq(single_eq.clone()).await, false);
+        assert_eq!(multi_eq.clone().eq(multi.clone()).await, false);
+        #
+        # }) }
+        ```
+    "#]
+    fn eq<S>(
+       self,
+       other: S
+    ) -> EqFuture<Self, S>
+    where
+        Self: Sized + Stream,
+        S: Sized + Stream,
+        <Self as Stream>::Item: PartialEq<S::Item>,
+    {
+        EqFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Determines if the elements of this `Stream` are lexicographically
+        greater than those of another.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let single = stream::from_iter(vec![1]);
+        let single_gt = stream::from_iter(vec![10]);
+        let multi = stream::from_iter(vec![1,2]);
+        let multi_gt = stream::from_iter(vec![1,5]);
+
+        assert_eq!(single.clone().gt(single.clone()).await, false);
+        assert_eq!(single_gt.clone().gt(single.clone()).await, true);
+        assert_eq!(multi.clone().gt(single_gt.clone()).await, false);
+        assert_eq!(multi_gt.clone().gt(multi.clone()).await, true);
+        #
+        # }) }
+        ```
+    "#]
+    fn gt<S>(
+       self,
+       other: S
+    ) -> GtFuture<Self, S>
+    where
+        Self: Sized + Stream,
+        S: Stream,
+        <Self as Stream>::Item: PartialOrd<S::Item>,
+    {
+        GtFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Determines if the elements of this `Stream` are lexicographically
+        less or equal to those of another.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let single = stream::from_iter(vec![1]);
+        let single_gt = stream::from_iter(vec![10]);
+        let multi = stream::from_iter(vec![1,2]);
+        let multi_gt = stream::from_iter(vec![1,5]);
+
+        assert_eq!(single.clone().le(single.clone()).await, true);
+        assert_eq!(single.clone().le(single_gt.clone()).await, true);
+        assert_eq!(multi.clone().le(single_gt.clone()).await, true);
+        assert_eq!(multi_gt.clone().le(multi.clone()).await, false);
+        #
+        # }) }
+        ```
+    "#]
+    fn le<S>(
+       self,
+       other: S
+    ) -> LeFuture<Self, S>
+    where
+        Self: Sized + Stream,
+        S: Stream,
+        <Self as Stream>::Item: PartialOrd<S::Item>,
+    {
+        LeFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Determines if the elements of this `Stream` are lexicographically
+        less than those of another.
+
+        # Examples
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let single = stream::from_iter(vec![1]);
+        let single_gt = stream::from_iter(vec![10]);
+        let multi = stream::from_iter(vec![1,2]);
+        let multi_gt = stream::from_iter(vec![1,5]);
+
+        assert_eq!(single.clone().lt(single.clone()).await, false);
+        assert_eq!(single.clone().lt(single_gt.clone()).await, true);
+        assert_eq!(multi.clone().lt(single_gt.clone()).await, true);
+        assert_eq!(multi_gt.clone().lt(multi.clone()).await, false);
+        #
+        # }) }
+        ```
+    "#]
+    fn lt<S>(
+       self,
+       other: S
+    ) -> LtFuture<Self, S>
+    where
+        Self: Sized + Stream,
+        S: Stream,
+        <Self as Stream>::Item: PartialOrd<S::Item>,
+    {
+        LtFuture::new(self, other)
+    }
+
+    #[doc = r#"
+        Sums the elements of a stream.
+
+        Takes each element, adds them together, and returns the result.
+
+        An empty streams returns the zero value of the type.
+
+        # Panics
+
+        When calling `sum()` and a primitive integer type is being returned, this
+        method will panic if the computation overflows and debug assertions are
+        enabled.
+
+        # Examples
+
+        Basic usage:
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        use async_std::prelude::*;
+        use async_std::stream;
+
+        let s = stream::from_iter(vec![0u8, 1, 2, 3, 4]);
+        let sum: u8 = s.sum().await;
+
+        assert_eq!(sum, 10);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn sum<'a, S>(
+        self,
+    ) -> Pin<Box<dyn Future<Output = S> + 'a>>
+    where
+        Self: Sized + Stream<Item = S> + 'a,
+        S: Sum<Self::Item>,
+    {
+        Sum::sum(self)
     }
 
-    impl<S: Stream> Stream for std::panic::AssertUnwindSafe<S> {
-        type Item = S::Item;
+    #[doc = r#"
+        Multiplies all elements of the stream.
+
+        An empty stream returns the one value of the type.
+
+        # Panics
+
+        When calling `product()` and a primitive integer type is being returned,
+        method will panic if the computation overflows and debug assertions are
+        enabled.
+
+        # Examples
+
+        This example calculates the factorial of n (i.e. the product of the numbers from 1 to
+        n, inclusive):
+
+        ```
+        # fn main() { async_std::task::block_on(async {
+        #
+        async fn factorial(n: u32) -> u32 {
+            use async_std::prelude::*;
+            use async_std::stream;
 
-        fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-            unreachable!("this impl only appears in the rendered docs")
+            let s = stream::from_iter(1..=n);
+            s.product().await
         }
+
+        assert_eq!(factorial(0).await, 1);
+        assert_eq!(factorial(1).await, 1);
+        assert_eq!(factorial(5).await, 120);
+        #
+        # }) }
+        ```
+    "#]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    fn product<'a, P>(
+        self,
+    ) -> Pin<Box<dyn Future<Output = P> + 'a>>
+    where
+        Self: Sized + Stream<Item = P> + 'a,
+        P: Product,
+    {
+        Product::product(self)
     }
 }
+
+impl<T: Stream + ?Sized> StreamExt for T {}
+
diff --git a/src/stream/stream/ne.rs b/src/stream/stream/ne.rs
new file mode 100644
index 000000000..c51ab31ef
--- /dev/null
+++ b/src/stream/stream/ne.rs
@@ -0,0 +1,65 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::fuse::Fuse;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Lexicographically compares the elements of this `Stream` with those
+    // of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct NeFuture<L: Stream, R: Stream> {
+        #[pin]
+        l: Fuse<L>,
+        #[pin]
+        r: Fuse<R>,
+    }
+}
+
+impl<L: Stream, R: Stream> NeFuture<L, R>
+where
+    L::Item: PartialEq<R::Item>,
+{
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            l: l.fuse(),
+            r: r.fuse(),
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for NeFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream + Sized,
+    L::Item: PartialEq<R::Item>,
+{
+    type Output = bool;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+
+        loop {
+            let l_val = futures_core::ready!(this.l.as_mut().poll_next(cx));
+            let r_val = futures_core::ready!(this.r.as_mut().poll_next(cx));
+
+            if this.l.done || this.r.done {
+                return Poll::Ready(false);
+            }
+
+            match (l_val, r_val) {
+                (Some(l), Some(r)) if l == r => {
+                    continue;
+                }
+                _ => {
+                    return Poll::Ready(true);
+                }
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/next.rs b/src/stream/stream/next.rs
index de75f5e93..7bd208320 100644
--- a/src/stream/stream/next.rs
+++ b/src/stream/stream/next.rs
@@ -1,6 +1,6 @@
-use std::pin::Pin;
+use core::pin::Pin;
+use core::future::Future;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs
index e7e042a95..8cdabb6c4 100644
--- a/src/stream/stream/nth.rs
+++ b/src/stream/stream/nth.rs
@@ -1,7 +1,7 @@
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use core::pin::Pin;
+use core::task::{Context, Poll};
+use core::future::Future;
 
-use crate::future::Future;
 use crate::stream::Stream;
 
 #[doc(hidden)]
@@ -15,7 +15,7 @@ impl<S: Unpin> Unpin for NthFuture<'_, S> {}
 
 impl<'a, S> NthFuture<'a, S> {
     pub(crate) fn new(stream: &'a mut S, n: usize) -> Self {
-        NthFuture { stream, n }
+        Self { stream, n }
     }
 }
 
diff --git a/src/stream/stream/partial_cmp.rs b/src/stream/stream/partial_cmp.rs
new file mode 100644
index 000000000..c328a9119
--- /dev/null
+++ b/src/stream/stream/partial_cmp.rs
@@ -0,0 +1,94 @@
+use core::cmp::Ordering;
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use super::fuse::Fuse;
+use crate::stream::stream::StreamExt;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    // Lexicographically compares the elements of this `Stream` with those
+    // of another.
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct PartialCmpFuture<L: Stream, R: Stream> {
+        #[pin]
+        l: Fuse<L>,
+        #[pin]
+        r: Fuse<R>,
+        l_cache: Option<L::Item>,
+        r_cache: Option<R::Item>,
+    }
+}
+
+impl<L: Stream, R: Stream> PartialCmpFuture<L, R> {
+    pub(super) fn new(l: L, r: R) -> Self {
+        Self {
+            l: l.fuse(),
+            r: r.fuse(),
+            l_cache: None,
+            r_cache: None,
+        }
+    }
+}
+
+impl<L: Stream, R: Stream> Future for PartialCmpFuture<L, R>
+where
+    L: Stream + Sized,
+    R: Stream + Sized,
+    L::Item: PartialOrd<R::Item>,
+{
+    type Output = Option<Ordering>;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+        loop {
+            // Short circuit logic
+            // Stream that completes earliest can be considered Less, etc
+            let l_complete = this.l.done && this.l_cache.is_none();
+            let r_complete = this.r.done && this.r_cache.is_none();
+
+            if l_complete && r_complete {
+                return Poll::Ready(Some(Ordering::Equal));
+            } else if l_complete {
+                return Poll::Ready(Some(Ordering::Less));
+            } else if r_complete {
+                return Poll::Ready(Some(Ordering::Greater));
+            }
+
+            // Get next value if possible and necessary
+            if !this.l.done && this.l_cache.is_none() {
+                let l_next = futures_core::ready!(this.l.as_mut().poll_next(cx));
+                if let Some(item) = l_next {
+                    *this.l_cache = Some(item);
+                }
+            }
+
+            if !this.r.done && this.r_cache.is_none() {
+                let r_next = futures_core::ready!(this.r.as_mut().poll_next(cx));
+                if let Some(item) = r_next {
+                    *this.r_cache = Some(item);
+                }
+            }
+
+            // Compare if both values are available.
+            if this.l_cache.is_some() && this.r_cache.is_some() {
+                let l_value = this.l_cache.as_mut().take().unwrap();
+                let r_value = this.r_cache.as_mut().take().unwrap();
+                let result = l_value.partial_cmp(&r_value);
+
+                if let Some(Ordering::Equal) = result {
+                    // Reset cache to prepare for next comparison
+                    *this.l_cache = None;
+                    *this.r_cache = None;
+                } else {
+                    // Return non equal value
+                    return Poll::Ready(result);
+                }
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/partition.rs b/src/stream/stream/partition.rs
new file mode 100644
index 000000000..69268ecb8
--- /dev/null
+++ b/src/stream/stream/partition.rs
@@ -0,0 +1,59 @@
+use pin_project_lite::pin_project;
+use core::default::Default;
+use core::future::Future;
+use core::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[derive(Debug)]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    pub struct PartitionFuture<S, F, B> {
+        #[pin]
+        stream: S,
+        f: F,
+        res: Option<(B, B)>,
+    }
+}
+
+impl<S, F, B: Default> PartitionFuture<S, F, B> {
+    pub(super) fn new(stream: S, f: F) -> Self {
+        Self {
+            stream,
+            f,
+            res: Some((B::default(), B::default())),
+        }
+    }
+}
+
+impl<S, F, B> Future for PartitionFuture<S, F, B>
+where
+    S: Stream + Sized,
+    F: FnMut(&S::Item) -> bool,
+    B: Default + Extend<S::Item>,
+{
+    type Output = (B, B);
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+
+        loop {
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
+
+            match next {
+                Some(v) => {
+                    let res = this.res.as_mut().unwrap();
+
+                    if (this.f)(&v) {
+                        res.0.extend(Some(v))
+                    } else {
+                        res.1.extend(Some(v))
+                    }
+                }
+                None => return Poll::Ready(this.res.take().unwrap()),
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/position.rs b/src/stream/stream/position.rs
new file mode 100644
index 000000000..2811b6d86
--- /dev/null
+++ b/src/stream/stream/position.rs
@@ -0,0 +1,50 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+pub struct PositionFuture<'a, S, P> {
+    stream: &'a mut S,
+    predicate: P,
+    index: usize,
+}
+
+impl<'a, S, P> Unpin for PositionFuture<'a, S, P> {}
+
+impl<'a, S, P> PositionFuture<'a, S, P> {
+    pub(super) fn new(stream: &'a mut S, predicate: P) -> Self {
+        Self {
+            stream,
+            predicate,
+            index: 0,
+        }
+    }
+}
+
+impl<'a, S, P> Future for PositionFuture<'a, S, P>
+where
+    S: Stream + Unpin,
+    P: FnMut(S::Item) -> bool,
+{
+    type Output = Option<usize>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx));
+
+        match next {
+            Some(v) => {
+                if (&mut self.predicate)(v) {
+                    Poll::Ready(Some(self.index))
+                } else {
+                    cx.waker().wake_by_ref();
+                    self.index += 1;
+                    Poll::Pending
+                }
+            }
+            None => Poll::Ready(None),
+        }
+    }
+}
diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs
index 78975161b..d72b0dc23 100644
--- a/src/stream/stream/scan.rs
+++ b/src/stream/stream/scan.rs
@@ -1,13 +1,24 @@
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream to maintain state while polling another stream.
-#[derive(Debug)]
-pub struct Scan<S, St, F> {
-    stream: S,
-    state_f: (St, F),
+pin_project! {
+    /// A stream to maintain state while polling another stream.
+    ///
+    /// This `struct` is created by the [`scan`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`scan`]: trait.Stream.html#method.scan
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct Scan<S, St, F> {
+        #[pin]
+        stream: S,
+        state_f: (St, F),
+    }
 }
 
 impl<S, St, F> Scan<S, St, F> {
@@ -17,13 +28,8 @@ impl<S, St, F> Scan<S, St, F> {
             state_f: (initial_state, f),
         }
     }
-
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(state_f: (St, F));
 }
 
-impl<S: Unpin, St, F> Unpin for Scan<S, St, F> {}
-
 impl<S, St, F, B> Stream for Scan<S, St, F>
 where
     S: Stream,
@@ -31,11 +37,12 @@ where
 {
     type Item = B;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<B>> {
-        let poll_result = self.as_mut().stream().poll_next(cx);
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<B>> {
+        let mut this = self.project();
+        let poll_result = this.stream.as_mut().poll_next(cx);
         poll_result.map(|item| {
             item.and_then(|item| {
-                let (state, f) = self.as_mut().state_f();
+                let (state, f) = this.state_f;
                 f(state, item)
             })
         })
diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs
index 8a2d966d8..52b137d06 100644
--- a/src/stream/stream/skip.rs
+++ b/src/stream/stream/skip.rs
@@ -1,21 +1,29 @@
-use std::pin::Pin;
-use std::task::{Context, Poll};
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 
-/// A stream to skip first n elements of another stream.
-#[derive(Debug)]
-pub struct Skip<S> {
-    stream: S,
-    n: usize,
+pin_project! {
+    /// A stream to skip first n elements of another stream.
+    ///
+    /// This `struct` is created by the [`skip`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`skip`]: trait.Stream.html#method.skip
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct Skip<S> {
+        #[pin]
+        stream: S,
+        n: usize,
+    }
 }
 
 impl<S> Skip<S> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(n: usize);
-
     pub(crate) fn new(stream: S, n: usize) -> Self {
-        Skip { stream, n }
+        Self { stream, n }
     }
 }
 
@@ -25,14 +33,15 @@ where
 {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
         loop {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
 
             match next {
-                Some(v) => match self.n {
+                Some(v) => match *this.n {
                     0 => return Poll::Ready(Some(v)),
-                    _ => *self.as_mut().n() -= 1,
+                    _ => *this.n -= 1,
                 },
                 None => return Poll::Ready(None),
             }
diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs
index a54b05109..d139de4d0 100644
--- a/src/stream/stream/skip_while.rs
+++ b/src/stream/stream/skip_while.rs
@@ -1,50 +1,55 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream to skip elements of another stream based on a predicate.
-#[derive(Debug)]
-pub struct SkipWhile<S, P, T> {
-    stream: S,
-    predicate: Option<P>,
-    __t: PhantomData<T>,
+pin_project! {
+    /// A stream to skip elements of another stream based on a predicate.
+    ///
+    /// This `struct` is created by the [`skip_while`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`skip_while`]: trait.Stream.html#method.skip_while
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct SkipWhile<S, P> {
+        #[pin]
+        stream: S,
+        predicate: Option<P>,
+    }
 }
 
-impl<S, P, T> SkipWhile<S, P, T> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(predicate: Option<P>);
-
+impl<S, P> SkipWhile<S, P> {
     pub(crate) fn new(stream: S, predicate: P) -> Self {
-        SkipWhile {
+        Self {
             stream,
             predicate: Some(predicate),
-            __t: PhantomData,
         }
     }
 }
 
-impl<S, P> Stream for SkipWhile<S, P, S::Item>
+impl<S, P> Stream for SkipWhile<S, P>
 where
     S: Stream,
     P: FnMut(&S::Item) -> bool,
 {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
         loop {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
 
             match next {
-                Some(v) => match self.as_mut().predicate() {
-                    Some(p) => match p(&v) {
-                        true => (),
-                        false => {
-                            *self.as_mut().predicate() = None;
+                Some(v) => match this.predicate {
+                    Some(p) => {
+                        if !p(&v) {
+                            *this.predicate = None;
                             return Poll::Ready(Some(v));
                         }
-                    },
+                    }
                     None => return Poll::Ready(Some(v)),
                 },
                 None => return Poll::Ready(None),
diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs
index f84feecd3..3dd3d6259 100644
--- a/src/stream/stream/step_by.rs
+++ b/src/stream/stream/step_by.rs
@@ -1,23 +1,30 @@
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream that steps a given amount of elements of another stream.
-#[derive(Debug)]
-pub struct StepBy<S> {
-    stream: S,
-    step: usize,
-    i: usize,
+pin_project! {
+    /// A stream that steps a given amount of elements of another stream.
+    ///
+    /// This `struct` is created by the [`step_by`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`step_by`]: trait.Stream.html#method.step_by
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct StepBy<S> {
+        #[pin]
+        stream: S,
+        step: usize,
+        i: usize,
+    }
 }
 
 impl<S> StepBy<S> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(step: usize);
-    pin_utils::unsafe_unpinned!(i: usize);
-
     pub(crate) fn new(stream: S, step: usize) -> Self {
-        StepBy {
+        Self {
             stream,
             step: step.checked_sub(1).unwrap(),
             i: 0,
@@ -31,17 +38,18 @@ where
 {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
         loop {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
 
             match next {
-                Some(v) => match self.i {
+                Some(v) => match this.i {
                     0 => {
-                        *self.as_mut().i() = self.step;
+                        *this.i = *this.step;
                         return Poll::Ready(Some(v));
                     }
-                    _ => *self.as_mut().i() -= 1,
+                    _ => *this.i -= 1,
                 },
                 None => return Poll::Ready(None),
             }
diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs
index 81d48d23e..ffc3e9935 100644
--- a/src/stream/stream/take.rs
+++ b/src/stream/stream/take.rs
@@ -1,33 +1,47 @@
-use std::pin::Pin;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// A stream that yields the first `n` items of another stream.
-#[derive(Clone, Debug)]
-pub struct Take<S> {
-    pub(crate) stream: S,
-    pub(crate) remaining: usize,
+pin_project! {
+    /// A stream that yields the first `n` items of another stream.
+    ///
+    /// This `struct` is created by the [`take`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`take`]: trait.Stream.html#method.take
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Clone, Debug)]
+    pub struct Take<S> {
+        #[pin]
+        pub(crate) stream: S,
+        pub(crate) remaining: usize,
+    }
 }
 
-impl<S: Unpin> Unpin for Take<S> {}
-
-impl<S: Stream> Take<S> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(remaining: usize);
+impl<S> Take<S> {
+    pub(super) fn new(stream: S, remaining: usize) -> Self {
+        Self {
+            stream,
+            remaining,
+        }
+    }
 }
 
 impl<S: Stream> Stream for Take<S> {
     type Item = S::Item;
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
-        if self.remaining == 0 {
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
+        let this = self.project();
+        if *this.remaining == 0 {
             Poll::Ready(None)
         } else {
-            let next = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let next = futures_core::ready!(this.stream.poll_next(cx));
             match next {
-                Some(_) => *self.as_mut().remaining() -= 1,
-                None => *self.as_mut().remaining() = 0,
+                Some(_) => *this.remaining -= 1,
+                None => *this.remaining = 0,
             }
             Poll::Ready(next)
         }
diff --git a/src/stream/stream/take_while.rs b/src/stream/stream/take_while.rs
new file mode 100644
index 000000000..60eb8c517
--- /dev/null
+++ b/src/stream/stream/take_while.rs
@@ -0,0 +1,55 @@
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    /// A stream that yields elements based on a predicate.
+    ///
+    /// This `struct` is created by the [`take_while`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`take_while`]: trait.Stream.html#method.take_while
+    /// [`Stream`]: trait.Stream.html
+    #[derive(Debug)]
+    pub struct TakeWhile<S, P> {
+        #[pin]
+        stream: S,
+        predicate: P,
+    }
+}
+
+impl<S, P> TakeWhile<S, P> {
+    pub(super) fn new(stream: S, predicate: P) -> Self {
+        Self {
+            stream,
+            predicate,
+        }
+    }
+}
+
+impl<S, P> Stream for TakeWhile<S, P>
+where
+    S: Stream,
+    P: FnMut(&S::Item) -> bool,
+{
+    type Item = S::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        let next = futures_core::ready!(this.stream.poll_next(cx));
+
+        match next {
+            Some(v) => {
+                if (this.predicate)(&v) {
+                    Poll::Ready(Some(v))
+                } else {
+                    Poll::Ready(None)
+                }
+            }
+            None => Poll::Ready(None),
+        }
+    }
+}
diff --git a/src/stream/stream/throttle.rs b/src/stream/stream/throttle.rs
new file mode 100644
index 000000000..d0e2cdd14
--- /dev/null
+++ b/src/stream/stream/throttle.rs
@@ -0,0 +1,67 @@
+use std::future::Future;
+use std::pin::Pin;
+use std::time::Duration;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+use crate::utils::{timer_after, Timer};
+
+pin_project! {
+    /// A stream that only yields one element once every `duration`.
+    ///
+    /// This `struct` is created by the [`throttle`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`throttle`]: trait.Stream.html#method.throttle
+    /// [`Stream`]: trait.Stream.html
+    #[doc(hidden)]
+    #[allow(missing_debug_implementations)]
+    pub struct Throttle<S> {
+        #[pin]
+        stream: S,
+        duration: Duration,
+        #[pin]
+        blocked: bool,
+        #[pin]
+        delay: Timer,
+    }
+}
+
+impl<S: Stream> Throttle<S> {
+    pub(super) fn new(stream: S, duration: Duration) -> Self {
+        Self {
+            stream,
+            duration,
+            blocked: false,
+            delay: timer_after(Duration::default()),
+        }
+    }
+}
+
+impl<S: Stream> Stream for Throttle<S> {
+    type Item = S::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
+        let mut this = self.project();
+        if *this.blocked {
+            let d = this.delay.as_mut();
+            if d.poll(cx).is_ready() {
+                *this.blocked = false;
+            } else {
+                return Poll::Pending;
+            }
+        }
+
+        match this.stream.poll_next(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(None) => Poll::Ready(None),
+            Poll::Ready(Some(v)) => {
+                *this.blocked = true;
+                let _ = std::mem::replace(&mut *this.delay, timer_after(*this.duration));
+                Poll::Ready(Some(v))
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/timeout.rs b/src/stream/stream/timeout.rs
new file mode 100644
index 000000000..ec15728b8
--- /dev/null
+++ b/src/stream/stream/timeout.rs
@@ -0,0 +1,68 @@
+use std::error::Error;
+use std::fmt;
+use std::future::Future;
+use std::pin::Pin;
+use std::time::Duration;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+use crate::utils::{timer_after, Timer};
+
+pin_project! {
+    /// A stream with timeout time set
+    #[derive(Debug)]
+    pub struct Timeout<S: Stream> {
+        #[pin]
+        stream: S,
+        #[pin]
+        delay: Timer,
+        duration: Duration,
+    }
+}
+
+impl<S: Stream> Timeout<S> {
+    pub(crate) fn new(stream: S, dur: Duration) -> Self {
+        let delay = timer_after(dur);
+
+        Self { stream, delay, duration: dur }
+    }
+}
+
+impl<S: Stream> Stream for Timeout<S> {
+    type Item = Result<S::Item, TimeoutError>;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+
+        let r = match this.stream.poll_next(cx) {
+            Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))),
+            Poll::Ready(None) => Poll::Ready(None),
+            Poll::Pending => match this.delay.as_mut().poll(cx) {
+                Poll::Ready(_) => Poll::Ready(Some(Err(TimeoutError { _private: () }))),
+                Poll::Pending => return Poll::Pending,
+            },
+        };
+
+        *this.delay.as_mut() = timer_after(*this.duration);
+
+        r
+    }
+}
+
+/// An error returned when a stream times out.
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[cfg(any(feature = "unstable", feature = "docs"))]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct TimeoutError {
+    _private: (),
+}
+
+impl Error for TimeoutError {}
+
+impl fmt::Display for TimeoutError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "stream has timed out".fmt(f)
+    }
+}
diff --git a/src/stream/stream/try_fold.rs b/src/stream/stream/try_fold.rs
new file mode 100644
index 000000000..73312ff78
--- /dev/null
+++ b/src/stream/stream/try_fold.rs
@@ -0,0 +1,52 @@
+use core::pin::Pin;
+
+use crate::future::Future;
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+#[doc(hidden)]
+#[allow(missing_debug_implementations)]
+pub struct TryFoldFuture<'a, S, F, T> {
+    stream: &'a mut S,
+    f: F,
+    acc: Option<T>,
+}
+
+impl<'a, S, F, T> Unpin for TryFoldFuture<'a, S, F, T> {}
+
+impl<'a, S, F, T> TryFoldFuture<'a, S, F, T> {
+    pub(super) fn new(stream: &'a mut S, init: T, f: F) -> Self {
+        Self {
+            stream,
+            f,
+            acc: Some(init),
+        }
+    }
+}
+
+impl<'a, S, F, T, E> Future for TryFoldFuture<'a, S, F, T>
+where
+    S: Stream + Unpin,
+    F: FnMut(T, S::Item) -> Result<T, E>,
+{
+    type Output = Result<T, E>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        loop {
+            let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx));
+
+            match next {
+                Some(v) => {
+                    let old = self.acc.take().unwrap();
+                    let new = (&mut self.f)(old, v);
+
+                    match new {
+                        Ok(o) => self.acc = Some(o),
+                        Err(e) => return Poll::Ready(Err(e)),
+                    }
+                }
+                None => return Poll::Ready(Ok(self.acc.take().unwrap())),
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/try_for_each.rs b/src/stream/stream/try_for_each.rs
index ae3d5ea5b..917bfd16e 100644
--- a/src/stream/stream/try_for_each.rs
+++ b/src/stream/stream/try_for_each.rs
@@ -1,49 +1,39 @@
-use std::marker::PhantomData;
-use std::pin::Pin;
+use core::future::Future;
+use core::pin::Pin;
 
-use crate::future::Future;
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
 #[doc(hidden)]
 #[allow(missing_debug_implementations)]
-pub struct TryForEeachFuture<S, F, T, R> {
-    stream: S,
+pub struct TryForEachFuture<'a, S, F> {
+    stream: &'a mut S,
     f: F,
-    __from: PhantomData<T>,
-    __to: PhantomData<R>,
 }
 
-impl<S, F, T, R> TryForEeachFuture<S, F, T, R> {
-    pin_utils::unsafe_pinned!(stream: S);
-    pin_utils::unsafe_unpinned!(f: F);
+impl<'a, S, F> Unpin for TryForEachFuture<'a, S, F> {}
 
-    pub(crate) fn new(stream: S, f: F) -> Self {
-        TryForEeachFuture {
-            stream,
-            f,
-            __from: PhantomData,
-            __to: PhantomData,
-        }
+impl<'a, S, F> TryForEachFuture<'a, S, F> {
+    pub(crate) fn new(stream: &'a mut S, f: F) -> Self {
+        Self { stream, f }
     }
 }
 
-impl<S, F, E> Future for TryForEeachFuture<S, F, S::Item, E>
+impl<'a, S, F, E> Future for TryForEachFuture<'a, S, F>
 where
-    S: Stream,
-    S::Item: std::fmt::Debug,
+    S: Stream + Unpin,
     F: FnMut(S::Item) -> Result<(), E>,
 {
     type Output = Result<(), E>;
 
     fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
         loop {
-            let item = futures_core::ready!(self.as_mut().stream().poll_next(cx));
+            let item = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx));
 
             match item {
                 None => return Poll::Ready(Ok(())),
                 Some(v) => {
-                    let res = (self.as_mut().f())(v);
+                    let res = (&mut self.f)(v);
                     if let Err(e) = res {
                         return Poll::Ready(Err(e));
                     }
diff --git a/src/stream/stream/unzip.rs b/src/stream/stream/unzip.rs
new file mode 100644
index 000000000..7771509a5
--- /dev/null
+++ b/src/stream/stream/unzip.rs
@@ -0,0 +1,57 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+pin_project! {
+    #[derive(Clone, Debug)]
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    pub struct UnzipFuture<S, FromA, FromB> {
+        #[pin]
+        stream: S,
+        res: Option<(FromA, FromB)>,
+    }
+}
+
+impl<S: Stream, FromA, FromB> UnzipFuture<S, FromA, FromB>
+where
+    FromA: Default,
+    FromB: Default,
+{
+    pub(super) fn new(stream: S) -> Self {
+        UnzipFuture {
+            stream,
+            res: Some((FromA::default(), FromB::default())),
+        }
+    }
+}
+
+impl<S, A, B, FromA, FromB> Future for UnzipFuture<S, FromA, FromB>
+where
+    S: Stream<Item = (A, B)>,
+    FromA: Default + Extend<A>,
+    FromB: Default + Extend<B>,
+{
+    type Output = (FromA, FromB);
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let mut this = self.project();
+
+        loop {
+            let next = futures_core::ready!(this.stream.as_mut().poll_next(cx));
+
+            match next {
+                Some((a, b)) => {
+                    let res = this.res.as_mut().unwrap();
+                    res.0.extend(Some(a));
+                    res.1.extend(Some(b));
+                }
+                None => return Poll::Ready(this.res.take().unwrap()),
+            }
+        }
+    }
+}
diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs
index 4c66aefd0..83d613c8c 100644
--- a/src/stream/stream/zip.rs
+++ b/src/stream/stream/zip.rs
@@ -1,14 +1,26 @@
-use std::fmt;
-use std::pin::Pin;
+use core::fmt;
+use core::pin::Pin;
+
+use pin_project_lite::pin_project;
 
 use crate::stream::Stream;
 use crate::task::{Context, Poll};
 
-/// An iterator that iterates two other iterators simultaneously.
-pub struct Zip<A: Stream, B> {
-    item_slot: Option<A::Item>,
-    first: A,
-    second: B,
+pin_project! {
+    /// A stream that takes items from two other streams simultaneously.
+    ///
+    /// This `struct` is created by the [`zip`] method on [`Stream`]. See its
+    /// documentation for more.
+    ///
+    /// [`zip`]: trait.Stream.html#method.zip
+    /// [`Stream`]: trait.Stream.html
+    pub struct Zip<A: Stream, B> {
+        item_slot: Option<A::Item>,
+        #[pin]
+        first: A,
+        #[pin]
+        second: B,
+    }
 }
 
 impl<A: Stream + fmt::Debug, B: fmt::Debug> fmt::Debug for Zip<A, B> {
@@ -20,35 +32,30 @@ impl<A: Stream + fmt::Debug, B: fmt::Debug> fmt::Debug for Zip<A, B> {
     }
 }
 
-impl<A: Stream + Unpin, B: Unpin> Unpin for Zip<A, B> {}
-
 impl<A: Stream, B> Zip<A, B> {
     pub(crate) fn new(first: A, second: B) -> Self {
-        Zip {
+        Self {
             item_slot: None,
             first,
             second,
         }
     }
-
-    pin_utils::unsafe_unpinned!(item_slot: Option<A::Item>);
-    pin_utils::unsafe_pinned!(first: A);
-    pin_utils::unsafe_pinned!(second: B);
 }
 
 impl<A: Stream, B: Stream> Stream for Zip<A, B> {
     type Item = (A::Item, B::Item);
 
-    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
-        if self.as_mut().item_slot().is_none() {
-            match self.as_mut().first().poll_next(cx) {
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+        if this.item_slot.is_none() {
+            match this.first.poll_next(cx) {
                 Poll::Pending => return Poll::Pending,
                 Poll::Ready(None) => return Poll::Ready(None),
-                Poll::Ready(Some(item)) => *self.as_mut().item_slot() = Some(item),
+                Poll::Ready(Some(item)) => *this.item_slot = Some(item),
             }
         }
-        let second_item = futures_core::ready!(self.as_mut().second().poll_next(cx));
-        let first_item = self.as_mut().item_slot().take().unwrap();
+        let second_item = futures_core::ready!(this.second.poll_next(cx));
+        let first_item = this.item_slot.take().unwrap();
         Poll::Ready(second_item.map(|second_item| (first_item, second_item)))
     }
 }
diff --git a/src/stream/successors.rs b/src/stream/successors.rs
new file mode 100644
index 000000000..eb0e4bf48
--- /dev/null
+++ b/src/stream/successors.rs
@@ -0,0 +1,77 @@
+use core::mem;
+use core::pin::Pin;
+
+use crate::stream::Stream;
+use crate::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+
+/// Creates a new stream where to produce each new element a closure is called with the previous
+/// value.
+///
+/// # Examples
+///
+/// ```
+/// # fn main() { async_std::task::block_on(async {
+/// #
+/// use async_std::prelude::*;
+/// use async_std::stream;
+///
+/// let mut s = stream::successors(Some(22), |&val| Some(val + 1));
+///
+/// assert_eq!(s.next().await, Some(22));
+/// assert_eq!(s.next().await, Some(23));
+/// assert_eq!(s.next().await, Some(24));
+/// assert_eq!(s.next().await, Some(25));
+///
+/// #
+/// # }) }
+/// ```
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub fn successors<F, T>(first: Option<T>, succ: F) -> Successors<F, T>
+where
+    F: FnMut(&T) -> Option<T>,
+{
+    Successors { succ, slot: first }
+}
+
+pin_project! {
+    /// A stream that yields elements by calling an async closure with the previous value as an
+    /// argument
+    ///
+    /// This stream is constructed by [`successors`] function
+    ///
+    /// [`successors`]: fn.successors.html
+    #[cfg(feature = "unstable")]
+    #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+    #[derive(Debug)]
+    pub struct Successors<F, T>
+    where
+        F: FnMut(&T) -> Option<T>
+    {
+        succ: F,
+        slot: Option<T>,
+    }
+}
+
+impl<F, T> Stream for Successors<F, T>
+where
+    F: FnMut(&T) -> Option<T>,
+{
+    type Item = T;
+
+    fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let this = self.project();
+
+        if this.slot.is_none() {
+            return Poll::Ready(None);
+        }
+
+        let mut next = (this.succ)(&this.slot.as_ref().unwrap());
+
+        // 'swapping' here means 'slot' will hold the next value and next will be th one from the previous iteration
+        mem::swap(this.slot, &mut next);
+        Poll::Ready(next)
+    }
+}
diff --git a/src/stream/sum.rs b/src/stream/sum.rs
new file mode 100644
index 000000000..65f6e8881
--- /dev/null
+++ b/src/stream/sum.rs
@@ -0,0 +1,65 @@
+use core::future::Future;
+use core::pin::Pin;
+
+use crate::stream::Stream;
+
+/// Trait to represent types that can be created by summing up a stream.
+///
+/// This trait is used to implement the [`sum`] method on streams. Types which
+/// implement the trait can be generated by the [`sum`] method. Like
+/// [`FromStream`] this trait should rarely be called directly and instead
+/// interacted with through [`Stream::sum`].
+///
+/// [`sum`]: trait.Sum.html#tymethod.sum
+/// [`FromStream`]: trait.FromStream.html
+/// [`Stream::sum`]: trait.Stream.html#method.sum
+#[cfg(feature = "unstable")]
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+pub trait Sum<A = Self>: Sized {
+    /// Method which takes a stream and generates `Self` from the elements by
+    /// "summing up" the items.
+    fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Self> + 'a>>
+    where
+        S: Stream<Item = A> + 'a;
+}
+
+use crate::stream::stream::StreamExt;
+use core::num::Wrapping;
+use core::ops::Add;
+
+macro_rules! num_sum {
+    ($zero:expr, $($a:ty)*) => ($(
+        impl Sum for $a {
+            fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Self>+ 'a>>
+            where
+                S: Stream<Item = $a> + 'a,
+            {
+                Box::pin(async move { stream.fold($zero, Add::add).await } )
+            }
+        }
+        impl<'a> Sum<&'a $a> for $a {
+            fn sum<'b, S>(stream: S) -> Pin<Box<dyn Future<Output = Self> + 'b>>
+            where
+                S: Stream<Item = &'a $a> + 'b,
+            {
+                Box::pin(async move { stream.fold($zero, Add::add).await } )
+            }
+        }
+    )*);
+}
+
+macro_rules! integer_sum {
+    ($($a:ty)*) => (
+        num_sum!(0, $($a)*);
+        num_sum!(Wrapping(0), $(Wrapping<$a>)*);
+    );
+}
+
+macro_rules! float_sum {
+    ($($a:ty)*) => (
+        num_sum!(0.0, $($a)*);
+    );
+}
+
+integer_sum! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
+float_sum! { f32 f64 }
diff --git a/src/string/extend.rs b/src/string/extend.rs
index 72260a546..eae824cbd 100644
--- a/src/string/extend.rs
+++ b/src/string/extend.rs
@@ -2,66 +2,105 @@ use std::borrow::Cow;
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl Extend<char> for String {
-    fn stream_extend<'a, S: IntoStream<Item = char> + 'a>(
+impl stream::Extend<char> for String {
+    fn extend<'a, S: IntoStream<Item = char> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>> 
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         let stream = stream.into_stream();
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        // let (lower_bound, _) = stream.size_hint();
-        // self.reserve(lower_bound);
+        self.reserve(stream.size_hint().0);
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
 
-        Box::pin(stream.for_each(move |c| self.push(c)))
+            while let Some(item) = stream.next().await {
+                self.push(item);
+            }
+        })
     }
 }
 
-impl<'b> Extend<&'b char> for String {
-    fn stream_extend<'a, S: IntoStream<Item = &'b char> + 'a>(
+impl<'b> stream::Extend<&'b char> for String {
+    fn extend<'a, S: IntoStream<Item = &'b char> + 'a>(
         &'a mut self,
-        //TODO: Remove the underscore when uncommenting the body of this impl
-        _stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>>
+        stream: S,
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>> 
     where
-        'b: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
-        //TODO: This can be uncommented when `copied` is added to Stream/StreamExt
-        //Box::pin(stream.into_stream().copied())
-        unimplemented!()
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
+
+            while let Some(item) = stream.next().await {
+                self.push(*item);
+            }
+        })
     }
 }
 
-impl<'b> Extend<&'b str> for String {
-    fn stream_extend<'a, S: IntoStream<Item = &'b str> + 'a>(
+impl<'b> stream::Extend<&'b str> for String {
+    fn extend<'a, S: IntoStream<Item = &'b str> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>> 
     where
-        'b: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
-        Box::pin(stream.into_stream().for_each(move |s| self.push_str(s)))
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
+
+            while let Some(item) = stream.next().await {
+                self.push_str(item);
+            }
+        })
     }
 }
 
-impl Extend<String> for String {
-    fn stream_extend<'a, S: IntoStream<Item = String> + 'a>(
+impl stream::Extend<String> for String {
+    fn extend<'a, S: IntoStream<Item = String> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
-        Box::pin(stream.into_stream().for_each(move |s| self.push_str(&s)))
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
+
+            while let Some(item) = stream.next().await {
+                self.push_str(&item);
+            }
+        })
     }
 }
 
-impl<'b> Extend<Cow<'b, str>> for String {
-    fn stream_extend<'a, S: IntoStream<Item = Cow<'b, str>> + 'a>(
+impl<'b> stream::Extend<Cow<'b, str>> for String {
+    fn extend<'a, S: IntoStream<Item = Cow<'b, str>> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
     where
-        'b: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
-        Box::pin(stream.into_stream().for_each(move |s| self.push_str(&s)))
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
+
+            while let Some(item) = stream.next().await {
+                self.push_str(&item);
+            }
+        })
     }
 }
diff --git a/src/string/from_stream.rs b/src/string/from_stream.rs
index 276d13a73..d9e824681 100644
--- a/src/string/from_stream.rs
+++ b/src/string/from_stream.rs
@@ -1,23 +1,22 @@
 use std::borrow::Cow;
 use std::pin::Pin;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
 impl FromStream<char> for String {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = char>>(
+    fn from_stream<'a, S: IntoStream<Item = char> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = String::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
@@ -25,19 +24,17 @@ impl FromStream<char> for String {
 
 impl<'b> FromStream<&'b char> for String {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = &'b char>>(
+    fn from_stream<'a, S: IntoStream<Item = &'b char> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = String::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
@@ -45,19 +42,17 @@ impl<'b> FromStream<&'b char> for String {
 
 impl<'b> FromStream<&'b str> for String {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = &'b str>>(
+    fn from_stream<'a, S: IntoStream<Item = &'b str> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = String::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
@@ -65,19 +60,17 @@ impl<'b> FromStream<&'b str> for String {
 
 impl FromStream<String> for String {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = String>>(
+    fn from_stream<'a, S: IntoStream<Item = String> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = String::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
@@ -85,19 +78,17 @@ impl FromStream<String> for String {
 
 impl<'b> FromStream<Cow<'b, str>> for String {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = Cow<'b, str>>>(
+    fn from_stream<'a, S: IntoStream<Item = Cow<'b, str>> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = String::new();
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
diff --git a/src/string/mod.rs b/src/string/mod.rs
index e382fcf2c..3f6141fa3 100644
--- a/src/string/mod.rs
+++ b/src/string/mod.rs
@@ -5,5 +5,6 @@
 mod extend;
 mod from_stream;
 
+#[allow(unused)]
 #[doc(inline)]
 pub use std::string::String;
diff --git a/src/sync/barrier.rs b/src/sync/barrier.rs
deleted file mode 100644
index 694bf99d6..000000000
--- a/src/sync/barrier.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-use broadcaster::BroadcastChannel;
-
-use crate::sync::Mutex;
-
-/// A barrier enables multiple tasks to synchronize the beginning
-/// of some computation.
-///
-/// # Examples
-///
-/// ```
-/// # fn main() { async_std::task::block_on(async {
-/// #
-/// use async_std::sync::{Arc, Barrier};
-/// use async_std::task;
-///
-/// let mut handles = Vec::with_capacity(10);
-/// let barrier = Arc::new(Barrier::new(10));
-/// for _ in 0..10 {
-///     let c = barrier.clone();
-///     // The same messages will be printed together.
-///     // You will NOT see any interleaving.
-///     handles.push(task::spawn(async move {
-///         println!("before wait");
-///         c.wait().await;
-///         println!("after wait");
-///     }));
-/// }
-/// // Wait for the other futures to finish.
-/// for handle in handles {
-///     handle.await;
-/// }
-/// # });
-/// # }
-/// ```
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[derive(Debug)]
-pub struct Barrier {
-    state: Mutex<BarrierState>,
-    wait: BroadcastChannel<(usize, usize)>,
-    n: usize,
-}
-
-// The inner state of a double barrier
-#[derive(Debug)]
-struct BarrierState {
-    waker: BroadcastChannel<(usize, usize)>,
-    count: usize,
-    generation_id: usize,
-}
-
-/// A `BarrierWaitResult` is returned by `wait` when all threads in the `Barrier` have rendezvoused.
-///
-/// [`wait`]: struct.Barrier.html#method.wait
-/// [`Barrier`]: struct.Barrier.html
-///
-/// # Examples
-///
-/// ```
-/// use async_std::sync::Barrier;
-///
-/// let barrier = Barrier::new(1);
-/// let barrier_wait_result = barrier.wait();
-/// ```
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[derive(Debug, Clone)]
-pub struct BarrierWaitResult(bool);
-
-impl Barrier {
-    /// Creates a new barrier that can block a given number of tasks.
-    ///
-    /// A barrier will block `n`-1 tasks which call [`wait`] and then wake up
-    /// all tasks at once when the `n`th task calls [`wait`].
-    ///
-    /// [`wait`]: #method.wait
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use std::sync::Barrier;
-    ///
-    /// let barrier = Barrier::new(10);
-    /// ```
-    pub fn new(mut n: usize) -> Barrier {
-        let waker = BroadcastChannel::new();
-        let wait = waker.clone();
-
-        if n == 0 {
-            // if n is 0, it's not clear what behavior the user wants.
-            // in std::sync::Barrier, an n of 0 exhibits the same behavior as n == 1, where every
-            // .wait() immediately unblocks, so we adopt that here as well.
-            n = 1;
-        }
-
-        Barrier {
-            state: Mutex::new(BarrierState {
-                waker,
-                count: 0,
-                generation_id: 1,
-            }),
-            n,
-            wait,
-        }
-    }
-
-    /// Blocks the current task until all tasks have rendezvoused here.
-    ///
-    /// Barriers are re-usable after all tasks have rendezvoused once, and can
-    /// be used continuously.
-    ///
-    /// A single (arbitrary) task will receive a [`BarrierWaitResult`] that
-    /// returns `true` from [`is_leader`] when returning from this function, and
-    /// all other tasks will receive a result that will return `false` from
-    /// [`is_leader`].
-    ///
-    /// [`BarrierWaitResult`]: struct.BarrierWaitResult.html
-    /// [`is_leader`]: struct.BarrierWaitResult.html#method.is_leader
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::{Arc, Barrier};
-    /// use async_std::task;
-    ///
-    /// let mut handles = Vec::with_capacity(10);
-    /// let barrier = Arc::new(Barrier::new(10));
-    /// for _ in 0..10 {
-    ///     let c = barrier.clone();
-    ///     // The same messages will be printed together.
-    ///     // You will NOT see any interleaving.
-    ///     handles.push(task::spawn(async move {
-    ///         println!("before wait");
-    ///         c.wait().await;
-    ///         println!("after wait");
-    ///     }));
-    /// }
-    /// // Wait for the other futures to finish.
-    /// for handle in handles {
-    ///     handle.await;
-    /// }
-    /// # });
-    /// # }
-    /// ```
-    pub async fn wait(&self) -> BarrierWaitResult {
-        let mut lock = self.state.lock().await;
-        let local_gen = lock.generation_id;
-
-        lock.count += 1;
-
-        if lock.count < self.n {
-            let mut wait = self.wait.clone();
-
-            let mut generation_id = lock.generation_id;
-            let mut count = lock.count;
-
-            drop(lock);
-
-            while local_gen == generation_id && count < self.n {
-                let (g, c) = wait.recv().await.expect("sender hasn not been closed");
-                generation_id = g;
-                count = c;
-            }
-
-            BarrierWaitResult(false)
-        } else {
-            lock.count = 0;
-            lock.generation_id = lock.generation_id.wrapping_add(1);
-
-            lock.waker
-                .send(&(lock.generation_id, lock.count))
-                .await
-                .expect("there should be at least one receiver");
-
-            BarrierWaitResult(true)
-        }
-    }
-}
-
-impl BarrierWaitResult {
-    /// Returns `true` if this task from [`wait`] is the "leader task".
-    ///
-    /// Only one task will have `true` returned from their result, all other
-    /// tasks will have `false` returned.
-    ///
-    /// [`wait`]: struct.Barrier.html#method.wait
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::Barrier;
-    ///
-    /// let barrier = Barrier::new(1);
-    /// let barrier_wait_result = barrier.wait().await;
-    /// println!("{:?}", barrier_wait_result.is_leader());
-    /// # });
-    /// # }
-    /// ```
-    pub fn is_leader(&self) -> bool {
-        self.0
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use futures_channel::mpsc::unbounded;
-    use futures_util::sink::SinkExt;
-    use futures_util::stream::StreamExt;
-
-    use crate::sync::{Arc, Barrier};
-    use crate::task;
-
-    #[test]
-    fn test_barrier() {
-        // NOTE(dignifiedquire): Based on the test in std, I was seeing some
-        // race conditions, so running it in a loop to make sure things are
-        // solid.
-
-        for _ in 0..1_000 {
-            task::block_on(async move {
-                const N: usize = 10;
-
-                let barrier = Arc::new(Barrier::new(N));
-                let (tx, mut rx) = unbounded();
-
-                for _ in 0..N - 1 {
-                    let c = barrier.clone();
-                    let mut tx = tx.clone();
-                    task::spawn(async move {
-                        let res = c.wait().await;
-
-                        tx.send(res.is_leader()).await.unwrap();
-                    });
-                }
-
-                // At this point, all spawned threads should be blocked,
-                // so we shouldn't get anything from the port
-                let res = rx.try_next();
-                assert!(match res {
-                    Err(_err) => true,
-                    _ => false,
-                });
-
-                let mut leader_found = barrier.wait().await.is_leader();
-
-                // Now, the barrier is cleared and we should get data.
-                for _ in 0..N - 1 {
-                    if rx.next().await.unwrap() {
-                        assert!(!leader_found);
-                        leader_found = true;
-                    }
-                }
-                assert!(leader_found);
-            });
-        }
-    }
-}
diff --git a/src/sync/condvar.rs b/src/sync/condvar.rs
new file mode 100644
index 000000000..b7e81ed29
--- /dev/null
+++ b/src/sync/condvar.rs
@@ -0,0 +1,417 @@
+use std::fmt;
+use std::pin::Pin;
+use std::time::Duration;
+
+use super::MutexGuard;
+use crate::future::{timeout, Future};
+use crate::sync::WakerSet;
+use crate::task::{Context, Poll};
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub struct WaitTimeoutResult(bool);
+
+/// A type indicating whether a timed wait on a condition variable returned due to a time out or
+/// not
+impl WaitTimeoutResult {
+    /// Returns `true` if the wait was known to have timed out.
+    pub fn timed_out(self) -> bool {
+        self.0
+    }
+}
+
+/// A Condition Variable
+///
+/// This type is an async version of [`std::sync::Condvar`].
+///
+/// [`std::sync::Condvar`]: https://doc.rust-lang.org/std/sync/struct.Condvar.html
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use std::sync::Arc;
+///
+/// use async_std::sync::{Mutex, Condvar};
+/// use async_std::task;
+///
+/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+/// let pair2 = pair.clone();
+///
+/// // Inside of our lock, spawn a new thread, and then wait for it to start.
+/// task::spawn(async move {
+///     let (lock, cvar) = &*pair2;
+///     let mut started = lock.lock().await;
+///     *started = true;
+///     // We notify the condvar that the value has changed.
+///     cvar.notify_one();
+/// });
+///
+/// // Wait for the thread to start up.
+/// let (lock, cvar) = &*pair;
+/// let mut started = lock.lock().await;
+/// while !*started {
+///     started = cvar.wait(started).await;
+/// }
+///
+/// # })
+/// ```
+pub struct Condvar {
+    wakers: WakerSet,
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Default for Condvar {
+    fn default() -> Self {
+        Condvar::new()
+    }
+}
+
+impl Condvar {
+    /// Creates a new condition variable
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use async_std::sync::Condvar;
+    ///
+    /// let cvar = Condvar::new();
+    /// ```
+    pub fn new() -> Self {
+        Condvar {
+            wakers: WakerSet::new(),
+        }
+    }
+
+    /// Blocks the current task until this condition variable receives a notification.
+    ///
+    /// Unlike the std equivalent, this does not check that a single mutex is used at runtime.
+    /// However, as a best practice avoid using with multiple mutexes.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// use std::sync::Arc;
+    ///
+    /// use async_std::sync::{Mutex, Condvar};
+    /// use async_std::task;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// task::spawn(async move {
+    ///     let (lock, cvar) = &*pair2;
+    ///     let mut started = lock.lock().await;
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_one();
+    /// });
+    ///
+    /// // Wait for the thread to start up.
+    /// let (lock, cvar) = &*pair;
+    /// let mut started = lock.lock().await;
+    /// while !*started {
+    ///     started = cvar.wait(started).await;
+    /// }
+    /// # })
+    /// ```
+    #[allow(clippy::needless_lifetimes)]
+    pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
+        let mutex = MutexGuard::source(&guard);
+
+        self.await_notify(guard).await;
+
+        mutex.lock().await
+    }
+
+    fn await_notify<'a, T>(&self, guard: MutexGuard<'a, T>) -> AwaitNotify<'_, 'a, T> {
+        AwaitNotify {
+            cond: self,
+            guard: Some(guard),
+            key: None,
+        }
+    }
+
+    /// Blocks the current task until this condition variable receives a notification and the
+    /// required condition is met. Spurious wakeups are ignored and this function will only
+    /// return once the condition has been met.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// #
+    /// use std::sync::Arc;
+    ///
+    /// use async_std::sync::{Mutex, Condvar};
+    /// use async_std::task;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// task::spawn(async move {
+    ///     let (lock, cvar) = &*pair2;
+    ///     let mut started = lock.lock().await;
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_one();
+    /// });
+    ///
+    /// // Wait for the thread to start up.
+    /// let (lock, cvar) = &*pair;
+    /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
+    /// let _guard = cvar.wait_until(lock.lock().await, |started| { *started }).await;
+    /// #
+    /// # })
+    /// ```
+    #[allow(clippy::needless_lifetimes)]
+    pub async fn wait_until<'a, T, F>(
+        &self,
+        mut guard: MutexGuard<'a, T>,
+        mut condition: F,
+    ) -> MutexGuard<'a, T>
+    where
+        F: FnMut(&mut T) -> bool,
+    {
+        while !condition(&mut *guard) {
+            guard = self.wait(guard).await;
+        }
+        guard
+    }
+
+    /// Waits on this condition variable for a notification, timing out after a specified duration.
+    ///
+    /// For these reasons `Condvar::wait_timeout_until` is recommended in most cases.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// #
+    /// use std::sync::Arc;
+    /// use std::time::Duration;
+    ///
+    /// use async_std::sync::{Mutex, Condvar};
+    /// use async_std::task;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// task::spawn(async move {
+    ///   let (lock, cvar) = &*pair2;
+    ///   let mut started = lock.lock().await;
+    ///   *started = true;
+    ///   // We notify the condvar that the value has changed.
+    ///   cvar.notify_one();
+    /// });
+    ///
+    /// // wait for the thread to start up
+    /// let (lock, cvar) = &*pair;
+    /// let mut started = lock.lock().await;
+    /// loop {
+    ///   let result = cvar.wait_timeout(started, Duration::from_millis(10)).await;
+    ///   started = result.0;
+    ///   if *started == true {
+    ///       // We received the notification and the value has been updated, we can leave.
+    ///       break
+    ///   }
+    /// }
+    /// #
+    /// # })
+    /// ```
+    #[allow(clippy::needless_lifetimes)]
+    pub async fn wait_timeout<'a, T>(
+        &self,
+        guard: MutexGuard<'a, T>,
+        dur: Duration,
+    ) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
+        let mutex = MutexGuard::source(&guard);
+        match timeout(dur, self.wait(guard)).await {
+            Ok(guard) => (guard, WaitTimeoutResult(false)),
+            Err(_) => (mutex.lock().await, WaitTimeoutResult(true)),
+        }
+    }
+
+    /// Waits on this condition variable for a notification, timing out after a specified duration.
+    /// Spurious wakes will not cause this function to return.
+    ///
+    /// # Examples
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// use std::sync::Arc;
+    /// use std::time::Duration;
+    ///
+    /// use async_std::sync::{Mutex, Condvar};
+    /// use async_std::task;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// task::spawn(async move {
+    ///     let (lock, cvar) = &*pair2;
+    ///     let mut started = lock.lock().await;
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_one();
+    /// });
+    ///
+    /// // wait for the thread to start up
+    /// let (lock, cvar) = &*pair;
+    /// let result = cvar.wait_timeout_until(
+    ///     lock.lock().await,
+    ///     Duration::from_millis(100),
+    ///     |&mut started| started,
+    /// ).await;
+    /// if result.1.timed_out() {
+    ///     // timed-out without the condition ever evaluating to true.
+    /// }
+    /// // access the locked mutex via result.0
+    /// # });
+    /// ```
+    #[allow(clippy::needless_lifetimes)]
+    pub async fn wait_timeout_until<'a, T, F>(
+        &self,
+        guard: MutexGuard<'a, T>,
+        dur: Duration,
+        condition: F,
+    ) -> (MutexGuard<'a, T>, WaitTimeoutResult)
+    where
+        F: FnMut(&mut T) -> bool,
+    {
+        let mutex = MutexGuard::source(&guard);
+        match timeout(dur, self.wait_until(guard, condition)).await {
+            Ok(guard) => (guard, WaitTimeoutResult(false)),
+            Err(_) => (mutex.lock().await, WaitTimeoutResult(true)),
+        }
+    }
+
+    /// Wakes up one blocked task on this condvar.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # fn main() { async_std::task::block_on(async {
+    /// use std::sync::Arc;
+    ///
+    /// use async_std::sync::{Mutex, Condvar};
+    /// use async_std::task;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// task::spawn(async move {
+    ///     let (lock, cvar) = &*pair2;
+    ///     let mut started = lock.lock().await;
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_one();
+    /// });
+    ///
+    /// // Wait for the thread to start up.
+    /// let (lock, cvar) = &*pair;
+    /// let mut started = lock.lock().await;
+    /// while !*started {
+    ///     started = cvar.wait(started).await;
+    /// }
+    /// # }) }
+    /// ```
+    pub fn notify_one(&self) {
+        self.wakers.notify_one();
+    }
+
+    /// Wakes up all blocked tasks on this condvar.
+    ///
+    /// # Examples
+    /// ```
+    /// # fn main() { async_std::task::block_on(async {
+    /// #
+    /// use std::sync::Arc;
+    ///
+    /// use async_std::sync::{Mutex, Condvar};
+    /// use async_std::task;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// task::spawn(async move {
+    ///     let (lock, cvar) = &*pair2;
+    ///     let mut started = lock.lock().await;
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_all();
+    /// });
+    ///
+    /// // Wait for the thread to start up.
+    /// let (lock, cvar) = &*pair;
+    /// let mut started = lock.lock().await;
+    /// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
+    /// while !*started {
+    ///     started = cvar.wait(started).await;
+    /// }
+    /// #
+    /// # }) }
+    /// ```
+    pub fn notify_all(&self) {
+        self.wakers.notify_all();
+    }
+}
+
+impl fmt::Debug for Condvar {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad("Condvar { .. }")
+    }
+}
+
+/// A future that waits for another task to notify the condition variable.
+///
+/// This is an internal future that `wait` and `wait_until` await on.
+struct AwaitNotify<'a, 'b, T> {
+    /// The condition variable that we are waiting on
+    cond: &'a Condvar,
+    /// The lock used with `cond`.
+    /// This will be released the first time the future is polled,
+    /// after registering the context to be notified.
+    guard: Option<MutexGuard<'b, T>>,
+    /// A key into the conditions variable's `WakerSet`.
+    /// This is set to the index of the `Waker` for the context each time
+    /// the future is polled and not completed.
+    key: Option<usize>,
+}
+
+impl<'a, 'b, T> Future for AwaitNotify<'a, 'b, T> {
+    type Output = ();
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        match self.guard.take() {
+            Some(_) => {
+                self.key = Some(self.cond.wakers.insert(cx));
+                // the guard is dropped when we return, which frees the lock
+                Poll::Pending
+            }
+            None => {
+                if let Some(key) = self.key {
+                    if self.cond.wakers.remove_if_notified(key, cx) {
+                        self.key = None;
+                        Poll::Ready(())
+                    } else {
+                        Poll::Pending
+                    }
+                } else {
+                    // This should only happen if it is polled twice after receiving a notification
+                    Poll::Ready(())
+                }
+            }
+        }
+    }
+}
+
+impl<'a, 'b, T> Drop for AwaitNotify<'a, 'b, T> {
+    fn drop(&mut self) {
+        if let Some(key) = self.key {
+            self.cond.wakers.cancel(key);
+        }
+    }
+}
diff --git a/src/sync/mod.rs b/src/sync/mod.rs
index df1d71ab3..1d8579614 100644
--- a/src/sync/mod.rs
+++ b/src/sync/mod.rs
@@ -4,16 +4,156 @@
 //!
 //! [`std::sync`]: https://doc.rust-lang.org/std/sync/index.html
 //!
+//! ## The need for synchronization
+//!
+//! async-std's sync primitives are scheduler-aware, making it possible to
+//! `.await` their operations - for example the locking of a [`Mutex`].
+//!
+//! Conceptually, a Rust program is a series of operations which will
+//! be executed on a computer. The timeline of events happening in the
+//! program is consistent with the order of the operations in the code.
+//!
+//! Consider the following code, operating on some global static variables:
+//!
+//! ```
+//! static mut A: u32 = 0;
+//! static mut B: u32 = 0;
+//! static mut C: u32 = 0;
+//!
+//! fn main() {
+//!     unsafe {
+//!         A = 3;
+//!         B = 4;
+//!         A = A + B;
+//!         C = B;
+//!         println!("{} {} {}", A, B, C);
+//!         C = A;
+//!     }
+//! }
+//! ```
+//!
+//! It appears as if some variables stored in memory are changed, an addition
+//! is performed, result is stored in `A` and the variable `C` is
+//! modified twice.
+//!
+//! When only a single thread is involved, the results are as expected:
+//! the line `7 4 4` gets printed.
+//!
+//! As for what happens behind the scenes, when optimizations are enabled the
+//! final generated machine code might look very different from the code:
+//!
+//! - The first store to `C` might be moved before the store to `A` or `B`,
+//!   _as if_ we had written `C = 4; A = 3; B = 4`.
+//!
+//! - Assignment of `A + B` to `A` might be removed, since the sum can be stored
+//!   in a temporary location until it gets printed, with the global variable
+//!   never getting updated.
+//!
+//! - The final result could be determined just by looking at the code
+//!   at compile time, so [constant folding] might turn the whole
+//!   block into a simple `println!("7 4 4")`.
+//!
+//! The compiler is allowed to perform any combination of these
+//! optimizations, as long as the final optimized code, when executed,
+//! produces the same results as the one without optimizations.
+//!
+//! Due to the [concurrency] involved in modern computers, assumptions
+//! about the program's execution order are often wrong. Access to
+//! global variables can lead to nondeterministic results, **even if**
+//! compiler optimizations are disabled, and it is **still possible**
+//! to introduce synchronization bugs.
+//!
+//! Note that thanks to Rust's safety guarantees, accessing global (static)
+//! variables requires `unsafe` code, assuming we don't use any of the
+//! synchronization primitives in this module.
+//!
+//! [constant folding]: https://en.wikipedia.org/wiki/Constant_folding
+//! [concurrency]: https://en.wikipedia.org/wiki/Concurrency_(computer_science)
+//!
+//! ## Out-of-order execution
+//!
+//! Instructions can execute in a different order from the one we define, due to
+//! various reasons:
+//!
+//! - The **compiler** reordering instructions: If the compiler can issue an
+//!   instruction at an earlier point, it will try to do so. For example, it
+//!   might hoist memory loads at the top of a code block, so that the CPU can
+//!   start [prefetching] the values from memory.
+//!
+//!   In single-threaded scenarios, this can cause issues when writing
+//!   signal handlers or certain kinds of low-level code.
+//!   Use [compiler fences] to prevent this reordering.
+//!
+//! - A **single processor** executing instructions [out-of-order]:
+//!   Modern CPUs are capable of [superscalar] execution,
+//!   i.e., multiple instructions might be executing at the same time,
+//!   even though the machine code describes a sequential process.
+//!
+//!   This kind of reordering is handled transparently by the CPU.
+//!
+//! - A **multiprocessor** system executing multiple hardware threads
+//!   at the same time: In multi-threaded scenarios, you can use two
+//!   kinds of primitives to deal with synchronization:
+//!   - [memory fences] to ensure memory accesses are made visible to
+//!     other CPUs in the right order.
+//!   - [atomic operations] to ensure simultaneous access to the same
+//!     memory location doesn't lead to undefined behavior.
+//!
+//! [prefetching]: https://en.wikipedia.org/wiki/Cache_prefetching
+//! [compiler fences]: https://doc.rust-lang.org/std/sync/atomic/fn.compiler_fence.html
+//! [out-of-order]: https://en.wikipedia.org/wiki/Out-of-order_execution
+//! [superscalar]: https://en.wikipedia.org/wiki/Superscalar_processor
+//! [memory fences]: https://doc.rust-lang.org/std/sync/atomic/fn.fence.html
+//! [atomic operations]: https://doc.rust-lang.org/std/sync/atomic/index.html
+//!
+//! ## Higher-level synchronization objects
+//!
+//! Most of the low-level synchronization primitives are quite error-prone and
+//! inconvenient to use, which is why async-std also exposes some
+//! higher-level synchronization objects.
+//!
+//! These abstractions can be built out of lower-level primitives.
+//! For efficiency, the sync objects in async-std are usually
+//! implemented with help from the scheduler, which is
+//! able to reschedule the tasks while they are blocked on acquiring
+//! a lock.
+//!
+//! The following is an overview of the available synchronization
+//! objects:
+//!
+//! - [`Arc`]: Atomically Reference-Counted pointer, which can be used
+//!   in multithreaded environments to prolong the lifetime of some
+//!   data until all the threads have finished using it.
+//!
+//! - [`Barrier`]: Ensures multiple threads will wait for each other
+//!   to reach a point in the program, before continuing execution all
+//!   together.
+//!
+//! - [`Mutex`]: Mutual exclusion mechanism, which ensures that at
+//!   most one task at a time is able to access some data.
+//!
+//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
+//!   multiple readers at the same time, while allowing only one
+//!   writer at a time. In some cases, this can be more efficient than
+//!   a mutex.
+//!
+//! If you're looking for channels, check out
+//! [`async_std::channel`][crate::channel].
+//!
+//! [`Arc`]: struct.Arc.html
+//! [`Barrier`]: struct.Barrier.html
+//! [`channel`]: fn.channel.html
+//! [`Mutex`]: struct.Mutex.html
+//! [`RwLock`]: struct.RwLock.html
+//!
 //! # Examples
 //!
 //! Spawn a task that updates an integer protected by a mutex:
 //!
 //! ```
-//! # fn main() { async_std::task::block_on(async {
+//! # async_std::task::block_on(async {
 //! #
-//! use std::sync::Arc;
-//!
-//! use async_std::sync::Mutex;
+//! use async_std::sync::{Arc, Mutex};
 //! use async_std::task;
 //!
 //! let m1 = Arc::new(Mutex::new(0));
@@ -26,20 +166,26 @@
 //!
 //! assert_eq!(*m1.lock().await, 1);
 //! #
-//! # }) }
+//! # })
 //! ```
 
+#![allow(clippy::needless_doctest_main)]
+
 #[doc(inline)]
 pub use std::sync::{Arc, Weak};
 
-#[cfg(any(feature = "unstable", feature = "docs"))]
-pub use barrier::{Barrier, BarrierWaitResult};
+#[doc(inline)]
+pub use async_lock::{Mutex, MutexGuard, MutexGuardArc};
+
+#[doc(inline)]
+pub use async_lock::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard};
+
+cfg_unstable! {
+    pub use async_lock::{Barrier, BarrierWaitResult};
+    pub use condvar::Condvar;
+    pub(crate) use waker_set::WakerSet;
 
-pub use mutex::{Mutex, MutexGuard};
-pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
+    mod condvar;
 
-#[cfg(any(feature = "unstable", feature = "docs"))]
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-mod barrier;
-mod mutex;
-mod rwlock;
+    pub(crate) mod waker_set;
+}
diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs
deleted file mode 100644
index 8ae51ad4c..000000000
--- a/src/sync/mutex.rs
+++ /dev/null
@@ -1,346 +0,0 @@
-use std::cell::UnsafeCell;
-use std::fmt;
-use std::ops::{Deref, DerefMut};
-use std::pin::Pin;
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-use slab::Slab;
-
-use crate::future::Future;
-use crate::task::{Context, Poll, Waker};
-
-/// Set if the mutex is locked.
-const LOCK: usize = 1 << 0;
-
-/// Set if there are tasks blocked on the mutex.
-const BLOCKED: usize = 1 << 1;
-
-/// A mutual exclusion primitive for protecting shared data.
-///
-/// This type is an async version of [`std::sync::Mutex`].
-///
-/// [`std::sync::Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
-///
-/// # Examples
-///
-/// ```
-/// # fn main() { async_std::task::block_on(async {
-/// #
-/// use std::sync::Arc;
-///
-/// use async_std::sync::Mutex;
-/// use async_std::task;
-///
-/// let m = Arc::new(Mutex::new(0));
-/// let mut tasks = vec![];
-///
-/// for _ in 0..10 {
-///     let m = m.clone();
-///     tasks.push(task::spawn(async move {
-///         *m.lock().await += 1;
-///     }));
-/// }
-///
-/// for t in tasks {
-///     t.await;
-/// }
-/// assert_eq!(*m.lock().await, 10);
-/// #
-/// # }) }
-/// ```
-pub struct Mutex<T> {
-    state: AtomicUsize,
-    blocked: std::sync::Mutex<Slab<Option<Waker>>>,
-    value: UnsafeCell<T>,
-}
-
-unsafe impl<T: Send> Send for Mutex<T> {}
-unsafe impl<T: Send> Sync for Mutex<T> {}
-
-impl<T> Mutex<T> {
-    /// Creates a new mutex.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use async_std::sync::Mutex;
-    ///
-    /// let mutex = Mutex::new(0);
-    /// ```
-    pub fn new(t: T) -> Mutex<T> {
-        Mutex {
-            state: AtomicUsize::new(0),
-            blocked: std::sync::Mutex::new(Slab::new()),
-            value: UnsafeCell::new(t),
-        }
-    }
-
-    /// Acquires the lock.
-    ///
-    /// Returns a guard that releases the lock when dropped.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use std::sync::Arc;
-    ///
-    /// use async_std::sync::Mutex;
-    /// use async_std::task;
-    ///
-    /// let m1 = Arc::new(Mutex::new(10));
-    /// let m2 = m1.clone();
-    ///
-    /// task::spawn(async move {
-    ///     *m1.lock().await = 20;
-    /// })
-    /// .await;
-    ///
-    /// assert_eq!(*m2.lock().await, 20);
-    /// #
-    /// # }) }
-    /// ```
-    pub async fn lock(&self) -> MutexGuard<'_, T> {
-        pub struct LockFuture<'a, T> {
-            mutex: &'a Mutex<T>,
-            opt_key: Option<usize>,
-            acquired: bool,
-        }
-
-        impl<'a, T> Future for LockFuture<'a, T> {
-            type Output = MutexGuard<'a, T>;
-
-            fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-                match self.mutex.try_lock() {
-                    Some(guard) => {
-                        self.acquired = true;
-                        Poll::Ready(guard)
-                    }
-                    None => {
-                        let mut blocked = self.mutex.blocked.lock().unwrap();
-
-                        // Register the current task.
-                        match self.opt_key {
-                            None => {
-                                // Insert a new entry into the list of blocked tasks.
-                                let w = cx.waker().clone();
-                                let key = blocked.insert(Some(w));
-                                self.opt_key = Some(key);
-
-                                if blocked.len() == 1 {
-                                    self.mutex.state.fetch_or(BLOCKED, Ordering::Relaxed);
-                                }
-                            }
-                            Some(key) => {
-                                // There is already an entry in the list of blocked tasks. Just
-                                // reset the waker if it was removed.
-                                if blocked[key].is_none() {
-                                    let w = cx.waker().clone();
-                                    blocked[key] = Some(w);
-                                }
-                            }
-                        }
-
-                        // Try locking again because it's possible the mutex got unlocked just
-                        // before the current task was registered as a blocked task.
-                        match self.mutex.try_lock() {
-                            Some(guard) => {
-                                self.acquired = true;
-                                Poll::Ready(guard)
-                            }
-                            None => Poll::Pending,
-                        }
-                    }
-                }
-            }
-        }
-
-        impl<T> Drop for LockFuture<'_, T> {
-            fn drop(&mut self) {
-                if let Some(key) = self.opt_key {
-                    let mut blocked = self.mutex.blocked.lock().unwrap();
-                    let opt_waker = blocked.remove(key);
-
-                    if opt_waker.is_none() && !self.acquired {
-                        // We were awoken but didn't acquire the lock. Wake up another task.
-                        if let Some((_, opt_waker)) = blocked.iter_mut().next() {
-                            if let Some(w) = opt_waker.take() {
-                                w.wake();
-                            }
-                        }
-                    }
-
-                    if blocked.is_empty() {
-                        self.mutex.state.fetch_and(!BLOCKED, Ordering::Relaxed);
-                    }
-                }
-            }
-        }
-
-        LockFuture {
-            mutex: self,
-            opt_key: None,
-            acquired: false,
-        }
-        .await
-    }
-
-    /// Attempts to acquire the lock.
-    ///
-    /// If the lock could not be acquired at this time, then [`None`] is returned. Otherwise, a
-    /// guard is returned that releases the lock when dropped.
-    ///
-    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use std::sync::Arc;
-    ///
-    /// use async_std::sync::Mutex;
-    /// use async_std::task;
-    ///
-    /// let m1 = Arc::new(Mutex::new(10));
-    /// let m2 = m1.clone();
-    ///
-    /// task::spawn(async move {
-    ///     if let Some(mut guard) = m1.try_lock() {
-    ///         *guard = 20;
-    ///     } else {
-    ///         println!("try_lock failed");
-    ///     }
-    /// })
-    /// .await;
-    ///
-    /// assert_eq!(*m2.lock().await, 20);
-    /// #
-    /// # }) }
-    /// ```
-    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
-        if self.state.fetch_or(LOCK, Ordering::Acquire) & LOCK == 0 {
-            Some(MutexGuard(self))
-        } else {
-            None
-        }
-    }
-
-    /// Consumes the mutex, returning the underlying data.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use async_std::sync::Mutex;
-    ///
-    /// let mutex = Mutex::new(10);
-    /// assert_eq!(mutex.into_inner(), 10);
-    /// ```
-    pub fn into_inner(self) -> T {
-        self.value.into_inner()
-    }
-
-    /// Returns a mutable reference to the underlying data.
-    ///
-    /// Since this call borrows the mutex mutably, no actual locking takes place -- the mutable
-    /// borrow statically guarantees no locks exist.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::Mutex;
-    ///
-    /// let mut mutex = Mutex::new(0);
-    /// *mutex.get_mut() = 10;
-    /// assert_eq!(*mutex.lock().await, 10);
-    /// #
-    /// # }) }
-    /// ```
-    pub fn get_mut(&mut self) -> &mut T {
-        unsafe { &mut *self.value.get() }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for Mutex<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.try_lock() {
-            None => {
-                struct LockedPlaceholder;
-                impl fmt::Debug for LockedPlaceholder {
-                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                        f.write_str("<locked>")
-                    }
-                }
-                f.debug_struct("Mutex")
-                    .field("data", &LockedPlaceholder)
-                    .finish()
-            }
-            Some(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(),
-        }
-    }
-}
-
-impl<T> From<T> for Mutex<T> {
-    fn from(val: T) -> Mutex<T> {
-        Mutex::new(val)
-    }
-}
-
-impl<T: Default> Default for Mutex<T> {
-    fn default() -> Mutex<T> {
-        Mutex::new(Default::default())
-    }
-}
-
-/// A guard that releases the lock when dropped.
-pub struct MutexGuard<'a, T>(&'a Mutex<T>);
-
-unsafe impl<T: Send> Send for MutexGuard<'_, T> {}
-unsafe impl<T: Sync> Sync for MutexGuard<'_, T> {}
-
-impl<T> Drop for MutexGuard<'_, T> {
-    fn drop(&mut self) {
-        let state = self.0.state.fetch_and(!LOCK, Ordering::AcqRel);
-
-        // If there are any blocked tasks, wake one of them up.
-        if state & BLOCKED != 0 {
-            let mut blocked = self.0.blocked.lock().unwrap();
-
-            if let Some((_, opt_waker)) = blocked.iter_mut().next() {
-                // If there is no waker in this entry, that means it was already woken.
-                if let Some(w) = opt_waker.take() {
-                    w.wake();
-                }
-            }
-        }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Debug::fmt(&**self, f)
-    }
-}
-
-impl<T: fmt::Display> fmt::Display for MutexGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (**self).fmt(f)
-    }
-}
-
-impl<T> Deref for MutexGuard<'_, T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { &*self.0.value.get() }
-    }
-}
-
-impl<T> DerefMut for MutexGuard<'_, T> {
-    fn deref_mut(&mut self) -> &mut T {
-        unsafe { &mut *self.0.value.get() }
-    }
-}
diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs
deleted file mode 100644
index 36f475b58..000000000
--- a/src/sync/rwlock.rs
+++ /dev/null
@@ -1,579 +0,0 @@
-use std::cell::UnsafeCell;
-use std::fmt;
-use std::ops::{Deref, DerefMut};
-use std::pin::Pin;
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-use slab::Slab;
-
-use crate::future::Future;
-use crate::task::{Context, Poll, Waker};
-
-/// Set if a write lock is held.
-const WRITE_LOCK: usize = 1 << 0;
-
-/// Set if there are read operations blocked on the lock.
-const BLOCKED_READS: usize = 1 << 1;
-
-/// Set if there are write operations blocked on the lock.
-const BLOCKED_WRITES: usize = 1 << 2;
-
-/// The value of a single blocked read contributing to the read count.
-const ONE_READ: usize = 1 << 3;
-
-/// The bits in which the read count is stored.
-const READ_COUNT_MASK: usize = !(ONE_READ - 1);
-
-/// A reader-writer lock for protecting shared data.
-///
-/// This type is an async version of [`std::sync::RwLock`].
-///
-/// [`std::sync::RwLock`]: https://doc.rust-lang.org/std/sync/struct.RwLock.html
-///
-/// # Examples
-///
-/// ```
-/// # fn main() { async_std::task::block_on(async {
-/// #
-/// use async_std::sync::RwLock;
-///
-/// let lock = RwLock::new(5);
-///
-/// // Multiple read locks can be held at a time.
-/// let r1 = lock.read().await;
-/// let r2 = lock.read().await;
-/// assert_eq!(*r1, 5);
-/// assert_eq!(*r2, 5);
-/// drop((r1, r2));
-///
-/// // Only one write locks can be held at a time.
-/// let mut w = lock.write().await;
-/// *w += 1;
-/// assert_eq!(*w, 6);
-/// #
-/// # }) }
-/// ```
-pub struct RwLock<T> {
-    state: AtomicUsize,
-    reads: std::sync::Mutex<Slab<Option<Waker>>>,
-    writes: std::sync::Mutex<Slab<Option<Waker>>>,
-    value: UnsafeCell<T>,
-}
-
-unsafe impl<T: Send> Send for RwLock<T> {}
-unsafe impl<T: Send> Sync for RwLock<T> {}
-
-impl<T> RwLock<T> {
-    /// Creates a new reader-writer lock.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use async_std::sync::RwLock;
-    ///
-    /// let lock = RwLock::new(0);
-    /// ```
-    pub fn new(t: T) -> RwLock<T> {
-        RwLock {
-            state: AtomicUsize::new(0),
-            reads: std::sync::Mutex::new(Slab::new()),
-            writes: std::sync::Mutex::new(Slab::new()),
-            value: UnsafeCell::new(t),
-        }
-    }
-
-    /// Acquires a read lock.
-    ///
-    /// Returns a guard that releases the lock when dropped.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::RwLock;
-    ///
-    /// let lock = RwLock::new(1);
-    ///
-    /// let n = lock.read().await;
-    /// assert_eq!(*n, 1);
-    ///
-    /// assert!(lock.try_read().is_some());
-    /// #
-    /// # }) }
-    /// ```
-    pub async fn read(&self) -> RwLockReadGuard<'_, T> {
-        pub struct LockFuture<'a, T> {
-            lock: &'a RwLock<T>,
-            opt_key: Option<usize>,
-            acquired: bool,
-        }
-
-        impl<'a, T> Future for LockFuture<'a, T> {
-            type Output = RwLockReadGuard<'a, T>;
-
-            fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-                match self.lock.try_read() {
-                    Some(guard) => {
-                        self.acquired = true;
-                        Poll::Ready(guard)
-                    }
-                    None => {
-                        let mut reads = self.lock.reads.lock().unwrap();
-
-                        // Register the current task.
-                        match self.opt_key {
-                            None => {
-                                // Insert a new entry into the list of blocked reads.
-                                let w = cx.waker().clone();
-                                let key = reads.insert(Some(w));
-                                self.opt_key = Some(key);
-
-                                if reads.len() == 1 {
-                                    self.lock.state.fetch_or(BLOCKED_READS, Ordering::Relaxed);
-                                }
-                            }
-                            Some(key) => {
-                                // There is already an entry in the list of blocked reads. Just
-                                // reset the waker if it was removed.
-                                if reads[key].is_none() {
-                                    let w = cx.waker().clone();
-                                    reads[key] = Some(w);
-                                }
-                            }
-                        }
-
-                        // Try locking again because it's possible the lock got unlocked just
-                        // before the current task was registered as a blocked task.
-                        match self.lock.try_read() {
-                            Some(guard) => {
-                                self.acquired = true;
-                                Poll::Ready(guard)
-                            }
-                            None => Poll::Pending,
-                        }
-                    }
-                }
-            }
-        }
-
-        impl<T> Drop for LockFuture<'_, T> {
-            fn drop(&mut self) {
-                if let Some(key) = self.opt_key {
-                    let mut reads = self.lock.reads.lock().unwrap();
-                    let opt_waker = reads.remove(key);
-
-                    if reads.is_empty() {
-                        self.lock.state.fetch_and(!BLOCKED_READS, Ordering::Relaxed);
-                    }
-
-                    if opt_waker.is_none() {
-                        // We were awoken. Wake up another blocked read.
-                        if let Some((_, opt_waker)) = reads.iter_mut().next() {
-                            if let Some(w) = opt_waker.take() {
-                                w.wake();
-                                return;
-                            }
-                        }
-                        drop(reads);
-
-                        if !self.acquired {
-                            // We didn't acquire the lock and didn't wake another blocked read.
-                            // Wake a blocked write instead.
-                            let mut writes = self.lock.writes.lock().unwrap();
-                            if let Some((_, opt_waker)) = writes.iter_mut().next() {
-                                if let Some(w) = opt_waker.take() {
-                                    w.wake();
-                                    return;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        LockFuture {
-            lock: self,
-            opt_key: None,
-            acquired: false,
-        }
-        .await
-    }
-
-    /// Attempts to acquire a read lock.
-    ///
-    /// If a read lock could not be acquired at this time, then [`None`] is returned. Otherwise, a
-    /// guard is returned that releases the lock when dropped.
-    ///
-    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::RwLock;
-    ///
-    /// let lock = RwLock::new(1);
-    ///
-    /// let n = lock.read().await;
-    /// assert_eq!(*n, 1);
-    ///
-    /// assert!(lock.try_read().is_some());
-    /// #
-    /// # }) }
-    /// ```
-    pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
-        let mut state = self.state.load(Ordering::Acquire);
-
-        loop {
-            // If a write lock is currently held, then a read lock cannot be acquired.
-            if state & WRITE_LOCK != 0 {
-                return None;
-            }
-
-            // Increment the number of active reads.
-            match self.state.compare_exchange_weak(
-                state,
-                state + ONE_READ,
-                Ordering::AcqRel,
-                Ordering::Acquire,
-            ) {
-                Ok(_) => return Some(RwLockReadGuard(self)),
-                Err(s) => state = s,
-            }
-        }
-    }
-
-    /// Acquires a write lock.
-    ///
-    /// Returns a guard that releases the lock when dropped.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::RwLock;
-    ///
-    /// let lock = RwLock::new(1);
-    ///
-    /// let mut n = lock.write().await;
-    /// *n = 2;
-    ///
-    /// assert!(lock.try_read().is_none());
-    /// #
-    /// # }) }
-    /// ```
-    pub async fn write(&self) -> RwLockWriteGuard<'_, T> {
-        pub struct LockFuture<'a, T> {
-            lock: &'a RwLock<T>,
-            opt_key: Option<usize>,
-            acquired: bool,
-        }
-
-        impl<'a, T> Future for LockFuture<'a, T> {
-            type Output = RwLockWriteGuard<'a, T>;
-
-            fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-                match self.lock.try_write() {
-                    Some(guard) => {
-                        self.acquired = true;
-                        Poll::Ready(guard)
-                    }
-                    None => {
-                        let mut writes = self.lock.writes.lock().unwrap();
-
-                        // Register the current task.
-                        match self.opt_key {
-                            None => {
-                                // Insert a new entry into the list of blocked writes.
-                                let w = cx.waker().clone();
-                                let key = writes.insert(Some(w));
-                                self.opt_key = Some(key);
-
-                                if writes.len() == 1 {
-                                    self.lock.state.fetch_or(BLOCKED_WRITES, Ordering::Relaxed);
-                                }
-                            }
-                            Some(key) => {
-                                // There is already an entry in the list of blocked writes. Just
-                                // reset the waker if it was removed.
-                                if writes[key].is_none() {
-                                    let w = cx.waker().clone();
-                                    writes[key] = Some(w);
-                                }
-                            }
-                        }
-
-                        // Try locking again because it's possible the lock got unlocked just
-                        // before the current task was registered as a blocked task.
-                        match self.lock.try_write() {
-                            Some(guard) => {
-                                self.acquired = true;
-                                Poll::Ready(guard)
-                            }
-                            None => Poll::Pending,
-                        }
-                    }
-                }
-            }
-        }
-
-        impl<T> Drop for LockFuture<'_, T> {
-            fn drop(&mut self) {
-                if let Some(key) = self.opt_key {
-                    let mut writes = self.lock.writes.lock().unwrap();
-                    let opt_waker = writes.remove(key);
-
-                    if writes.is_empty() {
-                        self.lock
-                            .state
-                            .fetch_and(!BLOCKED_WRITES, Ordering::Relaxed);
-                    }
-
-                    if opt_waker.is_none() && !self.acquired {
-                        // We were awoken but didn't acquire the lock. Wake up another write.
-                        if let Some((_, opt_waker)) = writes.iter_mut().next() {
-                            if let Some(w) = opt_waker.take() {
-                                w.wake();
-                                return;
-                            }
-                        }
-                        drop(writes);
-
-                        // There are no blocked writes. Wake a blocked read instead.
-                        let mut reads = self.lock.reads.lock().unwrap();
-                        if let Some((_, opt_waker)) = reads.iter_mut().next() {
-                            if let Some(w) = opt_waker.take() {
-                                w.wake();
-                                return;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        LockFuture {
-            lock: self,
-            opt_key: None,
-            acquired: false,
-        }
-        .await
-    }
-
-    /// Attempts to acquire a write lock.
-    ///
-    /// If a write lock could not be acquired at this time, then [`None`] is returned. Otherwise, a
-    /// guard is returned that releases the lock when dropped.
-    ///
-    /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::RwLock;
-    ///
-    /// let lock = RwLock::new(1);
-    ///
-    /// let n = lock.read().await;
-    /// assert_eq!(*n, 1);
-    ///
-    /// assert!(lock.try_write().is_none());
-    /// #
-    /// # }) }
-    /// ```
-    pub fn try_write(&self) -> Option<RwLockWriteGuard<'_, T>> {
-        let mut state = self.state.load(Ordering::Acquire);
-
-        loop {
-            // If any kind of lock is currently held, then a write lock cannot be acquired.
-            if state & (WRITE_LOCK | READ_COUNT_MASK) != 0 {
-                return None;
-            }
-
-            // Set the write lock.
-            match self.state.compare_exchange_weak(
-                state,
-                state | WRITE_LOCK,
-                Ordering::AcqRel,
-                Ordering::Acquire,
-            ) {
-                Ok(_) => return Some(RwLockWriteGuard(self)),
-                Err(s) => state = s,
-            }
-        }
-    }
-
-    /// Consumes the lock, returning the underlying data.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// use async_std::sync::RwLock;
-    ///
-    /// let lock = RwLock::new(10);
-    /// assert_eq!(lock.into_inner(), 10);
-    /// ```
-    pub fn into_inner(self) -> T {
-        self.value.into_inner()
-    }
-
-    /// Returns a mutable reference to the underlying data.
-    ///
-    /// Since this call borrows the lock mutably, no actual locking takes place -- the mutable
-    /// borrow statically guarantees no locks exist.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::sync::RwLock;
-    ///
-    /// let mut lock = RwLock::new(0);
-    /// *lock.get_mut() = 10;
-    /// assert_eq!(*lock.write().await, 10);
-    /// #
-    /// # }) }
-    /// ```
-    pub fn get_mut(&mut self) -> &mut T {
-        unsafe { &mut *self.value.get() }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for RwLock<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.try_read() {
-            None => {
-                struct LockedPlaceholder;
-                impl fmt::Debug for LockedPlaceholder {
-                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                        f.write_str("<locked>")
-                    }
-                }
-                f.debug_struct("RwLock")
-                    .field("data", &LockedPlaceholder)
-                    .finish()
-            }
-            Some(guard) => f.debug_struct("RwLock").field("data", &&*guard).finish(),
-        }
-    }
-}
-
-impl<T> From<T> for RwLock<T> {
-    fn from(val: T) -> RwLock<T> {
-        RwLock::new(val)
-    }
-}
-
-impl<T: Default> Default for RwLock<T> {
-    fn default() -> RwLock<T> {
-        RwLock::new(Default::default())
-    }
-}
-
-/// A guard that releases the read lock when dropped.
-pub struct RwLockReadGuard<'a, T>(&'a RwLock<T>);
-
-unsafe impl<T: Send> Send for RwLockReadGuard<'_, T> {}
-unsafe impl<T: Sync> Sync for RwLockReadGuard<'_, T> {}
-
-impl<T> Drop for RwLockReadGuard<'_, T> {
-    fn drop(&mut self) {
-        let state = self.0.state.fetch_sub(ONE_READ, Ordering::AcqRel);
-
-        // If this was the last read and there are blocked writes, wake one of them up.
-        if (state & READ_COUNT_MASK) == ONE_READ && state & BLOCKED_WRITES != 0 {
-            let mut writes = self.0.writes.lock().unwrap();
-
-            if let Some((_, opt_waker)) = writes.iter_mut().next() {
-                // If there is no waker in this entry, that means it was already woken.
-                if let Some(w) = opt_waker.take() {
-                    w.wake();
-                }
-            }
-        }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Debug::fmt(&**self, f)
-    }
-}
-
-impl<T: fmt::Display> fmt::Display for RwLockReadGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (**self).fmt(f)
-    }
-}
-
-impl<T> Deref for RwLockReadGuard<'_, T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { &*self.0.value.get() }
-    }
-}
-
-/// A guard that releases the write lock when dropped.
-pub struct RwLockWriteGuard<'a, T>(&'a RwLock<T>);
-
-unsafe impl<T: Send> Send for RwLockWriteGuard<'_, T> {}
-unsafe impl<T: Sync> Sync for RwLockWriteGuard<'_, T> {}
-
-impl<T> Drop for RwLockWriteGuard<'_, T> {
-    fn drop(&mut self) {
-        let state = self.0.state.fetch_and(!WRITE_LOCK, Ordering::AcqRel);
-
-        let mut guard = None;
-
-        // Check if there are any blocked reads or writes.
-        if state & BLOCKED_READS != 0 {
-            guard = Some(self.0.reads.lock().unwrap());
-        } else if state & BLOCKED_WRITES != 0 {
-            guard = Some(self.0.writes.lock().unwrap());
-        }
-
-        // Wake up a single blocked task.
-        if let Some(mut guard) = guard {
-            if let Some((_, opt_waker)) = guard.iter_mut().next() {
-                // If there is no waker in this entry, that means it was already woken.
-                if let Some(w) = opt_waker.take() {
-                    w.wake();
-                }
-            }
-        }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for RwLockWriteGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Debug::fmt(&**self, f)
-    }
-}
-
-impl<T: fmt::Display> fmt::Display for RwLockWriteGuard<'_, T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        (**self).fmt(f)
-    }
-}
-
-impl<T> Deref for RwLockWriteGuard<'_, T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { &*self.0.value.get() }
-    }
-}
-
-impl<T> DerefMut for RwLockWriteGuard<'_, T> {
-    fn deref_mut(&mut self) -> &mut T {
-        unsafe { &mut *self.0.value.get() }
-    }
-}
diff --git a/src/sync/waker_set.rs b/src/sync/waker_set.rs
new file mode 100644
index 000000000..05590f488
--- /dev/null
+++ b/src/sync/waker_set.rs
@@ -0,0 +1,235 @@
+//! A common utility for building synchronization primitives.
+//!
+//! When an async operation is blocked, it needs to register itself somewhere so that it can be
+//! notified later on. The `WakerSet` type helps with keeping track of such async operations and
+//! notifying them when they may make progress.
+
+use std::cell::UnsafeCell;
+use std::ops::{Deref, DerefMut};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::task::{Context, Waker};
+
+use crossbeam_utils::Backoff;
+use slab::Slab;
+
+/// Set when the entry list is locked.
+#[allow(clippy::identity_op)]
+const LOCKED: usize = 1 << 0;
+
+/// Set when there is at least one entry that has already been notified.
+const NOTIFIED: usize = 1 << 1;
+
+/// Set when there is at least one notifiable entry.
+const NOTIFIABLE: usize = 1 << 2;
+
+/// Inner representation of `WakerSet`.
+struct Inner {
+    /// A list of entries in the set.
+    ///
+    /// Each entry has an optional waker associated with the task that is executing the operation.
+    /// If the waker is set to `None`, that means the task has been woken up but hasn't removed
+    /// itself from the `WakerSet` yet.
+    ///
+    /// The key of each entry is its index in the `Slab`.
+    entries: Slab<Option<Waker>>,
+
+    /// The number of notifiable entries.
+    notifiable: usize,
+}
+
+/// A set holding wakers.
+pub struct WakerSet {
+    /// Holds three bits: `LOCKED`, `NOTIFY_ONE`, and `NOTIFY_ALL`.
+    flag: AtomicUsize,
+
+    /// A set holding wakers.
+    inner: UnsafeCell<Inner>,
+}
+
+impl WakerSet {
+    /// Creates a new `WakerSet`.
+    #[inline]
+    pub fn new() -> WakerSet {
+        WakerSet {
+            flag: AtomicUsize::new(0),
+            inner: UnsafeCell::new(Inner {
+                entries: Slab::new(),
+                notifiable: 0,
+            }),
+        }
+    }
+
+    /// Inserts a waker for a blocked operation and returns a key associated with it.
+    #[cold]
+    pub fn insert(&self, cx: &Context<'_>) -> usize {
+        let w = cx.waker().clone();
+        let mut inner = self.lock();
+
+        let key = inner.entries.insert(Some(w));
+        inner.notifiable += 1;
+        key
+    }
+
+    /// If the waker for this key is still waiting for a notification, then update
+    /// the waker for the entry, and return false. If the waker has been notified,
+    /// treat the entry as completed and return true.
+    #[cfg(feature = "unstable")]
+    pub fn remove_if_notified(&self, key: usize, cx: &Context<'_>) -> bool {
+        let mut inner = self.lock();
+
+        match &mut inner.entries[key] {
+            None => {
+                inner.entries.remove(key);
+                true
+            }
+            Some(w) => {
+                // We were never woken, so update instead
+                if !w.will_wake(cx.waker()) {
+                    *w = cx.waker().clone();
+                }
+                false
+            }
+        }
+    }
+
+    /// Removes the waker of a cancelled operation.
+    ///
+    /// Returns `true` if another blocked operation from the set was notified.
+    #[cold]
+    pub fn cancel(&self, key: usize) -> bool {
+        let mut inner = self.lock();
+
+        match inner.entries.remove(key) {
+            Some(_) => inner.notifiable -= 1,
+            None => {
+                // The operation was cancelled and notified so notify another operation instead.
+                for (_, opt_waker) in inner.entries.iter_mut() {
+                    // If there is no waker in this entry, that means it was already woken.
+                    if let Some(w) = opt_waker.take() {
+                        w.wake();
+                        inner.notifiable -= 1;
+                        return true;
+                    }
+                }
+            }
+        }
+
+        false
+    }
+
+    /// Notifies one additional blocked operation.
+    ///
+    /// Returns `true` if an operation was notified.
+    #[inline]
+    #[cfg(feature = "unstable")]
+    pub fn notify_one(&self) -> bool {
+        // Use `SeqCst` ordering to synchronize with `Lock::drop()`.
+        if self.flag.load(Ordering::SeqCst) & NOTIFIABLE != 0 {
+            self.notify(Notify::One)
+        } else {
+            false
+        }
+    }
+
+    /// Notifies all blocked operations.
+    ///
+    /// Returns `true` if at least one operation was notified.
+    #[inline]
+    pub fn notify_all(&self) -> bool {
+        // Use `SeqCst` ordering to synchronize with `Lock::drop()`.
+        if self.flag.load(Ordering::SeqCst) & NOTIFIABLE != 0 {
+            self.notify(Notify::All)
+        } else {
+            false
+        }
+    }
+
+    /// Notifies blocked operations, either one or all of them.
+    ///
+    /// Returns `true` if at least one operation was notified.
+    #[cold]
+    fn notify(&self, n: Notify) -> bool {
+        let inner = &mut *self.lock();
+        let mut notified = false;
+
+        for (_, opt_waker) in inner.entries.iter_mut() {
+            // If there is no waker in this entry, that means it was already woken.
+            if let Some(w) = opt_waker.take() {
+                w.wake();
+                inner.notifiable -= 1;
+                notified = true;
+
+                if n == Notify::One {
+                    break;
+                }
+            }
+
+            if n == Notify::Any {
+                break;
+            }
+        }
+
+        notified
+    }
+
+    /// Locks the list of entries.
+    fn lock(&self) -> Lock<'_> {
+        let backoff = Backoff::new();
+        while self.flag.fetch_or(LOCKED, Ordering::Acquire) & LOCKED != 0 {
+            backoff.snooze();
+        }
+        Lock { waker_set: self }
+    }
+}
+
+/// A guard holding a `WakerSet` locked.
+struct Lock<'a> {
+    waker_set: &'a WakerSet,
+}
+
+impl Drop for Lock<'_> {
+    #[inline]
+    fn drop(&mut self) {
+        let mut flag = 0;
+
+        // Set the `NOTIFIED` flag if there is at least one notified entry.
+        if self.entries.len() - self.notifiable > 0 {
+            flag |= NOTIFIED;
+        }
+
+        // Set the `NOTIFIABLE` flag if there is at least one notifiable entry.
+        if self.notifiable > 0 {
+            flag |= NOTIFIABLE;
+        }
+
+        // Use `SeqCst` ordering to synchronize with `WakerSet::lock_to_notify()`.
+        self.waker_set.flag.store(flag, Ordering::SeqCst);
+    }
+}
+
+impl Deref for Lock<'_> {
+    type Target = Inner;
+
+    #[inline]
+    fn deref(&self) -> &Inner {
+        unsafe { &*self.waker_set.inner.get() }
+    }
+}
+
+impl DerefMut for Lock<'_> {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut Inner {
+        unsafe { &mut *self.waker_set.inner.get() }
+    }
+}
+
+/// Notification strategy.
+#[derive(Clone, Copy, Eq, PartialEq)]
+enum Notify {
+    /// Make sure at least one entry is notified.
+    Any,
+    /// Notify one additional entry.
+    One,
+    /// Notify all entries.
+    All,
+}
diff --git a/src/task/block_on.rs b/src/task/block_on.rs
index 2d49dcaa2..3ab4dc06f 100644
--- a/src/task/block_on.rs
+++ b/src/task/block_on.rs
@@ -1,24 +1,16 @@
-use std::cell::UnsafeCell;
-use std::mem::{self, ManuallyDrop};
-use std::panic::{self, AssertUnwindSafe, UnwindSafe};
-use std::pin::Pin;
-use std::sync::Arc;
-use std::task::{RawWaker, RawWakerVTable};
-use std::thread::{self, Thread};
+use std::future::Future;
 
-use super::task;
-use super::task_local;
-use super::worker;
-use crate::future::Future;
-use crate::task::{Context, Poll, Waker};
-
-use kv_log_macro::trace;
+use crate::task::Builder;
 
 /// Spawns a task and blocks the current thread on its result.
 ///
 /// Calling this function is similar to [spawning] a thread and immediately [joining] it, except an
 /// asynchronous task will be spawned.
 ///
+/// See also: [`task::spawn_blocking`].
+///
+/// [`task::spawn_blocking`]: fn.spawn_blocking.html
+///
 /// [spawning]: https://doc.rust-lang.org/std/thread/fn.spawn.html
 /// [joining]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join
 ///
@@ -27,138 +19,24 @@ use kv_log_macro::trace;
 /// ```no_run
 /// use async_std::task;
 ///
-/// fn main() {
-///     task::block_on(async {
-///         println!("Hello, world!");
-///     })
-/// }
+/// task::block_on(async {
+///     println!("Hello, world!");
+/// })
 /// ```
+#[cfg(not(target_os = "unknown"))]
 pub fn block_on<F, T>(future: F) -> T
 where
     F: Future<Output = T>,
 {
-    unsafe {
-        // A place on the stack where the result will be stored.
-        let out = &mut UnsafeCell::new(None);
-
-        // Wrap the future into one that stores the result into `out`.
-        let future = {
-            let out = out.get();
-
-            async move {
-                let future = CatchUnwindFuture {
-                    future: AssertUnwindSafe(future),
-                };
-                *out = Some(future.await);
-            }
-        };
-
-        // Create a tag for the task.
-        let tag = task::Tag::new(None);
-
-        // Log this `block_on` operation.
-        let child_id = tag.task_id().as_u64();
-        let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0);
-
-        trace!("block_on", {
-            parent_id: parent_id,
-            child_id: child_id,
-        });
-
-        // Wrap the future into one that drops task-local variables on exit.
-        let future = task_local::add_finalizer(future);
-
-        let future = async move {
-            let res = future.await;
-            trace!("block_on completed", {
-                parent_id: parent_id,
-                child_id: child_id,
-            });
-            res
-        };
-
-        // Pin the future onto the stack.
-        pin_utils::pin_mut!(future);
-
-        // Transmute the future into one that is futurestatic.
-        let future = mem::transmute::<
-            Pin<&'_ mut dyn Future<Output = ()>>,
-            Pin<&'static mut dyn Future<Output = ()>>,
-        >(future);
-
-        // Block on the future and and wait for it to complete.
-        worker::set_tag(&tag, || block(future));
-
-        // Take out the result.
-        match (*out.get()).take().unwrap() {
-            Ok(v) => v,
-            Err(err) => panic::resume_unwind(err),
-        }
-    }
-}
-
-struct CatchUnwindFuture<F> {
-    future: F,
+    Builder::new().blocking(future)
 }
 
-impl<F> CatchUnwindFuture<F> {
-    pin_utils::unsafe_pinned!(future: F);
-}
-
-impl<F: Future + UnwindSafe> Future for CatchUnwindFuture<F> {
-    type Output = thread::Result<F::Output>;
-
-    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        panic::catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok)
-    }
-}
-
-fn block<F, T>(f: F) -> T
+/// Spawns a task and waits for it to finish.
+#[cfg(target_os = "unknown")]
+pub fn block_on<F, T>(future: F)
 where
-    F: Future<Output = T>,
+    F: Future<Output = T> + 'static,
+    T: 'static,
 {
-    thread_local! {
-        static ARC_THREAD: Arc<Thread> = Arc::new(thread::current());
-    }
-
-    pin_utils::pin_mut!(f);
-
-    ARC_THREAD.with(|arc_thread: &Arc<Thread>| {
-        let ptr = (&**arc_thread as *const Thread) as *const ();
-        let vt = vtable();
-
-        let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, vt))) };
-        let cx = &mut Context::from_waker(&waker);
-
-        loop {
-            if let Poll::Ready(t) = f.as_mut().poll(cx) {
-                return t;
-            }
-            thread::park();
-        }
-    })
-}
-
-fn vtable() -> &'static RawWakerVTable {
-    unsafe fn clone_raw(ptr: *const ()) -> RawWaker {
-        let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread));
-        mem::forget(arc.clone());
-        RawWaker::new(ptr, vtable())
-    }
-
-    unsafe fn wake_raw(ptr: *const ()) {
-        let arc = Arc::from_raw(ptr as *const Thread);
-        arc.unpark();
-    }
-
-    unsafe fn wake_by_ref_raw(ptr: *const ()) {
-        let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread));
-        arc.unpark();
-    }
-
-    unsafe fn drop_raw(ptr: *const ()) {
-        drop(Arc::from_raw(ptr as *const Thread))
-    }
-
-    &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw)
+    Builder::new().local(future).unwrap();
 }
diff --git a/src/task/blocking.rs b/src/task/blocking.rs
deleted file mode 100644
index 41177bc93..000000000
--- a/src/task/blocking.rs
+++ /dev/null
@@ -1,157 +0,0 @@
-//! A thread pool for running blocking functions asynchronously.
-
-use std::fmt;
-use std::pin::Pin;
-use std::sync::atomic::{AtomicU64, Ordering};
-use std::thread;
-use std::time::Duration;
-
-use crossbeam_channel::{bounded, Receiver, Sender};
-use lazy_static::lazy_static;
-
-use crate::future::Future;
-use crate::task::{Context, Poll};
-use crate::utils::abort_on_panic;
-
-const MAX_THREADS: u64 = 10_000;
-
-static DYNAMIC_THREAD_COUNT: AtomicU64 = AtomicU64::new(0);
-
-struct Pool {
-    sender: Sender<async_task::Task<()>>,
-    receiver: Receiver<async_task::Task<()>>,
-}
-
-lazy_static! {
-    static ref POOL: Pool = {
-        for _ in 0..2 {
-            thread::Builder::new()
-                .name("async-blocking-driver".to_string())
-                .spawn(|| abort_on_panic(|| {
-                    for task in &POOL.receiver {
-                        task.run();
-                    }
-                }))
-                .expect("cannot start a thread driving blocking tasks");
-        }
-
-        // We want to use an unbuffered channel here to help
-        // us drive our dynamic control. In effect, the
-        // kernel's scheduler becomes the queue, reducing
-        // the number of buffers that work must flow through
-        // before being acted on by a core. This helps keep
-        // latency snappy in the overall async system by
-        // reducing bufferbloat.
-        let (sender, receiver) = bounded(0);
-        Pool { sender, receiver }
-    };
-}
-
-// Create up to MAX_THREADS dynamic blocking task worker threads.
-// Dynamic threads will terminate themselves if they don't
-// receive any work after between one and ten seconds.
-fn maybe_create_another_blocking_thread() {
-    // We use a `Relaxed` atomic operation because
-    // it's just a heuristic, and would not lose correctness
-    // even if it's random.
-    let workers = DYNAMIC_THREAD_COUNT.load(Ordering::Relaxed);
-    if workers >= MAX_THREADS {
-        return;
-    }
-
-    // We want to avoid having all threads terminate at
-    // exactly the same time, causing thundering herd
-    // effects. We want to stagger their destruction over
-    // 10 seconds or so to make the costs fade into
-    // background noise.
-    //
-    // Generate a simple random number of milliseconds
-    let rand_sleep_ms = u64::from(random(10_000));
-
-    thread::Builder::new()
-        .name("async-blocking-driver-dynamic".to_string())
-        .spawn(move || {
-            let wait_limit = Duration::from_millis(1000 + rand_sleep_ms);
-
-            DYNAMIC_THREAD_COUNT.fetch_add(1, Ordering::Relaxed);
-            while let Ok(task) = POOL.receiver.recv_timeout(wait_limit) {
-                abort_on_panic(|| task.run());
-            }
-            DYNAMIC_THREAD_COUNT.fetch_sub(1, Ordering::Relaxed);
-        })
-        .expect("cannot start a dynamic thread driving blocking tasks");
-}
-
-// Enqueues work, attempting to send to the threadpool in a
-// nonblocking way and spinning up another worker thread if
-// there is not a thread ready to accept the work.
-fn schedule(t: async_task::Task<()>) {
-    if let Err(err) = POOL.sender.try_send(t) {
-        // We were not able to send to the channel without
-        // blocking. Try to spin up another thread and then
-        // retry sending while blocking.
-        maybe_create_another_blocking_thread();
-        POOL.sender.send(err.into_inner()).unwrap();
-    }
-}
-
-/// Spawns a blocking task.
-///
-/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks.
-pub fn spawn<F, R>(future: F) -> JoinHandle<R>
-where
-    F: Future<Output = R> + Send + 'static,
-    R: Send + 'static,
-{
-    let (task, handle) = async_task::spawn(future, schedule, ());
-    task.schedule();
-    JoinHandle(handle)
-}
-
-/// A handle to a blocking task.
-pub struct JoinHandle<R>(async_task::JoinHandle<R, ()>);
-
-impl<R> Unpin for JoinHandle<R> {}
-
-impl<R> Future for JoinHandle<R> {
-    type Output = R;
-
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        Pin::new(&mut self.0).poll(cx).map(|out| out.unwrap())
-    }
-}
-
-impl<R> fmt::Debug for JoinHandle<R> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("JoinHandle")
-            .field("handle", &self.0)
-            .finish()
-    }
-}
-
-/// Generates a random number in `0..n`.
-fn random(n: u32) -> u32 {
-    use std::cell::Cell;
-    use std::num::Wrapping;
-
-    thread_local! {
-        static RNG: Cell<Wrapping<u32>> = Cell::new(Wrapping(1406868647));
-    }
-
-    RNG.with(|rng| {
-        // This is the 32-bit variant of Xorshift.
-        //
-        // Source: https://en.wikipedia.org/wiki/Xorshift
-        let mut x = rng.get();
-        x ^= x << 13;
-        x ^= x >> 17;
-        x ^= x << 5;
-        rng.set(x);
-
-        // This is a fast alternative to `x % n`.
-        //
-        // Author: Daniel Lemire
-        // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-        ((x.0 as u64).wrapping_mul(n as u64) >> 32) as u32
-    })
-}
diff --git a/src/task/builder.rs b/src/task/builder.rs
index 630876c28..aba0d6115 100644
--- a/src/task/builder.rs
+++ b/src/task/builder.rs
@@ -1,32 +1,203 @@
-use super::pool;
-use super::JoinHandle;
-use crate::future::Future;
+use std::future::Future;
+use std::pin::Pin;
+use std::sync::Arc;
+use std::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+
 use crate::io;
+use crate::task::{JoinHandle, Task, TaskLocalsWrapper};
 
 /// Task builder that configures the settings of a new task.
-#[derive(Debug)]
+#[derive(Debug, Default)]
 pub struct Builder {
     pub(crate) name: Option<String>,
 }
 
 impl Builder {
     /// Creates a new builder.
+    #[inline]
     pub fn new() -> Builder {
         Builder { name: None }
     }
 
     /// Configures the name of the task.
+    #[inline]
     pub fn name(mut self, name: String) -> Builder {
         self.name = Some(name);
         self
     }
 
+    fn build<F, T>(self, future: F) -> SupportTaskLocals<F>
+    where
+        F: Future<Output = T>,
+    {
+        let name = self.name.map(Arc::new);
+
+        // Create a new task handle.
+        let task = Task::new(name);
+
+        #[cfg(not(target_os = "unknown"))]
+        once_cell::sync::Lazy::force(&crate::rt::RUNTIME);
+
+        let tag = TaskLocalsWrapper::new(task);
+
+        SupportTaskLocals { tag, future }
+    }
+
     /// Spawns a task with the configured settings.
+    #[cfg(not(target_os = "unknown"))]
     pub fn spawn<F, T>(self, future: F) -> io::Result<JoinHandle<T>>
     where
         F: Future<Output = T> + Send + 'static,
         T: Send + 'static,
     {
-        Ok(pool::get().spawn(future, self))
+        let wrapped = self.build(future);
+
+        kv_log_macro::trace!("spawn", {
+            task_id: wrapped.tag.id().0,
+            parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0),
+        });
+
+        let task = wrapped.tag.task().clone();
+        let handle = async_global_executor::spawn(wrapped);
+
+        Ok(JoinHandle::new(handle, task))
+    }
+
+    /// Spawns a task locally with the configured settings.
+    #[cfg(all(not(target_os = "unknown"), feature = "unstable"))]
+    pub fn local<F, T>(self, future: F) -> io::Result<JoinHandle<T>>
+    where
+        F: Future<Output = T> + 'static,
+        T: 'static,
+    {
+        let wrapped = self.build(future);
+
+        kv_log_macro::trace!("spawn_local", {
+            task_id: wrapped.tag.id().0,
+            parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0),
+        });
+
+        let task = wrapped.tag.task().clone();
+        let handle = async_global_executor::spawn_local(wrapped);
+
+        Ok(JoinHandle::new(handle, task))
+    }
+
+    /// Spawns a task locally with the configured settings.
+    #[cfg(all(target_arch = "wasm32", feature = "unstable"))]
+    pub fn local<F, T>(self, future: F) -> io::Result<JoinHandle<T>>
+    where
+        F: Future<Output = T> + 'static,
+        T: 'static,
+    {
+        use futures_channel::oneshot::channel;
+        let (sender, receiver) = channel();
+
+        let wrapped = self.build(async move {
+            let res = future.await;
+            let _ = sender.send(res);
+        });
+        kv_log_macro::trace!("spawn_local", {
+            task_id: wrapped.tag.id().0,
+            parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0),
+        });
+
+        let task = wrapped.tag.task().clone();
+        wasm_bindgen_futures::spawn_local(wrapped);
+
+        Ok(JoinHandle::new(receiver, task))
+    }
+
+    /// Spawns a task locally with the configured settings.
+    #[cfg(all(target_arch = "wasm32", not(feature = "unstable")))]
+    pub(crate) fn local<F, T>(self, future: F) -> io::Result<JoinHandle<T>>
+    where
+        F: Future<Output = T> + 'static,
+        T: 'static,
+    {
+        use futures_channel::oneshot::channel;
+        let (sender, receiver) = channel();
+
+        let wrapped = self.build(async move {
+            let res = future.await;
+            let _ = sender.send(res);
+        });
+
+        kv_log_macro::trace!("spawn_local", {
+            task_id: wrapped.tag.id().0,
+            parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0),
+        });
+
+        let task = wrapped.tag.task().clone();
+        wasm_bindgen_futures::spawn_local(wrapped);
+
+        Ok(JoinHandle::new(receiver, task))
+    }
+
+    /// Spawns a task with the configured settings, blocking on its execution.
+    #[cfg(not(target_os = "unknown"))]
+    pub fn blocking<F, T>(self, future: F) -> T
+    where
+        F: Future<Output = T>,
+    {
+        use std::cell::Cell;
+
+        let wrapped = self.build(future);
+
+        // Log this `block_on` operation.
+        kv_log_macro::trace!("block_on", {
+            task_id: wrapped.tag.id().0,
+            parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0),
+        });
+
+        thread_local! {
+            /// Tracks the number of nested block_on calls.
+            static NUM_NESTED_BLOCKING: Cell<usize> = Cell::new(0);
+        }
+
+        // Run the future as a task.
+        NUM_NESTED_BLOCKING.with(|num_nested_blocking| {
+            let count = num_nested_blocking.get();
+            let should_run = count == 0;
+            // increase the count
+            num_nested_blocking.replace(count + 1);
+
+            unsafe {
+                TaskLocalsWrapper::set_current(&wrapped.tag, || {
+                    let res = if should_run {
+                        // The first call should run the executor
+                        async_global_executor::block_on(wrapped)
+                    } else {
+                        futures_lite::future::block_on(wrapped)
+                    };
+                    num_nested_blocking.replace(num_nested_blocking.get() - 1);
+                    res
+                })
+            }
+        })
+    }
+}
+
+pin_project! {
+    /// Wrapper to add support for task locals.
+    struct SupportTaskLocals<F> {
+        tag: TaskLocalsWrapper,
+        #[pin]
+        future: F,
+    }
+}
+
+impl<F: Future> Future for SupportTaskLocals<F> {
+    type Output = F::Output;
+
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        unsafe {
+            TaskLocalsWrapper::set_current(&self.tag, || {
+                let this = self.project();
+                this.future.poll(cx)
+            })
+        }
     }
 }
diff --git a/src/task/current.rs b/src/task/current.rs
new file mode 100644
index 000000000..ad354d629
--- /dev/null
+++ b/src/task/current.rs
@@ -0,0 +1,48 @@
+use crate::task::{Task, TaskLocalsWrapper};
+
+/// Returns a handle to the current task.
+///
+/// # Panics
+///
+/// This function will panic if not called within the context of a task created by [`block_on`],
+/// [`spawn`], or [`Builder::spawn`].
+///
+/// [`block_on`]: fn.block_on.html
+/// [`spawn`]: fn.spawn.html
+/// [`Builder::spawn`]: struct.Builder.html#method.spawn
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::task;
+///
+/// println!("The name of this task is {:?}", task::current().name());
+/// #
+/// # })
+/// ```
+pub fn current() -> Task {
+    try_current().expect("`task::current()` called outside the context of a task")
+}
+
+/// Returns a handle to the current task if called within the context of a task created by [`block_on`],
+/// [`spawn`], or [`Builder::spawn`], otherwise returns `None`.
+///
+/// [`block_on`]: fn.block_on.html
+/// [`spawn`]: fn.spawn.html
+/// [`Builder::spawn`]: struct.Builder.html#method.spawn
+///
+/// # Examples
+///
+/// ```
+/// use async_std::task;
+///
+/// match task::try_current() {
+///     Some(t) => println!("The name of this task is {:?}", t.name()),
+///     None    => println!("Not inside a task!"),
+/// }
+/// ```
+pub fn try_current() -> Option<Task> {
+    TaskLocalsWrapper::get_current(|t| t.task().clone())
+}
\ No newline at end of file
diff --git a/src/task/join_handle.rs b/src/task/join_handle.rs
new file mode 100644
index 000000000..9fbab44c7
--- /dev/null
+++ b/src/task/join_handle.rs
@@ -0,0 +1,94 @@
+use std::future::Future;
+use std::pin::Pin;
+
+use crate::task::{Context, Poll, Task};
+
+/// A handle that awaits the result of a task.
+///
+/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer
+/// a handle to the task and no way to `join` on it.
+///
+/// Created when a task is [spawned].
+///
+/// [spawned]: fn.spawn.html
+#[derive(Debug)]
+pub struct JoinHandle<T> {
+    handle: Option<InnerHandle<T>>,
+    task: Task,
+}
+
+#[cfg(not(target_os = "unknown"))]
+type InnerHandle<T> = async_global_executor::Task<T>;
+#[cfg(target_arch = "wasm32")]
+type InnerHandle<T> = futures_channel::oneshot::Receiver<T>;
+
+impl<T> JoinHandle<T> {
+    /// Creates a new `JoinHandle`.
+    pub(crate) fn new(inner: InnerHandle<T>, task: Task) -> JoinHandle<T> {
+        JoinHandle {
+            handle: Some(inner),
+            task,
+        }
+    }
+
+    /// Returns a handle to the underlying task.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # async_std::task::block_on(async {
+    /// #
+    /// use async_std::task;
+    ///
+    /// let handle = task::spawn(async {
+    ///     1 + 2
+    /// });
+    /// println!("id = {}", handle.task().id());
+    /// #
+    /// # })
+    pub fn task(&self) -> &Task {
+        &self.task
+    }
+
+    /// Cancel this task.
+    #[cfg(not(target_os = "unknown"))]
+    pub async fn cancel(mut self) -> Option<T> {
+        let handle = self.handle.take().unwrap();
+        handle.cancel().await
+    }
+
+    /// Cancel this task.
+    #[cfg(target_arch = "wasm32")]
+    pub async fn cancel(mut self) -> Option<T> {
+        let mut handle = self.handle.take().unwrap();
+        handle.close();
+        handle.await.ok()
+    }
+}
+
+#[cfg(not(target_os = "unknown"))]
+impl<T> Drop for JoinHandle<T> {
+    fn drop(&mut self) {
+        if let Some(handle) = self.handle.take() {
+            handle.detach();
+        }
+    }
+}
+
+impl<T> Future for JoinHandle<T> {
+    type Output = T;
+
+    #[cfg(not(target_os = "unknown"))]
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx)
+    }
+
+    #[cfg(target_arch = "wasm32")]
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        match Pin::new(&mut self.handle.as_mut().unwrap()).poll(cx) {
+            Poll::Ready(Ok(t)) => Poll::Ready(t),
+            Poll::Ready(Err(_)) => unreachable!("channel must not be canceled"),
+            Poll::Pending => Poll::Pending,
+        }
+    }
+}
diff --git a/src/task/mod.rs b/src/task/mod.rs
index 9e92f35dd..0eda72b71 100644
--- a/src/task/mod.rs
+++ b/src/task/mod.rs
@@ -1,50 +1,172 @@
-//! Asynchronous tasks.
+//! Types and traits for working with asynchronous tasks.
 //!
 //! This module is similar to [`std::thread`], except it uses asynchronous tasks in place of
 //! threads.
 //!
-//! [`std::thread`]: https://doc.rust-lang.org/std/thread/index.html
+//! [`std::thread`]: https://doc.rust-lang.org/std/thread
 //!
-//! # Examples
+//! ## The task model
 //!
-//! Spawn a task and await its result:
+//! An executing asynchronous Rust program consists of a collection of native OS threads, on top of
+//! which multiple stackless coroutines are multiplexed. We refer to these as "tasks".  Tasks can
+//! be named, and provide some built-in support for synchronization.
+//!
+//! Communication between tasks can be done through channels, Rust's message-passing types, along
+//! with [other forms of tasks synchronization](../sync/index.html) and shared-memory data
+//! structures. In particular, types that are guaranteed to be threadsafe are easily shared between
+//! tasks using the atomically-reference-counted container, [`Arc`].
+//!
+//! Fatal logic errors in Rust cause *thread panic*, during which a thread will unwind the stack,
+//! running destructors and freeing owned resources. If a panic occurs inside a task, there is no
+//! meaningful way of recovering, so the panic will propagate through any thread boundaries all the
+//! way to the root task. This is also known as a "panic = abort" model.
+//!
+//! ## Spawning a task
+//!
+//! A new task can be spawned using the [`task::spawn`][`spawn`] function:
+//!
+//! ```no_run
+//! use async_std::task;
+//!
+//! task::spawn(async {
+//!     // some work here
+//! });
+//! ```
+//!
+//! In this example, the spawned task is "detached" from the current task. This means that it can
+//! outlive its parent (the task that spawned it), unless this parent is the root task.
+//!
+//! The root task can also wait on the completion of the child task; a call to [`spawn`] produces a
+//! [`JoinHandle`], which implements `Future` and can be `await`ed:
 //!
 //! ```
-//! # fn main() { async_std::task::block_on(async {
+//! use async_std::task;
+//!
+//! # async_std::task::block_on(async {
+//! #
+//! let child = task::spawn(async {
+//!     // some work here
+//! });
+//! // some work here
+//! let res = child.await;
 //! #
+//! # })
+//! ```
+//!
+//! The `await` operator returns the final value produced by the child task.
+//!
+//! ## Configuring tasks
+//!
+//! A new task can be configured before it is spawned via the [`Builder`] type,
+//! which currently allows you to set the name for the child task:
+//!
+//! ```
+//! # #![allow(unused_must_use)]
 //! use async_std::task;
 //!
-//! let handle = task::spawn(async {
-//!     1 + 2
+//! # async_std::task::block_on(async {
+//! #
+//! task::Builder::new().name("child1".to_string()).spawn(async {
+//!     println!("Hello, world!");
 //! });
-//! assert_eq!(handle.await, 3);
 //! #
-//! # }) }
+//! # })
 //! ```
+//!
+//! ## The `Task` type
+//!
+//! Tasks are represented via the [`Task`] type, which you can get in one of
+//! two ways:
+//!
+//! * By spawning a new task, e.g., using the [`task::spawn`][`spawn`]
+//!   function, and calling [`task`][`JoinHandle::task`] on the [`JoinHandle`].
+//! * By requesting the current task, using the [`task::current`] function.
+//!
+//! ## Task-local storage
+//!
+//! This module also provides an implementation of task-local storage for Rust
+//! programs. Task-local storage is a method of storing data into a global
+//! variable that each task in the program will have its own copy of.
+//! Tasks do not share this data, so accesses do not need to be synchronized.
+//!
+//! A task-local key owns the value it contains and will destroy the value when the
+//! task exits. It is created with the [`task_local!`] macro and can contain any
+//! value that is `'static` (no borrowed pointers). It provides an accessor function,
+//! [`with`], that yields a shared reference to the value to the specified
+//! closure. Task-local keys allow only shared access to values, as there would be no
+//! way to guarantee uniqueness if mutable borrows were allowed.
+//!
+//! ## Naming tasks
+//!
+//! Tasks are able to have associated names for identification purposes. By default, spawned
+//! tasks are unnamed. To specify a name for a task, build the task with [`Builder`] and pass
+//! the desired task name to [`Builder::name`]. To retrieve the task name from within the
+//! task, use [`Task::name`].
+//!
+//! [`Arc`]: ../sync/struct.Arc.html
+//! [`spawn`]: fn.spawn.html
+//! [`JoinHandle`]: struct.JoinHandle.html
+//! [`JoinHandle::task`]: struct.JoinHandle.html#method.task
+//! [`join`]: struct.JoinHandle.html#method.join
+//! [`panic!`]: https://doc.rust-lang.org/std/macro.panic.html
+//! [`Builder`]: struct.Builder.html
+//! [`Builder::name`]: struct.Builder.html#method.name
+//! [`task::current`]: fn.current.html
+//! [`Task`]: struct.Task.html
+//! [`Task::name`]: struct.Task.html#method.name
+//! [`task_local!`]: ../macro.task_local.html
+//! [`with`]: struct.LocalKey.html#method.with
+
+cfg_alloc! {
+    #[doc(inline)]
+    pub use core::task::{Context, Poll, Waker};
+    pub use ready::ready;
+
+    mod ready;
+}
+
+cfg_std! {
+    pub use yield_now::yield_now;
+    mod yield_now;
+}
+
+cfg_default! {
+    pub use block_on::block_on;
+    pub use builder::Builder;
+    pub use current::{current, try_current};
+    pub use task::Task;
+    pub use task_id::TaskId;
+    pub use join_handle::JoinHandle;
+    pub use sleep::sleep;
+    #[cfg(not(target_os = "unknown"))]
+    pub use spawn::spawn;
+    pub use task_local::{AccessError, LocalKey};
+
+    pub(crate) use task_local::LocalsMap;
+    pub(crate) use task_locals_wrapper::TaskLocalsWrapper;
+
+    mod block_on;
+    mod builder;
+    mod current;
+    mod join_handle;
+    mod sleep;
+    #[cfg(not(target_os = "unknown"))]
+    mod spawn;
+    #[cfg(not(target_os = "unknown"))]
+    mod spawn_blocking;
+    mod task;
+    mod task_id;
+    mod task_local;
+    mod task_locals_wrapper;
+
+    #[cfg(not(target_os = "unknown"))]
+    pub use spawn_blocking::spawn_blocking;
+}
+
+cfg_unstable! {
+    #[cfg(feature = "default")]
+    pub use spawn_local::spawn_local;
 
-#[doc(inline)]
-pub use std::task::{Context, Poll, Waker};
-
-#[cfg(feature = "unstable")]
-#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
-#[doc(inline)]
-pub use async_macros::ready;
-
-pub use block_on::block_on;
-pub use builder::Builder;
-pub use pool::spawn;
-pub use sleep::sleep;
-pub use task::{JoinHandle, Task, TaskId};
-pub use task_local::{AccessError, LocalKey};
-pub use worker::current;
-
-mod block_on;
-mod builder;
-mod pool;
-mod sleep;
-mod sleepers;
-mod task;
-mod task_local;
-mod worker;
-
-pub(crate) mod blocking;
+    #[cfg(feature = "default")]
+    mod spawn_local;
+}
diff --git a/src/task/pool.rs b/src/task/pool.rs
deleted file mode 100644
index 49fbfe490..000000000
--- a/src/task/pool.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-use std::iter;
-use std::thread;
-
-use crossbeam_deque::{Injector, Stealer, Worker};
-use kv_log_macro::trace;
-use lazy_static::lazy_static;
-
-use super::sleepers::Sleepers;
-use super::task;
-use super::task_local;
-use super::worker;
-use super::{Builder, JoinHandle};
-use crate::future::Future;
-use crate::utils::abort_on_panic;
-
-/// Spawns a task.
-///
-/// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task.
-///
-/// [`std::thread`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
-///
-/// # Examples
-///
-/// ```
-/// # fn main() { async_std::task::block_on(async {
-/// #
-/// use async_std::task;
-///
-/// let handle = task::spawn(async {
-///     1 + 2
-/// });
-///
-/// assert_eq!(handle.await, 3);
-/// #
-/// # }) }
-/// ```
-pub fn spawn<F, T>(future: F) -> JoinHandle<T>
-where
-    F: Future<Output = T> + Send + 'static,
-    T: Send + 'static,
-{
-    Builder::new().spawn(future).expect("cannot spawn future")
-}
-
-pub(crate) struct Pool {
-    pub injector: Injector<task::Runnable>,
-    pub stealers: Vec<Stealer<task::Runnable>>,
-    pub sleepers: Sleepers,
-}
-
-impl Pool {
-    /// Spawn a future onto the pool.
-    pub fn spawn<F, T>(&self, future: F, builder: Builder) -> JoinHandle<T>
-    where
-        F: Future<Output = T> + Send + 'static,
-        T: Send + 'static,
-    {
-        let tag = task::Tag::new(builder.name);
-
-        // Log this `spawn` operation.
-        let child_id = tag.task_id().as_u64();
-        let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0);
-
-        trace!("spawn", {
-            parent_id: parent_id,
-            child_id: child_id,
-        });
-
-        // Wrap the future into one that drops task-local variables on exit.
-        let future = unsafe { task_local::add_finalizer(future) };
-
-        // Wrap the future into one that logs completion on exit.
-        let future = async move {
-            let res = future.await;
-            trace!("spawn completed", {
-                parent_id: parent_id,
-                child_id: child_id,
-            });
-            res
-        };
-
-        let (task, handle) = async_task::spawn(future, worker::schedule, tag);
-        task.schedule();
-        JoinHandle::new(handle)
-    }
-
-    /// Find the next runnable task to run.
-    pub fn find_task(&self, local: &Worker<task::Runnable>) -> Option<task::Runnable> {
-        // Pop a task from the local queue, if not empty.
-        local.pop().or_else(|| {
-            // Otherwise, we need to look for a task elsewhere.
-            iter::repeat_with(|| {
-                // Try stealing a batch of tasks from the injector queue.
-                self.injector
-                    .steal_batch_and_pop(local)
-                    // Or try stealing a bach of tasks from one of the other threads.
-                    .or_else(|| {
-                        self.stealers
-                            .iter()
-                            .map(|s| s.steal_batch_and_pop(local))
-                            .collect()
-                    })
-            })
-            // Loop while no task was stolen and any steal operation needs to be retried.
-            .find(|s| !s.is_retry())
-            // Extract the stolen task, if there is one.
-            .and_then(|s| s.success())
-        })
-    }
-}
-
-#[inline]
-pub(crate) fn get() -> &'static Pool {
-    lazy_static! {
-        static ref POOL: Pool = {
-            let num_threads = num_cpus::get().max(1);
-            let mut stealers = Vec::new();
-
-            // Spawn worker threads.
-            for _ in 0..num_threads {
-                let worker = Worker::new_fifo();
-                stealers.push(worker.stealer());
-
-                thread::Builder::new()
-                    .name("async-task-driver".to_string())
-                    .spawn(|| abort_on_panic(|| worker::main_loop(worker)))
-                    .expect("cannot start a thread driving tasks");
-            }
-
-            Pool {
-                injector: Injector::new(),
-                stealers,
-                sleepers: Sleepers::new(),
-            }
-        };
-    }
-    &*POOL
-}
diff --git a/src/task/ready.rs b/src/task/ready.rs
new file mode 100644
index 000000000..aca04aa7b
--- /dev/null
+++ b/src/task/ready.rs
@@ -0,0 +1,4 @@
+/// Extracts the successful type of a `Poll<T>`.
+///
+/// This macro bakes in propagation of `Pending` signals by returning early.
+pub use futures_core::ready;
diff --git a/src/task/sleep.rs b/src/task/sleep.rs
index 9db09ffe7..380ee3e6d 100644
--- a/src/task/sleep.rs
+++ b/src/task/sleep.rs
@@ -11,10 +11,14 @@ use crate::io;
 ///
 /// [`std::thread::sleep`]: https://doc.rust-lang.org/std/thread/fn.sleep.html
 ///
+/// See also: [`stream::interval`].
+///
+/// [`stream::interval`]: ../stream/fn.interval.html
+///
 /// # Examples
 ///
 /// ```
-/// # fn main() { async_std::task::block_on(async {
+/// # async_std::task::block_on(async {
 /// #
 /// use std::time::Duration;
 ///
@@ -22,7 +26,7 @@ use crate::io;
 ///
 /// task::sleep(Duration::from_secs(1)).await;
 /// #
-/// # }) }
+/// # })
 /// ```
 pub async fn sleep(dur: Duration) {
     let _: io::Result<()> = io::timeout(dur, future::pending()).await;
diff --git a/src/task/sleepers.rs b/src/task/sleepers.rs
deleted file mode 100644
index a9071758a..000000000
--- a/src/task/sleepers.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::{Condvar, Mutex};
-
-/// The place where worker threads go to sleep.
-///
-/// Similar to how thread parking works, if a notification comes up while no threads are sleeping,
-/// the next thread that attempts to go to sleep will pick up the notification immediately.
-pub struct Sleepers {
-    /// How many threads are currently a sleep.
-    sleep: Mutex<usize>,
-
-    /// A condvar for notifying sleeping threads.
-    wake: Condvar,
-
-    /// Set to `true` if a notification came up while nobody was sleeping.
-    notified: AtomicBool,
-}
-
-impl Sleepers {
-    /// Creates a new `Sleepers`.
-    pub fn new() -> Sleepers {
-        Sleepers {
-            sleep: Mutex::new(0),
-            wake: Condvar::new(),
-            notified: AtomicBool::new(false),
-        }
-    }
-
-    /// Puts the current thread to sleep.
-    pub fn wait(&self) {
-        let mut sleep = self.sleep.lock().unwrap();
-
-        if self.notified.swap(false, Ordering::SeqCst) == false {
-            *sleep += 1;
-            let _ = self.wake.wait(sleep).unwrap();
-        }
-    }
-
-    /// Notifies one thread.
-    pub fn notify_one(&self) {
-        if self.notified.load(Ordering::SeqCst) == false {
-            let mut sleep = self.sleep.lock().unwrap();
-
-            if *sleep > 0 {
-                *sleep -= 1;
-                self.wake.notify_one();
-            } else {
-                self.notified.store(true, Ordering::SeqCst);
-            }
-        }
-    }
-}
diff --git a/src/task/spawn.rs b/src/task/spawn.rs
new file mode 100644
index 000000000..f81a483d2
--- /dev/null
+++ b/src/task/spawn.rs
@@ -0,0 +1,32 @@
+use std::future::Future;
+
+use crate::task::{Builder, JoinHandle};
+
+/// Spawns a task.
+///
+/// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task.
+///
+/// [`std::thread`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
+///
+/// # Examples
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::task;
+///
+/// let handle = task::spawn(async {
+///     1 + 2
+/// });
+///
+/// assert_eq!(handle.await, 3);
+/// #
+/// # })
+/// ```
+pub fn spawn<F, T>(future: F) -> JoinHandle<T>
+where
+    F: Future<Output = T> + Send + 'static,
+    T: Send + 'static,
+{
+    Builder::new().spawn(future).expect("cannot spawn task")
+}
diff --git a/src/task/spawn_blocking.rs b/src/task/spawn_blocking.rs
new file mode 100644
index 000000000..2439c123f
--- /dev/null
+++ b/src/task/spawn_blocking.rs
@@ -0,0 +1,37 @@
+use crate::task::{self, JoinHandle};
+
+/// Spawns a blocking task.
+///
+/// The task will be spawned onto a thread pool specifically dedicated to blocking tasks. This
+/// is useful to prevent long-running synchronous operations from blocking the main futures
+/// executor.
+///
+/// See also: [`task::block_on`], [`task::spawn`].
+///
+/// [`task::block_on`]: fn.block_on.html
+/// [`task::spawn`]: fn.spawn.html
+///
+/// # Examples
+///
+/// Basic usage:
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::task;
+///
+/// task::spawn_blocking(|| {
+///     println!("long-running task here");
+/// })
+/// .await;
+/// #
+/// # })
+/// ```
+#[inline]
+pub fn spawn_blocking<F, T>(f: F) -> JoinHandle<T>
+where
+    F: FnOnce() -> T + Send + 'static,
+    T: Send + 'static,
+{
+    task::spawn(async_global_executor::spawn_blocking(f))
+}
diff --git a/src/task/spawn_local.rs b/src/task/spawn_local.rs
new file mode 100644
index 000000000..7e0ce6c8f
--- /dev/null
+++ b/src/task/spawn_local.rs
@@ -0,0 +1,31 @@
+use std::future::Future;
+
+use crate::task::{Builder, JoinHandle};
+
+/// Spawns a task onto the thread-local executor.
+///
+/// # Examples
+///
+/// ```
+/// # #[cfg(feature = "unstable")]
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::task;
+///
+/// let handle = task::spawn_local(async {
+///     1 + 2
+/// });
+///
+/// assert_eq!(handle.await, 3);
+/// #
+/// # })
+/// ```
+#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+#[inline]
+pub fn spawn_local<F, T>(future: F) -> JoinHandle<T>
+where
+    F: Future<Output = T> + 'static,
+    T: 'static,
+{
+    Builder::new().local(future).expect("cannot spawn task")
+}
diff --git a/src/task/task.rs b/src/task/task.rs
index 8a1addea4..eba99c752 100644
--- a/src/task/task.rs
+++ b/src/task/task.rs
@@ -1,31 +1,32 @@
 use std::fmt;
-use std::i64;
-use std::mem;
-use std::num::NonZeroU64;
-use std::pin::Pin;
-use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
 use std::sync::Arc;
 
-use super::task_local;
-use crate::future::Future;
-use crate::task::{Context, Poll};
+use crate::task::TaskId;
 
 /// A handle to a task.
 #[derive(Clone)]
-pub struct Task(Arc<Metadata>);
+pub struct Task {
+    /// The task ID.
+    id: TaskId,
 
-unsafe impl Send for Task {}
-unsafe impl Sync for Task {}
+    /// The optional task name.
+    name: Option<Arc<String>>,
+}
 
 impl Task {
-    /// Returns a reference to task metadata.
-    pub(crate) fn metadata(&self) -> &Metadata {
-        &self.0
+    /// Creates a new task handle.
+    #[inline]
+    pub(crate) fn new(name: Option<Arc<String>>) -> Task {
+        Task {
+            id: TaskId::generate(),
+            name,
+        }
     }
 
     /// Gets the task's unique identifier.
+    #[inline]
     pub fn id(&self) -> TaskId {
-        self.metadata().task_id
+        self.id
     }
 
     /// Returns the name of this task.
@@ -34,176 +35,15 @@ impl Task {
     ///
     /// [`Builder::name`]: struct.Builder.html#method.name
     pub fn name(&self) -> Option<&str> {
-        self.metadata().name.as_ref().map(|s| s.as_str())
+        self.name.as_ref().map(|s| s.as_str())
     }
 }
 
 impl fmt::Debug for Task {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Task").field("name", &self.name()).finish()
-    }
-}
-
-/// A handle that awaits the result of a task.
-///
-/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer
-/// a handle to the task and no way to `join` on it.
-///
-/// Created when a task is [spawned].
-///
-/// [spawned]: fn.spawn.html
-#[derive(Debug)]
-pub struct JoinHandle<T>(async_task::JoinHandle<T, Tag>);
-
-unsafe impl<T> Send for JoinHandle<T> {}
-unsafe impl<T> Sync for JoinHandle<T> {}
-
-impl<T> JoinHandle<T> {
-    pub(crate) fn new(inner: async_task::JoinHandle<T, Tag>) -> JoinHandle<T> {
-        JoinHandle(inner)
-    }
-
-    /// Returns a handle to the underlying task.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// # fn main() { async_std::task::block_on(async {
-    /// #
-    /// use async_std::task;
-    ///
-    /// let handle = task::spawn(async {
-    ///     1 + 2
-    /// });
-    /// println!("id = {}", handle.task().id());
-    /// #
-    /// # }) }
-    pub fn task(&self) -> &Task {
-        self.0.tag().task()
-    }
-}
-
-impl<T> Future for JoinHandle<T> {
-    type Output = T;
-
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match Pin::new(&mut self.0).poll(cx) {
-            Poll::Pending => Poll::Pending,
-            Poll::Ready(None) => panic!("task has panicked"),
-            Poll::Ready(Some(val)) => Poll::Ready(val),
-        }
-    }
-}
-
-/// A unique identifier for a task.
-///
-/// # Examples
-///
-/// ```
-/// #
-/// use async_std::task;
-///
-/// task::block_on(async {
-///     println!("id = {:?}", task::current().id());
-/// })
-/// ```
-#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
-pub struct TaskId(NonZeroU64);
-
-impl TaskId {
-    pub(crate) fn new() -> TaskId {
-        static COUNTER: AtomicU64 = AtomicU64::new(1);
-
-        let id = COUNTER.fetch_add(1, Ordering::Relaxed);
-
-        if id > i64::MAX as u64 {
-            std::process::abort();
-        }
-        unsafe { TaskId(NonZeroU64::new_unchecked(id)) }
-    }
-
-    pub(crate) fn as_u64(&self) -> u64 {
-        self.0.get()
-    }
-}
-
-impl fmt::Display for TaskId {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.0)
-    }
-}
-
-pub(crate) type Runnable = async_task::Task<Tag>;
-
-pub(crate) struct Metadata {
-    pub task_id: TaskId,
-    pub name: Option<String>,
-    pub local_map: task_local::Map,
-}
-
-pub(crate) struct Tag {
-    task_id: TaskId,
-    raw_metadata: AtomicUsize,
-}
-
-impl Tag {
-    pub fn new(name: Option<String>) -> Tag {
-        let task_id = TaskId::new();
-
-        let opt_task = name.map(|name| {
-            Task(Arc::new(Metadata {
-                task_id,
-                name: Some(name),
-                local_map: task_local::Map::new(),
-            }))
-        });
-
-        Tag {
-            task_id,
-            raw_metadata: AtomicUsize::new(unsafe {
-                mem::transmute::<Option<Task>, usize>(opt_task)
-            }),
-        }
-    }
-
-    pub fn task(&self) -> &Task {
-        unsafe {
-            let raw = self.raw_metadata.load(Ordering::Acquire);
-
-            if mem::transmute::<&usize, &Option<Task>>(&raw).is_none() {
-                let new = Some(Task(Arc::new(Metadata {
-                    task_id: TaskId::new(),
-                    name: None,
-                    local_map: task_local::Map::new(),
-                })));
-
-                let new_raw = mem::transmute::<Option<Task>, usize>(new);
-
-                if self
-                    .raw_metadata
-                    .compare_exchange(raw, new_raw, Ordering::AcqRel, Ordering::Acquire)
-                    .is_err()
-                {
-                    let new = mem::transmute::<usize, Option<Task>>(new_raw);
-                    drop(new);
-                }
-            }
-
-            mem::transmute::<&AtomicUsize, &Option<Task>>(&self.raw_metadata)
-                .as_ref()
-                .unwrap()
-        }
-    }
-
-    pub fn task_id(&self) -> TaskId {
-        self.task_id
-    }
-}
-
-impl Drop for Tag {
-    fn drop(&mut self) {
-        let raw = *self.raw_metadata.get_mut();
-        let opt_task = unsafe { mem::transmute::<usize, Option<Task>>(raw) };
-        drop(opt_task);
+        f.debug_struct("Task")
+            .field("id", &self.id())
+            .field("name", &self.name())
+            .finish()
     }
 }
diff --git a/src/task/task_id.rs b/src/task/task_id.rs
new file mode 100644
index 000000000..92c607c71
--- /dev/null
+++ b/src/task/task_id.rs
@@ -0,0 +1,36 @@
+use std::fmt;
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+/// A unique identifier for a task.
+///
+/// # Examples
+///
+/// ```
+/// use async_std::task;
+///
+/// task::block_on(async {
+///     println!("id = {:?}", task::current().id());
+/// })
+/// ```
+#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
+pub struct TaskId(pub(crate) usize);
+
+impl TaskId {
+    /// Generates a new `TaskId`.
+    pub(crate) fn generate() -> TaskId {
+        // TODO: find a good version to emulate u64 atomics on 32 bit systems.
+        static COUNTER: AtomicUsize = AtomicUsize::new(1);
+
+        let id = COUNTER.fetch_add(1, Ordering::Relaxed);
+        if id > usize::max_value() / 2 {
+            std::process::abort();
+        }
+        TaskId(id)
+    }
+}
+
+impl fmt::Display for TaskId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
diff --git a/src/task/task_local.rs b/src/task/task_local.rs
index 8347e34b6..1661c0bb9 100644
--- a/src/task/task_local.rs
+++ b/src/task/task_local.rs
@@ -1,65 +1,9 @@
 use std::cell::UnsafeCell;
 use std::error::Error;
 use std::fmt;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::Mutex;
+use std::sync::atomic::{AtomicU32, Ordering};
 
-use lazy_static::lazy_static;
-
-use super::worker;
-use crate::future::Future;
-use crate::utils::abort_on_panic;
-
-/// Declares task-local values.
-///
-/// The macro wraps any number of static declarations and makes them task-local. Attributes and
-/// visibility modifiers are allowed.
-///
-/// Each declared value is of the accessor type [`LocalKey`].
-///
-/// [`LocalKey`]: task/struct.LocalKey.html
-///
-/// # Examples
-///
-/// ```
-/// #
-/// use std::cell::Cell;
-///
-/// use async_std::task;
-/// use async_std::prelude::*;
-///
-/// task_local! {
-///     static VAL: Cell<u32> = Cell::new(5);
-/// }
-///
-/// task::block_on(async {
-///     let v = VAL.with(|c| c.get());
-///     assert_eq!(v, 5);
-/// });
-/// ```
-#[macro_export]
-macro_rules! task_local {
-    () => ();
-
-    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
-        $(#[$attr])* $vis static $name: $crate::task::LocalKey<$t> = {
-            #[inline]
-            fn __init() -> $t {
-                $init
-            }
-
-            $crate::task::LocalKey {
-                __init,
-                __key: ::std::sync::atomic::AtomicUsize::new(0),
-            }
-        };
-    );
-
-    ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
-        $crate::task_local!($(#[$attr])* $vis static $name: $t = $init);
-        $crate::task_local!($($rest)*);
-    );
-}
+use crate::task::TaskLocalsWrapper;
 
 /// The key for accessing a task-local value.
 ///
@@ -71,7 +15,7 @@ pub struct LocalKey<T: Send + 'static> {
     pub __init: fn() -> T,
 
     #[doc(hidden)]
-    pub __key: AtomicUsize,
+    pub __key: AtomicU32,
 }
 
 impl<T: Send + 'static> LocalKey<T> {
@@ -154,14 +98,13 @@ impl<T: Send + 'static> LocalKey<T> {
     where
         F: FnOnce(&T) -> R,
     {
-        worker::get_task(|task| unsafe {
+        TaskLocalsWrapper::get_current(|task| unsafe {
             // Prepare the numeric key, initialization function, and the map of task-locals.
             let key = self.key();
             let init = || Box::new((self.__init)()) as Box<dyn Send>;
-            let map = &task.metadata().local_map;
 
             // Get the value in the map of task-locals, or initialize and insert one.
-            let value: *const dyn Send = map.get_or_insert(key, init);
+            let value: *const dyn Send = task.locals().get_or_insert(key, init);
 
             // Call the closure with the value passed as an argument.
             f(&*(value as *const T))
@@ -171,26 +114,26 @@ impl<T: Send + 'static> LocalKey<T> {
 
     /// Returns the numeric key associated with this task-local.
     #[inline]
-    fn key(&self) -> usize {
+    fn key(&self) -> u32 {
         #[cold]
-        fn init(key: &AtomicUsize) -> usize {
-            lazy_static! {
-                static ref COUNTER: Mutex<usize> = Mutex::new(1);
-            }
+        fn init(key: &AtomicU32) -> u32 {
+            static COUNTER: AtomicU32 = AtomicU32::new(1);
 
-            let mut counter = COUNTER.lock().unwrap();
-            let prev = key.compare_and_swap(0, *counter, Ordering::AcqRel);
+            let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
+            if counter > u32::max_value() / 2 {
+                std::process::abort();
+            }
 
-            if prev == 0 {
-                *counter += 1;
-                *counter - 1
-            } else {
-                prev
+            match key.compare_exchange(0, counter, Ordering::AcqRel, Ordering::Acquire) {
+                Ok(_) => counter,
+                Err(k) => k,
             }
         }
 
-        let key = self.__key.load(Ordering::Acquire);
-        if key == 0 { init(&self.__key) } else { key }
+        match self.__key.load(Ordering::Acquire) {
+            0 => init(&self.__key),
+            k => k,
+        }
     }
 }
 
@@ -216,51 +159,55 @@ impl fmt::Display for AccessError {
 
 impl Error for AccessError {}
 
+/// A key-value entry in a map of task-locals.
+struct Entry {
+    /// Key identifying the task-local variable.
+    key: u32,
+
+    /// Value stored in this entry.
+    value: Box<dyn Send>,
+}
+
 /// A map that holds task-locals.
-pub(crate) struct Map {
-    /// A list of `(key, value)` entries sorted by the key.
-    entries: UnsafeCell<Vec<(usize, Box<dyn Send>)>>,
+pub(crate) struct LocalsMap {
+    /// A list of key-value entries sorted by the key.
+    entries: UnsafeCell<Option<Vec<Entry>>>,
 }
 
-impl Map {
+impl LocalsMap {
     /// Creates an empty map of task-locals.
-    pub fn new() -> Map {
-        Map {
-            entries: UnsafeCell::new(Vec::new()),
+    pub fn new() -> LocalsMap {
+        LocalsMap {
+            entries: UnsafeCell::new(Some(Vec::new())),
         }
     }
 
-    /// Returns a thread-local value associated with `key` or inserts one constructed by `init`.
+    /// Returns a task-local value associated with `key` or inserts one constructed by `init`.
     #[inline]
-    pub fn get_or_insert(&self, key: usize, init: impl FnOnce() -> Box<dyn Send>) -> &dyn Send {
-        let entries = unsafe { &mut *self.entries.get() };
-
-        let index = match entries.binary_search_by_key(&key, |e| e.0) {
-            Ok(i) => i,
-            Err(i) => {
-                entries.insert(i, (key, init()));
-                i
+    pub fn get_or_insert(&self, key: u32, init: impl FnOnce() -> Box<dyn Send>) -> &dyn Send {
+        match unsafe { (*self.entries.get()).as_mut() } {
+            None => panic!("can't access task-locals while the task is being dropped"),
+            Some(entries) => {
+                let index = match entries.binary_search_by_key(&key, |e| e.key) {
+                    Ok(i) => i,
+                    Err(i) => {
+                        let value = init();
+                        entries.insert(i, Entry { key, value });
+                        i
+                    }
+                };
+                &*entries[index].value
             }
-        };
-
-        &*entries[index].1
+        }
     }
 
     /// Clears the map and drops all task-locals.
-    pub fn clear(&self) {
-        let entries = unsafe { &mut *self.entries.get() };
-        entries.clear();
-    }
-}
-
-// Wrap the future into one that drops task-local variables on exit.
-pub(crate) unsafe fn add_finalizer<T>(f: impl Future<Output = T>) -> impl Future<Output = T> {
-    async move {
-        let res = f.await;
-
-        // Abort on panic because thread-local variables behave the same way.
-        abort_on_panic(|| worker::get_task(|task| task.metadata().local_map.clear()));
-
-        res
+    ///
+    /// This method is only safe to call at the end of the task.
+    pub unsafe fn clear(&self) {
+        // Since destructors may attempt to access task-locals, we musnt't hold a mutable reference
+        // to the `Vec` while dropping them. Instead, we first take the `Vec` out and then drop it.
+        let entries = (*self.entries.get()).take();
+        drop(entries);
     }
 }
diff --git a/src/task/task_locals_wrapper.rs b/src/task/task_locals_wrapper.rs
new file mode 100644
index 000000000..2a7ddb7af
--- /dev/null
+++ b/src/task/task_locals_wrapper.rs
@@ -0,0 +1,84 @@
+use std::cell::Cell;
+use std::ptr;
+
+use crate::task::{LocalsMap, Task, TaskId};
+use crate::utils::abort_on_panic;
+
+thread_local! {
+    /// A pointer to the currently running task.
+    static CURRENT: Cell<*const TaskLocalsWrapper> = Cell::new(ptr::null_mut());
+}
+
+/// A wrapper to store task local data.
+pub(crate) struct TaskLocalsWrapper {
+    /// The actual task details.
+    task: Task,
+
+    /// The map holding task-local values.
+    locals: LocalsMap,
+}
+
+impl TaskLocalsWrapper {
+    /// Creates a new task handle.
+    ///
+    /// If the task is unnamed, the inner representation of the task will be lazily allocated on
+    /// demand.
+    #[inline]
+    pub(crate) fn new(task: Task) -> Self {
+        Self {
+            task,
+            locals: LocalsMap::new(),
+        }
+    }
+
+    /// Gets the task's unique identifier.
+    #[inline]
+    pub fn id(&self) -> TaskId {
+        self.task.id()
+    }
+
+    /// Returns a reference to the inner `Task`.
+    pub(crate) fn task(&self) -> &Task {
+        &self.task
+    }
+
+    /// Returns the map holding task-local values.
+    pub(crate) fn locals(&self) -> &LocalsMap {
+        &self.locals
+    }
+
+    /// Set a reference to the current task.
+    pub(crate) unsafe fn set_current<F, R>(task: *const TaskLocalsWrapper, f: F) -> R
+    where
+        F: FnOnce() -> R,
+    {
+        CURRENT.with(|current| {
+            let old_task = current.replace(task);
+            defer! {
+                current.set(old_task);
+            }
+            f()
+        })
+    }
+
+    /// Gets a reference to the current task.
+    pub(crate) fn get_current<F, R>(f: F) -> Option<R>
+    where
+        F: FnOnce(&TaskLocalsWrapper) -> R,
+    {
+        let res = CURRENT.try_with(|current| unsafe { current.get().as_ref().map(f) });
+        match res {
+            Ok(Some(val)) => Some(val),
+            Ok(None) | Err(_) => None,
+        }
+    }
+}
+
+impl Drop for TaskLocalsWrapper {
+    fn drop(&mut self) {
+        // Abort the process if dropping task-locals panics.
+        abort_on_panic(|| {
+            unsafe { self.locals.clear() };
+        });
+    }
+}
diff --git a/src/task/worker.rs b/src/task/worker.rs
deleted file mode 100644
index fc2a6e7ef..000000000
--- a/src/task/worker.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-use std::cell::Cell;
-use std::ptr;
-
-use crossbeam_deque::Worker;
-
-use super::pool;
-use super::task;
-use super::Task;
-use crate::utils::abort_on_panic;
-
-/// Returns a handle to the current task.
-///
-/// # Panics
-///
-/// This function will panic if not called within the context of a task created by [`block_on`],
-/// [`spawn`], or [`Builder::spawn`].
-///
-/// [`block_on`]: fn.block_on.html
-/// [`spawn`]: fn.spawn.html
-/// [`Builder::spawn`]: struct.Builder.html#method.spawn
-///
-/// # Examples
-///
-/// ```
-/// # fn main() { async_std::task::block_on(async {
-/// #
-/// use async_std::task;
-///
-/// println!("The name of this task is {:?}", task::current().name());
-/// #
-/// # }) }
-/// ```
-pub fn current() -> Task {
-    get_task(|task| task.clone()).expect("`task::current()` called outside the context of a task")
-}
-
-thread_local! {
-    static TAG: Cell<*const task::Tag> = Cell::new(ptr::null_mut());
-}
-
-pub(crate) fn set_tag<F, R>(tag: *const task::Tag, f: F) -> R
-where
-    F: FnOnce() -> R,
-{
-    struct ResetTag<'a>(&'a Cell<*const task::Tag>);
-
-    impl Drop for ResetTag<'_> {
-        fn drop(&mut self) {
-            self.0.set(ptr::null());
-        }
-    }
-
-    TAG.with(|t| {
-        t.set(tag);
-        let _guard = ResetTag(t);
-
-        f()
-    })
-}
-
-pub(crate) fn get_task<F, R>(f: F) -> Option<R>
-where
-    F: FnOnce(&Task) -> R,
-{
-    let res = TAG.try_with(|tag| unsafe { tag.get().as_ref().map(task::Tag::task).map(f) });
-
-    match res {
-        Ok(Some(val)) => Some(val),
-        Ok(None) | Err(_) => None,
-    }
-}
-
-thread_local! {
-    static IS_WORKER: Cell<bool> = Cell::new(false);
-    static QUEUE: Cell<Option<Worker<task::Runnable>>> = Cell::new(None);
-}
-
-pub(crate) fn is_worker() -> bool {
-    IS_WORKER.with(|is_worker| is_worker.get())
-}
-
-fn get_queue<F: FnOnce(&Worker<task::Runnable>) -> T, T>(f: F) -> T {
-    QUEUE.with(|queue| {
-        let q = queue.take().unwrap();
-        let ret = f(&q);
-        queue.set(Some(q));
-        ret
-    })
-}
-
-pub(crate) fn schedule(task: task::Runnable) {
-    if is_worker() {
-        get_queue(|q| q.push(task));
-    } else {
-        pool::get().injector.push(task);
-    }
-    pool::get().sleepers.notify_one();
-}
-
-pub(crate) fn main_loop(worker: Worker<task::Runnable>) {
-    IS_WORKER.with(|is_worker| is_worker.set(true));
-    QUEUE.with(|queue| queue.set(Some(worker)));
-
-    loop {
-        match get_queue(|q| pool::get().find_task(q)) {
-            Some(task) => set_tag(task.tag(), || abort_on_panic(|| task.run())),
-            None => pool::get().sleepers.wait(),
-        }
-    }
-}
diff --git a/src/task/yield_now.rs b/src/task/yield_now.rs
new file mode 100644
index 000000000..2b1fd0b92
--- /dev/null
+++ b/src/task/yield_now.rs
@@ -0,0 +1,51 @@
+use std::future::Future;
+use std::pin::Pin;
+
+use crate::task::{Context, Poll};
+
+/// Cooperatively gives up a timeslice to the task scheduler.
+///
+/// Calling this function will move the currently executing future to the back
+/// of the execution queue, making room for other futures to execute. This is
+/// especially useful after running CPU-intensive operations inside a future.
+///
+/// See also [`task::spawn_blocking`].
+///
+/// [`task::spawn_blocking`]: fn.spawn_blocking.html
+///
+/// # Examples
+///
+/// Basic usage:
+///
+/// ```
+/// # async_std::task::block_on(async {
+/// #
+/// use async_std::task;
+///
+/// task::yield_now().await;
+/// #
+/// # })
+/// ```
+#[inline]
+pub async fn yield_now() {
+    YieldNow(false).await
+}
+
+struct YieldNow(bool);
+
+impl Future for YieldNow {
+    type Output = ();
+
+    // The futures executor is implemented as a FIFO queue, so all this future
+    // does is re-schedule the future back to the end of the queue, giving room
+    // for other futures to progress.
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        if !self.0 {
+            self.0 = true;
+            cx.waker().wake_by_ref();
+            Poll::Pending
+        } else {
+            Poll::Ready(())
+        }
+    }
+}
diff --git a/src/unit/extend.rs b/src/unit/extend.rs
new file mode 100644
index 000000000..ffc0c2d9d
--- /dev/null
+++ b/src/unit/extend.rs
@@ -0,0 +1,22 @@
+use std::pin::Pin;
+
+use crate::prelude::*;
+use crate::stream::{self, IntoStream};
+
+impl stream::Extend<()> for () {
+    fn extend<'a, S: IntoStream<Item = ()> + 'a>(
+        &'a mut self,
+        stream: S,
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>> 
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
+        let stream = stream.into_stream();
+
+        Box::pin(async move {
+            pin_utils::pin_mut!(stream);
+
+            while let Some(_) = stream.next().await {}
+        })
+    }
+}
diff --git a/src/unit/from_stream.rs b/src/unit/from_stream.rs
index a238982da..e88758323 100644
--- a/src/unit/from_stream.rs
+++ b/src/unit/from_stream.rs
@@ -5,12 +5,12 @@ use crate::stream::{FromStream, IntoStream};
 
 impl FromStream<()> for () {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = ()>>(
+    fn from_stream<'a, S: IntoStream<Item = ()> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send,
     {
-        Box::pin(stream.into_stream().for_each(|_| ()))
+        Box::pin(stream.into_stream().for_each(drop))
     }
 }
diff --git a/src/unit/mod.rs b/src/unit/mod.rs
index cb8063d0b..bbf28987d 100644
--- a/src/unit/mod.rs
+++ b/src/unit/mod.rs
@@ -4,3 +4,4 @@
 //! asynchronously with values of type `()`.
 
 mod from_stream;
+mod extend;
diff --git a/src/utils.rs b/src/utils.rs
index bdf0f3b5d..d1cb063bd 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,138 +1,250 @@
-use std::mem;
-use std::process;
-
 /// Calls a function and aborts if it panics.
 ///
 /// This is useful in unsafe code where we can't recover from panics.
+#[cfg(feature = "default")]
 #[inline]
 pub fn abort_on_panic<T>(f: impl FnOnce() -> T) -> T {
     struct Bomb;
 
     impl Drop for Bomb {
         fn drop(&mut self) {
-            process::abort();
+            std::process::abort();
         }
     }
 
     let bomb = Bomb;
     let t = f();
-    mem::forget(bomb);
+    std::mem::forget(bomb);
     t
 }
 
-/// Defines an extension trait for a base trait from the `futures` crate.
-///
-/// In generated docs, the base trait will contain methods from the extension trait. In actual
-/// code, the base trait will be re-exported and the extension trait will be hidden. We then
-/// re-export the extension trait from the prelude.
-///
-/// Inside invocations of this macro, we write a definitions that looks similar to the final
-/// rendered docs, and the macro then generates all the boilerplate for us.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! extension_trait {
-    (
-        // Interesting patterns:
-        // - `$name`: trait name that gets rendered in the docs
-        // - `$ext`: name of the hidden extension trait
-        // - `$base`: base trait from the `futures` crate
-        $(#[$attr:meta])*
-        pub trait $name:ident [$ext:ident: $base:path] {
-            $($body:tt)*
+/// Generates a random number in `0..n`.
+#[cfg(feature = "unstable")]
+pub fn random(n: u32) -> u32 {
+    use std::cell::Cell;
+    use std::num::Wrapping;
+
+    thread_local! {
+        static RNG: Cell<Wrapping<u32>> = {
+            // Take the address of a local value as seed.
+            let mut x = 0i32;
+            let r = &mut x;
+            let addr = r as *mut i32 as usize;
+            Cell::new(Wrapping(addr as u32))
         }
+    }
 
-        // Shim trait impls that only appear in docs.
-        $($imp:item)*
-    ) => {
-        // A fake `impl Future` type that doesn't borrow.
-        #[allow(dead_code)]
-        mod owned {
-            #[doc(hidden)]
-            pub struct ImplFuture<T>(std::marker::PhantomData<T>);
-        }
+    RNG.with(|rng| {
+        // This is the 32-bit variant of Xorshift.
+        //
+        // Source: https://en.wikipedia.org/wiki/Xorshift
+        let mut x = rng.get();
+        x ^= x << 13;
+        x ^= x >> 17;
+        x ^= x << 5;
+        rng.set(x);
+
+        // This is a fast alternative to `x % n`.
+        //
+        // Author: Daniel Lemire
+        // Source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
+        ((u64::from(x.0)).wrapping_mul(u64::from(n)) >> 32) as u32
+    })
+}
 
-        // A fake `impl Future` type that borrows its environment.
-        #[allow(dead_code)]
-        mod borrowed {
-            #[doc(hidden)]
-            pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>);
-        }
+/// Add additional context to errors
+#[cfg(feature = "std")]
+pub(crate) trait Context {
+    fn context(self, message: impl Fn() -> String) -> Self;
+}
+
+#[cfg(all(
+    not(target_os = "unknown"),
+    any(feature = "default", feature = "unstable")
+))]
+mod timer {
+    pub type Timer = async_io::Timer;
+}
+
+#[cfg(any(feature = "unstable", feature = "default"))]
+pub(crate) fn timer_after(dur: std::time::Duration) -> timer::Timer {
+    Timer::after(dur)
+}
+
+#[cfg(any(all(target_arch = "wasm32", feature = "default"),))]
+mod timer {
+    use std::pin::Pin;
+    use std::task::Poll;
+
+    use gloo_timers::future::TimeoutFuture;
+
+    #[derive(Debug)]
+    pub(crate) struct Timer(TimeoutFuture);
+
+    impl Timer {
+        pub(crate) fn after(dur: std::time::Duration) -> Self {
+            // Round up to the nearest millisecond.
+            let mut timeout_ms = dur.as_millis() as u32;
+            if std::time::Duration::from_millis(timeout_ms as u64) < dur {
+                timeout_ms += 1;
+            }
 
-        // Render a fake trait containing all methods from the base trait and the extension trait.
-        #[cfg(feature = "docs")]
-        $(#[$attr])*
-        pub trait $name {
-            extension_trait!(@doc () $($body)*);
+            Timer(TimeoutFuture::new(timeout_ms))
         }
+    }
 
-        // When not rendering docs, re-export the base trait from the futures crate.
-        #[cfg(not(feature = "docs"))]
-        pub use $base as $name;
+    impl std::future::Future for Timer {
+        type Output = ();
 
-        // The extension trait that adds methods to any type implementing the base trait.
-        $(#[$attr])*
-        pub trait $ext: $base {
-            extension_trait!(@ext () $($body)*);
+        fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
+            match Pin::new(&mut self.0).poll(cx) {
+                Poll::Pending => Poll::Pending,
+                Poll::Ready(_) => Poll::Ready(()),
+            }
         }
+    }
+}
 
-        // Blanket implementation of the extension trait for any type implementing the base trait.
-        impl<T: $base + ?Sized> $ext for T {}
+#[cfg(any(feature = "unstable", feature = "default"))]
+pub(crate) use timer::*;
 
-        // Shim trait impls that only appear in docs.
-        $(#[cfg(feature = "docs")] $imp)*
+/// Defers evaluation of a block of code until the end of the scope.
+#[cfg(feature = "default")]
+#[doc(hidden)]
+macro_rules! defer {
+    ($($body:tt)*) => {
+        let _guard = {
+            pub struct Guard<F: FnOnce()>(Option<F>);
+
+            impl<F: FnOnce()> Drop for Guard<F> {
+                fn drop(&mut self) {
+                    (self.0).take().map(|f| f());
+                }
+            }
+
+            Guard(Some(|| {
+                let _ = { $($body)* };
+            }))
+        };
     };
+}
 
-    // Parse an associated type.
-    (@doc ($($head:tt)*) type $name:ident; $($tail:tt)*) => {
-        extension_trait!(@doc ($($head)* type $name;) $($tail)*);
-    };
-    (@ext ($($head:tt)*) type $ident:ty; $($tail:tt)*) => {
-        extension_trait!(@ext ($($head)*) $($tail)*);
-    };
+/// Declares unstable items.
+#[doc(hidden)]
+macro_rules! cfg_unstable {
+    ($($item:item)*) => {
+        $(
+            #[cfg(feature = "unstable")]
+            #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+            $item
+        )*
+    }
+}
 
-    // Parse a required method.
-    (@doc ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)?; $($tail:tt)*) => {
-        extension_trait!(@doc ($($head)* fn $name $args $(-> $ret)?;) $($tail)*);
-    };
-    (@ext ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)?; $($tail:tt)*) => {
-        extension_trait!(@ext ($($head)*) $($tail)*);
-    };
+/// Declares unstable and default items.
+#[doc(hidden)]
+macro_rules! cfg_unstable_default {
+    ($($item:item)*) => {
+        $(
+            #[cfg(all(feature = "default", feature = "unstable"))]
+            #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
+            $item
+        )*
+    }
+}
 
-    // Parse a provided method that exists in the base trait.
-    (@doc ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)? { $($body:tt)* } $($tail:tt)*) => {
-        extension_trait!(@doc ($($head)* fn $name $args $(-> $ret)? { $($body)* }) $($tail)*);
-    };
-    (@ext ($($head:tt)*) fn $name:ident $args:tt $(-> $ret:ty)? { $($body:tt)* } $($tail:tt)*) => {
-        extension_trait!(@ext ($($head)*) $($tail)*);
-    };
+/// Declares Unix-specific items.
+#[doc(hidden)]
+#[allow(unused_macros)]
+macro_rules! cfg_unix {
+    ($($item:item)*) => {
+        $(
+            #[cfg(any(unix, feature = "docs"))]
+            $item
+        )*
+    }
+}
 
-    // Parse the return type in an extension method where the future doesn't borrow.
-    (@doc ($($head:tt)*) -> impl Future<Output = $out:ty> [$f:ty] $($tail:tt)*) => {
-        extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*);
-    };
-    (@ext ($($head:tt)*) -> impl Future<Output = $out:ty> [$f:ty] $($tail:tt)*) => {
-        extension_trait!(@ext ($($head)* -> $f) $($tail)*);
-    };
+/// Declares Windows-specific items.
+#[doc(hidden)]
+#[allow(unused_macros)]
+macro_rules! cfg_windows {
+    ($($item:item)*) => {
+        $(
+            #[cfg(any(windows, feature = "docs"))]
+            $item
+        )*
+    }
+}
 
-    // Parse the return type in an extension method where the future borrows its environment.
-    (@doc ($($head:tt)*) -> impl Future<Output = $out:ty> + $lt:lifetime [$f:ty] $($tail:tt)*) => {
-        extension_trait!(@doc ($($head)* -> borrowed::ImplFuture<$lt, $out>) $($tail)*);
-    };
-    (@ext ($($head:tt)*) -> impl Future<Output = $out:ty> + $lt:lifetime [$f:ty] $($tail:tt)*) => {
-        extension_trait!(@ext ($($head)* -> $f) $($tail)*);
-    };
+/// Declares items when the "docs" feature is enabled.
+#[doc(hidden)]
+#[allow(unused_macros)]
+macro_rules! cfg_docs {
+    ($($item:item)*) => {
+        $(
+            #[cfg(feature = "docs")]
+            $item
+        )*
+    }
+}
 
-    // Parse a token that doesn't fit into any of the previous patterns.
-    (@doc ($($head:tt)*) $token:tt $($tail:tt)*) => {
-        extension_trait!(@doc ($($head)* $token) $($tail)*);
-    };
-    (@ext ($($head:tt)*) $token:tt $($tail:tt)*) => {
-        extension_trait!(@ext ($($head)* $token) $($tail)*);
-    };
+/// Declares items when the "docs" feature is disabled.
+#[doc(hidden)]
+#[allow(unused_macros)]
+macro_rules! cfg_not_docs {
+    ($($item:item)*) => {
+        $(
+            #[cfg(not(feature = "docs"))]
+            $item
+        )*
+    }
+}
+
+/// Declares std items.
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! cfg_std {
+    ($($item:item)*) => {
+        $(
+            #[cfg(feature = "std")]
+            $item
+        )*
+    }
+}
+
+/// Declares no-std items.
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! cfg_alloc {
+    ($($item:item)*) => {
+        $(
+            #[cfg(feature = "alloc")]
+            $item
+        )*
+    }
+}
 
-    // Handle the end of the token list.
-    (@doc ($($head:tt)*)) => { $($head)* };
-    (@ext ($($head:tt)*)) => { $($head)* };
+/// Declares default items.
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! cfg_default {
+    ($($item:item)*) => {
+        $(
+            #[cfg(feature = "default")]
+            $item
+        )*
+    }
 }
 
-pub use crate::extension_trait;
+/// Declares items that use I/O safety.
+#[allow(unused_macros)]
+#[doc(hidden)]
+macro_rules! cfg_io_safety {
+    ($($item:item)*) => {
+        $(
+            #[cfg(feature = "io_safety")]
+            $item
+        )*
+    }
+}
diff --git a/src/vec/extend.rs b/src/vec/extend.rs
index ca3373d68..717338ab7 100644
--- a/src/vec/extend.rs
+++ b/src/vec/extend.rs
@@ -1,17 +1,20 @@
 use std::pin::Pin;
 
 use crate::prelude::*;
-use crate::stream::{Extend, IntoStream};
+use crate::stream::{self, IntoStream};
 
-impl<T> Extend<T> for Vec<T> {
-    fn stream_extend<'a, S: IntoStream<Item = T> + 'a>(
+impl<T: Send> stream::Extend<T> for Vec<T> {
+    fn extend<'a, S: IntoStream<Item = T> + 'a>(
         &'a mut self,
         stream: S,
-    ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
+    ) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>
+    where
+        <S as IntoStream>::IntoStream: Send,
+    {
         let stream = stream.into_stream();
-        //TODO: Add this back in when size_hint is added to Stream/StreamExt
-        //let (lower_bound, _) = stream.size_hint();
-        //self.reserve(lower_bound);
+
+        self.reserve(stream.size_hint().0);
+
         Box::pin(stream.for_each(move |item| self.push(item)))
     }
 }
diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs
index b326b04c8..95a1f98c9 100644
--- a/src/vec/from_stream.rs
+++ b/src/vec/from_stream.rs
@@ -3,95 +3,86 @@ use std::pin::Pin;
 use std::rc::Rc;
 use std::sync::Arc;
 
-use crate::stream::{Extend, FromStream, IntoStream};
+use crate::prelude::*;
+use crate::stream::{self, FromStream, IntoStream};
 
-impl<T> FromStream<T> for Vec<T> {
+impl<T: Send> FromStream<T> for Vec<T> {
     #[inline]
     fn from_stream<'a, S: IntoStream<Item = T>>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>>
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: 'a + Send,
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             let mut out = vec![];
-            out.stream_extend(stream).await;
+            stream::extend(&mut out, stream).await;
             out
         })
     }
 }
 
-impl<'b, T: Clone> FromStream<T> for Cow<'b, [T]> {
+impl<'b, T: Clone + Send> FromStream<T> for Cow<'b, [T]> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             Cow::Owned(FromStream::from_stream(stream).await)
         })
     }
 }
 
-impl<T> FromStream<T> for Box<[T]> {
+impl<T: Send> FromStream<T> for Box<[T]> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             Vec::from_stream(stream).await.into_boxed_slice()
         })
     }
 }
 
-impl<T> FromStream<T> for Rc<[T]> {
+impl<T: Send> FromStream<T> for Rc<[T]> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             Vec::from_stream(stream).await.into()
         })
     }
 }
 
-impl<T> FromStream<T> for Arc<[T]> {
+impl<T: Send> FromStream<T> for Arc<[T]> {
     #[inline]
-    fn from_stream<'a, S: IntoStream<Item = T>>(
+    fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
         stream: S,
-    ) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>>
+    ) -> Pin<Box<dyn Future<Output = Self> + 'a + Send>> 
     where
-        <S as IntoStream>::IntoStream: 'a,
+        <S as IntoStream>::IntoStream: Send
     {
         let stream = stream.into_stream();
 
         Box::pin(async move {
-            pin_utils::pin_mut!(stream);
-
             Vec::from_stream(stream).await.into()
         })
     }
diff --git a/src/vec/mod.rs b/src/vec/mod.rs
index 77a0b746b..2efd3c3f9 100644
--- a/src/vec/mod.rs
+++ b/src/vec/mod.rs
@@ -6,5 +6,6 @@
 mod extend;
 mod from_stream;
 
+#[allow(unused)]
 #[doc(inline)]
 pub use std::vec::Vec;
diff --git a/tests/addr.rs b/tests/addr.rs
index aada557c3..fcd5aa1f0 100644
--- a/tests/addr.rs
+++ b/tests/addr.rs
@@ -1,3 +1,5 @@
+#![cfg(not(target_os = "unknown"))]
+
 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
 
 use async_std::net::ToSocketAddrs;
diff --git a/tests/block_on.rs b/tests/block_on.rs
index c422d0630..4c264804d 100644
--- a/tests/block_on.rs
+++ b/tests/block_on.rs
@@ -1,16 +1,63 @@
-use async_std::task;
+#![cfg(not(target_os = "unknown"))]
+
+use async_std::{future::ready, task::block_on};
 
 #[test]
 fn smoke() {
-    let res = task::block_on(async { 1 + 2 });
+    let res = block_on(async { 1 + 2 });
     assert_eq!(res, 3);
 }
 
 #[test]
 #[should_panic = "boom"]
 fn panic() {
-    task::block_on(async {
+    block_on(async {
         // This panic should get propagated into the parent thread.
         panic!("boom");
     });
 }
+
+#[cfg(feature = "unstable")]
+#[test]
+fn nested_block_on_local() {
+    use async_std::task::spawn_local;
+
+    let x = block_on(async {
+        let a = block_on(async { block_on(async { ready(3).await }) });
+        let b = spawn_local(async { block_on(async { ready(2).await }) }).await;
+        let c = block_on(async { block_on(async { ready(1).await }) });
+        a + b + c
+    });
+
+    assert_eq!(x, 3 + 2 + 1);
+
+    let y = block_on(async {
+        let a = block_on(async { block_on(async { ready(3).await }) });
+        let b = spawn_local(async { block_on(async { ready(2).await }) }).await;
+        let c = block_on(async { block_on(async { ready(1).await }) });
+        a + b + c
+    });
+
+    assert_eq!(y, 3 + 2 + 1);
+}
+
+#[test]
+fn nested_block_on() {
+    let x = block_on(async {
+        let a = block_on(async { block_on(async { ready(3).await }) });
+        let b = block_on(async { block_on(async { ready(2).await }) });
+        let c = block_on(async { block_on(async { ready(1).await }) });
+        a + b + c
+    });
+
+    assert_eq!(x, 3 + 2 + 1);
+
+    let y = block_on(async {
+        let a = block_on(async { block_on(async { ready(3).await }) });
+        let b = block_on(async { block_on(async { ready(2).await }) });
+        let c = block_on(async { block_on(async { ready(1).await }) });
+        a + b + c
+    });
+
+    assert_eq!(y, 3 + 2 + 1);
+}
diff --git a/tests/buf_writer.rs b/tests/buf_writer.rs
new file mode 100644
index 000000000..442cf8a4d
--- /dev/null
+++ b/tests/buf_writer.rs
@@ -0,0 +1,78 @@
+use async_std::io::{self, BufWriter, SeekFrom};
+use async_std::prelude::*;
+use async_std::task;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn test_buffered_writer() {
+    #![allow(clippy::cognitive_complexity)]
+    task::block_on(async {
+        let inner: Vec<u8> = Vec::new();
+        let mut writer = BufWriter::<Vec<u8>>::with_capacity(2, inner);
+
+        writer.write(&[0, 1]).await.unwrap();
+        assert!(writer.buffer().is_empty());
+        assert_eq!(*writer.get_ref(), [0, 1]);
+
+        writer.write(&[2]).await.unwrap();
+        assert_eq!(writer.buffer(), [2]);
+        assert_eq!(*writer.get_ref(), [0, 1]);
+
+        writer.write(&[3]).await.unwrap();
+        assert_eq!(writer.buffer(), [2, 3]);
+        assert_eq!(*writer.get_ref(), [0, 1]);
+
+        writer.flush().await.unwrap();
+        assert!(writer.buffer().is_empty());
+        assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
+
+        writer.write(&[4]).await.unwrap();
+        writer.write(&[5]).await.unwrap();
+        assert_eq!(writer.buffer(), [4, 5]);
+        assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
+
+        writer.write(&[6]).await.unwrap();
+        assert_eq!(writer.buffer(), [6]);
+        assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
+
+        writer.write(&[7, 8]).await.unwrap();
+        assert!(writer.buffer().is_empty());
+        assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+        writer.write(&[9, 10, 11]).await.unwrap();
+        assert!(writer.buffer().is_empty());
+        assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+
+        writer.flush().await.unwrap();
+        assert!(writer.buffer().is_empty());
+        assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn test_buffered_writer_inner_into_inner_flushes() {
+    task::block_on(async {
+        let mut w = BufWriter::with_capacity(3, Vec::<u8>::new());
+        w.write(&[0, 1]).await.unwrap();
+        assert!(w.get_ref().is_empty());
+        let w = w.into_inner().await.unwrap();
+        assert_eq!(w, [0, 1]);
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn test_buffered_writer_seek() {
+    task::block_on(async {
+        let mut w = BufWriter::with_capacity(3, io::Cursor::new(Vec::new()));
+        w.write_all(&[0, 1, 2, 3, 4, 5]).await.unwrap();
+        w.write_all(&[6, 7]).await.unwrap();
+        assert_eq!(w.seek(SeekFrom::Current(0)).await.ok(), Some(8));
+        assert_eq!(&w.get_ref().get_ref()[..], &[0, 1, 2, 3, 4, 5, 6, 7][..]);
+        assert_eq!(w.seek(SeekFrom::Start(2)).await.ok(), Some(2));
+    })
+}
diff --git a/tests/channel.rs b/tests/channel.rs
new file mode 100644
index 000000000..2aa271319
--- /dev/null
+++ b/tests/channel.rs
@@ -0,0 +1,376 @@
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::time::Duration;
+
+use async_std::channel::bounded as channel;
+use async_std::task;
+use rand::{Rng, SeedableRng};
+
+#[cfg(not(target_os = "unknown"))]
+use async_std::task::spawn;
+#[cfg(target_os = "unknown")]
+use async_std::task::spawn_local as spawn;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+fn ms(ms: u64) -> Duration {
+    Duration::from_millis(ms)
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn smoke() {
+    task::block_on(async {
+        let (s, r) = channel(1);
+
+        s.send(7).await.unwrap();
+        assert_eq!(r.recv().await.unwrap(), 7);
+
+        s.send(8).await.unwrap();
+        assert_eq!(r.recv().await.unwrap(), 8);
+
+        drop(s);
+        assert!(r.recv().await.is_err());
+    });
+
+    task::block_on(async {
+        let (s, r) = channel(10);
+        drop(r);
+        assert!(s.send(1).await.is_err());
+    });
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn capacity() {
+    for i in 1..10 {
+        let (s, r) = channel::<()>(i);
+        assert_eq!(s.capacity().unwrap(), i);
+        assert_eq!(r.capacity().unwrap(), i);
+    }
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn len_empty_full() {
+    #![allow(clippy::cognitive_complexity)]
+    task::block_on(async {
+        let (s, r) = channel(2);
+
+        assert_eq!(s.len(), 0);
+        assert_eq!(s.is_empty(), true);
+        assert_eq!(s.is_full(), false);
+        assert_eq!(r.len(), 0);
+        assert_eq!(r.is_empty(), true);
+        assert_eq!(r.is_full(), false);
+
+        s.send(()).await.unwrap();
+
+        assert_eq!(s.len(), 1);
+        assert_eq!(s.is_empty(), false);
+        assert_eq!(s.is_full(), false);
+        assert_eq!(r.len(), 1);
+        assert_eq!(r.is_empty(), false);
+        assert_eq!(r.is_full(), false);
+
+        s.send(()).await.unwrap();
+
+        assert_eq!(s.len(), 2);
+        assert_eq!(s.is_empty(), false);
+        assert_eq!(s.is_full(), true);
+        assert_eq!(r.len(), 2);
+        assert_eq!(r.is_empty(), false);
+        assert_eq!(r.is_full(), true);
+
+        let _ = r.recv().await;
+
+        assert_eq!(s.len(), 1);
+        assert_eq!(s.is_empty(), false);
+        assert_eq!(s.is_full(), false);
+        assert_eq!(r.len(), 1);
+        assert_eq!(r.is_empty(), false);
+        assert_eq!(r.is_full(), false);
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn recv() {
+    task::block_on(async {
+        let (s, r) = channel(100);
+
+        spawn(async move {
+            assert_eq!(r.recv().await.unwrap(), 7);
+            task::sleep(ms(1000)).await;
+            assert_eq!(r.recv().await.unwrap(), 8);
+            task::sleep(ms(1000)).await;
+            assert_eq!(r.recv().await.unwrap(), 9);
+            assert!(r.recv().await.is_err());
+        });
+
+        task::sleep(ms(1500)).await;
+        s.send(7).await.unwrap();
+        s.send(8).await.unwrap();
+        s.send(9).await.unwrap();
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn send() {
+    task::block_on(async {
+        let (s, r) = channel(1);
+
+        spawn(async move {
+            s.send(7).await.unwrap();
+            task::sleep(ms(1000)).await;
+            s.send(8).await.unwrap();
+            task::sleep(ms(1000)).await;
+            s.send(9).await.unwrap();
+            task::sleep(ms(1000)).await;
+            s.send(10).await.unwrap();
+        });
+
+        task::sleep(ms(1500)).await;
+        assert_eq!(r.recv().await.unwrap(), 7);
+        assert_eq!(r.recv().await.unwrap(), 8);
+        assert_eq!(r.recv().await.unwrap(), 9);
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn recv_after_disconnect() {
+    task::block_on(async {
+        let (s, r) = channel(100);
+
+        s.send(1).await.unwrap();
+        s.send(2).await.unwrap();
+        s.send(3).await.unwrap();
+
+        drop(s);
+
+        assert_eq!(r.recv().await.unwrap(), 1);
+        assert_eq!(r.recv().await.unwrap(), 2);
+        assert_eq!(r.recv().await.unwrap(), 3);
+        assert!(r.recv().await.is_err());
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn len() {
+    const COUNT: usize = 25_000;
+    const CAP: usize = 1000;
+
+    task::block_on(async {
+        let (s, r) = channel(CAP);
+
+        assert_eq!(s.len(), 0);
+        assert_eq!(r.len(), 0);
+
+        for _ in 0..CAP / 10 {
+            for i in 0..50 {
+                s.send(i).await.unwrap();
+                assert_eq!(s.len(), i + 1);
+            }
+
+            for i in 0..50 {
+                let _ = r.recv().await;
+                assert_eq!(r.len(), 50 - i - 1);
+            }
+        }
+
+        assert_eq!(s.len(), 0);
+        assert_eq!(r.len(), 0);
+
+        for i in 0..CAP {
+            s.send(i).await.unwrap();
+            assert_eq!(s.len(), i + 1);
+        }
+
+        for _ in 0..CAP {
+            r.recv().await.unwrap();
+        }
+
+        assert_eq!(s.len(), 0);
+        assert_eq!(r.len(), 0);
+
+        let child = spawn({
+            let r = r.clone();
+            async move {
+                for i in 0..COUNT {
+                    assert_eq!(r.recv().await.unwrap(), i);
+                    let len = r.len();
+                    assert!(len <= CAP);
+                }
+            }
+        });
+
+        for i in 0..COUNT {
+            s.send(i).await.unwrap();
+            let len = s.len();
+            assert!(len <= CAP);
+        }
+
+        child.await;
+
+        assert_eq!(s.len(), 0);
+        assert_eq!(r.len(), 0);
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn disconnect_wakes_receiver() {
+    task::block_on(async {
+        let (s, r) = channel::<()>(1);
+
+        let child = spawn(async move {
+            assert!(r.recv().await.is_err());
+        });
+
+        task::sleep(ms(1000)).await;
+        drop(s);
+
+        child.await;
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn spsc() {
+    const COUNT: usize = 100_000;
+
+    task::block_on(async {
+        let (s, r) = channel(3);
+
+        let child = spawn(async move {
+            for i in 0..COUNT {
+                assert_eq!(r.recv().await.unwrap(), i);
+            }
+            assert!(r.recv().await.is_err());
+        });
+
+        for i in 0..COUNT {
+            s.send(i).await.unwrap();
+        }
+        drop(s);
+
+        child.await;
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn mpmc() {
+    const COUNT: usize = 25_000;
+    const TASKS: usize = 4;
+
+    task::block_on(async {
+        let (s, r) = channel::<usize>(3);
+        let v = (0..COUNT).map(|_| AtomicUsize::new(0)).collect::<Vec<_>>();
+        let v = Arc::new(v);
+
+        let mut tasks = Vec::new();
+
+        for _ in 0..TASKS {
+            let r = r.clone();
+            let v = v.clone();
+            tasks.push(spawn(async move {
+                for _ in 0..COUNT {
+                    let n = r.recv().await.unwrap();
+                    v[n].fetch_add(1, Ordering::SeqCst);
+                }
+            }));
+        }
+
+        for _ in 0..TASKS {
+            let s = s.clone();
+            tasks.push(spawn(async move {
+                for i in 0..COUNT {
+                    s.send(i).await.unwrap();
+                }
+            }));
+        }
+
+        for t in tasks {
+            t.await;
+        }
+
+        for c in v.iter() {
+            assert_eq!(c.load(Ordering::SeqCst), TASKS);
+        }
+    });
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn oneshot() {
+    const COUNT: usize = 10_000;
+
+    task::block_on(async {
+        for _ in 0..COUNT {
+            let (s, r) = channel(1);
+
+            let c1 = spawn(async move { r.recv().await.unwrap() });
+            let c2 = spawn(async move { s.send(0).await.unwrap() });
+
+            c1.await;
+            c2.await;
+        }
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn drops() {
+    const RUNS: usize = 100;
+
+    static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+    #[derive(Debug, PartialEq)]
+    struct DropCounter;
+
+    impl Drop for DropCounter {
+        fn drop(&mut self) {
+            DROPS.fetch_add(1, Ordering::SeqCst);
+        }
+    }
+
+    for _ in 0..RUNS {
+        let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(0);
+        task::block_on(async move {
+            let steps = rng.gen_range(0..10_000);
+            let additional = rng.gen_range(0..50);
+
+            DROPS.store(0, Ordering::SeqCst);
+            let (s, r) = channel::<DropCounter>(50);
+
+            let child = spawn({
+                let r = r.clone();
+                async move {
+                    for _ in 0..steps {
+                        r.recv().await.unwrap();
+                    }
+                }
+            });
+
+            for _ in 0..steps {
+                s.send(DropCounter).await.unwrap();
+            }
+
+            child.await;
+
+            for _ in 0..additional {
+                s.send(DropCounter).await.unwrap();
+            }
+
+            assert_eq!(DROPS.load(Ordering::SeqCst), steps);
+            drop(s);
+            drop(r);
+            assert_eq!(DROPS.load(Ordering::SeqCst), steps + additional);
+        })
+    }
+}
diff --git a/tests/collect.rs b/tests/collect.rs
new file mode 100644
index 000000000..7ab80ccc9
--- /dev/null
+++ b/tests/collect.rs
@@ -0,0 +1,18 @@
+#[cfg(feature = "unstable")]
+#[test]
+fn test_send() {
+    use async_std::prelude::*;
+    use async_std::{stream, task};
+
+    task::block_on(async {
+        fn test_send_trait<T: Send>(_: &T) {}
+
+        let stream = stream::repeat(1u8).take(10);
+        test_send_trait(&stream);
+
+        let fut = stream.collect::<Vec<_>>();
+
+        // This line triggers a compilation error
+        test_send_trait(&fut);
+    });
+}
diff --git a/tests/condvar.rs b/tests/condvar.rs
new file mode 100644
index 000000000..b5ec12a1e
--- /dev/null
+++ b/tests/condvar.rs
@@ -0,0 +1,103 @@
+#![cfg(feature = "unstable")]
+use std::sync::Arc;
+use std::time::Duration;
+
+use async_std::sync::{Condvar, Mutex};
+use async_std::task::{self, JoinHandle};
+
+#[cfg(not(target_os = "unknown"))]
+use async_std::task::spawn;
+#[cfg(target_os = "unknown")]
+use async_std::task::spawn_local as spawn;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn wait_timeout_with_lock() {
+    task::block_on(async {
+        let pair = Arc::new((Mutex::new(false), Condvar::new()));
+        let pair2 = pair.clone();
+
+        spawn(async move {
+            let (m, c) = &*pair2;
+            let _g = m.lock().await;
+            task::sleep(Duration::from_millis(200)).await;
+            c.notify_one();
+        });
+
+        let (m, c) = &*pair;
+        let (_, wait_result) = c
+            .wait_timeout(m.lock().await, Duration::from_millis(50))
+            .await;
+        assert!(wait_result.timed_out());
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn wait_timeout_without_lock() {
+    task::block_on(async {
+        let m = Mutex::new(false);
+        let c = Condvar::new();
+
+        let (_, wait_result) = c
+            .wait_timeout(m.lock().await, Duration::from_millis(10))
+            .await;
+        assert!(wait_result.timed_out());
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn wait_timeout_until_timed_out() {
+    task::block_on(async {
+        let m = Mutex::new(false);
+        let c = Condvar::new();
+
+        let (_, wait_result) = c
+            .wait_timeout_until(m.lock().await, Duration::from_millis(100), |&mut started| {
+                started
+            })
+            .await;
+        assert!(wait_result.timed_out());
+    })
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn notify_all() {
+    task::block_on(async {
+        let mut tasks: Vec<JoinHandle<()>> = Vec::new();
+        let pair = Arc::new((Mutex::new(0u32), Condvar::new()));
+
+        for _ in 0..10 {
+            let pair = pair.clone();
+            tasks.push(spawn(async move {
+                let (m, c) = &*pair;
+                let mut count = m.lock().await;
+                while *count == 0 {
+                    count = c.wait(count).await;
+                }
+                *count += 1;
+            }));
+        }
+
+        // Give some time for tasks to start up
+        task::sleep(Duration::from_millis(50)).await;
+
+        let (m, c) = &*pair;
+        {
+            let mut count = m.lock().await;
+            *count += 1;
+            c.notify_all();
+        }
+
+        for t in tasks {
+            t.await;
+        }
+        let count = m.lock().await;
+        assert_eq!(11, *count);
+    })
+}
diff --git a/tests/io_copy.rs b/tests/io_copy.rs
new file mode 100644
index 000000000..394c34f8e
--- /dev/null
+++ b/tests/io_copy.rs
@@ -0,0 +1,72 @@
+use std::{
+    io::Result,
+    pin::Pin,
+    task::{Context, Poll},
+};
+
+struct ReaderThatPanicsAfterEof {
+    read_count: usize,
+    has_sent_eof: bool,
+    max_read: usize,
+}
+
+impl async_std::io::Read for ReaderThatPanicsAfterEof {
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        _cx: &mut Context<'_>,
+        buf: &mut [u8],
+    ) -> Poll<Result<usize>> {
+        if self.has_sent_eof {
+            panic!("this should be unreachable because we should not poll after eof (Ready(Ok(0)))")
+        } else if self.read_count >= self.max_read {
+            self.has_sent_eof = true;
+            Poll::Ready(Ok(0))
+        } else {
+            self.read_count += 1;
+            Poll::Ready(Ok(buf.len()))
+        }
+    }
+}
+
+struct WriterThatTakesAWhileToFlush {
+    max_flush: usize,
+    flush_count: usize,
+}
+
+impl async_std::io::Write for WriterThatTakesAWhileToFlush {
+    fn poll_write(self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
+        Poll::Ready(Ok(buf.len()))
+    }
+
+    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
+        self.flush_count += 1;
+        if self.flush_count >= self.max_flush {
+            Poll::Ready(Ok(()))
+        } else {
+            cx.waker().wake_by_ref();
+            Poll::Pending
+        }
+    }
+
+    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
+        Poll::Ready(Ok(()))
+    }
+}
+
+#[test]
+fn io_copy_does_not_poll_after_eof() {
+    async_std::task::block_on(async {
+        let mut reader = ReaderThatPanicsAfterEof {
+            has_sent_eof: false,
+            max_read: 10,
+            read_count: 0,
+        };
+
+        let mut writer = WriterThatTakesAWhileToFlush {
+            flush_count: 0,
+            max_flush: 10,
+        };
+
+        assert!(async_std::io::copy(&mut reader, &mut writer).await.is_ok());
+    })
+}
diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs
index 85a17ab75..371150693 100644
--- a/tests/io_timeout.rs
+++ b/tests/io_timeout.rs
@@ -5,6 +5,14 @@ use async_std::task;
 
 #[test]
 #[should_panic(expected = "timed out")]
+#[cfg(not(any(
+    target_os = "unknown",
+    target_arch = "arm",
+    target_arch = "mips",
+    target_arch = "powerpc",
+    target_arch = "powerpc64",
+    target_arch = "x86",
+)))] // stdin tests fail when running through cross
 fn io_timeout_timedout() {
     task::block_on(async {
         io::timeout(Duration::from_secs(1), async {
diff --git a/tests/mutex.rs b/tests/mutex.rs
index fd1c07b38..76f42e285 100644
--- a/tests/mutex.rs
+++ b/tests/mutex.rs
@@ -5,7 +5,16 @@ use async_std::sync::Mutex;
 use async_std::task;
 use futures::channel::mpsc;
 
+#[cfg(not(target_os = "unknown"))]
+use async_std::task::spawn;
+#[cfg(target_os = "unknown")]
+use async_std::task::spawn_local as spawn;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn smoke() {
     task::block_on(async {
         let m = Mutex::new(());
@@ -15,18 +24,21 @@ fn smoke() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn try_lock() {
     let m = Mutex::new(());
     *m.try_lock().unwrap() = ();
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn into_inner() {
     let m = Mutex::new(10);
     assert_eq!(m.into_inner(), 10);
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn get_mut() {
     let mut m = Mutex::new(10);
     *m.get_mut() = 20;
@@ -34,6 +46,7 @@ fn get_mut() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn contention() {
     task::block_on(async {
         let (tx, mut rx) = mpsc::unbounded();
@@ -42,22 +55,29 @@ fn contention() {
         let mutex = Arc::new(Mutex::new(0));
         let num_tasks = 10000;
 
+        let mut handles = Vec::new();
         for _ in 0..num_tasks {
             let tx = tx.clone();
             let mutex = mutex.clone();
 
-            task::spawn(async move {
+            handles.push(spawn(async move {
                 let mut lock = mutex.lock().await;
                 *lock += 1;
                 tx.unbounded_send(()).unwrap();
                 drop(lock);
-            });
+            }));
         }
 
         for _ in 0..num_tasks {
             rx.next().await.unwrap();
         }
 
+        for handle in handles.into_iter() {
+            handle.await;
+        }
+
+        dbg!("wait");
+
         let lock = mutex.lock().await;
         assert_eq!(num_tasks, *lock);
     });
diff --git a/tests/rwlock.rs b/tests/rwlock.rs
index ff25e862d..1d33a456d 100644
--- a/tests/rwlock.rs
+++ b/tests/rwlock.rs
@@ -10,10 +10,18 @@ use async_std::sync::RwLock;
 use async_std::task;
 use futures::channel::mpsc;
 
+#[cfg(not(target_os = "unknown"))]
+use async_std::task::spawn;
+#[cfg(target_os = "unknown")]
+use async_std::task::spawn_local as spawn;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
 /// Generates a random number in `0..n`.
 pub fn random(n: u32) -> u32 {
     thread_local! {
-        static RNG: Cell<Wrapping<u32>> = Cell::new(Wrapping(1406868647));
+        static RNG: Cell<Wrapping<u32>> = Cell::new(Wrapping(1_406_868_647));
     }
 
     RNG.with(|rng| {
@@ -35,6 +43,7 @@ pub fn random(n: u32) -> u32 {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn smoke() {
     task::block_on(async {
         let lock = RwLock::new(());
@@ -46,6 +55,7 @@ fn smoke() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn try_write() {
     task::block_on(async {
         let lock = RwLock::new(0isize);
@@ -56,12 +66,14 @@ fn try_write() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn into_inner() {
     let lock = RwLock::new(10);
     assert_eq!(lock.into_inner(), 10);
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn into_inner_and_drop() {
     struct Counter(Arc<AtomicUsize>);
 
@@ -84,6 +96,7 @@ fn into_inner_and_drop() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn get_mut() {
     let mut lock = RwLock::new(10);
     *lock.get_mut() = 20;
@@ -91,6 +104,7 @@ fn get_mut() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn contention() {
     const N: u32 = 10;
     const M: usize = 1000;
@@ -104,7 +118,7 @@ fn contention() {
         let tx = tx.clone();
         let rw = rw.clone();
 
-        task::spawn(async move {
+        spawn(async move {
             for _ in 0..M {
                 if random(N) == 0 {
                     drop(rw.write().await);
@@ -116,7 +130,7 @@ fn contention() {
         });
     }
 
-    task::block_on(async {
+    task::block_on(async move {
         for _ in 0..N {
             rx.next().await.unwrap();
         }
@@ -124,6 +138,7 @@ fn contention() {
 }
 
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn writer_and_readers() {
     #[derive(Default)]
     struct Yield(Cell<bool>);
@@ -146,7 +161,7 @@ fn writer_and_readers() {
     let (tx, mut rx) = mpsc::unbounded();
 
     // Spawn a writer task.
-    task::spawn({
+    spawn({
         let lock = lock.clone();
         async move {
             let mut lock = lock.write().await;
@@ -164,13 +179,13 @@ fn writer_and_readers() {
     let mut readers = Vec::new();
     for _ in 0..5 {
         let lock = lock.clone();
-        readers.push(task::spawn(async move {
+        readers.push(spawn(async move {
             let lock = lock.read().await;
             assert!(*lock >= 0);
         }));
     }
 
-    task::block_on(async {
+    task::block_on(async move {
         // Wait for readers to pass their asserts.
         for r in readers {
             r.await;
diff --git a/tests/stream.rs b/tests/stream.rs
new file mode 100644
index 000000000..654735b2a
--- /dev/null
+++ b/tests/stream.rs
@@ -0,0 +1,183 @@
+use std::convert::identity;
+use std::marker::Unpin;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+use pin_project_lite::pin_project;
+
+use async_std::channel::bounded as channel;
+use async_std::prelude::*;
+use async_std::stream;
+use async_std::task;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+#[cfg(not(target_os = "unknown"))]
+use async_std::task::spawn;
+#[cfg(target_os = "unknown")]
+use async_std::task::spawn_local as spawn;
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+/// Checks that streams are merged fully even if one of the components
+/// experiences delay.
+fn merging_delayed_streams_work() {
+    let (sender, receiver) = channel::<i32>(10);
+
+    let mut s = receiver.merge(stream::empty());
+    let t = spawn(async move {
+        let mut xs = Vec::new();
+        while let Some(x) = s.next().await {
+            xs.push(x);
+        }
+        xs
+    });
+
+    task::block_on(async move {
+        task::sleep(std::time::Duration::from_millis(500)).await;
+        sender.send(92).await.unwrap();
+        drop(sender);
+        let xs = t.await;
+        assert_eq!(xs, vec![92])
+    });
+
+    let (sender, receiver) = channel::<i32>(10);
+
+    let mut s = stream::empty().merge(receiver);
+    let t = spawn(async move {
+        let mut xs = Vec::new();
+        while let Some(x) = s.next().await {
+            xs.push(x);
+        }
+        xs
+    });
+
+    task::block_on(async move {
+        task::sleep(std::time::Duration::from_millis(500)).await;
+        sender.send(92).await.unwrap();
+        drop(sender);
+        let xs = t.await;
+        assert_eq!(xs, vec![92])
+    });
+}
+
+pin_project! {
+    /// The opposite of `Fuse`: makes the stream panic if polled after termination.
+    struct Explode<S> {
+        #[pin]
+        done: bool,
+        #[pin]
+        inner: S,
+    }
+}
+
+impl<S: Stream> Stream for Explode<S> {
+    type Item = S::Item;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+        let mut this = self.project();
+        if *this.done {
+            panic!("KABOOM!")
+        }
+        let res = this.inner.poll_next(cx);
+        if let Poll::Ready(None) = &res {
+            *this.done = true;
+        }
+        res
+    }
+}
+
+fn explode<S: Stream>(s: S) -> Explode<S> {
+    Explode {
+        done: false,
+        inner: s,
+    }
+}
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn merge_works_with_unfused_streams() {
+    let s1 = explode(stream::once(92));
+    let s2 = explode(stream::once(92));
+    let mut s = s1.merge(s2);
+
+    task::block_on(async move {
+        let mut xs = Vec::new();
+        while let Some(x) = s.next().await {
+            xs.push(x)
+        }
+        assert_eq!(xs, vec![92, 92]);
+    });
+}
+
+struct S<T>(T);
+
+impl<T: Stream + Unpin> Stream for S<T> {
+    type Item = T::Item;
+
+    fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<Self::Item>> {
+        unsafe { Pin::new_unchecked(&mut self.0) }.poll_next(ctx)
+    }
+}
+
+struct StrictOnce {
+    polled: bool,
+}
+
+impl Stream for StrictOnce {
+    type Item = ();
+
+    fn poll_next(mut self: Pin<&mut Self>, _: &mut Context) -> Poll<Option<Self::Item>> {
+        assert!(!self.polled, "Polled after completion!");
+        self.polled = true;
+        Poll::Ready(None)
+    }
+}
+
+struct Interchanger {
+    polled: bool,
+}
+
+impl Stream for Interchanger {
+    type Item = S<Box<dyn Stream<Item = ()> + Unpin>>;
+
+    fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<Self::Item>> {
+        if self.polled {
+            self.polled = false;
+            ctx.waker().wake_by_ref();
+            Poll::Pending
+        } else {
+            self.polled = true;
+            Poll::Ready(Some(S(Box::new(StrictOnce { polled: false }))))
+        }
+    }
+}
+
+#[test]
+fn flat_map_doesnt_poll_completed_inner_stream() {
+    task::block_on(async {
+        assert_eq!(
+            Interchanger { polled: false }
+                .take(2)
+                .flat_map(identity)
+                .count()
+                .await,
+            0
+        );
+    });
+}
+
+#[test]
+fn flatten_doesnt_poll_completed_inner_stream() {
+    task::block_on(async {
+        assert_eq!(
+            Interchanger { polled: false }
+                .take(2)
+                .flatten()
+                .count()
+                .await,
+            0
+        );
+    });
+}
diff --git a/tests/task_local.rs b/tests/task_local.rs
index 813185c84..b5345fec3 100644
--- a/tests/task_local.rs
+++ b/tests/task_local.rs
@@ -3,7 +3,16 @@ use std::sync::atomic::{AtomicBool, Ordering};
 use async_std::task;
 use async_std::task_local;
 
+#[cfg(not(target_os = "unknown"))]
+use async_std::task::spawn;
+#[cfg(target_os = "unknown")]
+use async_std::task::spawn_local as spawn;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
 #[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 fn drop_local() {
     static DROP_LOCAL: AtomicBool = AtomicBool::new(false);
 
@@ -20,7 +29,7 @@ fn drop_local() {
     }
 
     // Spawn a task that just touches its task-local.
-    let handle = task::spawn(async {
+    let handle = spawn(async {
         LOCAL.with(|_| ());
     });
     let task = handle.task().clone();
diff --git a/tests/tcp.rs b/tests/tcp.rs
index c8281d713..f21737e8f 100644
--- a/tests/tcp.rs
+++ b/tests/tcp.rs
@@ -1,3 +1,5 @@
+#![cfg(not(target_os = "unknown"))]
+
 use async_std::io;
 use async_std::net::{TcpListener, TcpStream};
 use async_std::prelude::*;
@@ -49,3 +51,70 @@ fn incoming_read() -> io::Result<()> {
         Ok(())
     })
 }
+
+#[test]
+fn smoke_std_stream_to_async_listener() -> io::Result<()> {
+    use std::io::Write;
+
+    task::block_on(async {
+        let listener = TcpListener::bind("127.0.0.1:0").await?;
+        let addr = listener.local_addr()?;
+
+        let mut std_stream = std::net::TcpStream::connect(&addr)?;
+        std_stream.write_all(THE_WINTERS_TALE)?;
+
+        let mut buf = vec![0; 1024];
+        let mut incoming = listener.incoming();
+        let mut stream = incoming.next().await.unwrap()?;
+
+        let n = stream.read(&mut buf).await?;
+        assert_eq!(&buf[..n], THE_WINTERS_TALE);
+
+        Ok(())
+    })
+}
+
+#[test]
+fn smoke_async_stream_to_std_listener() -> io::Result<()> {
+    use std::io::Read;
+
+    let std_listener = std::net::TcpListener::bind("127.0.0.1:0")?;
+    let addr = std_listener.local_addr()?;
+
+    task::block_on(async move {
+        let mut stream = TcpStream::connect(&addr).await?;
+        stream.write_all(THE_WINTERS_TALE).await?;
+        io::Result::Ok(())
+    })?;
+
+    let mut buf = vec![0; 1024];
+    let mut incoming = std_listener.incoming();
+    let mut stream = incoming.next().unwrap()?;
+
+    let n = stream.read(&mut buf).unwrap();
+    assert_eq!(&buf[..n], THE_WINTERS_TALE);
+
+    Ok(())
+}
+
+#[test]
+fn cloned_streams() -> io::Result<()> {
+    task::block_on(async {
+        let listener = TcpListener::bind("127.0.0.1:0").await?;
+        let addr = listener.local_addr()?;
+
+        let mut stream = TcpStream::connect(&addr).await?;
+        let mut cloned_stream = stream.clone();
+        let mut incoming = listener.incoming();
+        let mut write_stream = incoming.next().await.unwrap()?;
+        write_stream.write_all(b"Each your doing").await?;
+
+        let mut buf = [0; 15];
+        stream.read_exact(&mut buf[..8]).await?;
+        cloned_stream.read_exact(&mut buf[8..]).await?;
+
+        assert_eq!(&buf[..15], b"Each your doing");
+
+        Ok(())
+    })
+}
diff --git a/tests/timeout.rs b/tests/timeout.rs
new file mode 100644
index 000000000..60594d06b
--- /dev/null
+++ b/tests/timeout.rs
@@ -0,0 +1,26 @@
+use std::time::Duration;
+
+use async_std::future::timeout;
+use async_std::task;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+fn timeout_future_many() {
+    task::block_on(async {
+        let futures = (0..10)
+            .map(|i| {
+                timeout(Duration::from_millis(i * 50), async move {
+                    task::sleep(Duration::from_millis(i)).await;
+                    Ok::<(), async_std::future::TimeoutError>(())
+                })
+            })
+            .collect::<Vec<_>>();
+
+        for future in futures {
+            future.await.unwrap().unwrap();
+        }
+    });
+}
diff --git a/tests/udp.rs b/tests/udp.rs
index 319dc74ae..cd119ddcd 100644
--- a/tests/udp.rs
+++ b/tests/udp.rs
@@ -1,3 +1,5 @@
+#![cfg(not(target_os = "unknown"))]
+
 use async_std::io;
 use async_std::net::UdpSocket;
 use async_std::task;
@@ -10,17 +12,20 @@ const THE_MERCHANT_OF_VENICE: &[u8] = b"
 ";
 
 #[test]
-fn send_recv() -> io::Result<()> {
+fn send_recv_peek() -> io::Result<()> {
     task::block_on(async {
         let socket1 = UdpSocket::bind("127.0.0.1:0").await?;
         let socket2 = UdpSocket::bind("127.0.0.1:0").await?;
 
         socket1.connect(socket2.local_addr()?).await?;
         socket2.connect(socket1.local_addr()?).await?;
-
+        assert_eq!(socket1.peer_addr()?, socket2.local_addr()?);
         socket1.send(THE_MERCHANT_OF_VENICE).await?;
 
         let mut buf = [0u8; 1024];
+        let n = socket2.peek(&mut buf).await?;
+        assert_eq!(&buf[..n], THE_MERCHANT_OF_VENICE);
+
         let n = socket2.recv(&mut buf).await?;
         assert_eq!(&buf[..n], THE_MERCHANT_OF_VENICE);
 
diff --git a/tests/uds.rs b/tests/uds.rs
index e64af3cef..dcdce443c 100644
--- a/tests/uds.rs
+++ b/tests/uds.rs
@@ -1,9 +1,12 @@
-#![cfg(unix)]
+#![cfg(all(unix, not(target_os = "unknown")))]
 
 use async_std::io;
-use async_std::os::unix::net::UnixDatagram;
+use async_std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
+use async_std::prelude::*;
 use async_std::task;
 
+use std::time::Duration;
+
 const JULIUS_CAESAR: &[u8] = b"
     Friends, Romans, countrymen - lend me your ears!
     I come not to praise Caesar, but to bury him.
@@ -24,7 +27,7 @@ fn send_recv() -> io::Result<()> {
 }
 
 #[test]
-fn into_raw_fd() -> io::Result<()> {
+fn into_raw_fd_datagram() -> io::Result<()> {
     use async_std::os::unix::io::{FromRawFd, IntoRawFd};
     task::block_on(async {
         let (socket1, socket2) = UnixDatagram::pair().unwrap();
@@ -39,3 +42,100 @@ fn into_raw_fd() -> io::Result<()> {
         Ok(())
     })
 }
+
+#[test]
+fn into_raw_fd_stream() -> io::Result<()> {
+    use async_std::os::unix::io::{FromRawFd, IntoRawFd};
+    task::block_on(async {
+        let (mut socket1, socket2) = UnixStream::pair().unwrap();
+        socket1.write(JULIUS_CAESAR).await?;
+
+        let mut buf = vec![0; 1024];
+
+        let mut socket2 = unsafe { UnixStream::from_raw_fd(socket2.into_raw_fd()) };
+        let n = socket2.read(&mut buf).await?;
+        assert_eq!(&buf[..n], JULIUS_CAESAR);
+
+        Ok(())
+    })
+}
+
+const PING: &[u8] = b"ping";
+const PONG: &[u8] = b"pong";
+const TEST_TIMEOUT: Duration = Duration::from_secs(3);
+
+#[test]
+fn socket_ping_pong() {
+    let tmp_dir = tempfile::Builder::new()
+        .prefix("socket_ping_pong")
+        .tempdir()
+        .expect("Temp dir not created");
+    let sock_path = tmp_dir.as_ref().join("sock");
+    let iter_cnt = 16;
+
+    let listener =
+        task::block_on(async { UnixListener::bind(&sock_path).await.expect("Socket bind") });
+
+    let server_handle = std::thread::spawn(move || {
+        task::block_on(async { ping_pong_server(listener, iter_cnt).await }).unwrap()
+    });
+
+    let client_handle = std::thread::spawn(move || {
+        task::block_on(async { ping_pong_client(&sock_path, iter_cnt).await }).unwrap()
+    });
+
+    client_handle.join().unwrap();
+    server_handle.join().unwrap();
+}
+
+async fn ping_pong_server(listener: UnixListener, iterations: u32) -> std::io::Result<()> {
+    let mut incoming = listener.incoming();
+    let mut buf = [0; 1024];
+    for _ix in 0..iterations {
+        if let Some(s) = incoming.next().await {
+            let mut s = s?;
+            let n = s.read(&mut buf[..]).await?;
+            assert_eq!(&buf[..n], PING);
+            s.write_all(&PONG).await?;
+        }
+    }
+    Ok(())
+}
+
+async fn ping_pong_client(socket: &std::path::PathBuf, iterations: u32) -> std::io::Result<()> {
+    let mut buf = [0; 1024];
+    for _ix in 0..iterations {
+        let mut socket = UnixStream::connect(&socket).await?;
+        socket.write_all(&PING).await?;
+        let n = async_std::io::timeout(TEST_TIMEOUT, socket.read(&mut buf[..])).await?;
+        assert_eq!(&buf[..n], PONG);
+    }
+    Ok(())
+}
+
+#[test]
+fn uds_clone() -> io::Result<()> {
+    task::block_on(async {
+        let tmp_dir = tempfile::Builder::new()
+            .prefix("socket_ping_pong")
+            .tempdir()
+            .expect("Temp dir not created");
+        let sock_path = tmp_dir.as_ref().join("sock");
+        let input = UnixListener::bind(&sock_path).await?;
+
+        let mut writer = UnixStream::connect(&sock_path).await?;
+        let mut reader = input.incoming().next().await.unwrap()?;
+
+        writer.write(b"original").await.unwrap();
+        let mut original_buf = [0; 8];
+        reader.read(&mut original_buf).await?;
+        assert_eq!(&original_buf, b"original");
+
+        writer.clone().write(b"clone").await.unwrap();
+        let mut clone_buf = [0; 5];
+        reader.clone().read(&mut clone_buf).await?;
+        assert_eq!(&clone_buf, b"clone");
+
+        Ok(())
+    })
+}
diff --git a/tests/verbose_errors.rs b/tests/verbose_errors.rs
new file mode 100644
index 000000000..2876183ef
--- /dev/null
+++ b/tests/verbose_errors.rs
@@ -0,0 +1,33 @@
+#![cfg(not(target_os = "unknown"))]
+
+use async_std::{fs, io, net::ToSocketAddrs, task};
+
+#[test]
+fn open_file() {
+    task::block_on(async {
+        let non_existing_file = "/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas";
+        let res = fs::File::open(non_existing_file).await;
+        match res {
+            Ok(_) => panic!("Found file with random name: We live in a simulation"),
+            Err(e) => assert_eq!(
+                "could not open `/ashjudlkahasdasdsikdhajik/asdasdasdasdasdasd/fjuiklashdbflasas`",
+                &format!("{}", e)
+            ),
+        }
+    })
+}
+
+#[test]
+fn resolve_address() {
+    task::block_on(async {
+        let non_existing_addr = "ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80";
+        let res: Result<_, io::Error> = non_existing_addr.to_socket_addrs().await;
+        match res {
+            Ok(_) => panic!("Found address with random name: We live in a simulation"),
+            Err(e) => assert_eq!(
+                "could not resolve address `\"ashjudlkahasdasdsikdhajik.asdasdasdasdasdasd.fjuiklashdbflasas:80\"`",
+                &format!("{}", e)
+            ),
+        }
+    })
+}
