Showing preview only (775K chars total). Download the full file or copy to clipboard to get everything.
Repository: frostyplanet/crossfire-rs
Branch: master
Commit: b64cafe2a210
Files: 94
Total size: 741.0 KB
Directory structure:
gitextract_uux3elsf/
├── .github/
│ └── workflows/
│ ├── cron_2.0_arm.yml
│ ├── cron_2.0_x86.yml
│ ├── cron_dev.yml
│ ├── cron_dev_arm.yml
│ ├── cron_dev_arm_trace.yml
│ ├── cron_master_async_std_arm.yml
│ ├── cron_master_async_std_x86.yml
│ ├── cron_master_compio_arm.yml
│ ├── cron_master_compio_x86.yml
│ ├── cron_master_smol_arm.yml
│ ├── cron_master_smol_x86.yml
│ ├── cron_master_threaded_arm.yml
│ ├── cron_master_threaded_x86.yml
│ ├── cron_master_tokio_arm.yml
│ ├── cron_master_tokio_x86.yml
│ ├── fast.yml
│ ├── leak.yml
│ ├── miri_dev.yml
│ ├── miri_dev_log.yml
│ ├── miri_tokio.yml
│ ├── miri_tokio_cur.yml
│ ├── miri_tokio_cur_log.yml
│ ├── miri_tokio_log.yml
│ ├── pr.yml
│ └── typos.yml
├── .gitignore
├── AGENTS.md
├── CHANGELOG.md
├── CONTRIBUTION
├── Cargo.toml
├── LICENSE
├── Makefile
├── README.md
├── benches/
│ └── inner.rs
├── git-hooks/
│ └── pre-commit
├── rustfmt.toml
├── src/
│ ├── async_rx.rs
│ ├── async_tx.rs
│ ├── backoff.rs
│ ├── blocking_rx.rs
│ ├── blocking_tx.rs
│ ├── collections.rs
│ ├── compat.rs
│ ├── crossbeam/
│ │ ├── array_queue.rs
│ │ ├── array_queue_mpsc.rs
│ │ ├── array_queue_spsc.rs
│ │ ├── err.rs
│ │ ├── mod.rs
│ │ └── seg_queue.rs
│ ├── flavor/
│ │ ├── array.rs
│ │ ├── array_mpsc.rs
│ │ ├── array_spsc.rs
│ │ ├── list.rs
│ │ ├── mod.rs
│ │ ├── one.rs
│ │ ├── one_mpsc.rs
│ │ └── one_spmc.rs
│ ├── lib.rs
│ ├── mpmc.rs
│ ├── mpsc.rs
│ ├── null.rs
│ ├── oneshot.rs
│ ├── select/
│ │ ├── mod.rs
│ │ ├── multiplex.rs
│ │ └── select.rs
│ ├── shared.rs
│ ├── sink.rs
│ ├── spsc.rs
│ ├── stream.rs
│ ├── waitgroup.rs
│ ├── waker.rs
│ ├── waker_registry.rs
│ └── weak.rs
└── test-suite/
├── Cargo.toml
├── benches/
│ ├── async_channel.rs
│ ├── common.rs
│ ├── crossbeam.rs
│ ├── crossfire.rs
│ ├── crossfire_select.rs
│ ├── extra.rs
│ ├── flume.rs
│ ├── kanal.rs
│ └── tokio.rs
├── scripts/
│ └── miri.sh
└── src/
├── lib.rs
├── test_async.rs
├── test_async_blocking.rs
├── test_blocking_async.rs
├── test_blocking_context.rs
├── test_oneshot.rs
├── test_select_async.rs
├── test_select_blocking.rs
├── test_type_switch.rs
└── test_waitgroup.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/cron_2.0_arm.yml
================================================
name: cron-2.0-arm
on:
schedule: [cron: "30 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
with:
ref: v2.0
- name: Run tests with tokio multi-thread
run: env WORKFLOW=1 make test
- name: Run tests with --release
run: env WORKFLOW=1 make test_release
- name: Run tests with --release tokio single-thread
run: env WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 make test_release
================================================
FILE: .github/workflows/cron_2.0_x86.yml
================================================
name: cron-2.0-x86
on:
schedule: [cron: "30 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: v2.0
- name: Run tests with tokio multi-thread
run: env WORKFLOW=1 make test
- name: Run tests with --release
run: env WORKFLOW=1 make test_release
- name: Run tests with --release tokio single-thread
run: env WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 make test_release
================================================
FILE: .github/workflows/cron_dev.yml
================================================
name: cron-dev
on:
schedule: [cron: "0 */6 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
- name: Build
run: cargo build --verbose
- name: Run tests with tokio multi-thread
run: env WORKFLOW=1 make test
- name: Run tests with tokio single-thread
run: env WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 make test
- name: Run tests with --release
run: env WORKFLOW=1 make test_release
- name: Run tests with async_std
run: env WORKFLOW=1 make test_async_std
================================================
FILE: .github/workflows/cron_dev_arm.yml
================================================
name: cron-dev-arm
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
with:
ref: dev
- name: Build
run: cargo build --verbose
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with tokio multi-thread
run: cd test-suite; RUSTFLAGS="--cfg tokio_unstable" WORKFLOW=1 exec cargo nextest run -F="tokio" --hide-progress-bar -j 1 --no-capture
- name: Run tests with --release
run: cd test-suite; RUSTFLAGS="--cfg tokio_unstable" WORKFLOW=1 exec cargo nextest run -F="tokio" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with --release tokio single-thread
run: cd test-suite; RUSTFLAGS="--cfg tokio_unstable" WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 exec cargo nextest run -F="tokio" --hide-progress-bar -j 1 --no-capture -r
- name: Dump log on cancel
if: ${{ cancelled() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_ring
path: /tmp/crossfire_ring.log
================================================
FILE: .github/workflows/cron_dev_arm_trace.yml
================================================
name: cron-dev-arm-trace
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
with:
ref: dev
- name: Rust version
run: rustc -V
- name: rustup
run: rustup show
- name: Build
run: cargo build --verbose
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with tokio multi-thread
run: cd test-suite; RUSTFLAGS="--cfg tokio_unstable" WORKFLOW=1 exec cargo nextest run -F="tokio,trace_log" --hide-progress-bar -j 1 --no-capture
- name: Run tests with tokio multi thread --release
run: cd test-suite; RUSTFLAGS="--cfg tokio_unstable" WORKFLOW=1 exec cargo nextest run -F="tokio,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with tokio single-thread --release
run: cd test-suite; RUSTFLAGS="--cfg tokio_unstable" WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 exec cargo nextest run -F="tokio,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Dump log on cancel
if: ${{ cancelled() || failure() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_ring
path: /tmp/crossfire_ring.log
================================================
FILE: .github/workflows/cron_master_async_std_arm.yml
================================================
name: cron-master-async_std-arm
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with async_std
run: env WORKFLOW=1 make test_async_std
- name: Run test with async_std release
run: env WORKFLOW=1 make test_async_std_release
- name: Run test with async_std release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="async_std,trace_log" --hide-progress-bar -j 1 --no-capture -r
================================================
FILE: .github/workflows/cron_master_async_std_x86.yml
================================================
name: cron-master-async_std-x86
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with async_std
run: env WORKFLOW=1 make test_async_std
- name: Run test with async_std release
run: env WORKFLOW=1 make test_async_std_release
- name: Run test with async_std release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="async_std,trace_log" --hide-progress-bar -j 1 --no-capture -r
================================================
FILE: .github/workflows/cron_master_compio_arm.yml
================================================
name: cron-master-compio-arm
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with compio
run: env WORKFLOW=1 make test_compio
- name: Run test with compio release
run: env WORKFLOW=1 make test_compio_release
- name: Run test with compio release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="compio,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with compio_dispatcher
run: env WORKFLOW=1 make test_compio_dispatcher
- name: Run test with compio_dispatcher release
run: env WORKFLOW=1 make test_compio_dispatcher_release
- name: Run test with compio_dispatcher release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="compio_dispatcher,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Dump log on cancel
if: ${{ cancelled() || failure() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_ring
path: /tmp/crossfire_ring.log
================================================
FILE: .github/workflows/cron_master_compio_x86.yml
================================================
name: cron-master-compio-x86
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with compio
run: env WORKFLOW=1 make test_compio
- name: Run test with compio release
run: env WORKFLOW=1 make test_compio_release
- name: Run test with compio release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="compio,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with compio_dispatcher
run: env WORKFLOW=1 make test_compio_dispatcher
- name: Run test with compio_dispatcher release
run: env WORKFLOW=1 make test_compio_dispatcher_release
- name: Run test with compio_dispatcher release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="compio_dispatcher,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Dump log on cancel
if: ${{ cancelled() || failure() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_ring
path: /tmp/crossfire_ring.log
================================================
FILE: .github/workflows/cron_master_smol_arm.yml
================================================
name: cron-master-smol-arm
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with smol
run: env WORKFLOW=1 make test_smol
- name: Run test with smol release
run: env WORKFLOW=1 make test_smol_release
- name: Run test with smol release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="smol,trace_log" --hide-progress-bar -j 1 --no-capture -r
================================================
FILE: .github/workflows/cron_master_smol_x86.yml
================================================
name: cron-master-smol-x86
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with smol
run: env WORKFLOW=1 make test_smol
- name: Run test with smol release
run: env WORKFLOW=1 make test_smol_release
- name: Run test with smol release and trace_log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="smol,trace_log" --hide-progress-bar -j 1 --no-capture -r
================================================
FILE: .github/workflows/cron_master_threaded_arm.yml
================================================
name: cron-master-threaded-arm
on:
schedule: [cron: "20 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: env WORKFLOW=1 make test test_blocking_context
- name: Run tests with --release
run: env WORKFLOW=1 make test_release test_blocking_context
================================================
FILE: .github/workflows/cron_master_threaded_x86.yml
================================================
name: cron-master-threaded-x86
on:
schedule: [cron: "20 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: env WORKFLOW=1 make test test_blocking_context
- name: Run tests with --release
run: env WORKFLOW=1 make test_release test_blocking_context
================================================
FILE: .github/workflows/cron_master_tokio_arm.yml
================================================
name: cron-master-tokio-arm
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: "macos-15"
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
# We use cargo-nextest because cargo test does not forward cancellation signal
- uses: taiki-e/install-action@nextest
- name: Run tests with tokio multi-thread
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="tokio,time" --hide-progress-bar -j 1 --no-capture
- name: Run tests with tokio multi thread --release with trace log
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="tokio,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with tokio single-thread --release with trace_log
run: cd test-suite; WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 exec cargo nextest run -F="tokio,trace_log" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with tokio multi thread --release
run: cd test-suite; WORKFLOW=1 exec cargo nextest run -F="tokio,time" --hide-progress-bar -j 1 --no-capture -r
- name: Run tests with tokio single-thread --release
run: cd test-suite; WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 exec cargo nextest run -F="tokio,time" --hide-progress-bar -j 1 --no-capture -r
- name: Dump log on cancel
if: ${{ cancelled() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_ring
path: /tmp/crossfire_ring.log
================================================
FILE: .github/workflows/cron_master_tokio_x86.yml
================================================
name: cron-master-tokio-x86
on:
schedule: [cron: "0 */5 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests with tokio multi-thread
run: env WORKFLOW=1 make test
- name: Run tests with tokio current-thread
run: env WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 make test
- name: Run tests with tokio multi-thread --release
run: env WORKFLOW=1 make test_release
- name: Run tests with tokio current-thread --release
run: env WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 make test_release
================================================
FILE: .github/workflows/fast.yml
================================================
name: fast-validation
on:
push:
branches: [ "master" ]
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build with default
run: cargo build --verbose
- name: Clippy with default
run: cargo clippy -- -D warnings
- name: Build with tokio
run: cargo build -F tokio --verbose
- name: Build with async_std
run: cargo build -F async_std --verbose
- name: doc test & internal test
run: cargo test
- name: doc build
run: cargo doc --all-features
- name: Run basic tests with tokio
run: make test basic
- name: Run timeout tests with async_std
run: make test_async_std timeout
================================================
FILE: .github/workflows/leak.yml
================================================
name: leak
on:
schedule: [cron: "30 */10 * * *"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run test-suite with LSAN
run: env NIGHTLY="+nightly" RUSTFLAGS="-Zsanitizer=leak" WORKFLOW=1 make test
- name: Run internal test
run: make test_internal
================================================
FILE: .github/workflows/miri_dev.yml
================================================
name: miri-dev
on:
workflow_dispatch:
schedule: [cron: "20 */7 * * *"]
env:
CARGO_TERM_COLOR: always
jobs:
test_tokio:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
- name: install specified nightly toolchain
run: rustup toolchain install nightly-2025-12-01
- name: install miri component to specified nightly
run: rustup component add miri --toolchain nightly-2025-12-01
- name: Run miri tests without log (tokio multi thread)
run: cd test-suite; NIGHTLY_VERSION=nightly-2025-12-01 scripts/miri.sh -F tokio,time
- name: Run miri tests without log (tokio current thread)
run: cd test-suite; SINGLE_THREAD_RUNTIME=1 NIGHTLY_VERSION=nightly-2025-12-01 scripts/miri.sh -F tokio
================================================
FILE: .github/workflows/miri_dev_log.yml
================================================
name: miri-dev-log
on:
workflow_dispatch:
schedule: [cron: "30 */8 * * *"]
env:
CARGO_TERM_COLOR: always
jobs:
test_tokio:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: dev
- name: install specified nightly toolchain
run: rustup toolchain install nightly-2025-12-01
- name: install miri
run: rustup component add miri --toolchain nightly-2025-12-01
- name: Run miri tests with log (tokio multi thread)
run: cd test-suite; NIGHTLY_VERSION=nightly-2025-12-01 scripts/miri.sh --features trace_log,tokio
- name: collect log
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_miri_tokio_multithread
path: /tmp/crossfire_miri.log
================================================
FILE: .github/workflows/miri_tokio.yml
================================================
name: miri-tokio
on:
workflow_dispatch:
schedule: [cron: "20 */6 * * *"]
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install specified nightly toolchain
run: rustup toolchain install nightly-2025-12-01
- name: install miri
run: rustup component add miri --toolchain nightly-2025-12-01
#run: rustup component add --toolchain nightly-x86_64-unknown-linux-gnu miri
- name: Run miri tests without log (tokio multi thread)
run: cd test-suite; NIGHTLY_VERSION=nightly-2025-12-01 WORKFLOW=1 scripts/miri.sh -F tokio,time
================================================
FILE: .github/workflows/miri_tokio_cur.yml
================================================
name: miri-tokio-cur
on:
workflow_dispatch:
schedule: [cron: "50 */7 * * *"]
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install specified nightly toolchain
run: rustup toolchain install nightly-2025-12-01
- name: install miri
run: rustup component add miri --toolchain nightly-2025-12-01
#run: rustup component add --toolchain nightly-x86_64-unknown-linux-gnu miri
- name: Run miri tests without log (tokio current thread)
run: cd test-suite; NIGHTLY_VERSION=nightly-2025-12-01 WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 scripts/miri.sh -F tokio,time
================================================
FILE: .github/workflows/miri_tokio_cur_log.yml
================================================
name: miri-tokio-cur-log
on:
workflow_dispatch:
schedule: [cron: "20 */9 * * *"]
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install specified nightly toolchain
run: rustup toolchain install nightly-2025-12-01
- name: install miri
run: rustup component add miri --toolchain nightly-2025-12-01
#run: rustup component add --toolchain nightly-x86_64-unknown-linux-gnu miri
- name: Run miri tests with log (tokio current thread)
run: cd test-suite; NIGHTLY_VERSION=nightly-2025-12-01 WORKFLOW=1 SINGLE_THREAD_RUNTIME=1 scripts/miri.sh --features trace_log,tokio
- name: collect log
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_miri
path: /tmp/crossfire_miri.log
================================================
FILE: .github/workflows/miri_tokio_log.yml
================================================
name: miri-tokio-log
on:
workflow_dispatch:
schedule: [cron: "10 */7 * * *"]
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install specified nightly toolchain
run: rustup toolchain install nightly-2025-12-01
- name: install miri
run: rustup component add miri --toolchain nightly-2025-12-01
#run: rustup component add --toolchain nightly-x86_64-unknown-linux-gnu miri
- name: Run miri tests with log (tokio multi thread)
run: cd test-suite; NIGHTLY_VERSION=nightly-2025-12-01 WORKFLOW=1 scripts/miri.sh --features trace_log,tokio
- name: collect log
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: crossfire_miri_tokio_multithread
path: /tmp/crossfire_miri.log
================================================
FILE: .github/workflows/pr.yml
================================================
name: pr-validation
on:
pull_request:
types:
- opened
- synchronize
- ready_for_review
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build with default
run: cargo build --verbose
- name: Build with tokio
run: cargo build -F tokio --verbose
- name: Build with async_std
run: cargo build -F async_std --verbose
- name: Run tests with tokio
run: make test
- name: Run tests with tokio
run: make test_async_std timeout
================================================
FILE: .github/workflows/typos.yml
================================================
name: Typo checker
on:
pull_request:
types:
- opened
- synchronize
- ready_for_review
push:
branches: [ "master" ]
workflow_dispatch:
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-22.04
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v3
- name: Check spelling of the entire repository
uses: crate-ci/typos@v1.33.1
================================================
FILE: .gitignore
================================================
/target
Cargo.lock
tags
*.sw*
================================================
FILE: AGENTS.md
================================================
# General
- All comments and documents must be in English.
- Omit unnecessary obvious comments during coding.
- Documents must be concise, well organized into categories, with no duplicated topics or redundant information. Related topics should be organized in close proximity.
- If you don't know a 3rd-party API, look it up on `https://docs.rs/<crate>`.
- Do not run cargo clippy.
- Always use shorter token paths by importing traits or structures.
- Avoid importing namespaces inside functions.
# Test
- Because crate::spsc, mpsc, mpmc module have the same type alias, in the test just use `crossfire::*`, and distinguish the types and functions with `spsc::`, `mpsc::`, `mpmc::` prefix.
- Run test with `make test`. In order to prevent too long output truncated by AI tool, run test with `make test <test_name>` when you have a targeted test case.
- Do not use cargo test to run the test, always use `make test`. Test case cannot be run concurrently with cargo test default param.
- For statement that don't expect to fail, use `expect()` rather than `unwarp()`
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project 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://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
### Removed
### Changed
### Fixed
## [3.1.10] - 2026-05-05
### Fix
- waitgroup: Avoid miri report on stack borrow rule (issue #66)
## [3.1.9] - 2026-05-05
### Fix
- Reduce Send requirement in generic (issue #64), which makes the error prompt cleaner
## [3.1.8] - 2026-05-04
### Added
- Add WeakTx. which can downgrade from or upgrade to MTx / MAsyncTx
## [3.1.7] - 2026-03-19
### Added
- oneshot: Add TxOneshot::is_disconnected()
## [3.1.6] - 2026-03-18
### Added
- waitgroup: Add WaitGroupInline (which does not allocate)
## [3.1.4] - 2026-02-25
### Changed
- oneshot: Add Sync for TxOneshot
## [3.1.2] - 2026-02-16
### Changed
- waitgroup: Add inner T inside, just like Arc, this break previous 3.1.0 and 3.1.1
## [3.1.1] - 2026-02-15
### Changed
- waitgroup: Add Sync for WaitGroupGuard
## [3.1.0] - 2026-02-14
### Added
- Add WaitGroup that support async & blocking, with custom threshold.
- oneshot: Add recv_async_timeout & recv_async_with_timer
### Changed
- oneshot: Refactor oneshot and optimize out arc cost. try_recv() now require `&mut self`.
- async_tx/async_rx: Refactor SendTimeoutFuture/RecvTimeoutFuture signature, to remove boxed future usage
## [3.0.6] - 2026-02-11
### Fixed
- Fix multiplex: Ensure all message received before disconnect
## [3.0.5] - 2026-02-11
### Fixed
- Fix msrv to 1.79 (NonZero usage)
- Fix clippy warning and document
## [3.0.4] - 2026-02-03
### Fixed
- Avoid overflow evaluation in generic code
Remove Send/'static/Unpin limit from Flavor/Queue trait and struct definition,
add the limit to method.
- Blocking method and struct don't need Unpin.
- Async recv does not need Unpin.
## [3.0.3] - 2026-01-30
### Fixed
- Fix multiplex premature closing
## [3.0.2] - 2026-01-23
### Added
- Add missing into_async() method for blocking tx/rc
## [3.0.1] - 2026-01-22
### Changed
- Remove the mode setting from Multiplex (always use round-robin)
- Add default custom weight for Multiplex select, optimize selection cost (throughput +20%)
## [3.0.0] - 2026-01-18
### Changed
- Disable direct_copy to make miri happy
- Simplify waker cleaning logic
## [3.0.0.beta3] - 2026-01-16
### Change
- New implementation of ArraySpsc & ArrayMpsc, throughput +50%
- New implementation of OneMpsc, minor speed up.
- Change multiplex recv(), try_recv(), recv_timeout() to &self, and impl BlockingRxTrait.
- Remove unused lifetime param in BlockingRxTrait.
### Fixed
Problems from v3 beta
- Add more backoff yielding for One flavor, to ensure 8x1, 16x1 cases stable, and minor optimize.
- Fix commit_waiting state wrong condition, which lead to regression in cases like 1000 async tx.
- Spsc should disable direct_copy (which only safe for MP)
## [3.0.0.beta2] - 2026-01-15
- Fix Array visibility in flavor module
- Fix AsyncTxTrait for compio (The sleep does not have Send)
## [3.0.0.beta1] - 2026-01-14
### Changed
- Change interface to V3 generic flavor API
- Optimize for SPSC
### Added
- Add One flavor for bounded size 1 case
- Add Null flavor for cancellation purpose channel
- Add Select API
- Add Multiplex API
## [2.1.10] - 2026-01-10
### Added
- Add `oneshot` module
- Add test workflow for `compio` (by lisovskiy)
### Changed
- Allow Blocking/Async Tx/Rx trait to be used as trait objects
## [2.1.9] - 2025-12-31
- Fix speed regression on ARM (fix backoff)
## [2.1.8] - 2025-11-08
### Fixed
- Add `#[must_use]` to hint missing await on Future (by MathisWellmann)
## [2.1.7] - 2025-11-08
### Changed
- Depend on `futures-core` crate instead of `futures` (issue #45)
## [2.1.6] - 2025-10-10
### Changed
- Delete the code probing tokio (to prevent an issue in cargo 1.87-1.90 triggering the code without tokio feature enable)
## [2.1.5] - 2025-10-06
### Fixed
- Remove doc_auto_cfg because removal by rust
## [2.1.4] - 2025-10-01
### Changed
- Adjust backoff for Arm (increase size 1 speed)
- async: Use try_change_state() to reset init instead of get_state(), (Minor improvement on x86 bounded_100_async_n_n)
## [2.1.3] - 2025-09-26
### Added
- Add send_with_timer() and recv_with_timer() for other async runtime (eg. smol).
## [2.1.1-2.1.2]
### Changed
- Minor changed to doc
## [2.1.0] - 2025-9-21
### Changed
- Refactor to drop dependency of crossbeam-channel, the underlayering is modified version of crossbeam-queue.
- Bounded channel speed receive massive boost.
- AsyncTx can convert back and forth with Tx, and AsyncRx can convert back and forth with Rx.
- Optimise for VM machine that only have 1 cpu.
- Use MaybeUninit to optimise the moving of large blob message for bounded channel, in nearly full scenario.
- Rename ReceiveFuture to RecvFuture, ReceiveTimeoutFuture to RecvTimeoutFuture.
### Removed
- Remove AsyncTx::send_blocking() and AsyncRx::recv_blocking(), instead, you can use type conversion into Tx/Rx.
## [2.0.26] - 2025-08-30
### Fixed
- waker_registry: Fix hang detect by miri in cancel_waker(), issue #34
## [2.0.25] - 2025-08-29
### Fixed
- More strict with the waker status, issue #34 (use SeqCst in reset_init)
## [2.0.24] - 2025-08-26
### Fixed
- More strict with the waker status, issue #34 (spurious wake up, and waker commit)
## [2.0.23] - 2025-08-23
### Fixed
- Change is_disconnected() to SeqCst
## [2.0.22] - 2025-08-21
### Fixed
- RegistryMulti: Fix defend against infinite loop for sink/stream, code introduced from 2.0.20.
## [2.0.21] - 2025-08-21
### Added
- Add clone_to_vec() method in async / blocking tx/rx trait
### Fixed
- AsyncSink: Fix typo in clear waker on drop (Does not affect stability)
## [2.0.20] - 2025-08-17
### Added
- AsyncTxTrait: Add Into<AsyncSink<T>>
- AsyncRxTrait: Add Into<AsyncStream<T>>
### Fixed
- Change the behavior of AsyncSink::poll_send() and AsyncStream::poll_item(), to make sure
stream/sink wakers are notified, preventing deadlock from happening if user wants to cancel the operation.
Add explanation to the document.
- Defend against infinite loop when waking up all wakers, given the change of sink/stream.
## [2.0.19] - 2025-08-13
### Added
- Add capacity()
## [2.0.18] - 2025-08-11
### Fixed
- Change some atomic load ordering from Acquire to SeqCst to pass validation by Miri.
## [2.0.17] - 2025-08-08
### Fixed
- Reuse and cleanup waker as much as possible (for idle select scenario)
- Change some atomic store ordering from Release to SeqCst to avoid further trouble.
## [2.0.16] - 2025-08-04
### Added
- Add into_blocking()
- Add missing into_sink() for MAsyncTx.
- Add From for AsyncSink and AsyncStream.
## [2.0.15] - 2025-08-04
### Added
- Add missing conversion: MAsyncTx->AsyncTx and MTx->Tx
## [2.0.14] - 2025-08-03
### Changed
- Optimise bounded size 1 speed with backoff
- Updated benchmark result vs kanal to wiki
## [2.0.13] - 2025-07-24
### Fixed
- Fix a deadlock https://github.com/frostyplanet/crossfire-rs/issues/22
### Added
- Allow type conversion from AsyncTx -> Tx, AsyncRx -> Rx
## [2.0.12] - 2025-07-18
### Fixed
- Fix a possible hang in LockedQueue introduced from v2.0.5
## [2.0.11] - 2025-07-18
### Added
- Add Deref/AsRef for sender & receiver type to ChannelShared
- Add is_full(), get_tx_count(), get_rx_count()
- Revert the removal of send_blocking() and recv_blocking() (will maintain through 2.0.x)
### Removed
- Remove DerefMut because it's no used.
### Fixed
- Fix send_timeout() in blocking context
## [2.0.10] yanked
published with the wrong branch, do not use.
## [2.0.9] - 2025-07-16
### Added
- Add is_disconnected() to sender and receiver type.
- Add Deref for AsyncSink to AsyncTx, and AsyncStream to AsyncRx, remove duplicated code.
### Fixed
- Fix a rare deadlock, when only one future in async runtime (for example channel async-blocking or blocking-async).
Runtime will spuriously wake up with changed Waker.
### Removed
- Remove send_blocking() & recv_blocking(), which is anti-pattern. (Calling function that blocks might lead to deadlock in async runtime)
## [2.0.8] - 2025-07-14
### Added
- AsyncStream: Add try_recv(), len() & is_empty()
## [2.0.7] - 2025-07-13
### Added
- AsyncStream: Add poll_item() for writing custom future, as a replacement to AsyncRx's poll_item(),
but without the need of LockedWaker.
- Add AsyncSink::poll_send() for writing custom future, as a replacement to AsyncTx's poll_send(),
but without the need of LockedWaker.
- Implement Debug & Display for all senders and receivers.
### Remove
- Hide LockedWaker, since AsyncRx::poll_item() and AsyncTx::poll_send() is hidden.
### Changed
- Optimise speed for SPSC & MPSC up to 60% (with WeakCell)
- Add execution time log to test cases.
### Fixed
- Fix LockedQueue empty flag (not affecting usage, just not accurate to internal test cases)
## [2.0.6] - 2025-07-10
### Added
- Support timeout and tested on async-std
### Changed
- mark make_recv_future() & make_send_future() deprecated.
- Change poll_send() & poll_item() to private function.
## [2.0.5] - 2025-07-09
### Added
- Add send_timeout() & recv_timeout() for async context
### Fixed
- AsyncRx: Fix rare case that message left on disconnect
- Fixed document typo and improve description.
### Changed
- Optimise RegistryMulti, with 20%+ speed improved on MPSC / MPMC
## [2.0.4] - 2025-07-08
### Changed
- Remove Sync marker in Tx, Rx, AsyncTx, AsyncRx to prevent misuse with Arc
## [2.0.3] - 2025-07-07
### Changed
- Remove duplicated code.
### Fixed
- AsyncRx should not have Clone.
- Protect against misuse of spsc/mpsc when user should use mpmc (avoiding deadlocks)
## [2.0.2] - 2025-07-05
### Added
- Add channels for blocking context (which equals to crossbeam)
### Changed
- Remove unused Clone for LockedWaker
### Fixed
- spsc: Add missing unsupported size=0 overwrites
## [2.0.1] - 2025-07-03
### Added
- Add timeout API for blocking context (by Zach Schoenberger)
### Changed
- Set min Rust version and edition in alignment with crossbeam (by Zach Schoenberger)
## [2.0.0] - 2025-06-27
### Added
- spsc module
- Benchmark suite written with criterion.
### Changed
- Refactor the API design. Unify sender and receiver types.
- Removal of macro rules and refactor SendWakers & RecvWakers into Enum, thus removal of generic type in Channelshared structure.
- Removal of the spin lock in LockedWaker. Simplifying the logic without losing performance.
- Rewrite the test cases with rstest.
### Removed
- Drop SelectSame module, because of hard to maintain, can be replace with future-select.
## [1.1.0] - 2025-06-19
### Changed
- Migrate repo
From <http://github.com/qingstor/crossfire-rs> to <https://github.com/frostyplanet/crossfire-rs>
- Change rust edition to 2024, re-format the code and fix warnings.
## [1.0.1] - 2023-08-29
### Fixed
- Fix atomic ordering for ARM (Have been tested on some ARM deployment)
## [1.0.0] - 2022-12-03
### Changed
- Format all code and announcing v1.0
- I decided that x86_64 stable after one year test.
## [0.1.7] - 2021-08-22
### Fixed
- tx: Remove redundant old_waker.is_waked() on abandon
## [0.1.6] - 2021-08-21
### Fixed
- mpsc: Fix RxFuture old_waker.abandon in poll_item
## [0.1.5] - 2021-06-28
### Changed
- Replace deprecated compare_and_swap
### Fixed
- SelectSame: Fix close_handler last_index
- Fix fetch_add/sub ordering for ARM (discovered on test hang)
================================================
FILE: CONTRIBUTION
================================================
Original Author:
- Plan (frostyplanet at gmail.com)
Thanks:
- Zach Schoenberger
- MathisWellmann
- lisovskiy
- Sherlock-Holo
================================================
FILE: Cargo.toml
================================================
[workspace]
members = ["test-suite"]
[package]
name = "crossfire"
version = "3.1.10"
authors = ["plan <frostyplanet@gmail.com>"]
edition = "2021"
license = "Apache-2.0"
homepage = "https://github.com/frostyplanet/crossfire-rs"
readme = "README.md"
repository = "https://github.com/frostyplanet/crossfire-rs"
documentation = "https://docs.rs/crossfire"
keywords = ["async", "non-blocking", "lock-free", "channel"]
categories = ["concurrency", "data-structures"]
exclude = ["/ci/*", "/bors.toml"]
description = "channels for async and threads"
rust-version = "1.79"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
# Because cargo-show-asm cannot match local crossfire package
#[patch.crates-io]
#crossfire = { path = "." }
[dependencies]
crossbeam-utils = "0.8"
futures-core = "0.3"
parking_lot = "0"
tokio = { version = "1", features = ["time", "rt"], optional=true }
async-std = {version = "1", optional=true}
log = { version="0", optional=true}
smallvec = "1"
[dev-dependencies]
log = "0"
tokio = { version = "1", features = ["time", "sync", "rt-multi-thread", "rt", "macros"] }
smol = "2"
captains-log = "0"
## For profiling symbol
#[profile.release]
#debug = true
[features]
default = []
# Enable compat model for v2.x API
compat = []
# This will enable timeout function
tokio = ["dep:tokio"]
# This will enable timeout function
async_std = ["dep:async-std"]
# for test workflow debugging
trace_log = ["dep:log"]
[package.metadata.docs.rs]
all-features = true
# enable features in the documentation
rustdoc-args = ["--cfg", "docsrs"]
[lints.clippy]
new_ret_no_self = "allow"
needless_range_loop = "allow"
type_complexity = "allow"
needless_return = "allow"
mut_from_ref = "allow"
transmute_ptr_to_ref = "allow"
len_without_is_empty = "allow"
new_without_default = "allow"
result_unit_err = "allow"
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (C) 2023-2025 The Crossfire Project Developers
Copyright (C) 2016-2023 Yunify Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: Makefile
================================================
PRIMARY_TARGET := $(firstword $(MAKECMDGOALS))
ARGS := $(filter-out $(PRIMARY_TARGET), $(MAKECMDGOALS))
RUN_TEST_CASE = _run_test_case() { \
case="$(filter-out $ARGS,$(MAKECMDGOALS))"; \
if [ -n "$${WORKFLOW}" ]; then \
export TEST_FLAG=" -- -q --test-threads=1"; \
else \
export TEST_FLAG=" -- --nocapture --test-threads=1"; \
export LOG_FILE="/tmp/test_crossfire.log"; \
fi; \
RUST_BACKTRACE=full cargo ${NIGHTLY} test -p crossfire-test ${ARGS} $${FEATURE_FLAG} $${TEST_FLAG}; \
}
RUN_RELEASE_CASE = _run_test_release_case() { \
case="$(filter-out $@,$(MAKECMDGOALS))"; \
if [ -n "$${WORKFLOW}" ]; then \
export TEST_FLAG=" --release -- -q --test-threads=1"; \
else \
export LOG_FILE="/tmp/test_crossfire.log"; \
export TEST_FLAG=" --release -- --nocapture --test-threads=1"; \
fi; \
RUST_BACKTRACE=full cargo ${NIGHTLY} test -p crossfire-test ${ARGS} $${FEATURE_FLAG} $${TEST_FLAG}; \
}
RUN_BENCH = _run_bench() { \
cd test-suite; \
cargo bench --bench ${ARGS}; \
}
INSTALL_GITHOOKS = _install_githooks() { \
git config core.hooksPath ./git-hooks; \
}
.PHONY: git-hooks
git-hooks:
@$(INSTALL_GITHOOKS); _install_githooks
.PHONY: init
init: git-hooks
.PHONY: fmt
fmt: init
cargo fmt
.PHONY: doc
doc:
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
# usage:
# make test
# make test test_async
.PHONY: test
test: init
@echo "Run test"
@${RUN_TEST_CASE}; FEATURE_FLAG="-F tokio,time"; _run_test_case
@echo "Done"
# test with ringfile for deadlog
.PHONY: test_log
test_log: init
@echo "Run test"
@${RUN_TEST_CASE}; FEATURE_FLAG="-F tokio,time,trace_log"; _run_test_case
@echo "Done"
.PHONY: test_async_std
test_async_std: init
@echo "Run test"
@${RUN_TEST_CASE}; FEATURE_FLAG="-F async_std,time,"; _run_test_case
@echo "Done"
.PHONY: test_log_async_std
test_log_async_std: init
@echo "Run test"
@${RUN_TEST_CASE}; FEATURE_FLAG="-F async_std,time,trace_log"; _run_test_case
@echo "Done"
.PHONY: test_release
test_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F tokio,time"; _run_test_release_case
# test with ringfile for deadlog
.PHONY: test_log_release
test_log_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F tokio,time,trace_log"; _run_test_release_case
.PHONY: test_async_std_release
test_async_std_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F async_std,time"; _run_test_release_case
# test with ringfile for deadlog
.PHONY: test_log_async_std_release
test_log_async_std_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F async_std,time,trace_log"; _run_test_release_case
.PHONY: test_smol
test_smol:
@${RUN_TEST_CASE}; FEATURE_FLAG="-F smol,time"; _run_test_case
# test with ringfile for deadlog
.PHONY: test_log_smol
test_log_smol:
@${RUN_TEST_CASE}; FEATURE_FLAG="-F smol,time,trace_log"; _run_test_case
.PHONY: test_smol_release
test_smol_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F smol,time"; _run_test_release_case
# test with ringfile for deadlog
.PHONY: test_log_smol_release
test_log_smol_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F smol,trace_log,time"; _run_test_release_case
.PHONY: test_compio
test_compio:
@${RUN_TEST_CASE}; FEATURE_FLAG="-F compio"; _run_test_case
# test with ringfile for deadlog
.PHONY: test_log_compio
test_log_compio:
@${RUN_TEST_CASE}; FEATURE_FLAG="-F compio,trace_log"; _run_test_case
.PHONY: test_compio_release
test_compio_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F compio"; _run_test_release_case
# test with ringfile for deadlog
.PHONY: test_log_compio_release
test_log_compio_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F compio,trace_log"; _run_test_release_case
.PHONY: test_compio_dispatcher
test_compio_dispatcher:
@${RUN_TEST_CASE}; FEATURE_FLAG="-F compio_dispatcher"; _run_test_case
# test with ringfile for deadlog
.PHONY: test_log_compio_dispatcher
test_log_compio_dispatcher:
@${RUN_TEST_CASE}; FEATURE_FLAG="-F compio_dispatcher,trace_log"; _run_test_case
.PHONY: test_compio_dispatcher_release
test_compio_dispatcher_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F compio_dispatcher"; _run_test_release_case
# test with ringfile for deadlog
.PHONY: test_log_compio_dispatcher_release
test_log_compio_dispatcher_release:
@${RUN_RELEASE_CASE}; FEATURE_FLAG="-F compio_dispatcher,trace_log"; _run_test_release_case
# Usage: make bench crossfire bounded_100_async_1_1
.PHONY: bench
bench:
@${RUN_BENCH}; _run_bench
.PHONY: test_leak
test_leak: test_internal
@${RUN_TEST_CASE}; NIGHTLY="+nightly" RUSTFLAGS="-Zsanitizer=leak"; _run_test_case
.PHONY: test_internal
test_internal:
RUSTFLAGS="-Zsanitizer=leak" cargo +nightly test -F trace_log --lib -- --nocapture ${ARGS}
.PHONY: build
build: init
cargo build
.DEFAULT_GOAL = build
# Target name % means that it is a rule that matches anything, @: is a recipe;
# the : means do nothing
%:
@:
================================================
FILE: README.md
================================================
# Crossfire
[](
https://github.com/frostyplanet/crossfire-rs/actions)
[](
https://github.com/qignstor/crossfire-rs#license)
[](
https://crates.io/crates/crossfire)
[](
https://docs.rs/crossfire)
[](
https://www.rust-lang.org)
High-performance lockless spsc/mpsc/mpmc channels, algorithm derives crossbeam with improvements.
It supports async contexts and bridges the gap between async and blocking contexts.
For the concept, please refer to the [wiki](https://github.com/frostyplanet/crossfire-rs/wiki).
## Version history
* v1.0: Used in production since 2022.12.
* v2.0: [2025.6] Refactored the codebase and API
by removing generic types from the ChannelShared type, which made it easier to code with.
* v2.1: [2025.9] Removed the dependency on crossbeam-channel
and implemented with [a modified version of crossbeam-queue](https://github.com/frostyplanet/crossfire-rs/wiki/crossbeam-related),
brings 2x performance improvements for both async and blocking contexts.
* v3.0: [2026.1] Refactored API back to generic flavor interface, added [select](https://docs.rs/crossfire/latest/crossfire/select/index.html).
Dedicated optimization: Bounded SPSC +70%, MPSC +30%, one-size +20%.
Eliminate enum dispatch cost, async performance improved for another 33%.
Checkout [compat](https://docs.rs/crossfire/latest/crossfire/compat/index.html) for migration from v2.x.
## Performance
Being a lockless channel, crossfire outperforms other async-capable channels.
And thanks to a lighter notification mechanism, in a blocking context, most cases are even
better than the original crossbeam-channel,
<img src="https://github.com/frostyplanet/crossfire-rs/wiki/images/benchmark-3.0.0-2026-01-18/mpsc_size_100_sync.png" alt="mpsc bounded size 100 blocking context">
<img src="https://github.com/frostyplanet/crossfire-rs/wiki/images/benchmark-3.0.0-2026-01-18/mpmc_size_100_sync.png" alt="mpmc bounded size 100 blocking context">
<img src="https://github.com/frostyplanet/crossfire-rs/wiki/images/benchmark-3.0.0-2026-01-18/mpsc_size_100_tokio.png" alt="mpsc bounded size 100 async context">
<img src="https://github.com/frostyplanet/crossfire-rs/wiki/images/benchmark-3.0.0-2026-01-18/mpmc_size_100_tokio.png" alt="mpmc bounded size 100 async context">
More benchmark data is posted on [wiki](https://github.com/frostyplanet/crossfire-rs/wiki/benchmark-v3.0.0-2026%E2%80%9001%E2%80%9018).
Also, being a lockless channel, the algorithm relies on spinning and yielding. Spinning is good on
multi-core systems, but not friendly to single-core systems (like virtual machines).
So we provide a function `detect_backoff_cfg()` to detect the running platform.
Calling it within the initialization section of your code, will get a 2x performance boost on
VPS.
The benchmark is written in the criterion framework. You can run the benchmark by:
``` shell
make bench crossfire
make bench crossfire_select
```
## APIs
### Concurrency Modules
- [spsc](https://docs.rs/crossfire/latest/crossfire/spsc/index.html), [mpsc](https://docs.rs/crossfire/latest/crossfire/mpsc/index.html), [mpmc](https://docs.rs/crossfire/latest/crossfire/mpmc/index.html). Each has different underlying implementation
optimized to its concurrent model.
The SP or SC interface is only for non-concurrent operation. It's more memory-efficient in waker registration,
and has atomic ops cost reduced in the lockless algorithm.
- [oneshot](https://docs.rs/crossfire/latest/crossfire/oneshot/index.html) has its special sender/receiver type because using `Tx` / `Rx` will be too heavy.
- [select](https://docs.rs/crossfire/latest/crossfire/select/index.html):
- [Select<'a>](https://docs.rs/crossfire/latest/crossfire/select/struct.Select.html): crossbeam-channel style type erased API, borrows receiver address and select with "token"
- [Multiplex](https://docs.rs/crossfire/latest/crossfire/select/struct.Multiplex.html): Multiplex stream that owns multiple receiver, select from the same type of
channel flavors, for the same type of message.
- [waitgroup](https://docs.rs/crossfire/latest/crossfire/waitgroup/index.html) High performance WaitGroup that allows custom threshold.
### Flavors
The following lockless queues are expose in [flavor](https://docs.rs/crossfire/latest/crossfire/flavor/index.html) module, and each one have type alias in spsc/mpsc/mpmc:
- `List` (which use crossbeam `SegQueue`)
- `Array` (which is an enum that wraps crossbeam `ArrayQueue`, and a `One` if init with size<=1)
- For a bounded channel, a 0 size case is not supported yet. (rewrite as 1 size).
- The implementation for spsc & mpsc is simplified from mpmc version.
- `One` (which derives from `ArrayQueue` algorithm, but have better performance in size=1
scenario, because it have two slots to allow reader and writer works concurrently)
- `Null` (See the doc [null](https://docs.rs/crossfire/latest/crossfire/null/index.html)), for cancellation purpose channel, that only wakeup on
closing.
**NOTE** :
Although the name `Array`, `List` are the same between spsc/mpsc/mpmc module,
they are different type alias local to its parent module. We suggest distinguish by
namespace when import for use.
### Channel builder function
Aside from function `bounded_*`, `unbounded_*` which specify the sender / receiver type,
each module has [build()](https://docs.rs/crossfire/latest/crossfire/mpmc/fn.build.html) and [new()](https://docs.rs/crossfire/latest/crossfire/mpmc/fn.new.html) function, which can apply to any channel flavors, and any async/blocking combinations.
### Types
<table align="center" cellpadding="30">
<tr> <th rowspan="2"> Context </th><th colspan="2" align="center"> Sender (Producer) </th> <th colspan="2" align="center"> Receiver (Consumer) </th> </tr>
<tr> <td> Single </td> <td> Multiple </td><td> Single </td><td> Multiple </td></tr>
<tr><td align="center" rowspan="2"> <b>Blocking</b> </td>
<td colspan="2" align="center"> BlockingTxTrait </td>
<td colspan="2" align="center"> BlockingRxTrait </td></tr>
<tr>
<td align="center">Tx </td>
<td align="center">MTx</td>
<td align="center">Rx</td>
<td align="center">MRx</td>
</tr>
<tr><td><b>Weak reference</b></td><td></td><td><a>WeakTx</a></td></tr>
<tr><td align="center" rowspan="2"><b>Async</b></td>
<td colspan="2" align="center">AsyncTxTrait</td>
<td colspan="2" align="center">AsyncRxTrait</td></tr>
<tr>
<td>AsyncTx</td>
<td>MAsyncTx</td>
<td>AsyncRx</td>
<td>MAsyncRx</td></tr>
</table>
*Safety*: For the SP / SC version, `AsyncTx`, `AsyncRx`, `Tx`, and `Rx` are not `Clone` and without `Sync`.
Although can be moved to other threads, but not allowed to use send/recv while in an Arc. (Refer to the compile_fail
examples in the type document).
The benefit of using the SP / SC API is completely lockless waker registration, in exchange for a performance boost.
The sender/receiver can use the **`From`** trait to convert between blocking and async context
counterparts (refer to the [example](#example) below)
### Error types
Error types are the same as crossbeam-channel:
`TrySendError`, `SendError`, `SendTimeoutError`, `TryRecvError`, `RecvError`, `RecvTimeoutError`
### Async compatibility
Tested on tokio-1.x and async-std-1.x, crossfire is runtime-agnostic.
The following scenarios are considered:
* The `AsyncTx::send()` and `AsyncRx::recv()` operations are **cancellation-safe** in an async context.
You can safely use the select! macro and timeout() function in tokio/futures in combination with recv().
On cancellation, `SendFuture` and `RecvFuture` will trigger drop(), which will clean up the state of the waker,
making sure there is no memory-leak and deadlock.
But you cannot know the true result from SendFuture, since it's dropped
upon cancellation. Thus, we suggest using `AsyncTx::send_timeout()` instead.
* When the "tokio" or "async_std" feature is enabled, we also provide two additional functions:
- `AsyncTx::send_timeout()`, which will return the message that failed to be sent in
`SendTimeoutError`. We guarantee the result is atomic. Alternatively, you can use
`AsyncTx::send_with_timer()`.
- `AsyncRx::recv_timeout()`, we guarantee the result is atomic.
Alternatively, you can use `AsyncRx::recv_with_timer()`.
* The waker footprint:
When using a multi-producer and multi-consumer scenario, there's a small memory overhead to pass along a `Weak`
reference of wakers.
Because we aim to be lockless, when the sending/receiving futures are canceled (like tokio::time::timeout()),
it might trigger an immediate cleanup if the try-lock is successful, otherwise will rely on lazy cleanup.
(This won't be an issue because weak wakers will be consumed by actual message send and recv).
On an idle-select scenario, like a notification for close, the waker will be reused as much as possible
if poll() returns pending.
* Handle written future:
The future object created by `AsyncTx::send()`, `AsyncTx::send_timeout()`, `AsyncRx::recv()`,
`AsyncRx::recv_timeout()` is `Sized`. You don't need to put them in `Box`.
If you like to use poll function directly for complex behavior, you can call
`AsyncSink::poll_send()` or `AsyncStream::poll_item()` with Context.
## Usage
Cargo.toml:
```toml
[dependencies]
crossfire = "3.1"
```
### Feature flags
* `compat`: Enable the compat model, which has the same API namespace struct as V2.x
* `tokio`: Enable `send_timeout()`, `recv_timeout()` with tokio sleep function. (conflict
with `async_std` feature)
* `async_std`: Enable send_timeout, recv_timeout with async-std sleep function. (conflict
with `tokio` feature)
* `trace_log`: Development mode, to enable internal log while testing or benchmark, to debug deadlock issues.
### Example
blocking / async sender receiver mixed together
```rust
extern crate crossfire;
use crossfire::*;
#[macro_use]
extern crate tokio;
use tokio::time::{sleep, interval, Duration};
#[tokio::main]
async fn main() {
let (tx, rx) = mpmc::bounded_async::<usize>(100);
let mut recv_counter = 0;
let mut co_tx = Vec::new();
let mut co_rx = Vec::new();
const ROUND: usize = 1000;
let _tx: MTx<mpmc::Array<usize>> = tx.clone().into_blocking();
co_tx.push(tokio::task::spawn_blocking(move || {
for i in 0..ROUND {
_tx.send(i).expect("send ok");
}
}));
co_tx.push(tokio::spawn(async move {
for i in 0..ROUND {
tx.send(i).await.expect("send ok");
}
}));
let _rx: MRx<mpmc::Array<usize>> = rx.clone().into_blocking();
co_rx.push(tokio::task::spawn_blocking(move || {
let mut count: usize = 0;
'A: loop {
match _rx.recv() {
Ok(_i) => {
count += 1;
}
Err(_) => break 'A,
}
}
count
}));
co_rx.push(tokio::spawn(async move {
let mut count: usize = 0;
'A: loop {
match rx.recv().await {
Ok(_i) => {
count += 1;
}
Err(_) => break 'A,
}
}
count
}));
for th in co_tx {
let _ = th.await.unwrap();
}
for th in co_rx {
recv_counter += th.await.unwrap();
}
assert_eq!(recv_counter, ROUND * 2);
}
```
## Test status
**NOTE**: Because we has push the speed to a level no one has gone before,
it can put a pure pressure to the async runtime.
Some hidden bug (especially atomic ops on weaker ordering platform) might occur:
The test is placed in test-suite directory, run with:
```
make test
```
<table cellpadding="30">
<tr><th>arch</th><th>runtime</th><th>workflow</th><th>status</th></tr>
<tr>
<td align="center" rowspan="5">x86_64</td>
<td>threaded</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_threaded_x86.yml">cron_master_threaded_x86</a> </td>
<td>STABLE</td>
</tr>
<tr><td>tokio 1.47.1</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_tokio_x86.yml">cron_master_tokio_x86</a></td>
<td>STABLE<br/>
</td>
</tr>
<tr><td>async-std</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_async_std_x86.yml">cron_master_async_std_x86</a></td>
<td>STABLE</td>
</tr>
<tr><td>smol</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_smol_x86.yml">cron_master_smol-x86</a></td>
<td>STABLE</td>
<tr><td>compio</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_compio_x86.yml">cron_master_compio-x86</a></td>
<td>verifying</td>
</tr>
<tr><td align="center" rowspan="5">arm</td>
<td>threaded</td>
<td>
<a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_threaded_arm.yml">cron_master_threaded_arm</a><br/>
</td>
<td>STABLE</td>
</tr>
<tr>
<td>tokio >= 1.48 (<a href="https://github.com/tokio-rs/tokio/pull/7622">tokio PR #7622</a>)
</td>
<td>
<a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_tokio_arm.yml">cron_master_tokio_arm</a><br/>
</td>
<td> SHOULD UPGRADE tokio to 1.48<br/>
STABLE
</td>
</tr>
<tr>
<td>async-std</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_async_std_arm.yml">cron_master_async_std_arm</a></td>
<td>STABLE</td>
</tr>
<tr>
<td>smol</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_smol_arm.yml">cron_master_smol_arm</a> </td>
<td>STABLE</td>
</tr>
<tr>
<td>compio</td>
<td><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/cron_master_compio_arm.yml">cron_master_compio_arm</a> </td>
<td>verifying</td>
</tr>
<tr>
<td rowspan="4">miri (emulation)</td>
<td>threaded</td>
<td rowspan="2"><a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/miri_tokio.yml">miri_tokio</a><br />
<a href="https://github.com/frostyplanet/crossfire-rs/actions/workflows/miri_tokio_cur.yml">miri_tokio_cur</a>
</td>
<td>STABLE</td>
</tr>
<tr><td>tokio</td><td>STABLE</td>
</tr>
<tr><td>async-std</td><td>-</td> <td> (timerfd_create) not supported by miri </td>
</tr>
<tr><td>smol</td><td>-</td> <td> (timerfd_create) not supported by miri </td>
</tr>
</table>
### Debugging deadlock issue
**Debug locally**:
Use `--features trace_log` to run the bench or test until it hangs, then press `ctrl+c` or send `SIGINT`, there will be latest log dump to /tmp/crossfire_ring.log (refer to tests/common.rs `_setup_log()`)
**Debug with github workflow**: https://github.com/frostyplanet/crossfire-rs/issues/37
================================================
FILE: benches/inner.rs
================================================
use criterion::*;
use crossbeam_queue::{ArrayQueue, SegQueue};
use crossbeam_utils::Backoff;
use crossfire::collections::*;
use parking_lot::Mutex;
use std::cell::UnsafeCell;
use std::collections::VecDeque;
use std::sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc, Weak,
};
use std::thread;
use std::time::Duration;
const ONE_MILLION: usize = 1000000;
struct Foo {
_inner: usize,
}
pub struct LockedQueue<T> {
empty: AtomicBool,
queue: Mutex<VecDeque<T>>,
}
impl<T> LockedQueue<T> {
#[inline]
pub fn new(cap: usize) -> Self {
Self { empty: AtomicBool::new(true), queue: Mutex::new(VecDeque::with_capacity(cap)) }
}
#[inline(always)]
pub fn push(&self, msg: T) {
let mut guard = self.queue.lock();
if guard.is_empty() {
self.empty.store(false, Ordering::Release);
}
guard.push_back(msg);
}
#[inline(always)]
pub fn pop(&self) -> Option<T> {
if self.empty.load(Ordering::Acquire) {
return None;
}
let mut guard = self.queue.lock();
if let Some(item) = guard.pop_front() {
if guard.len() == 0 {
self.empty.store(true, Ordering::Release);
}
Some(item)
} else {
None
}
}
#[inline(always)]
pub fn len(&self) -> usize {
let guard = self.queue.lock();
guard.len()
}
#[allow(dead_code)]
#[inline(always)]
pub fn exists(&self) -> bool {
!self.empty.load(Ordering::Acquire)
}
}
pub struct SpinQueue<T> {
lock: AtomicBool,
queue: UnsafeCell<VecDeque<T>>,
}
unsafe impl<T> Send for SpinQueue<T> {}
unsafe impl<T> Sync for SpinQueue<T> {}
impl<T> SpinQueue<T> {
fn new(cap: usize) -> Self {
Self { lock: AtomicBool::new(false), queue: UnsafeCell::new(VecDeque::with_capacity(cap)) }
}
#[inline(always)]
fn get_queue(&self) -> &mut VecDeque<T> {
unsafe { std::mem::transmute(self.queue.get()) }
}
#[inline]
fn push(&self, msg: T) {
let backoff = Backoff::new();
while self.lock.swap(true, Ordering::SeqCst) {
backoff.spin();
}
self.get_queue().push_back(msg);
self.lock.store(false, Ordering::Release);
}
#[inline]
fn pop(&self) -> Option<T> {
let backoff = Backoff::new();
while self.lock.swap(true, Ordering::SeqCst) {
backoff.spin();
}
let r = self.get_queue().pop_front();
self.lock.store(false, Ordering::Release);
r
}
}
fn _bench_spin_queue(count: usize) {
let queue = Arc::new(SpinQueue::<Weak<Foo>>::new(10));
let mut th_s = Vec::new();
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..count {
let _queue = queue.clone();
let _counter = counter.clone();
th_s.push(thread::spawn(move || loop {
let i = _counter.fetch_add(1, Ordering::SeqCst);
if i < ONE_MILLION {
if let Some(weak) = _queue.pop() {
let _ = weak.upgrade();
}
} else {
break;
}
}));
}
th_s.push(thread::spawn(move || {
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
queue.push(Arc::downgrade(&foo));
}
}));
for th in th_s {
let _ = th.join();
}
}
fn _bench_locked_queue(count: usize) {
let queue = Arc::new(LockedQueue::<Weak<Foo>>::new(10));
let mut th_s = Vec::new();
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..count {
let _queue = queue.clone();
let _counter = counter.clone();
th_s.push(thread::spawn(move || loop {
let i = _counter.fetch_add(1, Ordering::SeqCst);
if i < ONE_MILLION {
if let Some(weak) = _queue.pop() {
let _ = weak.upgrade();
}
} else {
break;
}
}));
}
th_s.push(thread::spawn(move || {
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
queue.push(Arc::downgrade(&foo));
}
}));
for th in th_s {
let _ = th.join();
}
}
fn _bench_array_queue(count: usize) {
let queue = Arc::new(ArrayQueue::<Weak<Foo>>::new(1));
let mut th_s = Vec::new();
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..count {
let _queue = queue.clone();
let _counter = counter.clone();
th_s.push(thread::spawn(move || loop {
let i = _counter.fetch_add(1, Ordering::SeqCst);
if i < ONE_MILLION {
if let Some(weak) = _queue.pop() {
let _ = weak.upgrade();
}
} else {
break;
}
}));
}
th_s.push(thread::spawn(move || {
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
queue.force_push(Arc::downgrade(&foo));
}
}));
for th in th_s {
let _ = th.join();
}
}
fn _bench_seg_queue(count: usize) {
let queue = Arc::new(SegQueue::<Weak<Foo>>::new());
let mut th_s = Vec::new();
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..count {
let _queue = queue.clone();
let _counter = counter.clone();
th_s.push(thread::spawn(move || loop {
let i = _counter.fetch_add(1, Ordering::SeqCst);
if i < ONE_MILLION {
if let Some(weak) = _queue.pop() {
let _ = weak.upgrade();
}
} else {
break;
}
}));
}
th_s.push(thread::spawn(move || {
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
queue.push(Arc::downgrade(&foo));
}
}));
for th in th_s {
let _ = th.join();
}
}
fn _bench_weak_cell(count: usize) {
let cell = Arc::new(WeakCell::<Foo>::new());
let mut th_s = Vec::new();
let counter = Arc::new(AtomicUsize::new(0));
for _ in 0..count {
let _cell = cell.clone();
let _counter = counter.clone();
th_s.push(thread::spawn(move || loop {
let i = _counter.fetch_add(1, Ordering::SeqCst);
if i < ONE_MILLION {
let _ = _cell.pop();
} else {
break;
}
}));
}
th_s.push(thread::spawn(move || {
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
cell.put(Arc::downgrade(&foo));
}
}));
for th in th_s {
let _ = th.join();
}
}
fn _bench_empty(c: &mut Criterion) {
let mut group = c.benchmark_group("empty");
group.significance_level(0.1).sample_size(50);
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("weak_cell", |b| {
b.iter(|| {
let cell = WeakCell::<Foo>::new();
for _ in 0..ONE_MILLION {
let _ = cell.pop();
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("spin VecDeque", |b| {
b.iter(|| {
let queue = SpinQueue::<Foo>::new(10);
for _ in 0..ONE_MILLION {
let _ = queue.pop();
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("locked VecDeque", |b| {
b.iter(|| {
let queue = LockedQueue::<Foo>::new(10);
for _ in 0..ONE_MILLION {
let _ = queue.pop();
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("array_queue", |b| {
b.iter(|| {
let queue = ArrayQueue::<Foo>::new(1);
for _ in 0..ONE_MILLION {
let _ = queue.pop();
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("seg_queue", |b| {
b.iter(|| {
let queue = SegQueue::<Foo>::new();
for _ in 0..ONE_MILLION {
let _ = queue.pop();
}
})
});
}
fn _bench_sequence(c: &mut Criterion) {
let mut group = c.benchmark_group("sequence");
group.significance_level(0.1).sample_size(50);
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("weak_cell", |b| {
b.iter(|| {
let cell = WeakCell::<Foo>::new();
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
cell.put(Arc::downgrade(&foo));
let _ = cell.pop();
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("spin VecDeque", |b| {
b.iter(|| {
let queue = SpinQueue::new(10);
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
let _ = queue.push(Arc::downgrade(&foo));
if let Some(w) = queue.pop() {
let _ = w.upgrade();
}
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("locked VecDeque", |b| {
b.iter(|| {
let queue = LockedQueue::new(10);
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
let _ = queue.push(Arc::downgrade(&foo));
if let Some(w) = queue.pop() {
let _ = w.upgrade();
}
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("array_queue", |b| {
b.iter(|| {
let queue = ArrayQueue::<Weak<Foo>>::new(1);
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
let _ = queue.push(Arc::downgrade(&foo));
if let Some(w) = queue.pop() {
let _ = w.upgrade();
}
}
})
});
group.measurement_time(Duration::from_secs(10));
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_function("seg_queue", |b| {
b.iter(|| {
let queue = SegQueue::<Weak<Foo>>::new();
for _ in 0..ONE_MILLION {
let foo = Arc::new(Foo { _inner: 1 });
let _ = queue.push(Arc::downgrade(&foo));
if let Some(w) = queue.pop() {
let _ = w.upgrade();
}
}
})
});
}
fn _bench_threads(c: &mut Criterion) {
let mut group = c.benchmark_group("threads");
group.significance_level(0.1).sample_size(50);
group.measurement_time(Duration::from_secs(10));
for input in n_1() {
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_with_input(BenchmarkId::new("weak_cell", input), &input, |b, i| {
b.iter(|| _bench_weak_cell(*i))
});
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_with_input(BenchmarkId::new("spin VecDeque", input), &input, |b, i| {
b.iter(|| _bench_spin_queue(*i))
});
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_with_input(BenchmarkId::new("locked VecDeque", input), &input, |b, i| {
b.iter(|| _bench_locked_queue(*i))
});
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_with_input(BenchmarkId::new("array_queue", input), &input, |b, i| {
b.iter(|| _bench_array_queue(*i))
});
group.throughput(Throughput::Elements(ONE_MILLION as u64));
group.bench_with_input(BenchmarkId::new("seg_queue", input), &input, |b, i| {
b.iter(|| _bench_seg_queue(*i))
});
}
}
criterion_group!(benches, _bench_empty, _bench_sequence, _bench_threads);
criterion_main!(benches);
================================================
FILE: git-hooks/pre-commit
================================================
#!/bin/bash
make fmt || exit 1
# re add the files since changed by fmt
files=$(git diff --cached --name-only --diff-filter=ACM | grep '.rs$')
for f in $files; do
echo "git add $f"
git add $f
done
exit 0
================================================
FILE: rustfmt.toml
================================================
edition = "2021"
fn_params_layout = "Compressed"
newline_style = "Unix"
use_small_heuristics = "Max"
max_width = 100
use_field_init_shorthand = true
================================================
FILE: src/async_rx.rs
================================================
use crate::flavor::{FlavorMC, FlavorSelect};
use crate::select::SelectResult;
use crate::stream::AsyncStream;
#[cfg(feature = "trace_log")]
use crate::tokio_task_id;
use crate::{shared::*, trace_log, MRx, NotCloneable, ReceiverType, Rx};
use std::cell::Cell;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::ops::Deref;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
/// A single consumer (receiver) that works in an async context.
///
/// Additional methods in [ChannelShared] can be accessed through `Deref`.
///
/// `AsyncRx` can be converted into `Rx` via the `From` trait,
/// which means you can have two types of receivers, both within async and
/// blocking contexts, for the same channel.
///
/// **NOTE**: `AsyncRx` is not `Clone` or `Sync`.
/// If you need concurrent access, use [MAsyncRx] instead.
///
/// `AsyncRx` has a `Send` marker and can be moved to other coroutines.
/// The following code is OK:
///
/// ``` rust
/// use crossfire::*;
/// async fn foo() {
/// let (tx, rx) = mpsc::bounded_async::<usize>(100);
/// tokio::spawn(async move {
/// let _ = rx.recv().await;
/// });
/// drop(tx);
/// }
/// ```
///
/// Because `AsyncRx` does not have a `Sync` marker, using `Arc<AsyncRx>` will lose the `Send` marker.
///
/// For your safety, the following code **should not compile**:
///
/// ``` compile_fail
/// use crossfire::*;
/// use std::sync::Arc;
/// async fn foo() {
/// let (tx, rx) = mpsc::bounded_async::<usize>(100);
/// let rx = Arc::new(rx);
/// tokio::spawn(async move {
/// let _ = rx.recv().await;
/// });
/// drop(tx);
/// }
/// ```
pub struct AsyncRx<F: Flavor> {
pub(crate) shared: Arc<ChannelShared<F>>,
// Remove the Sync marker to prevent being put in Arc
_phan: PhantomData<Cell<()>>,
}
unsafe impl<F: Flavor> Send for AsyncRx<F> {}
impl<F: Flavor> fmt::Debug for AsyncRx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AsyncRx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for AsyncRx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AsyncRx{:p}", self)
}
}
impl<F: Flavor> Drop for AsyncRx<F> {
#[inline(always)]
fn drop(&mut self) {
self.shared.close_rx();
}
}
impl<F: Flavor> From<Rx<F>> for AsyncRx<F> {
fn from(value: Rx<F>) -> Self {
value.add_rx();
Self::new(value.shared.clone())
}
}
impl<F: Flavor> AsyncRx<F> {
#[inline]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self { shared, _phan: Default::default() }
}
/// Return true if the other side has closed
#[inline(always)]
pub fn is_disconnected(&self) -> bool {
self.shared.is_tx_closed()
}
#[inline]
pub fn into_stream(self) -> AsyncStream<F> {
AsyncStream::new(self)
}
#[inline]
pub fn into_blocking(self) -> Rx<F> {
self.into()
}
}
impl<F: Flavor> AsyncRx<F> {
/// Receives a message from the channel. This method will await until a message is received or the channel is closed.
///
/// This function is cancellation-safe, so it's safe to use with `timeout()` and the `select!` macro.
/// When a [RecvFuture] is dropped, no message will be received from the channel.
///
/// For timeout scenarios, there's an alternative: [AsyncRx::recv_timeout()].
///
/// Returns `Ok(T)` on success.
///
/// Returns Err([RecvError]) if the sender has been dropped.
#[inline(always)]
pub fn recv<'a>(&'a self) -> RecvFuture<'a, F> {
RecvFuture { rx: self, waker: None }
}
// NOTE: we cannot use async fn recv_timeout signature because &self is not Send
/// Receives a message from the channel with a timeout.
/// Will await when channel is empty.
///
/// The behavior is atomic: the message is either received successfully or the operation is canceled due to a timeout.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([RecvTimeoutError::Timeout]) when a message could not be received because the channel is empty and the operation timed out.
///
/// Returns Err([RecvTimeoutError::Disconnected]) if the sender has been dropped and the channel is empty.
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
#[inline]
pub fn recv_timeout(
&self, duration: std::time::Duration,
) -> RecvTimeoutFuture<'_, F, tokio::time::Sleep, ()> {
let sleep = tokio::time::sleep(duration);
self.recv_with_timer(sleep)
}
#[cfg(feature = "async_std")]
#[cfg_attr(docsrs, doc(cfg(feature = "async_std")))]
#[inline]
pub fn recv_timeout(
&self, duration: std::time::Duration,
) -> RecvTimeoutFuture<'_, F, impl Future<Output = ()>, ()> {
let sleep = async_std::task::sleep(duration);
self.recv_with_timer(sleep)
}
/// Receives a message from the channel with a custom timer function (from other async runtime).
///
/// The behavior is atomic: the message is either received successfully or the operation is canceled due to a timeout.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([RecvTimeoutError::Timeout]) when a message could not be received because the channel is empty and the operation timed out.
///
/// Returns Err([RecvTimeoutError::Disconnected]) if the sender has been dropped and the channel is empty.
///
/// # Argument:
///
/// * `sleep`: The sleep function.
/// The return value of `sleep` is ignore. We add generic `R` just in order to support smol::Timer
///
/// # Example:
///
/// with smol timer
///
/// ```rust
/// extern crate smol;
/// use std::time::Duration;
/// use crossfire::*;
/// async fn foo() {
/// let (tx, rx) = mpmc::bounded_async::<usize>(10);
/// match rx.recv_with_timer(smol::Timer::after(Duration::from_secs(1))).await {
/// Ok(_item)=>{
/// println!("message recv");
/// }
/// Err(RecvTimeoutError::Timeout)=>{
/// println!("timeout");
/// }
/// Err(RecvTimeoutError::Disconnected)=>{
/// println!("sender-side closed");
/// }
/// }
/// }
/// ```
#[inline]
pub fn recv_with_timer<'a, FR, R>(&'a self, sleep: FR) -> RecvTimeoutFuture<'a, F, FR, R>
where
FR: Future<Output = R>,
{
RecvTimeoutFuture { rx: self, waker: None, sleep }
}
/// Attempts to receive a message from the channel without blocking.
///
/// Returns `Ok(T)` on successful.
///
/// Returns Err([TryRecvError::Empty]) if the channel is empty.
///
/// Returns Err([TryRecvError::Disconnected]) if the sender has been dropped and the channel is empty.
#[inline(always)]
pub fn try_recv(&self) -> Result<F::Item, TryRecvError> {
self.shared.try_recv()
}
/// This method use with [select](crate::select::Select), guarantee non-blocking
///
/// # Panics
///
/// Panics if SelectResult from other receiver is passed.
#[inline(always)]
pub fn read_select(&self, result: SelectResult) -> Result<F::Item, RecvError>
where
F: FlavorSelect,
{
assert_eq!(
self as *const Self as *const u8, result.channel,
"invalid use select with another channel"
);
self.as_ref().read_with_token(result.token)
}
/// Internal function might change in the future. For public version, use AsyncStream::poll_item() instead
///
/// Returns `Ok(T)` on successful.
///
/// Return Err([TryRecvError::Empty]) for Poll::Pending case.
///
/// Return Err([TryRecvError::Disconnected]) when all Tx dropped and channel is empty.
#[inline(always)]
pub(crate) fn poll_item<const STREAM: bool>(
&self, ctx: &mut Context, o_waker: &mut Option<<F::Recv as Registry>::Waker>,
) -> Result<F::Item, TryRecvError> {
let shared = &self.shared;
// When the result is not TryRecvError::Empty,
// make sure always take the o_waker out and abandon,
// to skip the timeout cleaning logic in Drop.
macro_rules! on_recv_no_waker {
() => {{
trace_log!("rx{:?}: recv", tokio_task_id!());
}};
}
macro_rules! on_recv_waker {
($state: expr) => {{
trace_log!("rx{:?}: recv {:?} {:?}", tokio_task_id!(), o_waker, $state);
shared.recvs.cancel_waker(o_waker);
}};
}
macro_rules! try_recv {
($recv_func: ident => $waker_handle: block) => {
if let Some(item) = shared.inner.$recv_func() {
shared.on_recv();
$waker_handle
return Ok(item);
}
};
}
loop {
if o_waker.is_none() {
try_recv!(try_recv=>{ on_recv_no_waker!()});
// First call
if let Some(mut backoff) = shared.get_async_backoff() {
loop {
let complete = backoff.spin();
try_recv!(try_recv=>{ on_recv_no_waker!()});
if complete {
break;
}
}
}
} else {
try_recv!(try_recv => {on_recv_waker!(WakerState::Woken)});
}
if shared.recvs.reg_waker_async(ctx, o_waker).is_some() {
break;
}
// NOTE: The other side put something while reg_send and did not see the waker,
// should check the channel again, otherwise might incur a dead lock.
// NOTE: special API before we park
// because Miri is not happy about ArrayQueue pop ordering, which is not SeqCst
try_recv!(try_recv_final =>{ on_recv_waker!(WakerState::Init)});
if !STREAM {
let state = shared.recvs.commit_waiting(o_waker);
trace_log!("rx{:?}: commit_waiting {:?} {}", tokio_task_id!(), o_waker, state);
if state == WakerState::Woken as u8 {
continue;
}
}
break;
}
if shared.is_tx_closed() {
try_recv!(try_recv =>{ on_recv_waker!(WakerState::Closed)});
trace_log!("rx{:?}: disconnected {:?}", tokio_task_id!(), o_waker);
Err(TryRecvError::Disconnected)
} else {
Err(TryRecvError::Empty)
}
}
}
/// A fixed-sized future object constructed by [AsyncRx::recv()]
#[must_use]
pub struct RecvFuture<'a, F: Flavor> {
rx: &'a AsyncRx<F>,
waker: Option<<F::Recv as Registry>::Waker>,
}
unsafe impl<F: Flavor> Send for RecvFuture<'_, F> {}
impl<F: Flavor> Drop for RecvFuture<'_, F> {
#[inline]
fn drop(&mut self) {
if let Some(waker) = self.waker.as_ref() {
self.rx.shared.abandon_recv_waker(waker);
}
}
}
impl<F: Flavor> Future for RecvFuture<'_, F> {
type Output = Result<F::Item, RecvError>;
#[inline]
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
let mut _self = self.get_mut();
match _self.rx.poll_item::<false>(ctx, &mut _self.waker) {
Err(e) => {
if !e.is_empty() {
let _ = _self.waker.take();
Poll::Ready(Err(RecvError {}))
} else {
Poll::Pending
}
}
Ok(item) => {
debug_assert!(_self.waker.is_none());
Poll::Ready(Ok(item))
}
}
}
}
/// A fixed-sized future object constructed by [AsyncRx::recv_timeout()]
#[must_use]
pub struct RecvTimeoutFuture<'a, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
rx: &'a AsyncRx<F>,
waker: Option<<F::Recv as Registry>::Waker>,
sleep: FR,
}
unsafe impl<F, FR, R> Send for RecvTimeoutFuture<'_, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
}
impl<F, FR, R> Drop for RecvTimeoutFuture<'_, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
#[inline]
fn drop(&mut self) {
if let Some(waker) = self.waker.as_ref() {
self.rx.shared.abandon_recv_waker(waker);
}
}
}
impl<F, FR, R> Future for RecvTimeoutFuture<'_, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
type Output = Result<F::Item, RecvTimeoutError>;
#[inline]
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
// NOTE: we can use unchecked to bypass pin because we are not movig "sleep",
// neither it's exposed outside
let mut _self = unsafe { self.get_unchecked_mut() };
match _self.rx.poll_item::<false>(ctx, &mut _self.waker) {
Err(TryRecvError::Empty) => {
if unsafe { Pin::new_unchecked(&mut _self.sleep) }.poll(ctx).is_ready() {
return Poll::Ready(Err(RecvTimeoutError::Timeout));
}
Poll::Pending
}
Err(TryRecvError::Disconnected) => Poll::Ready(Err(RecvTimeoutError::Disconnected)),
Ok(item) => Poll::Ready(Ok(item)),
}
}
}
/// For writing generic code with MAsyncRx & AsyncRx
pub trait AsyncRxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
/// Receive message, will await when channel is empty.
///
/// Returns `Ok(T)` when successful.
///
/// returns Err([RecvError]) when all Tx dropped.
fn recv(&self) -> impl Future<Output = Result<T, RecvError>> + Send;
/// Waits for a message to be received from the channel, but only for a limited time.
/// Will await when channel is empty.
///
/// The behavior is atomic, either successfully polls a message,
/// or operation cancelled due to timeout.
///
/// Returns Ok(T) when successful.
///
/// Returns Err([RecvTimeoutError::Timeout]) when a message could not be received because the channel is empty and the operation timed out.
///
/// returns Err([RecvTimeoutError::Disconnected]) when all Tx dropped and channel is empty.
#[cfg(any(feature = "tokio", feature = "async_std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async_std"))))]
fn recv_timeout(
&self, timeout: std::time::Duration,
) -> impl Future<Output = Result<T, RecvTimeoutError>> + Send;
/// Receives a message from the channel with a custom timer function (from other async runtime).
///
/// The behavior is atomic: the message is either received successfully or the operation is canceled due to a timeout.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([RecvTimeoutError::Timeout]) when a message could not be received because the channel is empty and the operation timed out.
///
/// Returns Err([RecvTimeoutError::Disconnected]) if the sender has been dropped and the channel is empty.
///
/// # Argument:
///
/// * `fut`: The sleep function. It's possible to wrap this function with cancelable handle,
/// you can control when to stop polling. the return value of `fut` is ignore.
/// We add generic `R` just in order to support smol::Timer.
fn recv_with_timer<FR, R>(
&self, fut: FR,
) -> impl Future<Output = Result<T, RecvTimeoutError>> + Send
where
FR: Future<Output = R>;
/// Try to receive message, non-blocking.
///
/// Returns Ok(T) when successful.
///
/// Returns Err([TryRecvError::Empty]) when channel is empty.
///
/// Returns Err([TryRecvError::Disconnected]) when all Tx dropped and channel is empty.
fn try_recv(&self) -> Result<T, TryRecvError>;
/// The number of messages in the channel at the moment
fn len(&self) -> usize;
/// The capacity of the channel, return None for unbounded channel.
fn capacity(&self) -> Option<usize>;
/// Whether channel is empty at the moment
fn is_empty(&self) -> bool;
/// Whether the channel is full at the moment
fn is_full(&self) -> bool;
/// Return true if the other side has closed
fn is_disconnected(&self) -> bool;
/// Return the number of senders
fn get_tx_count(&self) -> usize;
/// Return the number of receivers
fn get_rx_count(&self) -> usize;
fn clone_to_vec(self, count: usize) -> Vec<Self>
where
Self: Sized;
fn to_stream(self) -> Pin<Box<dyn futures_core::stream::Stream<Item = T>>>;
fn get_wakers_count(&self) -> (usize, usize);
}
impl<F: Flavor> AsyncRxTrait<F::Item> for AsyncRx<F> {
#[inline(always)]
fn clone_to_vec(self, _count: usize) -> Vec<Self> {
assert_eq!(_count, 1);
vec![self]
}
#[inline(always)]
fn recv(&self) -> impl Future<Output = Result<F::Item, RecvError>> + Send {
AsyncRx::recv(self)
}
#[cfg(any(feature = "tokio", feature = "async_std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async_std"))))]
#[inline(always)]
fn recv_timeout(
&self, duration: std::time::Duration,
) -> impl Future<Output = Result<F::Item, RecvTimeoutError>> + Send {
AsyncRx::recv_timeout(self, duration)
}
#[inline(always)]
fn recv_with_timer<FR, R>(
&self, sleep: FR,
) -> impl Future<Output = Result<F::Item, RecvTimeoutError>> + Send
where
FR: Future<Output = R>,
{
AsyncRx::recv_with_timer(self, sleep)
}
#[inline(always)]
fn try_recv(&self) -> Result<F::Item, TryRecvError> {
AsyncRx::<F>::try_recv(self)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().get_tx_count() == 0
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
#[inline(always)]
fn to_stream(self) -> Pin<Box<dyn futures_core::stream::Stream<Item = F::Item>>> {
Box::pin(self.into_stream())
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
/// A multi-consumer (receiver) that works in an async context.
///
/// Inherits from [`AsyncRx<F>`] and implements `Clone`.
/// Additional methods in [ChannelShared] can be accessed through `Deref`.
///
/// You can use `into()` to convert it to `AsyncRx<F>`.
///
/// `MAsyncRx` can be converted into `MRx` via the `From` trait,
/// which means you can have two types of receivers, both within async and
/// blocking contexts, for the same channel.
pub struct MAsyncRx<F: Flavor>(pub(crate) AsyncRx<F>);
impl<F: Flavor> fmt::Debug for MAsyncRx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAsyncRx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for MAsyncRx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAsyncRx{:p}", self)
}
}
unsafe impl<F: Flavor> Sync for MAsyncRx<F> {}
impl<F: Flavor> Clone for MAsyncRx<F> {
#[inline]
fn clone(&self) -> Self {
let inner = &self.0;
inner.shared.add_rx();
Self(AsyncRx::new(inner.shared.clone()))
}
}
impl<F: Flavor> From<MAsyncRx<F>> for AsyncRx<F> {
fn from(rx: MAsyncRx<F>) -> Self {
rx.0
}
}
impl<F: Flavor + FlavorMC> MAsyncRx<F> {
#[inline]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self(AsyncRx::new(shared))
}
}
impl<F: Flavor> MAsyncRx<F> {
#[inline]
pub fn into_stream(self) -> AsyncStream<F> {
AsyncStream::new(self.0)
}
#[inline]
pub fn into_blocking(self) -> MRx<F> {
self.into()
}
}
impl<F: Flavor> Deref for MAsyncRx<F> {
type Target = AsyncRx<F>;
/// inherit all the functions of [AsyncRx]
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F: Flavor> From<MRx<F>> for MAsyncRx<F> {
fn from(value: MRx<F>) -> Self {
value.add_rx();
Self(AsyncRx::new(value.shared.clone()))
}
}
impl<F: Flavor + FlavorMC> AsyncRxTrait<F::Item> for MAsyncRx<F> {
#[inline(always)]
fn clone_to_vec(self, count: usize) -> Vec<Self> {
let mut v = Vec::with_capacity(count);
for _ in 0..count - 1 {
v.push(self.clone());
}
v.push(self);
v
}
#[inline(always)]
fn try_recv(&self) -> Result<F::Item, TryRecvError> {
self.0.try_recv()
}
#[inline(always)]
fn recv(&self) -> impl Future<Output = Result<F::Item, RecvError>> + Send {
self.0.recv()
}
#[cfg(any(feature = "tokio", feature = "async_std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async_std"))))]
#[inline(always)]
fn recv_timeout(
&self, duration: std::time::Duration,
) -> impl Future<Output = Result<F::Item, RecvTimeoutError>> + Send {
self.0.recv_timeout(duration)
}
#[inline(always)]
fn recv_with_timer<FR, R>(
&self, fut: FR,
) -> impl Future<Output = Result<F::Item, RecvTimeoutError>>
where
FR: Future<Output = R>,
{
self.0.recv_with_timer(fut)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().get_tx_count() == 0
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
#[inline(always)]
fn to_stream(self) -> Pin<Box<dyn futures_core::stream::Stream<Item = F::Item>>> {
Box::pin(self.into_stream())
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
impl<F: Flavor> Deref for AsyncRx<F> {
type Target = ChannelShared<F>;
#[inline(always)]
fn deref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for AsyncRx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for MAsyncRx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.0.shared
}
}
impl<T, F: Flavor<Item = T>> ReceiverType for AsyncRx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
AsyncRx::new(shared)
}
}
impl<F: Flavor> NotCloneable for AsyncRx<F> {}
impl<T, F: Flavor<Item = T> + FlavorMC> ReceiverType for MAsyncRx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
MAsyncRx::new(shared)
}
}
================================================
FILE: src/async_tx.rs
================================================
use crate::flavor::FlavorMP;
use crate::sink::AsyncSink;
#[cfg(feature = "trace_log")]
use crate::tokio_task_id;
use crate::weak::WeakTx;
use crate::{shared::*, trace_log, MTx, NotCloneable, SenderType, Tx};
use std::cell::Cell;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::mem::{needs_drop, MaybeUninit};
use std::ops::Deref;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
/// A single producer (sender) that works in an async context.
///
/// Additional methods in [ChannelShared] can be accessed through `Deref`.
///
/// `AsyncTx` can be converted into `Tx` via the `From` trait.
/// This means you can have two types of senders, both within async and blocking contexts, for the same channel.
///
/// **NOTE**: `AsyncTx` is not `Clone` or `Sync`.
/// If you need concurrent access, use [MAsyncTx] instead.
///
/// `AsyncTx` has a `Send` marker and can be moved to other coroutines.
/// The following code is OK:
///
/// ``` rust
/// use crossfire::*;
/// async fn foo() {
/// let (tx, rx) = spsc::bounded_async::<usize>(100);
/// tokio::spawn(async move {
/// let _ = tx.send(2).await;
/// });
/// drop(rx);
/// }
/// ```
///
/// Because `AsyncTx` does not have a `Sync` marker, using `Arc<AsyncTx>` will lose the `Send` marker.
///
/// For your safety, the following code **should not compile**:
///
/// ``` compile_fail
/// use crossfire::*;
/// use std::sync::Arc;
/// async fn foo() {
/// let (tx, rx) = spsc::bounded_async::<usize>(100);
/// let tx = Arc::new(tx);
/// tokio::spawn(async move {
/// let _ = tx.send(2).await;
/// });
/// drop(rx);
/// }
/// ```
pub struct AsyncTx<F: Flavor> {
pub(crate) shared: Arc<ChannelShared<F>>,
// Remove the Sync marker to prevent being put in Arc
_phan: PhantomData<Cell<()>>,
}
impl<F: Flavor> fmt::Debug for AsyncTx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AsyncTx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for AsyncTx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AsyncTx{:p}", self)
}
}
unsafe impl<F: Flavor> Send for AsyncTx<F> {}
impl<F: Flavor> Drop for AsyncTx<F> {
#[inline(always)]
fn drop(&mut self) {
self.shared.close_tx();
}
}
impl<F: Flavor> From<Tx<F>> for AsyncTx<F> {
fn from(value: Tx<F>) -> Self {
value.add_tx();
Self::new(value.shared.clone())
}
}
impl<F: Flavor> AsyncTx<F> {
#[inline]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self { shared, _phan: Default::default() }
}
#[inline]
pub fn into_sink(self) -> AsyncSink<F> {
AsyncSink::new(self)
}
#[inline]
pub fn into_blocking(self) -> Tx<F> {
self.into()
}
/// Return true if the other side has closed
#[inline(always)]
pub fn is_disconnected(&self) -> bool {
self.shared.is_rx_closed()
}
}
impl<F: Flavor> AsyncTx<F> {
/// Sends a message. This method will await until the message is sent or the channel is closed.
///
/// This function is cancellation-safe, so it's safe to use with `timeout()` and the `select!` macro.
/// When a [SendFuture] is dropped, no message will be sent. However, the original message
/// cannot be returned due to API limitations. For timeout scenarios, we recommend using
/// [AsyncTx::send_timeout()], which returns the message in a [SendTimeoutError].
///
/// Returns `Ok(())` on success.
///
/// Returns Err([SendError]) if the receiver has been dropped.
#[inline(always)]
pub fn send<'a>(&'a self, item: F::Item) -> SendFuture<'a, F> {
SendFuture { tx: self, item: MaybeUninit::new(item), waker: None }
}
/// Attempts to send a message without blocking.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([TrySendError::Full]) if the channel is full.
///
/// Returns Err([TrySendError::Disconnected]) if the receiver has been dropped.
#[inline]
pub fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
if self.shared.is_rx_closed() {
return Err(TrySendError::Disconnected(item));
}
let _item = MaybeUninit::new(item);
if self.shared.inner.try_send(&_item) {
self.shared.on_send();
Ok(())
} else {
unsafe { Err(TrySendError::Full(_item.assume_init())) }
}
}
/// Sends a message with a timeout.
/// Will await when channel is full.
///
/// The behavior is atomic: the message is either sent successfully or returned with error.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([SendTimeoutError::Timeout]) if the operation timed out. The error contains the message that failed to be sent.
///
/// Returns Err([SendTimeoutError::Disconnected]) if the receiver has been dropped. The error contains the message that failed to be sent.
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
#[inline]
pub fn send_timeout(
&self, item: F::Item, duration: std::time::Duration,
) -> SendTimeoutFuture<'_, F, tokio::time::Sleep, ()> {
let sleep = tokio::time::sleep(duration);
self.send_with_timer(item, sleep)
}
#[cfg(feature = "async_std")]
#[cfg_attr(docsrs, doc(cfg(feature = "async_std")))]
#[inline]
pub fn send_timeout(
&self, item: F::Item, duration: std::time::Duration,
) -> SendTimeoutFuture<'_, F, impl Future<Output = ()>, ()> {
let sleep = async_std::task::sleep(duration);
self.send_with_timer(item, sleep)
}
/// Sends a message with a custom timer function (from other async runtime).
///
/// The behavior is atomic: the message is either sent successfully or returned with error.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([SendTimeoutError::Timeout]) if the operation timed out. The error contains the message that failed to be sent.
///
/// Returns Err([SendTimeoutError::Disconnected]) if the receiver has been dropped. The error contains the message that failed to be sent.
///
/// # Argument:
///
/// * `fut`: The sleep function. It's possible to wrap this function with cancelable handle,
/// you can control when to stop polling. the return value of `fut` is ignore.
/// We add generic `R` just in order to support smol::Timer.
///
/// # Example:
///
/// ```rust
/// extern crate smol;
/// use std::time::Duration;
/// use crossfire::*;
/// async fn foo() {
/// let (tx, rx) = mpmc::bounded_async::<usize>(10);
/// match tx.send_with_timer(1, smol::Timer::after(Duration::from_secs(1))).await {
/// Ok(_)=>{
/// println!("message sent");
/// }
/// Err(SendTimeoutError::Timeout(_item))=>{
/// println!("send timeout");
/// }
/// Err(SendTimeoutError::Disconnected(_item))=>{
/// println!("receiver-side closed");
/// }
/// }
/// }
/// ```
#[inline]
pub fn send_with_timer<FR, R>(&self, item: F::Item, fut: FR) -> SendTimeoutFuture<'_, F, FR, R>
where
FR: Future<Output = R>,
{
SendTimeoutFuture { tx: self, item: MaybeUninit::new(item), waker: None, sleep: fut }
}
/// Internal function might change in the future. For public version, use AsyncSink::poll_send() instead.
///
/// Returns `Poll::Ready(Ok(()))` on message sent.
///
/// Returns `Poll::Pending` for Poll::Pending case.
///
/// Returns `Poll::Ready(Err(())` when all Rx dropped.
#[inline(always)]
pub(crate) fn poll_send<'a, const SINK: bool>(
&self, ctx: &'a mut Context, item: &MaybeUninit<F::Item>,
o_waker: &'a mut Option<<F::Send as Registry>::Waker>,
) -> Poll<Result<(), ()>> {
let shared = &self.shared;
if shared.is_rx_closed() {
trace_log!("tx{:?}: closed {:?}", tokio_task_id!(), o_waker);
return Poll::Ready(Err(()));
}
// When the result is not TrySendError::Full,
// make sure always take the o_waker out and abandon,
// to skip the timeout cleaning logic in Drop.
loop {
if shared.inner.try_send(item) {
shared.on_send();
if let Some(_waker) = o_waker.take() {
trace_log!("tx{:?}: send {:?}", tokio_task_id!(), _waker);
} else {
trace_log!("tx{:?}: send", tokio_task_id!());
}
return Poll::Ready(Ok(()));
}
if o_waker.is_none() {
if let Some(mut backoff) = shared.get_async_backoff() {
loop {
backoff.spin();
if shared.inner.try_send(item) {
shared.on_send();
trace_log!("tx{:?}: send", tokio_task_id!());
return Poll::Ready(Ok(()));
}
if backoff.is_completed() {
break;
}
}
}
}
match shared.senders.reg_waker_async(ctx, o_waker) {
Some(Poll::Pending) => return Poll::Pending,
Some(Poll::Ready(())) => return Poll::Ready(Err(())),
_ => {}
}
let state = shared.sender_double_check::<SINK>(item, o_waker);
trace_log!("tx{:?}: sender_double_check {:?} {}", tokio_task_id!(), o_waker, state);
if state < WakerState::Woken as u8 {
return Poll::Pending;
} else if state > WakerState::Woken as u8 {
if state == WakerState::Done as u8 {
trace_log!("tx{:?}: send {:?} done", o_waker, tokio_task_id!());
let _ = o_waker.take();
return Poll::Ready(Ok(()));
} else {
debug_assert_eq!(state, WakerState::Closed as u8);
trace_log!("tx{:?}: closed {:?}", o_waker, tokio_task_id!());
let _ = o_waker.take();
return Poll::Ready(Err(()));
}
}
debug_assert_eq!(state, WakerState::Woken as u8);
continue;
}
}
}
/// A fixed-sized future object constructed by [AsyncTx::send()]
#[must_use]
pub struct SendFuture<'a, F: Flavor> {
tx: &'a AsyncTx<F>,
item: MaybeUninit<F::Item>,
waker: Option<<F::Send as Registry>::Waker>,
}
unsafe impl<F: Flavor> Send for SendFuture<'_, F> where F::Item: Send {}
impl<F: Flavor> Drop for SendFuture<'_, F> {
#[inline]
fn drop(&mut self) {
// Cancelling the future, poll is not ready
if let Some(waker) = self.waker.as_ref() {
if self.tx.shared.abandon_send_waker(waker) && needs_drop::<F::Item>() {
unsafe { self.item.assume_init_drop() };
}
}
}
}
impl<F: Flavor> Future for SendFuture<'_, F>
where
F::Item: Unpin,
{
type Output = Result<(), SendError<F::Item>>;
#[inline]
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
let mut _self = self.get_mut();
match _self.tx.poll_send::<false>(ctx, &_self.item, &mut _self.waker) {
Poll::Ready(Ok(())) => {
debug_assert!(_self.waker.is_none());
Poll::Ready(Ok(()))
}
Poll::Ready(Err(())) => {
let _ = _self.waker.take();
Poll::Ready(Err(SendError(unsafe { _self.item.assume_init_read() })))
}
Poll::Pending => Poll::Pending,
}
}
}
/// A fixed-sized future object constructed by [AsyncTx::send_timeout()]
#[must_use]
pub struct SendTimeoutFuture<'a, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
tx: &'a AsyncTx<F>,
sleep: FR,
item: MaybeUninit<F::Item>,
waker: Option<<F::Send as Registry>::Waker>,
}
unsafe impl<F, FR, R> Send for SendTimeoutFuture<'_, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
}
impl<F, FR, R> Drop for SendTimeoutFuture<'_, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
{
#[inline]
fn drop(&mut self) {
if let Some(waker) = self.waker.as_ref() {
// Cancelling the future, poll is not ready
if self.tx.shared.abandon_send_waker(waker) && needs_drop::<F::Item>() {
unsafe { self.item.assume_init_drop() };
}
}
}
}
impl<F, FR, R> Future for SendTimeoutFuture<'_, F, FR, R>
where
F: Flavor,
FR: Future<Output = R>,
F::Item: Send + 'static + Unpin,
{
type Output = Result<(), SendTimeoutError<F::Item>>;
#[inline]
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
// NOTE: we can use unchecked to bypass pin because we are not movig "sleep",
// neither it's exposed outside
let mut _self = unsafe { self.get_unchecked_mut() };
match _self.tx.poll_send::<false>(ctx, &_self.item, &mut _self.waker) {
Poll::Ready(Ok(())) => {
debug_assert!(_self.waker.is_none());
Poll::Ready(Ok(()))
}
Poll::Ready(Err(())) => {
let _ = _self.waker.take();
Poll::Ready(Err(SendTimeoutError::Disconnected(unsafe {
_self.item.assume_init_read()
})))
}
Poll::Pending => {
let sleep = unsafe { Pin::new_unchecked(&mut _self.sleep) };
if sleep.poll(ctx).is_ready() {
if _self.tx.shared.abandon_send_waker(&_self.waker.take().unwrap()) {
return Poll::Ready(Err(SendTimeoutError::Timeout(unsafe {
_self.item.assume_init_read()
})));
} else {
// Message already sent in background (on_recv).
return Poll::Ready(Ok(()));
}
}
Poll::Pending
}
}
}
}
/// For writing generic code with MAsyncTx & AsyncTx
pub trait AsyncTxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
/// Try to send message, non-blocking
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([TrySendError::Full]) on channel full for bounded channel.
///
/// Returns Err([TrySendError::Disconnected]) when all Rx dropped.
fn try_send(&self, item: T) -> Result<(), TrySendError<T>>;
/// The number of messages in the channel at the moment
fn len(&self) -> usize;
/// The capacity of the channel, return None for unbounded channel.
fn capacity(&self) -> Option<usize>;
/// Whether channel is empty at the moment
fn is_empty(&self) -> bool;
/// Whether the channel is full at the moment
fn is_full(&self) -> bool;
/// Return true if the other side has closed
fn is_disconnected(&self) -> bool;
/// Return the number of senders
fn get_tx_count(&self) -> usize;
/// Return the number of receivers
fn get_rx_count(&self) -> usize;
fn clone_to_vec(self, count: usize) -> Vec<Self>
where
Self: Sized;
fn get_wakers_count(&self) -> (usize, usize);
/// Send message. Will await when channel is full.
///
/// Returns `Ok(())` on successful.
///
/// Returns Err([SendError]) when all Rx is dropped.
fn send(&self, item: T) -> impl Future<Output = Result<(), SendError<T>>> + Send
where
T: Send + 'static + Unpin;
/// Waits for a message to be sent into the channel, but only for a limited time.
/// Will await when channel is full.
///
/// The behavior is atomic, either message sent successfully or returned on error.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([SendTimeoutError::Timeout]) when the operation timed out.
///
/// Returns Err([SendTimeoutError::Disconnected]) when all Rx dropped.
#[cfg(any(feature = "tokio", feature = "async_std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async_std"))))]
fn send_timeout<'a>(
&'a self, item: T, duration: std::time::Duration,
) -> impl Future<Output = Result<(), SendTimeoutError<T>>> + Send
where
T: Send + 'static + Unpin;
/// Sends a message with a custom timer function.
/// Will await when channel is full.
///
/// The behavior is atomic: the message is either sent successfully or returned with error.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([SendTimeoutError::Timeout]) if the operation timed out. The error contains the message that failed to be sent.
///
/// Returns Err([SendTimeoutError::Disconnected]) if the receiver has been dropped. The error contains the message that failed to be sent.
///
/// # Argument:
///
/// * `fut`: The sleep function. It's possible to wrap this function with cancelable handle,
/// you can control when to stop polling. the return value of `fut` is ignore.
/// We add generic `R` just in order to support smol::Timer
fn send_with_timer<FR, R>(
&self, item: T, fut: FR,
) -> impl Future<Output = Result<(), SendTimeoutError<T>>> + Send
where
FR: Future<Output = R>,
T: Send + 'static + Unpin;
}
impl<F: Flavor> AsyncTxTrait<F::Item> for AsyncTx<F> {
#[inline(always)]
fn clone_to_vec(self, count: usize) -> Vec<Self> {
assert_eq!(count, 1);
vec![self]
}
#[inline(always)]
fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
AsyncTx::try_send(self, item)
}
#[inline(always)]
fn send(&self, item: F::Item) -> impl Future<Output = Result<(), SendError<F::Item>>> + Send
where
F::Item: Send + 'static + Unpin,
{
AsyncTx::send(self, item)
}
#[cfg(any(feature = "tokio", feature = "async_std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async_std"))))]
#[inline(always)]
fn send_timeout<'a>(
&'a self, item: F::Item, duration: std::time::Duration,
) -> impl Future<Output = Result<(), SendTimeoutError<F::Item>>> + Send
where
F::Item: Send + 'static + Unpin,
{
AsyncTx::send_timeout(self, item, duration)
}
#[inline(always)]
fn send_with_timer<FR, R>(
&self, item: F::Item, fut: FR,
) -> impl Future<Output = Result<(), SendTimeoutError<F::Item>>> + Send
where
FR: Future<Output = R>,
F::Item: Send + 'static + Unpin,
{
AsyncTx::send_with_timer(self, item, fut)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().get_rx_count() == 0
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
/// A multi-producer (sender) that works in an async context.
///
/// Inherits from [`AsyncTx<T>`] and implements `Clone`.
/// Additional methods in [ChannelShared] can be accessed through `Deref`.
///
/// You can use `into()` to convert it to `AsyncTx<T>`.
///
/// `MAsyncTx` can be converted into `MTx` via the `From` trait,
/// which means you can have two types of senders, both within async and
/// blocking contexts, for the same channel.
pub struct MAsyncTx<F: Flavor>(pub(crate) AsyncTx<F>);
impl<F: Flavor> fmt::Debug for MAsyncTx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAsyncTx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for MAsyncTx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MAsyncTx{:p}", self)
}
}
unsafe impl<F: Flavor> Sync for MAsyncTx<F> {}
impl<F: Flavor> Clone for MAsyncTx<F> {
#[inline]
fn clone(&self) -> Self {
let inner = &self.0;
inner.shared.add_tx();
Self(AsyncTx::new(inner.shared.clone()))
}
}
impl<F: Flavor> From<MAsyncTx<F>> for AsyncTx<F> {
fn from(tx: MAsyncTx<F>) -> Self {
tx.0
}
}
impl<F: Flavor> MAsyncTx<F> {
#[inline]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self(AsyncTx::new(shared))
}
#[inline]
pub fn into_sink(self) -> AsyncSink<F> {
AsyncSink::new(self.0)
}
#[inline]
pub fn into_blocking(self) -> MTx<F> {
self.into()
}
/// Get a weak reference of sender.
///
/// # Example
/// ```
/// use crossfire::*;
/// let (tx, rx) = mpsc::bounded_async::<usize>(100);
/// assert_eq!(tx.get_tx_count(), 1);
/// let weak_tx = tx.downgrade();
/// let tx_clone = weak_tx.upgrade::<MAsyncTx<_>>().unwrap();
/// assert_eq!(tx.get_tx_count(), 2);
/// drop(tx);
/// drop(tx_clone);
/// assert!(weak_tx.upgrade::<MAsyncTx<_>>().is_none());
/// assert_eq!(weak_tx.get_tx_count(), 0);
/// drop(rx);
/// ```
#[inline]
pub fn downgrade(&self) -> WeakTx<F>
where
F: FlavorMP,
{
WeakTx(self.shared.clone())
}
}
impl<F: Flavor> Deref for MAsyncTx<F> {
type Target = AsyncTx<F>;
/// inherit all the functions of [AsyncTx]
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F: Flavor> From<MTx<F>> for MAsyncTx<F> {
fn from(value: MTx<F>) -> Self {
value.add_tx();
Self(AsyncTx::new(value.shared.clone()))
}
}
impl<F: Flavor + FlavorMP> AsyncTxTrait<F::Item> for MAsyncTx<F> {
#[inline(always)]
fn clone_to_vec(self, count: usize) -> Vec<Self> {
let mut v = Vec::with_capacity(count);
for _ in 0..count - 1 {
v.push(self.clone());
}
v.push(self);
v
}
#[inline(always)]
fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
self.0.try_send(item)
}
#[inline(always)]
fn send(&self, item: F::Item) -> impl Future<Output = Result<(), SendError<F::Item>>> + Send
where
F::Item: Send + 'static + Unpin,
{
self.0.send(item)
}
#[cfg(any(feature = "tokio", feature = "async_std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async_std"))))]
#[inline(always)]
fn send_timeout<'a>(
&'a self, item: F::Item, duration: std::time::Duration,
) -> impl Future<Output = Result<(), SendTimeoutError<F::Item>>> + Send
where
F::Item: Send + 'static + Unpin,
{
self.0.send_timeout(item, duration)
}
#[inline(always)]
fn send_with_timer<FR, R>(
&self, item: F::Item, fut: FR,
) -> impl Future<Output = Result<(), SendTimeoutError<F::Item>>> + Send
where
FR: Future<Output = R>,
F::Item: Send + 'static + Unpin,
{
self.0.send_with_timer::<FR, R>(item, fut)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().get_rx_count() == 0
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
impl<F: Flavor> Deref for AsyncTx<F> {
type Target = ChannelShared<F>;
#[inline(always)]
fn deref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for AsyncTx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for MAsyncTx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.0.shared
}
}
impl<T, F: Flavor<Item = T>> SenderType for AsyncTx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
AsyncTx::new(shared)
}
}
impl<F: Flavor> NotCloneable for AsyncTx<F> {}
impl<T, F: Flavor<Item = T> + FlavorMP> SenderType for MAsyncTx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
MAsyncTx::new(shared)
}
}
================================================
FILE: src/backoff.rs
================================================
use core::num::NonZero;
use std::mem::transmute;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::thread;
pub const SPIN_LIMIT: u16 = 6;
#[cfg(target_arch = "x86_64")]
pub const DEFAULT_LIMIT: u16 = 6;
#[cfg(not(target_arch = "x86_64"))]
pub const DEFAULT_LIMIT: u16 = 10;
pub const MAX_LIMIT: u16 = 10;
pub const DEFAULT_CONFIG: u32 =
BackoffConfig { spin_limit: SPIN_LIMIT, limit: DEFAULT_LIMIT }.to_u32();
static DETECT_CONFIG: AtomicU32 = AtomicU32::new(DEFAULT_CONFIG);
static _INIT: AtomicBool = AtomicBool::new(false);
/// Detect cpu number and auto setting backoff config.
///
/// On one core system, it will be more effective (as much as 2x faster) to use yield than spinning.
///
/// The function need to be invoke manually in your initialization code, which does not interrupt
/// channel operation on other thread. By saving the result to global atomic, the effect will apply after execution.
///
/// The result we choose not to include this in default channel initialization code, because
/// Cpu detection process is somehow slow for benchmark standard,
/// and `thread::available_parallelism()` might require I/O on system files, you may not
/// like it in sandbox scenario.
pub fn detect_backoff_cfg() {
if _INIT.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_err() {
return;
}
if thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap())
== NonZero::new(1).unwrap()
{
// For one core (like VM machine), better use yield_now instead of spin_loop.
DETECT_CONFIG.store(
BackoffConfig { spin_limit: 0, limit: DEFAULT_LIMIT }.to_u32(),
Ordering::Release,
);
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct BackoffConfig {
pub spin_limit: u16,
pub limit: u16,
}
impl Default for BackoffConfig {
#[inline(always)]
fn default() -> Self {
Self::from_u32(DETECT_CONFIG.load(Ordering::Relaxed))
}
}
impl BackoffConfig {
#[inline(always)]
pub fn detect() -> Self {
Self::from_u32(DETECT_CONFIG.load(Ordering::Relaxed))
}
#[inline(always)]
pub const fn to_u32(self) -> u32 {
let i: u32 = unsafe { transmute(self) };
i
}
#[inline(always)]
pub const fn from_u32(config: u32) -> Self {
unsafe { transmute(config) }
}
#[allow(dead_code)]
#[inline(always)]
pub const fn async_limit(mut self, limit: u16) -> Self {
if limit < self.limit {
self.limit = limit;
}
self.spin_limit = limit;
self
}
#[allow(dead_code)]
#[inline(always)]
pub const fn limit(mut self, limit: u16) -> Self {
self.limit = limit;
self
}
#[allow(dead_code)]
#[inline(always)]
pub const fn spin(mut self, spin_limit: u16) -> Self {
if spin_limit < self.spin_limit {
self.spin_limit = spin_limit;
}
self
}
}
pub struct Backoff {
step: u16,
pub config: BackoffConfig,
}
impl Backoff {
#[inline(always)]
pub fn new() -> Self {
Self { step: 0, config: BackoffConfig::default() }
}
#[inline(always)]
pub fn from(config: BackoffConfig) -> Self {
Self { step: 0, config }
}
#[allow(dead_code)]
#[inline(always)]
pub fn spin(&mut self) -> bool {
for _ in 0..1 << self.step.min(SPIN_LIMIT) {
std::hint::spin_loop();
}
if self.step < MAX_LIMIT {
self.step += 1;
self.step > self.config.limit
} else {
true
}
}
#[inline(always)]
pub fn set_step(&mut self, step: u16) {
self.step = step;
}
#[inline(always)]
pub fn snooze(&mut self) -> bool {
if self.step >= self.config.limit {
return true;
}
if self.step < self.config.spin_limit {
for _ in 0..1 << self.step {
std::hint::spin_loop();
}
} else {
std::thread::yield_now();
}
self.step += 1;
false
}
#[allow(dead_code)]
#[inline(always)]
pub fn yield_now(&mut self) -> bool {
if self.step >= self.config.limit {
return true;
}
std::thread::yield_now();
self.step += 1;
false
}
#[inline(always)]
pub fn is_completed(&self) -> bool {
self.step >= self.config.limit
}
#[allow(dead_code)]
#[inline(always)]
pub fn step(&self) -> usize {
self.step as usize
}
#[inline(always)]
pub fn reset(&mut self) {
self.step = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_backoff() {
let backoff = Backoff::from(BackoffConfig { spin_limit: 1, limit: 0 });
assert!(backoff.is_completed());
println!("Option<backoff> size {}", size_of::<Option<Backoff>>());
println!("backoff size {}", size_of::<Backoff>());
println!("BackoffConfig size {}", size_of::<BackoffConfig>());
assert_eq!(size_of::<BackoffConfig>(), size_of::<u32>());
let config = BackoffConfig { spin_limit: 6, limit: 7 };
let config_i = config.to_u32();
let _config = BackoffConfig::from_u32(config_i);
assert_eq!(config.spin_limit, _config.spin_limit);
assert_eq!(config.limit, _config.limit);
let mut backoff = Backoff::from(BackoffConfig { spin_limit: 2, limit: 4 });
assert_eq!(backoff.step, 0);
backoff.spin();
assert_eq!(backoff.step, 1);
backoff.snooze();
assert_eq!(backoff.step, 2);
backoff.snooze();
backoff.snooze();
backoff.snooze();
backoff.snooze();
assert_eq!(backoff.step, 4);
backoff.spin();
assert_eq!(backoff.step, 5);
}
}
================================================
FILE: src/blocking_rx.rs
================================================
use crate::backoff::*;
use crate::flavor::{FlavorMC, FlavorSelect};
use crate::select::SelectResult;
use crate::{shared::*, trace_log, AsyncRx, MAsyncRx, NotCloneable, ReceiverType};
use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::{atomic::Ordering, Arc};
use std::time::{Duration, Instant};
/// A single consumer (receiver) that works in a blocking context.
///
/// Additional methods in [ChannelShared] can be accessed through `Deref`.
///
/// **NOTE**: `Rx` is not `Clone` or `Sync`.
/// If you need concurrent access, use [MRx] instead.
///
/// `Rx` has a `Send` marker and can be moved to other threads.
/// The following code is OK:
///
/// ``` rust
/// use crossfire::*;
/// let (tx, rx) = mpsc::bounded_blocking::<usize>(100);
/// std::thread::spawn(move || {
/// let _ = rx.recv();
/// });
/// drop(tx);
/// ```
///
/// Because `Rx` does not have a `Sync` marker, using `Arc<Rx>` will lose the `Send` marker.
///
/// For your safety, the following code **should not compile**:
///
/// ``` compile_fail
/// use crossfire::*;
/// use std::sync::Arc;
/// let (tx, rx) = mpsc::bounded_blocking(100);
/// let rx = Arc::new(rx);
/// std::thread::spawn(move || {
/// let _ = rx.recv();
/// });
/// drop(tx);
/// ```
pub struct Rx<F: Flavor> {
pub(crate) shared: Arc<ChannelShared<F>>,
// Remove the Sync marker to prevent being put in Arc
_phan: PhantomData<Cell<()>>,
waker_cache: WakerCache<()>,
}
unsafe impl<F: Flavor> Send for Rx<F> {}
impl<F: Flavor> fmt::Debug for Rx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Rx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for Rx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Rx{:p}", self)
}
}
impl<F: Flavor> Drop for Rx<F> {
#[inline(always)]
fn drop(&mut self) {
self.shared.close_rx();
}
}
impl<F: Flavor> From<AsyncRx<F>> for Rx<F> {
fn from(value: AsyncRx<F>) -> Self {
value.add_rx();
Self::new(value.shared.clone())
}
}
impl<F: Flavor> Rx<F> {
#[inline(always)]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self { shared, waker_cache: WakerCache::new(), _phan: Default::default() }
}
#[inline(always)]
pub(crate) fn _recv_blocking(
&self, deadline: Option<Instant>,
) -> Result<F::Item, RecvTimeoutError> {
let shared = &self.shared;
let mut o_waker: Option<<F::Recv as Registry>::Waker> = None;
macro_rules! on_recv_no_waker {
() => {{
trace_log!("rx: recv");
}};
}
macro_rules! on_recv_waker {
() => {{
trace_log!("rx: recv {:?}", o_waker);
self.recvs.cache_waker(o_waker, &self.waker_cache);
}};
}
macro_rules! try_recv {
($handle_waker: block) => {
if let Some(item) = shared.inner.try_recv() {
shared.on_recv();
$handle_waker
return Ok(item);
}
};
}
try_recv!({ on_recv_no_waker!() });
let mut cfg = BackoffConfig::detect().limit(shared.backoff_limit);
if shared.large {
cfg = cfg.spin(2);
}
let mut backoff = Backoff::from(cfg);
loop {
let r = backoff.snooze();
try_recv!({ on_recv_no_waker!() });
if r {
break;
}
}
let mut state;
'MAIN: loop {
shared.recvs.reg_waker_blocking(&mut o_waker, &self.waker_cache);
// NOTE: special API before we park
// because Miri is not happy about ArrayQueue pop ordering, which is not SeqCst
if let Some(item) = shared.inner.try_recv_final() {
shared.on_recv();
trace_log!("rx: recv cancel {:?} Init", o_waker);
self.recvs.cancel_waker(&mut o_waker);
return Ok(item);
}
state = shared.recvs.commit_waiting(&o_waker);
trace_log!("rx: {:?} commit_waiting state={}", o_waker, state);
if shared.is_tx_closed() {
break 'MAIN;
}
while state < WakerState::Woken as u8 {
match check_timeout(deadline) {
Ok(None) => {
std::thread::park();
}
Ok(Some(dur)) => {
std::thread::park_timeout(dur);
}
Err(_) => {
shared.abandon_recv_waker(o_waker.as_ref().unwrap());
return Err(RecvTimeoutError::Timeout);
}
}
state = self.recvs.get_waker_state(&o_waker, Ordering::SeqCst);
trace_log!("rx: after park state={}", state);
}
if state == WakerState::Closed as u8 {
break 'MAIN;
}
backoff.reset();
loop {
try_recv!({ on_recv_waker!() });
if backoff.snooze() {
break;
}
}
}
try_recv!({ on_recv_waker!() });
// make sure all msgs received, since we have soonze
Err(RecvTimeoutError::Disconnected)
}
/// Receives a message from the channel. This method will block until a message is received or the channel is closed.
///
/// Returns `Ok(T)` on success.
///
/// Returns Err([RecvError]) if the sender has been dropped.
#[inline]
pub fn recv(&self) -> Result<F::Item, RecvError> {
self._recv_blocking(None).map_err(|err| match err {
RecvTimeoutError::Disconnected => RecvError,
RecvTimeoutError::Timeout => unreachable!(),
})
}
/// Attempts to receive a message from the channel without blocking.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([TryRecvError::Empty]) if the channel is empty.
///
/// Returns Err([TryRecvError::Disconnected]) if the sender has been dropped and the channel is empty.
#[inline]
pub fn try_recv(&self) -> Result<F::Item, TryRecvError> {
self.shared.try_recv()
}
/// Receives a message from the channel with a timeout.
/// Will block when channel is empty.
///
/// The behavior is atomic: the message is either received successfully or the operation is canceled due to a timeout.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([RecvTimeoutError::Timeout]) when a message could not be received because the channel is empty and the operation timed out.
///
/// Returns Err([RecvTimeoutError::Disconnected]) if the sender has been dropped and the channel is empty.
#[inline]
pub fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTimeoutError> {
match Instant::now().checked_add(timeout) {
Some(deadline) => self._recv_blocking(Some(deadline)),
None => self.try_recv().map_err(|e| match e {
TryRecvError::Disconnected => RecvTimeoutError::Disconnected,
TryRecvError::Empty => RecvTimeoutError::Timeout,
}),
}
}
/// Return true if the other side has closed
#[inline(always)]
pub fn is_disconnected(&self) -> bool {
self.shared.is_tx_closed()
}
/// This method use with [select](crate::select::Select::select), guarantee non-blocking
/// # Panics
///
/// Panics if SelectResult from other receiver is passed.
#[inline(always)]
pub fn read_select(&self, result: SelectResult) -> Result<F::Item, RecvError>
where
F: FlavorSelect,
{
assert_eq!(
self as *const Self as *const u8, result.channel,
"invalid use select with another channel"
);
self.as_ref().read_with_token(result.token)
}
#[inline(always)]
pub fn into_async(self) -> AsyncRx<F> {
self.into()
}
}
/// A multi-consumer (receiver) that works in a blocking context.
///
/// Inherits from [`Rx<F>`] and implements `Clone`.
/// Additional methods can be accessed through `Deref<Target=[ChannelShared]>`.
///
/// You can use `into()` to convert it to `Rx<F>`.
pub struct MRx<F: Flavor>(pub(crate) Rx<F>);
impl<F: Flavor> fmt::Debug for MRx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MRx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for MRx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MRx{:p}", self)
}
}
unsafe impl<F: Flavor> Sync for MRx<F> {}
impl<F: Flavor> MRx<F>
where
F: FlavorMC,
{
#[inline(always)]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self(Rx::new(shared))
}
#[inline(always)]
pub fn into_async(self) -> MAsyncRx<F> {
self.into()
}
}
impl<F: Flavor> Clone for MRx<F> {
#[inline(always)]
fn clone(&self) -> Self {
let inner = &self.0;
inner.shared.add_rx();
Self(Rx::new(inner.shared.clone()))
}
}
impl<F: Flavor> Deref for MRx<F> {
type Target = Rx<F>;
/// Inherits all the functions of [Rx].
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F: Flavor> From<MRx<F>> for Rx<F> {
fn from(rx: MRx<F>) -> Self {
rx.0
}
}
impl<F: Flavor> From<MAsyncRx<F>> for MRx<F> {
fn from(value: MAsyncRx<F>) -> Self {
value.add_rx();
Self(Rx::new(value.shared.clone()))
}
}
/// For writing generic code with MRx & Rx
pub trait BlockingRxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
/// Receives a message from the channel. This method will block until a message is received or the channel is closed.
///
/// Returns `Ok(T)` on success.
///
/// Returns Err([RecvError]) if the sender has been dropped.
fn recv(&self) -> Result<T, RecvError>;
/// Attempts to receive a message from the channel without blocking.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([TryRecvError::Empty]) if the channel is empty.
///
/// Returns Err([TryRecvError::Disconnected]) if the sender has been dropped and the channel is empty.
fn try_recv(&self) -> Result<T, TryRecvError>;
/// Receives a message from the channel with a timeout.
/// Will block when channel is empty.
///
/// Returns `Ok(T)` when successful.
///
/// Returns Err([RecvTimeoutError::Timeout]) when a message could not be received because the channel is empty and the operation timed out.
///
/// Returns Err([RecvTimeoutError::Disconnected]) if the sender has been dropped and the channel is empty.
fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError>;
/// The number of messages in the channel at the moment
fn len(&self) -> usize;
/// The capacity of the channel, return None for unbounded channel.
fn capacity(&self) -> Option<usize>;
/// Whether channel is empty at the moment
fn is_empty(&self) -> bool;
/// Whether the channel is full at the moment
fn is_full(&self) -> bool;
/// Return true if the other side has closed
fn is_disconnected(&self) -> bool;
/// Return the number of senders
fn get_tx_count(&self) -> usize;
/// Return the number of receivers
fn get_rx_count(&self) -> usize;
fn clone_to_vec(self, count: usize) -> Vec<Self>
where
Self: Sized;
fn get_wakers_count(&self) -> (usize, usize);
}
impl<F: Flavor> BlockingRxTrait<F::Item> for Rx<F> {
#[inline(always)]
fn clone_to_vec(self, _count: usize) -> Vec<Self> {
assert_eq!(_count, 1);
vec![self]
}
#[inline(always)]
fn recv(&self) -> Result<F::Item, RecvError> {
Rx::recv(self)
}
#[inline(always)]
fn try_recv(&self) -> Result<F::Item, TryRecvError> {
Rx::try_recv(self)
}
#[inline(always)]
fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTimeoutError> {
Rx::recv_timeout(self, timeout)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().is_tx_closed()
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
impl<F> BlockingRxTrait<F::Item> for MRx<F>
where
F: Flavor + FlavorMC,
{
#[inline(always)]
fn clone_to_vec(self, count: usize) -> Vec<Self> {
let mut v = Vec::with_capacity(count);
for _ in 0..count - 1 {
v.push(self.clone());
}
v.push(self);
v
}
#[inline(always)]
fn recv(&self) -> Result<F::Item, RecvError> {
self.0.recv()
}
#[inline(always)]
fn try_recv(&self) -> Result<F::Item, TryRecvError> {
self.0.try_recv()
}
#[inline(always)]
fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTimeoutError> {
self.0.recv_timeout(timeout)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().is_tx_closed()
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
impl<F: Flavor> Deref for Rx<F> {
type Target = ChannelShared<F>;
#[inline(always)]
fn deref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for Rx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for MRx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.0.shared
}
}
impl<T, F: Flavor<Item = T>> ReceiverType for Rx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
Rx::new(shared)
}
}
impl<F: Flavor> NotCloneable for Rx<F> {}
impl<F> ReceiverType for MRx<F>
where
F: Flavor + FlavorMC,
{
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
MRx::new(shared)
}
}
================================================
FILE: src/blocking_tx.rs
================================================
use crate::backoff::*;
use crate::flavor::FlavorMP;
use crate::weak::WeakTx;
use crate::{shared::*, trace_log, AsyncTx, MAsyncTx, NotCloneable, SenderType};
use std::cell::Cell;
use std::fmt;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::{Duration, Instant};
/// A single producer (sender) that works in a blocking context.
///
/// Additional methods in [ChannelShared] can be accessed through `Deref`.
///
/// **NOTE**: `Tx` is not `Clone` or `Sync`.
/// If you need concurrent access, use [MTx] instead.
///
/// `Tx` has a `Send` marker and can be moved to other threads.
/// The following code is OK:
///
/// ``` rust
/// use crossfire::*;
/// let (tx, rx) = spsc::bounded_blocking::<usize>(100);
/// std::thread::spawn(move || {
/// let _ = tx.send(1);
/// });
/// drop(rx);
/// ```
///
/// Because `Tx` does not have a `Sync` marker, using `Arc<Tx>` will lose the `Send` marker.
///
/// For your safety, the following code **should not compile**:
///
/// ``` compile_fail
/// use crossfire::*;
/// use std::sync::Arc;
/// let (tx, rx) = spsc::bounded_blocking::<usize>(100);
/// let tx = Arc::new(tx);
/// std::thread::spawn(move || {
/// let _ = tx.send(1);
/// });
/// drop(rx);
/// ```
pub struct Tx<F: Flavor> {
pub(crate) shared: Arc<ChannelShared<F>>,
// Remove the Sync marker to prevent being put in Arc
_phan: PhantomData<Cell<()>>,
waker_cache: WakerCache<*const F::Item>,
}
unsafe impl<F: Flavor> Send for Tx<F> {}
impl<F: Flavor> fmt::Debug for Tx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for Tx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tx{:p}", self)
}
}
impl<F: Flavor> Drop for Tx<F> {
#[inline(always)]
fn drop(&mut self) {
self.shared.close_tx();
}
}
impl<F: Flavor> From<AsyncTx<F>> for Tx<F> {
fn from(value: AsyncTx<F>) -> Self {
value.add_tx();
Self::new(value.shared.clone())
}
}
impl<F: Flavor> Tx<F> {
#[inline]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self { shared, waker_cache: WakerCache::new(), _phan: Default::default() }
}
/// Return true if the other side has closed
#[inline(always)]
pub fn is_disconnected(&self) -> bool {
self.shared.is_rx_closed()
}
#[inline]
pub fn into_async(self) -> AsyncTx<F> {
self.into()
}
}
impl<F: Flavor> Tx<F> {
#[inline(always)]
pub(crate) fn _send_bounded(
&self, item: &MaybeUninit<F::Item>, deadline: Option<Instant>,
) -> Result<(), SendTimeoutError<F::Item>> {
let shared = &self.shared;
let large = shared.large;
let backoff_cfg = BackoffConfig::detect().spin(2).limit(shared.backoff_limit);
let mut backoff = Backoff::from(backoff_cfg);
let congest = shared.sender_direct_copy();
// disable because of issue #54
let direct_copy = false;
// let direct_copy = deadline.is_none() && shared.sender_direct_copy();
if large {
backoff.set_step(2);
}
loop {
let r = if large { backoff.yield_now() } else { backoff.spin() };
if direct_copy && large {
match shared.inner.try_send_oneshot(item.as_ptr()) {
Some(false) => break,
None => {
if r {
break;
}
continue;
}
_ => {
shared.on_send();
trace_log!("tx: send");
std::thread::yield_now();
return Ok(());
}
}
} else {
if !shared.inner.try_send(item) {
if r {
break;
}
continue;
}
shared.on_send();
trace_log!("tx: send");
return Ok(());
}
}
let direct_copy_ptr: *const F::Item = std::ptr::null();
// if direct_copy { item.as_ptr() } else { std::ptr::null() };
let mut state: u8;
let mut o_waker: Option<<F::Send as Registry>::Waker> = None;
macro_rules! return_ok {
() => {
trace_log!("tx: send {:?}", o_waker);
if shared.is_full() {
// It's for 8x1, 16x1.
std::thread::yield_now();
self.senders.cache_waker(o_waker, &self.waker_cache);
}
return Ok(())
};
}
loop {
self.senders.reg_waker_blocking(&mut o_waker, &self.waker_cache, direct_copy_ptr);
// For nx1 (more likely congest), need to reset backoff
// to allow more yield to receivers.
// For nxn (the backoff is already complete), wait a little bit.
state = shared.sender_double_check::<false>(item, &mut o_waker);
trace_log!("tx: sender_double_check {:?} state={}", o_waker, state);
while state < WakerState::Woken as u8 {
if congest {
state = shared.sender_snooze(&o_waker, &mut backoff);
}
if state <= WakerState::Waiting as u8 {
match check_timeout(deadline) {
Ok(None) => {
std::thread::park();
}
Ok(Some(dur)) => {
std::thread::park_timeout(dur);
}
Err(_) => {
if shared.abandon_send_waker(o_waker.as_ref().unwrap()) {
return Err(SendTimeoutError::Timeout(unsafe {
item.assume_init_read()
}));
} else {
// NOTE: Unlikely since we disable direct copy with deadline
// state is WakerState::Done
return Ok(());
}
}
}
state = self.senders.get_waker_state(&o_waker, Ordering::SeqCst);
trace_log!("tx: after park state={}", state);
}
}
if state == WakerState::Woken as u8 {
backoff.reset();
loop {
if shared.inner.try_send(item) {
shared.on_send();
return_ok!();
}
if backoff.is_completed() {
break;
}
backoff.snooze();
}
} else if state == WakerState::Done as u8 {
return_ok!();
} else {
debug_assert_eq!(state, WakerState::Closed as u8);
return Err(SendTimeoutError::Disconnected(unsafe { item.assume_init_read() }));
}
}
}
/// Sends a message. This method will block until the message is sent or the channel is closed.
///
/// Returns `Ok(())` on success.
///
/// Returns `Err(SendError)` if the receiver has been dropped.
///
#[inline]
pub fn send(&self, item: F::Item) -> Result<(), SendError<F::Item>> {
let shared = &self.shared;
if shared.is_rx_closed() {
return Err(SendError(item));
}
let _item = MaybeUninit::new(item);
if shared.inner.try_send(&_item) {
shared.on_send();
return Ok(());
}
match self._send_bounded(&_item, None) {
Ok(_) => Ok(()),
Err(SendTimeoutError::Disconnected(e)) => Err(SendError(e)),
Err(SendTimeoutError::Timeout(_)) => unreachable!(),
}
}
/// Attempts to send a message without blocking.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([TrySendError::Full]) if the channel is full.
///
/// Returns Err([TrySendError::Disconnected]) if the receiver has been dropped.
#[inline]
pub fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
let shared = &self.shared;
if shared.is_rx_closed() {
return Err(TrySendError::Disconnected(item));
}
let _item = MaybeUninit::new(item);
if shared.inner.try_send(&_item) {
shared.on_send();
Ok(())
} else {
Err(TrySendError::Full(unsafe { _item.assume_init_read() }))
}
}
/// Sends a message with a timeout.
/// Will block when channel is full.
///
/// The behavior is atomic: the message is either sent successfully or returned on error.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([SendTimeoutError::Timeout]) if the operation timed out.
///
/// Returns Err([SendTimeoutError::Disconnected]) if the receiver has been dropped.
#[inline]
pub fn send_timeout(
&self, item: F::Item, timeout: Duration,
) -> Result<(), SendTimeoutError<F::Item>> {
let shared = &self.shared;
if shared.is_rx_closed() {
return Err(SendTimeoutError::Disconnected(item));
}
match Instant::now().checked_add(timeout) {
None => self.try_send(item).map_err(|e| match e {
TrySendError::Disconnected(t) => SendTimeoutError::Disconnected(t),
TrySendError::Full(t) => SendTimeoutError::Timeout(t),
}),
Some(deadline) => {
let _item = MaybeUninit::new(item);
if shared.inner.try_send(&_item) {
shared.on_send();
return Ok(());
}
match self._send_bounded(&_item, Some(deadline)) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
}
}
}
/// A multi-producer (sender) that works in a blocking context.
///
/// Inherits from [`Tx<F>`] and implements `Clone`.
/// Additional methods can be accessed through `Deref<Target=[ChannelShared]>`.
///
/// You can use `into()` to convert it to `Tx<F>`.
pub struct MTx<F: Flavor>(pub(crate) Tx<F>);
impl<F: Flavor> fmt::Debug for MTx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MTx{:p}", self)
}
}
impl<F: Flavor> fmt::Display for MTx<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MTx{:p}", self)
}
}
impl<F: Flavor> From<MTx<F>> for Tx<F> {
fn from(tx: MTx<F>) -> Self {
tx.0
}
}
impl<F: Flavor> From<MAsyncTx<F>> for MTx<F> {
fn from(value: MAsyncTx<F>) -> Self {
value.add_tx();
Self(Tx::new(value.shared.clone()))
}
}
unsafe impl<F: Flavor> Sync for MTx<F> {}
impl<F: Flavor + FlavorMP> MTx<F> {
#[inline]
pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self(Tx::new(shared))
}
#[inline]
pub fn into_async(self) -> MAsyncTx<F> {
self.into()
}
/// Get a weak reference of sender.
///
/// # Example
/// ```
/// use crossfire::*;
/// let (tx, rx) = mpsc::bounded_blocking::<usize>(100);
/// let weak_tx = tx.downgrade();
/// assert_eq!(tx.get_tx_count(), 1);
/// let tx_clone = weak_tx.upgrade::<MTx<_>>().unwrap();
/// assert_eq!(tx.get_tx_count(), 2);
/// drop(tx);
/// drop(tx_clone);
/// assert!(weak_tx.upgrade::<MTx<_>>().is_none());
/// assert_eq!(weak_tx.get_tx_count(), 0);
/// ```
#[inline]
pub fn downgrade(&self) -> WeakTx<F> {
WeakTx(self.shared.clone())
}
}
impl<F: Flavor> Clone for MTx<F> {
#[inline]
fn clone(&self) -> Self {
let inner = &self.0;
inner.shared.add_tx();
Self(Tx::new(inner.shared.clone()))
}
}
impl<F: Flavor> Deref for MTx<F> {
type Target = Tx<F>;
/// Inherits all the functions of [Tx].
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// For writing generic code with MTx & Tx
pub trait BlockingTxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
/// Sends a message. This method will block until the message is sent or the channel is closed.
///
/// Returns `Ok(())` on success.
///
/// Returns Err([SendError]) if the receiver has been dropped.
fn send(&self, _item: T) -> Result<(), SendError<T>>;
/// Attempts to send a message without blocking.
///
/// Returns `Ok(())` when successful.
///
/// Returns `Err([TrySendError::Full])` if the channel is full.
///
/// Returns Err([TrySendError::Disconnected]) if the receiver has been dropped.
fn try_send(&self, _item: T) -> Result<(), TrySendError<T>>;
/// Sends a message with a timeout.
/// Will block when channel is empty.
///
/// Returns `Ok(())` when successful.
///
/// Returns Err([SendTimeoutError::Timeout]) if the message could not be sent because the channel is full and the operation timed out.
///
/// Returns Err([SendTimeoutError::Disconnected]) if the receiver has been dropped.
fn send_timeout(&self, item: T, timeout: Duration) -> Result<(), SendTimeoutError<T>>;
/// The number of messages in the channel at the moment
fn len(&self) -> usize;
/// The capacity of the channel, return None for unbounded channel.
fn capacity(&self) -> Option<usize>;
/// Whether channel is empty at the moment
fn is_empty(&self) -> bool;
/// Whether the channel is full at the moment
fn is_full(&self) -> bool;
/// Return true if the other side has closed
fn is_disconnected(&self) -> bool;
/// Return the number of senders
fn get_tx_count(&self) -> usize;
/// Return the number of receivers
fn get_rx_count(&self) -> usize;
fn clone_to_vec(self, count: usize) -> Vec<Self>
where
Self: Sized;
fn get_wakers_count(&self) -> (usize, usize);
}
impl<F: Flavor> BlockingTxTrait<F::Item> for Tx<F> {
#[inline(always)]
fn clone_to_vec(self, _count: usize) -> Vec<Self> {
assert_eq!(_count, 1);
vec![self]
}
#[inline(always)]
fn send(&self, item: F::Item) -> Result<(), SendError<F::Item>> {
Tx::send(self, item)
}
#[inline(always)]
fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
Tx::try_send(self, item)
}
#[inline(always)]
fn send_timeout(
&self, item: F::Item, timeout: Duration,
) -> Result<(), SendTimeoutError<F::Item>> {
Tx::send_timeout(self, item, timeout)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().get_rx_count() == 0
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
impl<F: Flavor + FlavorMP> BlockingTxTrait<F::Item> for MTx<F> {
#[inline(always)]
fn clone_to_vec(self, count: usize) -> Vec<Self> {
let mut v = Vec::with_capacity(count);
for _ in 0..count - 1 {
v.push(self.clone());
}
v.push(self);
v
}
#[inline(always)]
fn send(&self, item: F::Item) -> Result<(), SendError<F::Item>> {
self.0.send(item)
}
#[inline(always)]
fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
self.0.try_send(item)
}
#[inline(always)]
fn send_timeout(
&self, item: F::Item, timeout: Duration,
) -> Result<(), SendTimeoutError<F::Item>> {
self.0.send_timeout(item, timeout)
}
/// The number of messages in the channel at the moment
#[inline(always)]
fn len(&self) -> usize {
self.as_ref().len()
}
/// The capacity of the channel, return None for unbounded channel.
#[inline(always)]
fn capacity(&self) -> Option<usize> {
self.as_ref().capacity()
}
/// Whether channel is empty at the moment
#[inline(always)]
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
/// Whether the channel is full at the moment
#[inline(always)]
fn is_full(&self) -> bool {
self.as_ref().is_full()
}
/// Return true if the other side has closed
#[inline(always)]
fn is_disconnected(&self) -> bool {
self.as_ref().get_rx_count() == 0
}
#[inline(always)]
fn get_tx_count(&self) -> usize {
self.as_ref().get_tx_count()
}
#[inline(always)]
fn get_rx_count(&self) -> usize {
self.as_ref().get_rx_count()
}
fn get_wakers_count(&self) -> (usize, usize) {
self.as_ref().get_wakers_count()
}
}
impl<F: Flavor> Deref for Tx<F> {
type Target = ChannelShared<F>;
#[inline(always)]
fn deref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for Tx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.shared
}
}
impl<F: Flavor> AsRef<ChannelShared<F>> for MTx<F> {
#[inline(always)]
fn as_ref(&self) -> &ChannelShared<F> {
&self.0.shared
}
}
impl<T, F: Flavor<Item = T>> SenderType for Tx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
Self::new(shared)
}
}
impl<F: Flavor> NotCloneable for Tx<F> {}
impl<T, F: Flavor<Item = T> + FlavorMP> SenderType for MTx<F> {
type Flavor = F;
#[inline(always)]
fn new(shared: Arc<ChannelShared<F>>) -> Self {
MTx::new(shared)
}
}
================================================
FILE: src/collections.rs
================================================
use std::ptr;
use std::sync::{
atomic::{AtomicPtr, Ordering},
Arc, Weak,
};
pub struct ArcCell<T> {
ptr: AtomicPtr<T>,
}
impl<T> Drop for ArcCell<T> {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
unsafe impl<T> Send for ArcCell<T> {}
unsafe impl<T> Sync for ArcCell<T> {}
impl<T> ArcCell<T> {
#[inline(always)]
pub fn new() -> Self {
Self { ptr: AtomicPtr::new(ptr::null_mut()) }
}
#[inline(always)]
pub fn exists(&self) -> bool {
!self.ptr.load(Ordering::Acquire).is_null()
}
#[inline(always)]
pub fn pop(&self) -> Option<Arc<T>> {
let ptr = self.ptr.swap(ptr::null_mut(), Ordering::SeqCst);
if !ptr.is_null() {
Some(unsafe { Arc::from_raw(ptr) })
} else {
None
}
}
#[allow(dead_code)]
#[inline(always)]
pub fn clear(&self) {
let ptr = self.ptr.swap(ptr::null_mut(), Ordering::SeqCst);
if !ptr.is_null() {
// Convert into Weak and drop
let _ = unsafe { Arc::from_raw(ptr) };
}
}
#[inline(always)]
pub fn try_put(&self, item: Arc<T>) {
let item_ptr = Arc::into_raw(item) as *mut T;
match self.ptr.compare_exchange(
ptr::null_mut(),
item_ptr,
Ordering::SeqCst,
Ordering::Relaxed,
) {
Ok(_) => {}
Err(_) => {
let _ = unsafe { Arc::from_raw(item_ptr) };
}
}
}
}
#[allow(dead_code)]
pub struct WeakCell<T> {
ptr: AtomicPtr<T>,
}
unsafe impl<T> Send for WeakCell<T> {}
unsafe impl<T> Sync for WeakCell<T> {}
impl<T> Drop for WeakCell<T> {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
impl<T> WeakCell<T> {
#[inline(always)]
pub fn new() -> Self {
Self { ptr: AtomicPtr::new(ptr::null_mut()) }
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.ptr.load(Ordering::SeqCst).is_null()
}
#[inline(always)]
pub fn pop(&self) -> Option<Arc<T>> {
let mut v = self.ptr.load(Ordering::SeqCst);
if v.is_null() {
return None;
}
loop {
match self.ptr.compare_exchange(v, ptr::null_mut(), Ordering::SeqCst, Ordering::Acquire)
{
Ok(_) => return unsafe { Weak::from_raw(v) }.upgrade(),
Err(_v) => {
if _v.is_null() {
return None;
}
v = _v;
}
}
}
}
//// it is allow to fail, with only one shot and weak Ops
#[inline(always)]
pub fn clear(&self) -> bool {
// Don't need accurate, it's optional
let v = self.ptr.load(Ordering::Acquire);
if v.is_null() {
return false;
}
match self.ptr.compare_exchange(v, ptr::null_mut(), Ordering::Release, Ordering::Relaxed) {
Ok(_) => {
let _ = unsafe { Weak::from_raw(v) };
true
}
Err(_v) => {
// We don't really have to clear this on spurious failure
false
}
}
}
#[inline(always)]
pub fn replace(&self, item: Weak<T>) {
let old_ptr = self.ptr.swap(item.into_raw() as *mut T, Ordering::SeqCst);
if !old_ptr.is_null() {
let _ = unsafe { Weak::from_raw(old_ptr) };
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_weak_cell() {
use super::*;
use std::sync::Arc;
let cell = WeakCell::new();
assert!(cell.is_empty());
let item = Arc::new(1);
cell.replace(Arc::downgrade(&item));
assert!(!cell.is_empty());
let _item = cell.pop().unwrap();
assert!(cell.is_empty());
assert!(Arc::ptr_eq(&item, &_item));
cell.replace(Arc::downgrade(&item));
assert!(!cell.is_empty());
// it is allow to fail under miri
println!("clear");
while !cell.clear() {
assert!(!cell.is_empty());
println!("try clear again");
}
assert!(cell.is_empty());
drop(_item);
assert_eq!(Arc::strong_count(&item), 1);
assert_eq!(Arc::weak_count(&item), 0);
}
}
================================================
FILE: src/compat.rs
================================================
//! compatible layer for V2.0 API
//!
//! # Migration from v2.* to v3
//!
//! If you want to migrate to v3 API, you may add the flavor type in [MTx], [MRx], [Tx], [Rx] type,
//! and change the channel initialization function accordingly.
//!
//! If you have a large project that use v2 API, and want to migrate gradually,
//! only need to change original import from `use crossfire::*` to `use crossfire::compat::*`.
//! This module provides the [CompatFlavor] which erase the difference between `List` and `Array`,
//! but registry only use RegistryMulti for spsc and mpsc for compatibility.
//!
//! # Compatible consideration
//!
//! - In the legacy API, the sender/receiver types had erased the signature between bounded or unbounded channels
//! - The low level queue implement is for MPMC regardless of MPSC/SPSC model (which is exactly the
//! same with V2.1)
//! - The module structure in `crossfire::compat::*`, is exactly the same as v2.x `crossfire::*`.
//!
//! # Incompatible notes
//!
//! - keeping Into<AsyncStream<T, F>> for `AsyncRxTrait<T>` is not possible, due to `AsyncRxTrait<T>`
//! is erased out Flavor parameter, so we add `AsyncRxTrait::to_stream()` which returns `Pin<Box<dyn futures_core::stream::Stream<Item = T>>>`.
//!
//! # The reason of complete API refactor
//!
//! I know we all hate the contagious nature of generic code, and reluctant to use trait object,
//! it's common practice to use static dispatch like `enum-dispatch`. Originally crossfire only
//! have 2 channel variance ([CompatFlavor]), when adding more channel flavor for specific scenario,
//! other than common list and array, and specialized implement for spsc, mpsc, etc,
//! I notice that when the flavor enum grow from 2 types to 4+ types,
//! although the positive result can be observed on Arm, there was a regression in x86 async benchmark,
//! which offset the optimization effort.
//! It's impossible to erased the type while keeping the performance goal having so much types.
//!
//! From the aspect of compiler:
//! - In blocking context, the compiler can eliminate the unused branch according to the context,
//! and keeping the function calls inline, unless you put multiple variant of enum together into a
//! collection.
//! - In async context, the compiler is ignorance, since most of the async code is indirect calls.
//! We can see in generated asm from cargo-show-asm, even you initialize the channel with ArrayQueue, there's still
//! SeqQueue match branch inside the `RecvFuture::poll()`. What's worse when we have 4 types
//! variant in the flavor enum, the compiler think the internal queue ops function no longer worth
//! to inline (because overall flatten code will be too big), and the match branch might fallen
//! back to a big match table instead of simple comparison. This is the reason of performance regression.
//!
//! From the aspect of CPU:
//! - I had tried a manual Vtable by putting method ptr inside AsyncTx/AsyncRx, which is ok on X86,
//! but Arm will have -50% penalty. It looks like Arm is poor on loading / caching function ptr.
//! - Generic Arm CPU has overall poor performance (1/3 ~ 1/2) compared to mainstream x86_64, and
//! bad at atomic CAS, a big match branch might be not so obvious than the positive effect from
//! changing some CAS to direct load/store in the lockless algorithm.
//!
//! From the aspect of API usage:
//! - There're already nice native select mechanisms on async ecology, we don't have to worry about the
//! difference of receiver types, for flexibility.
//! - For blocking context, it might be more common scenario to select from the same type of channels for efficiency.
//! - The crossbeam implementation of select is decouple from channel types and message type, which
//! means the API is possible for crossfire too.
use crate::flavor::{
flavor_dispatch, flavor_select_dispatch, queue_dispatch, Flavor, FlavorImpl, FlavorMC,
FlavorMP, Queue,
};
use crate::shared::*;
pub use crate::{AsyncRxTrait, AsyncTxTrait, BlockingRxTrait, BlockingTxTrait};
use std::mem::MaybeUninit;
/// Compatible flavor that wraps the Array and list type
#[allow(clippy::large_enum_variant)]
pub enum CompatFlavor<T> {
Array(crate::flavor::Array<T>),
List(crate::flavor::List<T>),
}
macro_rules! wrap_compat {
($self: expr, $method:ident $($arg:expr)*)=>{
match $self {
Self::Array(inner) => inner.$method($($arg)*),
Self::List(inner) => inner.$method($($arg)*),
}
};
}
impl<T> Queue for CompatFlavor<T> {
type Item = T;
queue_dispatch!(wrap_compat);
}
impl<T> FlavorImpl for CompatFlavor<T> {
flavor_dispatch!(wrap_compat);
}
impl<T> FlavorSelect for CompatFlavor<T> {
flavor_select_dispatch!(wrap_compat);
}
impl<T> FlavorMP for CompatFlavor<T> {}
impl<T> FlavorMC for CompatFlavor<T> {}
// There's not much performance difference between old RegistrySingle and RegistryMulti,
// we just use RegistryMulti here since this is just for compatible reason.
impl<T: Send + 'static> Flavor for CompatFlavor<T> {
type Send = RegistryMultiSend<T>;
type Recv = RegistryMultiRecv;
}
#[inline(always)]
fn new_list<T: Send + Unpin + 'static>() -> CompatFlavor<T> {
CompatFlavor::<T>::List(crate::flavor::List::new())
}
#[inline(always)]
fn new_array<T: Send + Unpin + 'static>(mut size: usize) -> CompatFlavor<T> {
if size <= 1 {
size = 1;
}
CompatFlavor::<T>::Array(crate::flavor::Array::<T>::new(size))
}
pub type Tx<T> = crate::Tx<CompatFlavor<T>>;
pub type MTx<T> = crate::MTx<CompatFlavor<T>>;
pub type Rx<T> = crate::Rx<CompatFlavor<T>>;
pub type MRx<T> = crate::MRx<CompatFlavor<T>>;
pub type AsyncTx<T> = crate::AsyncTx<CompatFlavor<T>>;
pub type MAsyncTx<T> = crate::MAsyncTx<CompatFlavor<T>>;
pub type AsyncRx<T> = crate::AsyncRx<CompatFlavor<T>>;
pub type MAsyncRx<T> = crate::MAsyncRx<CompatFlavor<T>>;
pub use crate::{
RecvError, RecvTimeoutError, SendError, SendTimeoutError, TryRecvError, TrySendError,
};
pub mod sink {
use super::*;
pub type AsyncSink<T> = crate::sink::AsyncSink<CompatFlavor<T>>;
}
pub mod stream {
use super::*;
pub type AsyncStream<T> = crate::stream::AsyncStream<CompatFlavor<T>>;
}
pub mod spsc {
use super::*;
macro_rules! init_share {
($flavor: expr) => {{
ChannelShared::new($flavor, RegistryMultiSend::new(), RegistryMultiRecv::new())
}};
}
/// Creates an unbounded channel for use in a blocking context.
///
/// The sender will never block, so we use the same `Tx` for all threads.
pub fn unbounded_blocking<T: Unpin + Send + 'static>() -> (Tx<T>, Rx<T>) {
let shared = init_share!(new_list::<T>());
let tx = Tx::new(shared.clone());
let rx = Rx::new(shared);
(tx, rx)
}
/// Creates an unbounded channel for use in an async context.
///
/// The sender will never block, so we use the same `Tx` for all threads.
pub fn unbounded_async<T: Unpin + Send + 'static>() -> (Tx<T>, AsyncRx<T>) {
let shared = init_share!(new_list::<T>());
let tx = Tx::new(shared.clone());
let rx = AsyncRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel for use in a blocking context.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_blocking<T: Unpin + Send + 'static>(size: usize) -> (Tx<T>, Rx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = Tx::new(shared.clone());
let rx = Rx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where both the sender and receiver are async.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_async<T: Unpin + Send + 'static>(size: usize) -> (AsyncTx<T>, AsyncRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = AsyncTx::new(shared.clone());
let rx = AsyncRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where the sender is async and the receiver is blocking.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_tx_async_rx_blocking<T: Unpin + Send + 'static>(
size: usize,
) -> (AsyncTx<T>, Rx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = AsyncTx::new(shared.clone());
let rx = Rx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where the sender is blocking and the receiver is async.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_tx_blocking_rx_async<T: Unpin + Send + 'static>(
size: usize,
) -> (Tx<T>, AsyncRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = Tx::new(shared.clone());
let rx = AsyncRx::new(shared);
(tx, rx)
}
}
pub mod mpsc {
use super::*;
macro_rules! init_share {
($flavor: expr) => {{
ChannelShared::new($flavor, RegistryMultiSend::new(), RegistryMultiRecv::new())
}};
}
/// Creates an unbounded channel for use in a blocking context.
///
/// The sender will never block, so we use the same `Tx` for all threads.
pub fn unbounded_blocking<T: Send + 'static + Unpin>() -> (MTx<T>, Rx<T>) {
let shared = init_share!(new_list::<T>());
let tx = MTx::new(shared.clone());
let rx = Rx::new(shared);
(tx, rx)
}
/// Creates an unbounded channel for use in an async context.
///
/// Although the sender type is `MTx`, it will never block.
pub fn unbounded_async<T: Send + 'static + Unpin>() -> (MTx<T>, AsyncRx<T>) {
let shared = init_share!(new_list::<T>());
let tx = MTx::new(shared.clone());
let rx = AsyncRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel for use in a blocking context.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_blocking<T: Send + 'static + Unpin>(size: usize) -> (MTx<T>, Rx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MTx::new(shared.clone());
let rx = Rx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where both the sender and receiver are async.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_async<T: Send + 'static + Unpin>(size: usize) -> (MAsyncTx<T>, AsyncRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MAsyncTx::new(shared.clone());
let rx = AsyncRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where the sender is async and the receiver is blocking.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_tx_async_rx_blocking<T: Send + 'static + Unpin>(
size: usize,
) -> (MAsyncTx<T>, Rx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MAsyncTx::new(shared.clone());
let rx = Rx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where the sender is blocking and the receiver is async.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_tx_blocking_rx_async<T: Send + 'static + Unpin>(
size: usize,
) -> (MTx<T>, AsyncRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MTx::new(shared.clone());
let rx = AsyncRx::new(shared);
(tx, rx)
}
}
pub mod mpmc {
//! v2 API Compatible Multiple producers, multiple consumers.
use super::*;
macro_rules! init_share {
($flavor: expr) => {{
ChannelShared::new($flavor, RegistryMultiSend::new(), RegistryMultiRecv::new())
}};
}
/// Creates an unbounded channel for use in a blocking context.
///
/// The sender will never block, so we use the same `Tx` for all threads.
pub fn unbounded_blocking<T: Send + 'static + Unpin>() -> (MTx<T>, MRx<T>) {
let shared = init_share!(new_list::<T>());
let tx = MTx::new(shared.clone());
let rx = MRx::new(shared);
(tx, rx)
}
/// Creates an unbounded channel for use in an async context.
///
/// Although the sender type is `MTx`, it will never block.
pub fn unbounded_async<T: Send + 'static + Unpin>() -> (MTx<T>, MAsyncRx<T>) {
let shared = init_share!(new_list::<T>());
let tx = MTx::new(shared.clone());
let rx = MAsyncRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel for use in a blocking context.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_blocking<T: Send + 'static + Unpin>(size: usize) -> (MTx<T>, MRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MTx::new(shared.clone());
let rx = MRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel for use in an async context.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_async<T: Send + 'static + Unpin>(size: usize) -> (MAsyncTx<T>, MAsyncRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MAsyncTx::new(shared.clone());
let rx = MAsyncRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where the sender is async and the receiver is blocking.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_tx_async_rx_blocking<T: Send + 'static + Unpin>(
size: usize,
) -> (MAsyncTx<T>, MRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MAsyncTx::new(shared.clone());
let rx = MRx::new(shared);
(tx, rx)
}
/// Creates a bounded channel where the sender is blocking and the receiver is async.
///
/// As a special case, a channel size of 0 is not supported and will be treated as a channel of size 1.
pub fn bounded_tx_blocking_rx_async<T: Send + 'static + Unpin>(
size: usize,
) -> (MTx<T>, MAsyncRx<T>) {
let shared = init_share!(new_array::<T>(size));
let tx = MTx::new(shared.clone());
let rx = MAsyncRx::new(shared);
(tx, rx)
}
}
================================================
FILE: src/crossbeam/array_queue.rs
================================================
//! Modify by frostyplanet@gmail.com for the crossfire crate:
//!
//! - Optimise for single consumer scenario;
//! - Add token interface according to crossbeam-channel
//! - Modified push() to push_with_ptr();
//! - Add try_push_oneshot() which combinds the logic of push and check_full in one step;
//! - Remove unused functions.
//!
//! Fork from crossbeam-queue crate commit 5a154def002304814d50f3c7658bd30eb46b2fad
//!
//! The MIT License (MIT)
//!
//! Copyright (c) 2025, 2026 frostyplanet@gmail.com
//!
//! Copyright (c) 2019 The Crossbeam Project Developers
//!
//! Permission is hereby granted, free of charge, to any
//! person obtaining a copy of this software and associated
//! documentation files (the "Software"), to deal in the
//! Software without restriction, including without
//! limitation the rights to use, copy, modify, merge,
//! publish, distribute, sublicense, and/or sell copies of
//! the Software, and to permit persons to whom the Software
//! is furnished to do so, subject to the following
//! conditions:
//!
//! The above copyright notice and this permission notice
//! shall be included in all copies or substantial portions
//! of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//! DEALINGS IN THE SOFTWARE.
//!
//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
//!
//! Source:
//! - <http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue>
use core::cell::UnsafeCell;
use crate::flavor::Token;
use core::mem::{self, MaybeUninit};
use core::panic::{RefUnwindSafe, UnwindSafe};
use core::ptr;
use core::sync::atomic::{self, AtomicUsize, Ordering};
use crossbeam_utils::{Backoff, CachePadded};
/// A slot in a queue.
struct Slot<T> {
/// The current stamp.
///
/// If the stamp equals the tail, this node will be next written to. If it equals head + 1,
/// this node will be next read from.
stamp: AtomicUsize,
/// The value in this slot.
value: UnsafeCell<MaybeUninit<T>>,
}
/// A bounded multi-producer multi-consumer queue.
///
/// This queue allocates a fixed-capacity buffer on construction, which is used to store pushed
/// elements. The queue cannot hold more elements than the buffer allows. Attempting to push an
/// element into a full queue will fail. Alternatively, [`force_push`] makes it possible for
/// this queue to be used as a ring-buffer. Having a buffer allocated upfront makes this queue
/// a bit faster than [`SegQueue`].
///
/// [`SegQueue`]: super::SegQueue
pub struct ArrayQueue<T, const MP: bool, const MC: bool> {
/// The head of the queue.
///
/// This value is a "stamp" consisting of an index into the buffer and a lap, but packed into a
/// single `usize`. The lower bits represent the index, while the upper bits represent the lap.
///
/// Elements are popped from the head of the queue.
head: CachePadded<AtomicUsize>,
/// The tail of the queue.
///
/// This value is a "stamp" consisting of an index into the buffer and a lap, but packed into a
/// single `usize`. The lower bits represent the index, while the upper bits represent the lap.
///
/// Elements are pushed into the tail of the queue.
tail: CachePadded<AtomicUsize>,
/// The buffer holding slots.
buffer: Box<[Slot<T>]>,
/// A stamp with the value of `{ lap: 1, index: 0 }`.
one_lap: usize,
}
unsafe impl<T, const MP: bool, const MC: bool> Sync for ArrayQueue<T, MP, MC> {}
unsafe impl<T, const MP: bool, const MC: bool> Send for ArrayQueue<T, MP, MC> {}
impl<T, const MP: bool, const MC: bool> UnwindSafe for ArrayQueue<T, MP, MC> {}
impl<T, const MP: bool, const MC: bool> RefUnwindSafe for ArrayQueue<T, MP, MC> {}
impl<T, const MP: bool, const MC: bool> ArrayQueue<T, MP, MC> {
/// Creates a new bounded queue with the given capacity.
///
/// # Panics
///
/// Panics if the capacity is zero.
pub fn new(cap: usize) -> Self {
assert!(cap > 0, "capacity must be non-zero");
// Head is initialized to `{ lap: 0, index: 0 }`.
// Tail is initialized to `{ lap: 0, index: 0 }`.
let head = 0;
let tail = 0;
// Allocate a buffer of `cap` slots initialized
// with stamps.
let buffer: Box<[Slot<T>]> = (0..cap)
.map(|i| {
// Set the stamp to `{ lap: 0, index: i }`.
Slot { stamp: AtomicUsize::new(i), value: UnsafeCell::new(MaybeUninit::uninit()) }
})
.collect();
// One lap is the smallest power of two greater than `cap`.
gitextract_uux3elsf/
├── .github/
│ └── workflows/
│ ├── cron_2.0_arm.yml
│ ├── cron_2.0_x86.yml
│ ├── cron_dev.yml
│ ├── cron_dev_arm.yml
│ ├── cron_dev_arm_trace.yml
│ ├── cron_master_async_std_arm.yml
│ ├── cron_master_async_std_x86.yml
│ ├── cron_master_compio_arm.yml
│ ├── cron_master_compio_x86.yml
│ ├── cron_master_smol_arm.yml
│ ├── cron_master_smol_x86.yml
│ ├── cron_master_threaded_arm.yml
│ ├── cron_master_threaded_x86.yml
│ ├── cron_master_tokio_arm.yml
│ ├── cron_master_tokio_x86.yml
│ ├── fast.yml
│ ├── leak.yml
│ ├── miri_dev.yml
│ ├── miri_dev_log.yml
│ ├── miri_tokio.yml
│ ├── miri_tokio_cur.yml
│ ├── miri_tokio_cur_log.yml
│ ├── miri_tokio_log.yml
│ ├── pr.yml
│ └── typos.yml
├── .gitignore
├── AGENTS.md
├── CHANGELOG.md
├── CONTRIBUTION
├── Cargo.toml
├── LICENSE
├── Makefile
├── README.md
├── benches/
│ └── inner.rs
├── git-hooks/
│ └── pre-commit
├── rustfmt.toml
├── src/
│ ├── async_rx.rs
│ ├── async_tx.rs
│ ├── backoff.rs
│ ├── blocking_rx.rs
│ ├── blocking_tx.rs
│ ├── collections.rs
│ ├── compat.rs
│ ├── crossbeam/
│ │ ├── array_queue.rs
│ │ ├── array_queue_mpsc.rs
│ │ ├── array_queue_spsc.rs
│ │ ├── err.rs
│ │ ├── mod.rs
│ │ └── seg_queue.rs
│ ├── flavor/
│ │ ├── array.rs
│ │ ├── array_mpsc.rs
│ │ ├── array_spsc.rs
│ │ ├── list.rs
│ │ ├── mod.rs
│ │ ├── one.rs
│ │ ├── one_mpsc.rs
│ │ └── one_spmc.rs
│ ├── lib.rs
│ ├── mpmc.rs
│ ├── mpsc.rs
│ ├── null.rs
│ ├── oneshot.rs
│ ├── select/
│ │ ├── mod.rs
│ │ ├── multiplex.rs
│ │ └── select.rs
│ ├── shared.rs
│ ├── sink.rs
│ ├── spsc.rs
│ ├── stream.rs
│ ├── waitgroup.rs
│ ├── waker.rs
│ ├── waker_registry.rs
│ └── weak.rs
└── test-suite/
├── Cargo.toml
├── benches/
│ ├── async_channel.rs
│ ├── common.rs
│ ├── crossbeam.rs
│ ├── crossfire.rs
│ ├── crossfire_select.rs
│ ├── extra.rs
│ ├── flume.rs
│ ├── kanal.rs
│ └── tokio.rs
├── scripts/
│ └── miri.sh
└── src/
├── lib.rs
├── test_async.rs
├── test_async_blocking.rs
├── test_blocking_async.rs
├── test_blocking_context.rs
├── test_oneshot.rs
├── test_select_async.rs
├── test_select_blocking.rs
├── test_type_switch.rs
└── test_waitgroup.rs
SYMBOL INDEX (1428 symbols across 56 files)
FILE: benches/inner.rs
constant ONE_MILLION (line 15) | const ONE_MILLION: usize = 1000000;
type Foo (line 17) | struct Foo {
type LockedQueue (line 21) | pub struct LockedQueue<T> {
function new (line 28) | pub fn new(cap: usize) -> Self {
function push (line 33) | pub fn push(&self, msg: T) {
function pop (line 42) | pub fn pop(&self) -> Option<T> {
function len (line 58) | pub fn len(&self) -> usize {
function exists (line 65) | pub fn exists(&self) -> bool {
type SpinQueue (line 70) | pub struct SpinQueue<T> {
function new (line 79) | fn new(cap: usize) -> Self {
function get_queue (line 84) | fn get_queue(&self) -> &mut VecDeque<T> {
function push (line 89) | fn push(&self, msg: T) {
function pop (line 99) | fn pop(&self) -> Option<T> {
function _bench_spin_queue (line 110) | fn _bench_spin_queue(count: usize) {
function _bench_locked_queue (line 139) | fn _bench_locked_queue(count: usize) {
function _bench_array_queue (line 168) | fn _bench_array_queue(count: usize) {
function _bench_seg_queue (line 197) | fn _bench_seg_queue(count: usize) {
function _bench_weak_cell (line 226) | fn _bench_weak_cell(count: usize) {
function _bench_empty (line 253) | fn _bench_empty(c: &mut Criterion) {
function _bench_sequence (line 309) | fn _bench_sequence(c: &mut Criterion) {
function _bench_threads (line 383) | fn _bench_threads(c: &mut Criterion) {
FILE: src/async_rx.rs
type AsyncRx (line 57) | pub struct AsyncRx<F: Flavor> {
function fmt (line 66) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 72) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method drop (line 79) | fn drop(&mut self) {
function from (line 85) | fn from(value: Rx<F>) -> Self {
function new (line 93) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function is_disconnected (line 99) | pub fn is_disconnected(&self) -> bool {
function into_stream (line 104) | pub fn into_stream(self) -> AsyncStream<F> {
function into_blocking (line 109) | pub fn into_blocking(self) -> Rx<F> {
function recv (line 126) | pub fn recv<'a>(&'a self) -> RecvFuture<'a, F> {
function recv_timeout (line 144) | pub fn recv_timeout(
function recv_timeout (line 153) | pub fn recv_timeout(
function recv_with_timer (line 199) | pub fn recv_with_timer<'a, FR, R>(&'a self, sleep: FR) -> RecvTimeoutFut...
function try_recv (line 214) | pub fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function read_select (line 224) | pub fn read_select(&self, result: SelectResult) -> Result<F::Item, RecvE...
function poll_item (line 243) | pub(crate) fn poll_item<const STREAM: bool>(
type RecvFuture (line 315) | pub struct RecvFuture<'a, F: Flavor> {
method drop (line 324) | fn drop(&mut self) {
type Output (line 332) | type Output = Result<F::Item, RecvError>;
method poll (line 335) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
type RecvTimeoutFuture (line 356) | pub struct RecvTimeoutFuture<'a, F, FR, R>
method drop (line 379) | fn drop(&mut self) {
type Output (line 391) | type Output = Result<F::Item, RecvTimeoutError>;
method poll (line 394) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
type AsyncRxTrait (line 412) | pub trait AsyncRxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
method recv (line 418) | fn recv(&self) -> impl Future<Output = Result<T, RecvError>> + Send;
method recv_timeout (line 433) | fn recv_timeout(
method recv_with_timer (line 452) | fn recv_with_timer<FR, R>(
method try_recv (line 465) | fn try_recv(&self) -> Result<T, TryRecvError>;
method len (line 468) | fn len(&self) -> usize;
method capacity (line 471) | fn capacity(&self) -> Option<usize>;
method is_empty (line 474) | fn is_empty(&self) -> bool;
method is_full (line 477) | fn is_full(&self) -> bool;
method is_disconnected (line 480) | fn is_disconnected(&self) -> bool;
method get_tx_count (line 483) | fn get_tx_count(&self) -> usize;
method get_rx_count (line 486) | fn get_rx_count(&self) -> usize;
method clone_to_vec (line 488) | fn clone_to_vec(self, count: usize) -> Vec<Self>
method to_stream (line 492) | fn to_stream(self) -> Pin<Box<dyn futures_core::stream::Stream<Item = ...
method get_wakers_count (line 494) | fn get_wakers_count(&self) -> (usize, usize);
function clone_to_vec (line 499) | fn clone_to_vec(self, _count: usize) -> Vec<Self> {
function recv (line 505) | fn recv(&self) -> impl Future<Output = Result<F::Item, RecvError>> + Send {
function recv_timeout (line 512) | fn recv_timeout(
function recv_with_timer (line 519) | fn recv_with_timer<FR, R>(
function try_recv (line 529) | fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function len (line 535) | fn len(&self) -> usize {
function capacity (line 541) | fn capacity(&self) -> Option<usize> {
function is_empty (line 547) | fn is_empty(&self) -> bool {
function is_full (line 553) | fn is_full(&self) -> bool {
function is_disconnected (line 559) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 564) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 569) | fn get_rx_count(&self) -> usize {
function to_stream (line 574) | fn to_stream(self) -> Pin<Box<dyn futures_core::stream::Stream<Item = F:...
function get_wakers_count (line 578) | fn get_wakers_count(&self) -> (usize, usize) {
type MAsyncRx (line 593) | pub struct MAsyncRx<F: Flavor>(pub(crate) AsyncRx<F>);
function fmt (line 596) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 602) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method clone (line 611) | fn clone(&self) -> Self {
function from (line 619) | fn from(rx: MAsyncRx<F>) -> Self {
function new (line 626) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function into_stream (line 633) | pub fn into_stream(self) -> AsyncStream<F> {
function into_blocking (line 638) | pub fn into_blocking(self) -> MRx<F> {
type Target (line 644) | type Target = AsyncRx<F>;
method deref (line 648) | fn deref(&self) -> &Self::Target {
function from (line 654) | fn from(value: MRx<F>) -> Self {
function clone_to_vec (line 662) | fn clone_to_vec(self, count: usize) -> Vec<Self> {
function try_recv (line 672) | fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function recv (line 677) | fn recv(&self) -> impl Future<Output = Result<F::Item, RecvError>> + Send {
function recv_timeout (line 684) | fn recv_timeout(
function recv_with_timer (line 691) | fn recv_with_timer<FR, R>(
function len (line 702) | fn len(&self) -> usize {
function capacity (line 708) | fn capacity(&self) -> Option<usize> {
function is_empty (line 714) | fn is_empty(&self) -> bool {
function is_full (line 720) | fn is_full(&self) -> bool {
function is_disconnected (line 726) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 731) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 736) | fn get_rx_count(&self) -> usize {
function to_stream (line 741) | fn to_stream(self) -> Pin<Box<dyn futures_core::stream::Stream<Item = F:...
function get_wakers_count (line 745) | fn get_wakers_count(&self) -> (usize, usize) {
type Target (line 751) | type Target = ChannelShared<F>;
method deref (line 753) | fn deref(&self) -> &ChannelShared<F> {
function as_ref (line 760) | fn as_ref(&self) -> &ChannelShared<F> {
function as_ref (line 767) | fn as_ref(&self) -> &ChannelShared<F> {
type Flavor (line 773) | type Flavor = F;
method new (line 775) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
type Flavor (line 783) | type Flavor = F;
method new (line 785) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
FILE: src/async_tx.rs
type AsyncTx (line 57) | pub struct AsyncTx<F: Flavor> {
function fmt (line 64) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 70) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method drop (line 79) | fn drop(&mut self) {
function from (line 85) | fn from(value: Tx<F>) -> Self {
function new (line 93) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function into_sink (line 98) | pub fn into_sink(self) -> AsyncSink<F> {
function into_blocking (line 103) | pub fn into_blocking(self) -> Tx<F> {
function is_disconnected (line 109) | pub fn is_disconnected(&self) -> bool {
function send (line 126) | pub fn send<'a>(&'a self, item: F::Item) -> SendFuture<'a, F> {
function try_send (line 138) | pub fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item...
function send_timeout (line 164) | pub fn send_timeout(
function send_timeout (line 173) | pub fn send_timeout(
function send_with_timer (line 218) | pub fn send_with_timer<FR, R>(&self, item: F::Item, fut: FR) -> SendTime...
function poll_send (line 233) | pub(crate) fn poll_send<'a, const SINK: bool>(
type SendFuture (line 299) | pub struct SendFuture<'a, F: Flavor> {
method drop (line 309) | fn drop(&mut self) {
type Output (line 323) | type Output = Result<(), SendError<F::Item>>;
method poll (line 326) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
type SendTimeoutFuture (line 344) | pub struct SendTimeoutFuture<'a, F, FR, R>
method drop (line 368) | fn drop(&mut self) {
type Output (line 384) | type Output = Result<(), SendTimeoutError<F::Item>>;
method poll (line 387) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
type AsyncTxTrait (line 421) | pub trait AsyncTxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
method try_send (line 429) | fn try_send(&self, item: T) -> Result<(), TrySendError<T>>;
method len (line 432) | fn len(&self) -> usize;
method capacity (line 435) | fn capacity(&self) -> Option<usize>;
method is_empty (line 438) | fn is_empty(&self) -> bool;
method is_full (line 441) | fn is_full(&self) -> bool;
method is_disconnected (line 444) | fn is_disconnected(&self) -> bool;
method get_tx_count (line 447) | fn get_tx_count(&self) -> usize;
method get_rx_count (line 450) | fn get_rx_count(&self) -> usize;
method clone_to_vec (line 452) | fn clone_to_vec(self, count: usize) -> Vec<Self>
method get_wakers_count (line 456) | fn get_wakers_count(&self) -> (usize, usize);
method send (line 463) | fn send(&self, item: T) -> impl Future<Output = Result<(), SendError<T...
method send_timeout (line 479) | fn send_timeout<'a>(
method send_with_timer (line 501) | fn send_with_timer<FR, R>(
function clone_to_vec (line 511) | fn clone_to_vec(self, count: usize) -> Vec<Self> {
function try_send (line 517) | fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
function send (line 522) | fn send(&self, item: F::Item) -> impl Future<Output = Result<(), SendErr...
function send_timeout (line 532) | fn send_timeout<'a>(
function send_with_timer (line 542) | fn send_with_timer<FR, R>(
function len (line 554) | fn len(&self) -> usize {
function capacity (line 560) | fn capacity(&self) -> Option<usize> {
function is_empty (line 566) | fn is_empty(&self) -> bool {
function is_full (line 572) | fn is_full(&self) -> bool {
function is_disconnected (line 578) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 583) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 588) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 592) | fn get_wakers_count(&self) -> (usize, usize) {
type MAsyncTx (line 607) | pub struct MAsyncTx<F: Flavor>(pub(crate) AsyncTx<F>);
function fmt (line 610) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 616) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method clone (line 625) | fn clone(&self) -> Self {
function from (line 633) | fn from(tx: MAsyncTx<F>) -> Self {
function new (line 640) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function into_sink (line 645) | pub fn into_sink(self) -> AsyncSink<F> {
function into_blocking (line 650) | pub fn into_blocking(self) -> MTx<F> {
function downgrade (line 671) | pub fn downgrade(&self) -> WeakTx<F>
type Target (line 680) | type Target = AsyncTx<F>;
method deref (line 684) | fn deref(&self) -> &Self::Target {
function from (line 690) | fn from(value: MTx<F>) -> Self {
function clone_to_vec (line 698) | fn clone_to_vec(self, count: usize) -> Vec<Self> {
function try_send (line 708) | fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
function send (line 713) | fn send(&self, item: F::Item) -> impl Future<Output = Result<(), SendErr...
function send_timeout (line 723) | fn send_timeout<'a>(
function send_with_timer (line 733) | fn send_with_timer<FR, R>(
function len (line 745) | fn len(&self) -> usize {
function capacity (line 751) | fn capacity(&self) -> Option<usize> {
function is_empty (line 757) | fn is_empty(&self) -> bool {
function is_full (line 763) | fn is_full(&self) -> bool {
function is_disconnected (line 769) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 774) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 779) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 783) | fn get_wakers_count(&self) -> (usize, usize) {
type Target (line 789) | type Target = ChannelShared<F>;
method deref (line 791) | fn deref(&self) -> &ChannelShared<F> {
function as_ref (line 798) | fn as_ref(&self) -> &ChannelShared<F> {
function as_ref (line 805) | fn as_ref(&self) -> &ChannelShared<F> {
type Flavor (line 811) | type Flavor = F;
method new (line 813) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
type Flavor (line 821) | type Flavor = F;
method new (line 823) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
FILE: src/backoff.rs
constant SPIN_LIMIT (line 6) | pub const SPIN_LIMIT: u16 = 6;
constant DEFAULT_LIMIT (line 9) | pub const DEFAULT_LIMIT: u16 = 6;
constant DEFAULT_LIMIT (line 11) | pub const DEFAULT_LIMIT: u16 = 10;
constant MAX_LIMIT (line 12) | pub const MAX_LIMIT: u16 = 10;
constant DEFAULT_CONFIG (line 14) | pub const DEFAULT_CONFIG: u32 =
function detect_backoff_cfg (line 32) | pub fn detect_backoff_cfg() {
type BackoffConfig (line 49) | pub struct BackoffConfig {
method detect (line 63) | pub fn detect() -> Self {
method to_u32 (line 68) | pub const fn to_u32(self) -> u32 {
method from_u32 (line 74) | pub const fn from_u32(config: u32) -> Self {
method async_limit (line 80) | pub const fn async_limit(mut self, limit: u16) -> Self {
method limit (line 90) | pub const fn limit(mut self, limit: u16) -> Self {
method spin (line 97) | pub const fn spin(mut self, spin_limit: u16) -> Self {
method default (line 56) | fn default() -> Self {
type Backoff (line 105) | pub struct Backoff {
method new (line 112) | pub fn new() -> Self {
method from (line 117) | pub fn from(config: BackoffConfig) -> Self {
method spin (line 123) | pub fn spin(&mut self) -> bool {
method set_step (line 136) | pub fn set_step(&mut self, step: u16) {
method snooze (line 141) | pub fn snooze(&mut self) -> bool {
method yield_now (line 158) | pub fn yield_now(&mut self) -> bool {
method is_completed (line 168) | pub fn is_completed(&self) -> bool {
method step (line 174) | pub fn step(&self) -> usize {
method reset (line 179) | pub fn reset(&mut self) {
function test_backoff (line 190) | fn test_backoff() {
FILE: src/blocking_rx.rs
type Rx (line 45) | pub struct Rx<F: Flavor> {
function fmt (line 55) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 61) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method drop (line 68) | fn drop(&mut self) {
function from (line 74) | fn from(value: AsyncRx<F>) -> Self {
function new (line 82) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function _recv_blocking (line 87) | pub(crate) fn _recv_blocking(
function recv (line 179) | pub fn recv(&self) -> Result<F::Item, RecvError> {
function try_recv (line 194) | pub fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function recv_timeout (line 209) | pub fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTim...
function is_disconnected (line 221) | pub fn is_disconnected(&self) -> bool {
function read_select (line 230) | pub fn read_select(&self, result: SelectResult) -> Result<F::Item, RecvE...
function into_async (line 242) | pub fn into_async(self) -> AsyncRx<F> {
type MRx (line 253) | pub struct MRx<F: Flavor>(pub(crate) Rx<F>);
function fmt (line 256) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 262) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function new (line 274) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function into_async (line 279) | pub fn into_async(self) -> MAsyncRx<F> {
method clone (line 286) | fn clone(&self) -> Self {
type Target (line 294) | type Target = Rx<F>;
method deref (line 298) | fn deref(&self) -> &Self::Target {
function from (line 304) | fn from(rx: MRx<F>) -> Self {
function from (line 310) | fn from(value: MAsyncRx<F>) -> Self {
type BlockingRxTrait (line 317) | pub trait BlockingRxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
method recv (line 323) | fn recv(&self) -> Result<T, RecvError>;
method try_recv (line 332) | fn try_recv(&self) -> Result<T, TryRecvError>;
method recv_timeout (line 342) | fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError>;
method len (line 345) | fn len(&self) -> usize;
method capacity (line 348) | fn capacity(&self) -> Option<usize>;
method is_empty (line 351) | fn is_empty(&self) -> bool;
method is_full (line 354) | fn is_full(&self) -> bool;
method is_disconnected (line 357) | fn is_disconnected(&self) -> bool;
method get_tx_count (line 360) | fn get_tx_count(&self) -> usize;
method get_rx_count (line 363) | fn get_rx_count(&self) -> usize;
method clone_to_vec (line 365) | fn clone_to_vec(self, count: usize) -> Vec<Self>
method get_wakers_count (line 369) | fn get_wakers_count(&self) -> (usize, usize);
function clone_to_vec (line 374) | fn clone_to_vec(self, _count: usize) -> Vec<Self> {
function recv (line 380) | fn recv(&self) -> Result<F::Item, RecvError> {
function try_recv (line 385) | fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function recv_timeout (line 390) | fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTimeout...
function len (line 396) | fn len(&self) -> usize {
function capacity (line 402) | fn capacity(&self) -> Option<usize> {
function is_empty (line 408) | fn is_empty(&self) -> bool {
function is_full (line 414) | fn is_full(&self) -> bool {
function is_disconnected (line 420) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 425) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 430) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 434) | fn get_wakers_count(&self) -> (usize, usize) {
function clone_to_vec (line 444) | fn clone_to_vec(self, count: usize) -> Vec<Self> {
function recv (line 454) | fn recv(&self) -> Result<F::Item, RecvError> {
function try_recv (line 459) | fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function recv_timeout (line 464) | fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTimeout...
function len (line 470) | fn len(&self) -> usize {
function capacity (line 476) | fn capacity(&self) -> Option<usize> {
function is_empty (line 482) | fn is_empty(&self) -> bool {
function is_full (line 488) | fn is_full(&self) -> bool {
function is_disconnected (line 494) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 499) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 504) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 508) | fn get_wakers_count(&self) -> (usize, usize) {
type Target (line 514) | type Target = ChannelShared<F>;
method deref (line 517) | fn deref(&self) -> &ChannelShared<F> {
function as_ref (line 524) | fn as_ref(&self) -> &ChannelShared<F> {
function as_ref (line 531) | fn as_ref(&self) -> &ChannelShared<F> {
type Flavor (line 537) | type Flavor = F;
method new (line 539) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
type Flavor (line 550) | type Flavor = F;
method new (line 553) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
FILE: src/blocking_tx.rs
type Tx (line 47) | pub struct Tx<F: Flavor> {
function fmt (line 57) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 63) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method drop (line 70) | fn drop(&mut self) {
function from (line 76) | fn from(value: AsyncTx<F>) -> Self {
function new (line 84) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function is_disconnected (line 90) | pub fn is_disconnected(&self) -> bool {
function into_async (line 95) | pub fn into_async(self) -> AsyncTx<F> {
function _send_bounded (line 102) | pub(crate) fn _send_bounded(
function send (line 225) | pub fn send(&self, item: F::Item) -> Result<(), SendError<F::Item>> {
function try_send (line 250) | pub fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item...
function send_timeout (line 275) | pub fn send_timeout(
type MTx (line 308) | pub struct MTx<F: Flavor>(pub(crate) Tx<F>);
function fmt (line 311) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 317) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function from (line 323) | fn from(tx: MTx<F>) -> Self {
function from (line 329) | fn from(value: MAsyncTx<F>) -> Self {
function new (line 339) | pub(crate) fn new(shared: Arc<ChannelShared<F>>) -> Self {
function into_async (line 344) | pub fn into_async(self) -> MAsyncTx<F> {
function downgrade (line 364) | pub fn downgrade(&self) -> WeakTx<F> {
method clone (line 371) | fn clone(&self) -> Self {
type Target (line 379) | type Target = Tx<F>;
method deref (line 383) | fn deref(&self) -> &Self::Target {
type BlockingTxTrait (line 389) | pub trait BlockingTxTrait<T>: Send + 'static + fmt::Debug + fmt::Display {
method send (line 395) | fn send(&self, _item: T) -> Result<(), SendError<T>>;
method try_send (line 404) | fn try_send(&self, _item: T) -> Result<(), TrySendError<T>>;
method send_timeout (line 414) | fn send_timeout(&self, item: T, timeout: Duration) -> Result<(), SendT...
method len (line 417) | fn len(&self) -> usize;
method capacity (line 420) | fn capacity(&self) -> Option<usize>;
method is_empty (line 423) | fn is_empty(&self) -> bool;
method is_full (line 426) | fn is_full(&self) -> bool;
method is_disconnected (line 429) | fn is_disconnected(&self) -> bool;
method get_tx_count (line 432) | fn get_tx_count(&self) -> usize;
method get_rx_count (line 435) | fn get_rx_count(&self) -> usize;
method clone_to_vec (line 437) | fn clone_to_vec(self, count: usize) -> Vec<Self>
method get_wakers_count (line 441) | fn get_wakers_count(&self) -> (usize, usize);
function clone_to_vec (line 446) | fn clone_to_vec(self, _count: usize) -> Vec<Self> {
function send (line 452) | fn send(&self, item: F::Item) -> Result<(), SendError<F::Item>> {
function try_send (line 457) | fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
function send_timeout (line 462) | fn send_timeout(
function len (line 470) | fn len(&self) -> usize {
function capacity (line 476) | fn capacity(&self) -> Option<usize> {
function is_empty (line 482) | fn is_empty(&self) -> bool {
function is_full (line 488) | fn is_full(&self) -> bool {
function is_disconnected (line 494) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 499) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 504) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 508) | fn get_wakers_count(&self) -> (usize, usize) {
function clone_to_vec (line 515) | fn clone_to_vec(self, count: usize) -> Vec<Self> {
function send (line 525) | fn send(&self, item: F::Item) -> Result<(), SendError<F::Item>> {
function try_send (line 530) | fn try_send(&self, item: F::Item) -> Result<(), TrySendError<F::Item>> {
function send_timeout (line 535) | fn send_timeout(
function len (line 543) | fn len(&self) -> usize {
function capacity (line 549) | fn capacity(&self) -> Option<usize> {
function is_empty (line 555) | fn is_empty(&self) -> bool {
function is_full (line 561) | fn is_full(&self) -> bool {
function is_disconnected (line 567) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 572) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 577) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 581) | fn get_wakers_count(&self) -> (usize, usize) {
type Target (line 587) | type Target = ChannelShared<F>;
method deref (line 589) | fn deref(&self) -> &ChannelShared<F> {
function as_ref (line 596) | fn as_ref(&self) -> &ChannelShared<F> {
function as_ref (line 603) | fn as_ref(&self) -> &ChannelShared<F> {
type Flavor (line 609) | type Flavor = F;
method new (line 611) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
type Flavor (line 619) | type Flavor = F;
method new (line 621) | fn new(shared: Arc<ChannelShared<F>>) -> Self {
FILE: src/collections.rs
type ArcCell (line 7) | pub struct ArcCell<T> {
method drop (line 13) | fn drop(&mut self) {
function new (line 23) | pub fn new() -> Self {
function exists (line 28) | pub fn exists(&self) -> bool {
function pop (line 33) | pub fn pop(&self) -> Option<Arc<T>> {
function clear (line 44) | pub fn clear(&self) {
function try_put (line 53) | pub fn try_put(&self, item: Arc<T>) {
type WeakCell (line 70) | pub struct WeakCell<T> {
method drop (line 79) | fn drop(&mut self) {
function new (line 86) | pub fn new() -> Self {
function is_empty (line 91) | pub fn is_empty(&self) -> bool {
function pop (line 96) | pub fn pop(&self) -> Option<Arc<T>> {
function clear (line 117) | pub fn clear(&self) -> bool {
function replace (line 136) | pub fn replace(&self, item: Weak<T>) {
function test_weak_cell (line 148) | fn test_weak_cell() {
FILE: src/compat.rs
type CompatFlavor (line 71) | pub enum CompatFlavor<T> {
type Item (line 85) | type Item = T;
type Send (line 103) | type Send = RegistryMultiSend<T>;
type Recv (line 104) | type Recv = RegistryMultiRecv;
function new_list (line 108) | fn new_list<T: Send + Unpin + 'static>() -> CompatFlavor<T> {
function new_array (line 113) | fn new_array<T: Send + Unpin + 'static>(mut size: usize) -> CompatFlavor...
type Tx (line 120) | pub type Tx<T> = crate::Tx<CompatFlavor<T>>;
type MTx (line 122) | pub type MTx<T> = crate::MTx<CompatFlavor<T>>;
type Rx (line 124) | pub type Rx<T> = crate::Rx<CompatFlavor<T>>;
type MRx (line 126) | pub type MRx<T> = crate::MRx<CompatFlavor<T>>;
type AsyncTx (line 128) | pub type AsyncTx<T> = crate::AsyncTx<CompatFlavor<T>>;
type MAsyncTx (line 130) | pub type MAsyncTx<T> = crate::MAsyncTx<CompatFlavor<T>>;
type AsyncRx (line 132) | pub type AsyncRx<T> = crate::AsyncRx<CompatFlavor<T>>;
type MAsyncRx (line 134) | pub type MAsyncRx<T> = crate::MAsyncRx<CompatFlavor<T>>;
type AsyncSink (line 143) | pub type AsyncSink<T> = crate::sink::AsyncSink<CompatFlavor<T>>;
type AsyncStream (line 149) | pub type AsyncStream<T> = crate::stream::AsyncStream<CompatFlavor<T>>;
function unbounded_blocking (line 165) | pub fn unbounded_blocking<T: Unpin + Send + 'static>() -> (Tx<T>, Rx<T>) {
function unbounded_async (line 175) | pub fn unbounded_async<T: Unpin + Send + 'static>() -> (Tx<T>, AsyncRx<T...
function bounded_blocking (line 185) | pub fn bounded_blocking<T: Unpin + Send + 'static>(size: usize) -> (Tx<T...
function bounded_async (line 195) | pub fn bounded_async<T: Unpin + Send + 'static>(size: usize) -> (AsyncTx...
function bounded_tx_async_rx_blocking (line 205) | pub fn bounded_tx_async_rx_blocking<T: Unpin + Send + 'static>(
function bounded_tx_blocking_rx_async (line 217) | pub fn bounded_tx_blocking_rx_async<T: Unpin + Send + 'static>(
function unbounded_blocking (line 240) | pub fn unbounded_blocking<T: Send + 'static + Unpin>() -> (MTx<T>, Rx<T>) {
function unbounded_async (line 250) | pub fn unbounded_async<T: Send + 'static + Unpin>() -> (MTx<T>, AsyncRx<...
function bounded_blocking (line 260) | pub fn bounded_blocking<T: Send + 'static + Unpin>(size: usize) -> (MTx<...
function bounded_async (line 270) | pub fn bounded_async<T: Send + 'static + Unpin>(size: usize) -> (MAsyncT...
function bounded_tx_async_rx_blocking (line 280) | pub fn bounded_tx_async_rx_blocking<T: Send + 'static + Unpin>(
function bounded_tx_blocking_rx_async (line 292) | pub fn bounded_tx_blocking_rx_async<T: Send + 'static + Unpin>(
function unbounded_blocking (line 316) | pub fn unbounded_blocking<T: Send + 'static + Unpin>() -> (MTx<T>, MRx<T...
function unbounded_async (line 326) | pub fn unbounded_async<T: Send + 'static + Unpin>() -> (MTx<T>, MAsyncRx...
function bounded_blocking (line 336) | pub fn bounded_blocking<T: Send + 'static + Unpin>(size: usize) -> (MTx<...
function bounded_async (line 346) | pub fn bounded_async<T: Send + 'static + Unpin>(size: usize) -> (MAsyncT...
function bounded_tx_async_rx_blocking (line 356) | pub fn bounded_tx_async_rx_blocking<T: Send + 'static + Unpin>(
function bounded_tx_blocking_rx_async (line 368) | pub fn bounded_tx_blocking_rx_async<T: Send + 'static + Unpin>(
FILE: src/crossbeam/array_queue.rs
type Slot (line 56) | struct Slot<T> {
type ArrayQueue (line 76) | pub struct ArrayQueue<T, const MP: bool, const MC: bool> {
function new (line 112) | pub fn new(cap: usize) -> Self {
function try_push_oneshot (line 146) | pub unsafe fn try_push_oneshot(&self, value: *const T) -> Option<bool> {
function _try_push (line 171) | fn _try_push(&self, tail: usize, value: *const T) -> Result<bool, (usize...
function push_with_ptr (line 219) | pub unsafe fn push_with_ptr(&self, value: *const T) -> bool {
function start_read (line 261) | pub fn start_read(&self, final_check: bool) -> Option<Token> {
function pop (line 270) | pub fn pop(&self, final_check: bool) -> Option<T> {
function _start_read (line 281) | fn _start_read(&self, final_check: bool) -> Option<(&Slot<T>, usize)> {
function read (line 357) | pub fn read(&self, token: Token) -> T {
function capacity (line 366) | pub fn capacity(&self) -> usize {
function is_empty (line 372) | pub fn is_empty(&self) -> bool {
function is_full (line 386) | pub fn is_full(&self) -> bool {
function len (line 399) | pub fn len(&self) -> usize {
method drop (line 425) | fn drop(&mut self) {
FILE: src/crossbeam/array_queue_mpsc.rs
type Slot (line 56) | struct Slot<T> {
type ArrayQueueMpsc (line 65) | pub struct ArrayQueueMpsc<T> {
function new (line 97) | pub fn new(cap: usize) -> Self {
function _try_push (line 127) | fn _try_push(
function push_with_ptr (line 160) | pub unsafe fn push_with_ptr(&self, value: *const T) -> bool {
function try_push_oneshot (line 190) | pub unsafe fn try_push_oneshot(&self, value: *const T) -> Option<bool> {
function start_read (line 205) | pub fn start_read(&self, final_check: bool) -> Option<Token> {
function pop (line 215) | pub fn pop(&self, final_check: bool) -> Option<T> {
function pop_cached (line 228) | pub fn pop_cached(&self) -> Option<T> {
function _start_read (line 242) | fn _start_read<const SPIN: bool>(&self, _final_check: bool) -> Option<(u...
function _read (line 267) | fn _read(&self, head: u32, tail_cached: u32) -> (&Slot<T>, u64) {
function read (line 292) | pub fn read(&self, token: Token) -> T {
function capacity (line 302) | pub fn capacity(&self) -> usize {
function is_empty (line 308) | pub fn is_empty(&self) -> bool {
function is_full (line 316) | pub fn is_full(&self) -> bool {
function len (line 324) | pub fn len(&self) -> usize {
method drop (line 348) | fn drop(&mut self) {
FILE: src/crossbeam/array_queue_spsc.rs
type Slot (line 57) | struct Slot<T> {
type ArrayQueueSpsc (line 71) | pub struct ArrayQueueSpsc<T> {
function new (line 113) | pub fn new(cap: usize) -> Self {
function _try_push (line 139) | fn _try_push(&self, order: Ordering, value: *const T) -> bool {
function push_with_ptr (line 178) | pub unsafe fn push_with_ptr(&self, value: *const T) -> bool {
function push_with_ptr_final (line 183) | pub unsafe fn push_with_ptr_final(&self, value: *const T) -> bool {
function start_read (line 188) | pub fn start_read(&self, final_check: bool) -> Option<Token> {
function pop (line 198) | pub fn pop(&self, final_check: bool) -> Option<T> {
function pop_cached (line 210) | pub fn pop_cached(&self) -> Option<T> {
function _start_read (line 223) | fn _start_read<const SPIN: bool>(&self, _final_check: bool) -> Option<(u...
function _read (line 254) | fn _read(&self, head: u32, tail_cached: u32) -> (&Slot<T>, u64) {
function read (line 275) | pub fn read(&self, token: Token) -> T {
function capacity (line 284) | pub fn capacity(&self) -> usize {
function is_empty (line 290) | pub fn is_empty(&self) -> bool {
function is_full (line 304) | pub fn is_full(&self) -> bool {
function len (line 317) | pub fn len(&self) -> usize {
method drop (line 343) | fn drop(&mut self) {
FILE: src/crossbeam/err.rs
type SendError (line 38) | pub struct SendError<T>(pub T);
type TrySendError (line 44) | pub enum TrySendError<T> {
type SendTimeoutError (line 59) | pub enum SendTimeoutError<T> {
type RecvError (line 74) | pub struct RecvError;
method fmt (line 256) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type TryRecvError (line 78) | pub enum TryRecvError {
method fmt (line 264) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method from (line 275) | fn from(err: RecvError) -> Self {
method is_empty (line 284) | pub fn is_empty(&self) -> bool {
method is_disconnected (line 289) | pub fn is_disconnected(&self) -> bool {
type RecvTimeoutError (line 91) | pub enum RecvTimeoutError {
method fmt (line 295) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method from (line 306) | fn from(err: RecvError) -> Self {
method is_timeout (line 315) | pub fn is_timeout(&self) -> bool {
method is_disconnected (line 320) | pub fn is_disconnected(&self) -> bool {
function fmt (line 103) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function fmt (line 109) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function into_inner (line 131) | pub fn into_inner(self) -> T {
function fmt (line 137) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function fmt (line 146) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function from (line 157) | fn from(err: SendError<T>) -> Self {
function into_inner (line 178) | pub fn into_inner(self) -> T {
function is_full (line 186) | pub fn is_full(&self) -> bool {
function is_disconnected (line 191) | pub fn is_disconnected(&self) -> bool {
function fmt (line 197) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function fmt (line 203) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function from (line 214) | fn from(err: SendError<T>) -> Self {
function into_inner (line 237) | pub fn into_inner(self) -> T {
function is_timeout (line 245) | pub fn is_timeout(&self) -> bool {
function is_disconnected (line 250) | pub fn is_disconnected(&self) -> bool {
FILE: src/crossbeam/seg_queue.rs
constant WRITE (line 54) | const WRITE: usize = 1;
constant READ (line 55) | const READ: usize = 2;
constant DESTROY (line 56) | const DESTROY: usize = 4;
constant LAP (line 59) | const LAP: usize = 32;
constant BLOCK_CAP (line 61) | const BLOCK_CAP: usize = LAP - 1;
constant SHIFT (line 63) | const SHIFT: usize = 1;
constant HAS_NEXT (line 65) | const HAS_NEXT: usize = 1;
type Slot (line 68) | struct Slot<T> {
function wait_write (line 78) | fn wait_write(&self) {
type Block (line 89) | struct Block<T> {
constant LAYOUT (line 98) | const LAYOUT: Layout = {
function new (line 108) | fn new() -> Box<Self> {
function wait_next (line 126) | fn wait_next(&self) -> *mut Self {
function destroy (line 138) | unsafe fn destroy(this: *mut Self, start: usize) {
type Position (line 159) | struct Position<T> {
type SegQueue (line 175) | pub struct SegQueue<T> {
function new (line 194) | pub const fn new() -> Self {
function push (line 210) | pub fn push(&self, value: T) {
function start_read (line 291) | pub fn start_read(&self) -> Option<Token> {
function pop (line 300) | pub fn pop<const FINAL: bool>(&self) -> Option<T> {
function _pop (line 309) | fn _pop<const FINAL: bool>(&self) -> Option<(*mut Block<T>, usize)> {
function read (line 392) | pub fn read(&self, token: Token) -> T {
function _read (line 399) | fn _read(&self, block: *mut Block<T>, offset: usize) -> T {
function is_empty (line 418) | pub fn is_empty(&self) -> bool {
function len (line 425) | pub fn len(&self) -> usize {
method drop (line 463) | fn drop(&mut self) {
function fmt (line 500) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method default (line 506) | fn default() -> Self {
type Item (line 512) | type Item = T;
type IntoIter (line 514) | type IntoIter = IntoIter<T>;
method into_iter (line 516) | fn into_iter(self) -> Self::IntoIter {
type IntoIter (line 522) | pub struct IntoIter<T> {
type Item (line 527) | type Item = T;
method next (line 529) | fn next(&mut self) -> Option<Self::Item> {
FILE: src/flavor/array.rs
type Array (line 6) | pub type Array<T> = _Array<T, true, true>;
type _Array (line 8) | pub struct _Array<T, const MP: bool, const MC: bool>(ArrayQueue<T, MP, M...
function new (line 11) | pub fn new(mut bound: usize) -> Self {
type Item (line 21) | type Item = T;
method pop (line 24) | fn pop(&self) -> Option<T> {
method push (line 29) | fn push(&self, item: T) -> Result<(), T> {
method is_full (line 39) | fn is_full(&self) -> bool {
method is_empty (line 44) | fn is_empty(&self) -> bool {
method len (line 49) | fn len(&self) -> usize {
method capacity (line 54) | fn capacity(&self) -> Option<usize> {
method try_send (line 61) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_send_oneshot (line 66) | fn try_send_oneshot(&self, item: *const T) -> Option<bool> {
method try_recv (line 71) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 76) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 81) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 97) | fn may_direct_copy(&self) -> bool {
method try_select (line 109) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 114) | fn read_with_token(&self, token: Token) -> T {
method new_with_bound (line 121) | fn new_with_bound(size: usize) -> Self {
FILE: src/flavor/array_mpsc.rs
type ArrayMpsc (line 10) | pub struct ArrayMpsc<T>(ArrayQueueMpsc<T>);
function new (line 13) | pub fn new(mut bound: usize) -> Self {
type Item (line 23) | type Item = T;
method pop (line 26) | fn pop(&self) -> Option<T> {
method push (line 31) | fn push(&self, item: T) -> Result<(), T> {
method is_full (line 41) | fn is_full(&self) -> bool {
method is_empty (line 46) | fn is_empty(&self) -> bool {
method len (line 51) | fn len(&self) -> usize {
method capacity (line 56) | fn capacity(&self) -> Option<usize> {
method try_send (line 63) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_send_oneshot (line 68) | fn try_send_oneshot(&self, item: *const T) -> Option<bool> {
method try_recv_cached (line 73) | fn try_recv_cached(&self) -> Option<T> {
method try_recv (line 78) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 83) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 88) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 104) | fn may_direct_copy(&self) -> bool {
method try_select (line 111) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 116) | fn read_with_token(&self, token: Token) -> T {
method new_with_bound (line 123) | fn new_with_bound(size: usize) -> Self {
FILE: src/flavor/array_spsc.rs
type ArraySpsc (line 10) | pub struct ArraySpsc<T>(ArrayQueueSpsc<T>);
function new (line 13) | pub fn new(mut bound: usize) -> Self {
type Item (line 23) | type Item = T;
method pop (line 26) | fn pop(&self) -> Option<T> {
method push (line 31) | fn push(&self, item: T) -> Result<(), T> {
method is_full (line 41) | fn is_full(&self) -> bool {
method is_empty (line 46) | fn is_empty(&self) -> bool {
method len (line 51) | fn len(&self) -> usize {
method capacity (line 56) | fn capacity(&self) -> Option<usize> {
method try_send (line 63) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_send_oneshot (line 68) | fn try_send_oneshot(&self, item: *const T) -> Option<bool> {
method try_recv_cached (line 73) | fn try_recv_cached(&self) -> Option<T> {
method try_recv (line 78) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 83) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 88) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 93) | fn may_direct_copy(&self) -> bool {
method try_select (line 103) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 108) | fn read_with_token(&self, token: Token) -> T {
method new_with_bound (line 115) | fn new_with_bound(size: usize) -> Self {
FILE: src/flavor/list.rs
type List (line 6) | pub struct List<T>(SegQueue<T>);
function new (line 10) | pub fn new() -> Self {
type Item (line 16) | type Item = T;
method pop (line 19) | fn pop(&self) -> Option<T> {
method push (line 24) | fn push(&self, item: T) -> Result<(), T> {
method len (line 30) | fn len(&self) -> usize {
method capacity (line 35) | fn capacity(&self) -> Option<usize> {
method is_full (line 40) | fn is_full(&self) -> bool {
method is_empty (line 45) | fn is_empty(&self) -> bool {
method try_send (line 52) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_recv (line 58) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 63) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 68) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 73) | fn may_direct_copy(&self) -> bool {
method new (line 80) | fn new() -> Self {
method try_select (line 87) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 95) | fn read_with_token(&self, token: Token) -> T {
FILE: src/flavor/mod.rs
type Token (line 22) | pub(crate) struct Token {
method new (line 29) | pub(crate) fn new(pos: *const u8, stamp: usize) -> Self {
method default (line 36) | fn default() -> Self {
type Queue (line 43) | pub trait Queue {
method pop (line 46) | fn pop(&self) -> Option<Self::Item>;
method push (line 48) | fn push(&self, item: Self::Item) -> Result<(), Self::Item>;
method len (line 50) | fn len(&self) -> usize;
method capacity (line 52) | fn capacity(&self) -> Option<usize>;
method is_full (line 54) | fn is_full(&self) -> bool;
method is_empty (line 56) | fn is_empty(&self) -> bool;
type Item (line 310) | type Item = F::Item;
type FlavorImpl (line 60) | pub(crate) trait FlavorImpl: Queue {
method try_send (line 61) | fn try_send(&self, item: &MaybeUninit<Self::Item>) -> bool;
method try_send_oneshot (line 64) | fn try_send_oneshot(&self, _item: *const Self::Item) -> Option<bool> {
method try_recv_cached (line 72) | fn try_recv_cached(&self) -> Option<Self::Item> {
method try_recv (line 76) | fn try_recv(&self) -> Option<Self::Item>;
method try_recv_final (line 78) | fn try_recv_final(&self) -> Option<Self::Item>;
method backoff_limit (line 80) | fn backoff_limit(&self) -> u16;
method may_direct_copy (line 83) | fn may_direct_copy(&self) -> bool {
type FlavorSelect (line 88) | pub(crate) trait FlavorSelect: Queue {
method try_select (line 90) | fn try_select(&self, final_check: bool) -> Option<Token>;
method read_with_token (line 93) | fn read_with_token(&self, token: Token) -> Self::Item;
type Flavor (line 190) | pub trait Flavor: Send + 'static + FlavorImpl {
type Send (line 265) | type Send = S;
type Recv (line 266) | type Recv = R;
type FlavorMP (line 195) | pub trait FlavorMP {}
type FlavorMC (line 196) | pub trait FlavorMC {}
type FlavorNew (line 198) | pub trait FlavorNew {
method new (line 199) | fn new() -> Self;
method new (line 242) | fn new() -> Self {
type FlavorBounded (line 202) | pub trait FlavorBounded {
method new_with_bound (line 203) | fn new_with_bound(size: usize) -> Self;
method new_with_bound (line 254) | fn new_with_bound(size: usize) -> Self {
type FlavorWrap (line 207) | pub struct FlavorWrap<F, S, R> {
function new (line 222) | pub fn new() -> Self
function from_inner (line 230) | pub(crate) fn from_inner(f: F) -> Self {
type Target (line 275) | type Target = F;
method deref (line 278) | fn deref(&self) -> &F {
function print_flavor_size (line 338) | fn print_flavor_size() {
FILE: src/flavor/one.rs
type One (line 19) | pub struct One<T> {
type Item (line 30) | type Item = T;
method pop (line 33) | fn pop(&self) -> Option<T> {
method push (line 38) | fn push(&self, item: T) -> Result<(), T> {
method len (line 48) | fn len(&self) -> usize {
method capacity (line 57) | fn capacity(&self) -> Option<usize> {
method is_full (line 62) | fn is_full(&self) -> bool {
method is_empty (line 67) | fn is_empty(&self) -> bool {
function new (line 76) | pub fn new() -> Self {
function unpack (line 81) | fn unpack(pos: u32) -> (u16, u16) {
function pack (line 88) | fn pack(head: u16, tail: u16) -> u32 {
function _try_push (line 94) | unsafe fn _try_push(
function _start_read (line 120) | fn _start_read(&self, order: Ordering) -> Option<(u16, u16)> {
function _pop (line 142) | fn _pop(&self, order: Ordering) -> Option<T> {
type Slot (line 151) | struct Slot<T> {
function init (line 158) | fn init(i: u16) -> Self {
function write (line 163) | fn write(&self, tail: u16, value: *const T) {
function read (line 180) | fn read(&self, head: u16) -> T {
function drop (line 199) | fn drop(&self) {
method drop (line 205) | fn drop(&mut self) {
method try_send (line 219) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_send_oneshot (line 225) | fn try_send_oneshot(&self, item: *const T) -> Option<bool> {
method try_recv (line 230) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 235) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 240) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 247) | fn may_direct_copy(&self) -> bool {
method new (line 254) | fn new() -> Self {
method try_select (line 261) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 275) | fn read_with_token(&self, token: Token) -> T {
FILE: src/flavor/one_mpsc.rs
type OneMpsc (line 13) | pub struct OneMpsc<T> {
type Item (line 24) | type Item = T;
method pop (line 27) | fn pop(&self) -> Option<T> {
method push (line 32) | fn push(&self, item: T) -> Result<(), T> {
method len (line 42) | fn len(&self) -> usize {
method capacity (line 51) | fn capacity(&self) -> Option<usize> {
method is_full (line 56) | fn is_full(&self) -> bool {
method is_empty (line 61) | fn is_empty(&self) -> bool {
function new (line 70) | pub fn new() -> Self {
function unpack (line 75) | fn unpack(pos: u32) -> (u16, u16) {
function pack (line 82) | fn pack(head: u16, tail: u16) -> u32 {
function _try_push (line 88) | unsafe fn _try_push(
function _start_read (line 113) | fn _start_read(&self, order: Ordering) -> Option<(u16, u16)> {
function _read (line 124) | fn _read(&self, slot: &Slot<T>, next_head: u16) -> T {
function _pop (line 134) | fn _pop(&self, order: Ordering) -> Option<T> {
type Slot (line 143) | struct Slot<T> {
function init (line 150) | fn init(i: u16) -> Self {
function write (line 158) | fn write(&self, tail: u16, value: *const T) {
function read (line 164) | fn read(&self, head: u16) -> T {
function drop (line 181) | fn drop(&self) {
method drop (line 188) | fn drop(&mut self) {
method try_send (line 202) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_send_oneshot (line 208) | fn try_send_oneshot(&self, item: *const T) -> Option<bool> {
method try_recv (line 213) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 218) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 223) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 230) | fn may_direct_copy(&self) -> bool {
method new (line 237) | fn new() -> Self {
method try_select (line 244) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 258) | fn read_with_token(&self, token: Token) -> T {
FILE: src/flavor/one_spmc.rs
type OneSpsc (line 14) | pub type OneSpsc<T> = OneSp<T, false>;
type OneSp (line 26) | pub struct OneSp<T, const MC: bool> {
function new (line 38) | pub fn new() -> Self {
function unpack (line 43) | fn unpack(pos: u64) -> (u32, u32) {
function pack (line 50) | fn pack(head: u32, tail: u32) -> u64 {
function is_empty (line 55) | pub fn is_empty(&self) -> bool {
function len (line 62) | pub fn len(&self) -> usize {
function try_push (line 71) | fn try_push(&self, value: *const T, order: Ordering) -> bool {
method drop (line 88) | fn drop(&mut self) {
function _read (line 102) | fn _read(&self, slot: &Slot<T>, next_head: u32) -> T {
function _pop (line 112) | fn _pop(&self, order: Ordering) -> Option<T> {
function start_read (line 122) | fn start_read(&self, order: Ordering) -> Option<u32> {
type Slot (line 134) | struct Slot<T> {
function init (line 140) | fn init() -> Self {
function write (line 145) | fn write(&self, value: *const T) {
function read (line 158) | fn read(&self) -> T {
function drop (line 163) | fn drop(&self) {
type Item (line 285) | type Item = T;
method len (line 288) | fn len(&self) -> usize {
method is_empty (line 297) | fn is_empty(&self) -> bool {
method capacity (line 302) | fn capacity(&self) -> Option<usize> {
method is_full (line 307) | fn is_full(&self) -> bool {
method pop (line 312) | fn pop(&self) -> Option<T> {
method push (line 317) | fn push(&self, value: T) -> Result<(), T> {
method try_send (line 329) | fn try_send(&self, item: &MaybeUninit<T>) -> bool {
method try_send_oneshot (line 334) | fn try_send_oneshot(&self, item: *const T) -> Option<bool> {
method try_recv (line 339) | fn try_recv(&self) -> Option<T> {
method try_recv_final (line 344) | fn try_recv_final(&self) -> Option<T> {
method backoff_limit (line 349) | fn backoff_limit(&self) -> u16 {
method may_direct_copy (line 356) | fn may_direct_copy(&self) -> bool {
method new (line 364) | fn new() -> Self {
method try_select (line 371) | fn try_select(&self, final_check: bool) -> Option<Token> {
method read_with_token (line 383) | fn read_with_token(&self, token: Token) -> T {
FILE: src/lib.rs
type SenderType (line 335) | pub trait SenderType {
method new (line 337) | fn new(shared: Arc<ChannelShared<Self::Flavor>>) -> Self;
type ReceiverType (line 341) | pub trait ReceiverType: AsRef<ChannelShared<Self::Flavor>> {
method new (line 344) | fn new(shared: Arc<ChannelShared<Self::Flavor>>) -> Self;
type NotCloneable (line 347) | pub trait NotCloneable {}
FILE: src/mpmc.rs
type List (line 59) | pub type List<T> = FlavorWrap<crate::flavor::List<T>, RegistryDummy, Reg...
type One (line 62) | pub type One<T> = FlavorWrap<crate::flavor::One<T>, RegistryMultiSend<T>...
type Array (line 66) | pub enum Array<T> {
function new (line 73) | pub fn new(size: usize) -> Self {
type Item (line 95) | type Item = T;
method new_with_bound (line 109) | fn new_with_bound(size: usize) -> Self {
type Send (line 115) | type Send = RegistryMultiSend<T>;
type Recv (line 116) | type Recv = RegistryMultiRecv;
function new (line 132) | pub fn new<F, S, R>() -> (S, R)
function build (line 155) | pub fn build<F, S, R>(flavor: F) -> (S, R)
function unbounded_new (line 166) | fn unbounded_new<T, R>() -> (MTx<List<T>>, R)
function unbounded_blocking (line 175) | pub fn unbounded_blocking<T>() -> (MTx<List<T>>, MRx<List<T>>)
function unbounded_async (line 183) | pub fn unbounded_async<T>() -> (MTx<List<T>>, MAsyncRx<List<T>>)
function bounded_new (line 190) | fn bounded_new<T, S, R>(size: usize) -> (S, R)
function bounded_blocking (line 213) | pub fn bounded_blocking<T>(size: usize) -> (MTx<Array<T>>, MRx<Array<T>>)
function bounded_async (line 224) | pub fn bounded_async<T>(size: usize) -> (MAsyncTx<Array<T>>, MAsyncRx<Ar...
function bounded_blocking_async (line 235) | pub fn bounded_blocking_async<T>(size: usize) -> (MTx<Array<T>>, MAsyncR...
function bounded_async_blocking (line 246) | pub fn bounded_async_blocking<T>(size: usize) -> (MAsyncTx<Array<T>>, MR...
type Null (line 254) | pub type Null = FlavorWrap<crate::null::Null, RegistryDummy, RegistryMul...
method new_blocking (line 258) | pub fn new_blocking(&self) -> (CloseHandle<Null>, MRx<Null>) {
method new_async (line 263) | pub fn new_async(self) -> (CloseHandle<Null>, MAsyncRx<Null>) {
FILE: src/mpsc.rs
type List (line 54) | pub type List<T> = FlavorWrap<crate::flavor::List<T>, RegistryDummy, Reg...
type One (line 57) | pub type One<T> = FlavorWrap<crate::flavor::One<T>, RegistryMultiSend<T>...
type Array (line 61) | pub enum Array<T> {
function new (line 68) | pub fn new(size: usize) -> Self {
type Item (line 89) | type Item = T;
method new_with_bound (line 103) | fn new_with_bound(size: usize) -> Self {
type Send (line 109) | type Send = RegistryMultiSend<T>;
type Recv (line 110) | type Recv = RegistrySingle;
function new (line 126) | pub fn new<F, S, R>() -> (S, R)
function build (line 149) | pub fn build<F, S, R>(flavor: F) -> (S, R)
function unbounded_new (line 160) | fn unbounded_new<T, R>() -> (MTx<List<T>>, R)
function unbounded_blocking (line 169) | pub fn unbounded_blocking<T>() -> (MTx<List<T>>, Rx<List<T>>)
function unbounded_async (line 177) | pub fn unbounded_async<T>() -> (MTx<List<T>>, AsyncRx<List<T>>)
function bounded_new (line 184) | fn bounded_new<T, S, R>(size: usize) -> (S, R)
function bounded_blocking (line 197) | pub fn bounded_blocking<T>(size: usize) -> (MTx<Array<T>>, Rx<Array<T>>)
function bounded_async (line 208) | pub fn bounded_async<T>(size: usize) -> (MAsyncTx<Array<T>>, AsyncRx<Arr...
function bounded_blocking_async (line 219) | pub fn bounded_blocking_async<T>(size: usize) -> (MTx<Array<T>>, AsyncRx...
function bounded_async_blocking (line 230) | pub fn bounded_async_blocking<T>(size: usize) -> (MAsyncTx<Array<T>>, Rx...
type Null (line 238) | pub type Null = FlavorWrap<crate::null::Null, RegistryDummy, RegistrySin...
method new_blocking (line 242) | pub fn new_blocking(&self) -> (CloseHandle<Null>, Rx<Null>) {
method new_async (line 247) | pub fn new_async(self) -> (CloseHandle<Null>, AsyncRx<Null>) {
FILE: src/null.rs
type Null (line 67) | pub struct Null();
type Item (line 70) | type Item = ();
method pop (line 73) | fn pop(&self) -> Option<()> {
method push (line 78) | fn push(&self, _item: ()) -> Result<(), ()> {
method len (line 83) | fn len(&self) -> usize {
method capacity (line 88) | fn capacity(&self) -> Option<usize> {
method is_full (line 93) | fn is_full(&self) -> bool {
method is_empty (line 98) | fn is_empty(&self) -> bool {
method try_send (line 105) | fn try_send(&self, _item: &MaybeUninit<()>) -> bool {
method try_send_oneshot (line 111) | fn try_send_oneshot(&self, _item: *const ()) -> Option<bool> {
method try_recv (line 116) | fn try_recv(&self) -> Option<Self::Item> {
method try_recv_final (line 122) | fn try_recv_final(&self) -> Option<Self::Item> {
method backoff_limit (line 127) | fn backoff_limit(&self) -> u16 {
method new (line 134) | fn new() -> Self {
method try_select (line 141) | fn try_select(&self, _final_check: bool) -> Option<Token> {
method read_with_token (line 146) | fn read_with_token(&self, _token: Token) {
type CloseHandle (line 152) | pub struct CloseHandle<F: Flavor>(Arc<ChannelShared<F>>);
method clone (line 156) | fn clone(&self) -> Self {
method drop (line 164) | fn drop(&mut self) {
type Flavor (line 173) | type Flavor = F;
method new (line 176) | fn new(shared: Arc<ChannelShared<Self::Flavor>>) -> Self {
FILE: src/oneshot.rs
constant LOCK_FLAG (line 57) | const LOCK_FLAG: u8 = 0x1;
constant WAKER_SET_FLAG (line 59) | const WAKER_SET_FLAG: u8 = 0x2;
constant CLOSE_FLAG (line 61) | const CLOSE_FLAG: u8 = 0x4;
constant EXIST_FLAG (line 62) | const EXIST_FLAG: u8 = 0x8;
type OneShotInner (line 64) | struct OneShotInner<T> {
function new (line 75) | fn new() -> Box<Self> {
function get_waker (line 84) | fn get_waker(&self) -> &mut Option<ThinWaker> {
function value_mut (line 89) | fn value_mut(&self) -> &mut Option<T> {
function set_state (line 94) | fn set_state(&self, flag: u8) -> u8 {
function _try_recv (line 99) | fn _try_recv(&self, order: Ordering) -> Result<u8, u8> {
function _consume_value (line 110) | fn _consume_value(p: NonNull<Self>, mut state: u8) -> Option<T> {
function _notify_rx (line 152) | fn _notify_rx(p: NonNull<Self>, exist: bool) -> bool {
function set_waker (line 208) | fn set_waker(&self, waker: ThinWaker) -> Result<(), u8> {
function cancel_waker (line 219) | fn cancel_waker(&self, abandon: bool) -> Result<(), u8> {
function is_empty (line 231) | fn is_empty(&self) -> bool {
type TxOneshot (line 238) | pub struct TxOneshot<T>(NonNull<OneShotInner<T>>);
function send (line 246) | pub fn send(self, item: T) {
function is_disconnected (line 261) | pub fn is_disconnected(&self) -> bool {
method drop (line 268) | fn drop(&mut self) {
type RxOneshot (line 278) | pub struct RxOneshot<T>(Option<NonNull<OneShotInner<T>>>);
method drop (line 284) | fn drop(&mut self) {
function recv (line 316) | pub fn recv(self) -> Result<T, RecvError> {
function recv_timeout (line 325) | pub fn recv_timeout(self, timeout: Duration) -> Result<T, RecvTimeoutErr...
function is_empty (line 335) | pub fn is_empty(&self) -> bool {
function try_recv (line 345) | pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
function recv_async (line 364) | pub async fn recv_async(self) -> Result<T, RecvError> {
function poll (line 369) | fn poll(&mut self, ctx: &mut Context<'_>) -> Poll<Result<T, ()>> {
function _recv_blocking (line 417) | pub(crate) fn _recv_blocking(self, deadline: Option<Instant>) -> Result<...
function recv_async_timeout (line 482) | pub async fn recv_async_timeout(
function recv_async_with_timer (line 546) | pub fn recv_async_with_timer<F, R>(self, sleep: F) -> OneshotTimeoutFutu...
type Output (line 555) | type Output = Result<T, RecvError>;
method poll (line 558) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
type OneshotTimeoutFuture (line 568) | pub struct OneshotTimeoutFuture<T, F, R>
type Output (line 580) | type Output = Result<T, RecvTimeoutError>;
method poll (line 583) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
function oneshot (line 602) | pub fn oneshot<T>() -> (TxOneshot<T>, RxOneshot<T>) {
FILE: src/select/mod.rs
type SelectMode (line 19) | pub enum SelectMode {
FILE: src/select/multiplex.rs
constant DEFAULT_WEIGHT (line 16) | pub const DEFAULT_WEIGHT: u32 = 128;
type Mux (line 19) | pub type Mux<F> = FlavorWrap<F, <F as Flavor>::Send, SelectWakerWrapper>;
type Multiplex (line 76) | pub struct Multiplex<F: Flavor> {
type MultiplexHandle (line 85) | struct MultiplexHandle<F: Flavor> {
function new (line 92) | pub fn new() -> Self {
function _add_item (line 102) | fn _add_item(&mut self, flavor: F, weight: u32) -> Arc<ChannelShared<Mux...
function new_tx (line 159) | pub fn new_tx<S>(&mut self) -> S
function new_tx_with_weight (line 170) | pub fn new_tx_with_weight<S>(&mut self, weight: u32) -> S
function bounded_tx (line 208) | pub fn bounded_tx<S>(&mut self, size: usize) -> S
function bounded_tx_with_weight (line 218) | pub fn bounded_tx_with_weight<S>(&mut self, size: usize, weight: u32) -> S
function try_recv (line 248) | pub fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function recv (line 264) | pub fn recv(&self) -> Result<F::Item, RecvError> {
function recv_timeout (line 282) | pub fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTim...
function _try_select_cached (line 299) | fn _try_select_cached<const FINAL: bool>(&self) -> Result<F::Item, usize> {
function _try_select_all (line 320) | fn _try_select_all<const FINAL: bool>(
function _recv_blocking (line 351) | fn _recv_blocking(&self, deadline: Option<Instant>) -> Result<F::Item, b...
method drop (line 406) | fn drop(&mut self) {
function fmt (line 415) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function fmt (line 422) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function recv (line 432) | fn recv(&self) -> Result<F::Item, RecvError> {
function try_recv (line 437) | fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function recv_timeout (line 442) | fn recv_timeout(&self, timeout: Duration) -> Result<F::Item, RecvTimeout...
function len (line 448) | fn len(&self) -> usize {
function capacity (line 454) | fn capacity(&self) -> Option<usize> {
function is_empty (line 460) | fn is_empty(&self) -> bool {
function is_full (line 471) | fn is_full(&self) -> bool {
function is_disconnected (line 477) | fn is_disconnected(&self) -> bool {
function get_tx_count (line 483) | fn get_tx_count(&self) -> usize {
function get_rx_count (line 489) | fn get_rx_count(&self) -> usize {
function get_wakers_count (line 493) | fn get_wakers_count(&self) -> (usize, usize) {
function clone_to_vec (line 497) | fn clone_to_vec(self, _count: usize) -> Vec<Self> {
FILE: src/select/select.rs
type Select (line 116) | pub struct Select<'a> {
function new (line 126) | pub fn new() -> Self {
function new_random (line 132) | pub fn new_random() -> Self {
function new_bias (line 138) | pub fn new_bias() -> Self {
function new_with (line 143) | pub fn new_with(mode: SelectMode) -> Self {
function add (line 164) | pub fn add<R: ReceiverType>(&mut self, recv: &'a R)
function remove (line 177) | pub fn remove<R: ReceiverType>(&mut self, recv: &R) {
function try_select (line 200) | pub fn try_select(&mut self) -> Result<SelectResult, TryRecvError> {
function _try_select (line 212) | fn _try_select(&mut self, mut idx: usize, final_check: bool) -> Option<S...
function _try_select_begin (line 236) | fn _try_select_begin(&mut self) -> usize {
function select (line 267) | pub fn select(&mut self) -> Result<SelectResult, RecvError> {
function select_timeout (line 286) | pub fn select_timeout(&mut self, timeout: Duration) -> Result<SelectResu...
function _select_blocking (line 296) | fn _select_blocking(&mut self, deadline: Option<Instant>) -> Result<Sele...
method drop (line 354) | fn drop(&mut self) {
function fmt (line 362) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type RecvHandle (line 367) | struct RecvHandle<'a> {
function try_select (line 377) | fn try_select(&self, final_check: bool) -> Result<SelectResult, ()> {
function reg_waker (line 385) | fn reg_waker(&mut self, index: usize, global_waker: &Arc<SelectWaker>) {
type SelectResult (line 401) | pub struct SelectResult {
method fmt (line 408) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method is_from (line 416) | pub fn is_from<R: ReceiverType>(&self, rx: &R) -> bool {
method eq (line 424) | fn eq(&self, other: &R) -> bool {
type SelectHandle (line 430) | pub(crate) trait SelectHandle: Send {
method try_select (line 432) | fn try_select(&self, final_check: bool) -> Option<Token>;
method reg_waker (line 435) | fn reg_waker(&self, channel_id: usize, waker: &Arc<SelectWaker>) -> bool;
method cancel_waker (line 437) | fn cancel_waker(&self, waker: &Arc<SelectWaker>);
FILE: src/shared.rs
type ChannelShared (line 13) | pub struct ChannelShared<F: Flavor> {
function new (line 25) | pub(crate) fn new(inner: F, senders: F::Send, recvs: F::Recv) -> Arc<Sel...
function try_recv (line 45) | pub(crate) fn try_recv(&self) -> Result<F::Item, TryRecvError> {
function read_with_token (line 58) | pub(crate) fn read_with_token(&self, token: Token) -> Result<F::Item, Re...
function len (line 73) | pub fn len(&self) -> usize {
function capacity (line 79) | pub fn capacity(&self) -> Option<usize> {
function is_empty (line 85) | pub fn is_empty(&self) -> bool {
function is_full (line 90) | pub fn is_full(&self) -> bool {
function get_tx_count (line 96) | pub fn get_tx_count(&self) -> usize {
function get_rx_count (line 102) | pub fn get_rx_count(&self) -> usize {
function sender_direct_copy (line 107) | pub(crate) fn sender_direct_copy(&self) -> bool {
function get_wakers_count (line 112) | pub fn get_wakers_count(&self) -> (usize, usize) {
function is_tx_closed (line 117) | pub(crate) fn is_tx_closed(&self) -> bool {
function is_rx_closed (line 122) | pub(crate) fn is_rx_closed(&self) -> bool {
function add_tx (line 127) | pub(crate) fn add_tx(&self) {
function try_add_tx (line 134) | pub(crate) fn try_add_tx(&self) -> bool {
function add_rx (line 158) | pub(crate) fn add_rx(&self) {
function close_tx (line 165) | pub(crate) fn close_tx(&self) {
function close_rx (line 178) | pub(crate) fn close_rx(&self) {
function sender_double_check (line 195) | pub(crate) fn sender_double_check<const SINK: bool>(
function sender_snooze (line 223) | pub(crate) fn sender_snooze(
function on_send (line 241) | pub(crate) fn on_send(&self) {
function on_recv (line 247) | pub(crate) fn on_recv(&self) {
function abandon_send_waker (line 256) | pub(crate) fn abandon_send_waker(&self, waker: &<F::Send as Registry>::W...
function abandon_recv_waker (line 277) | pub(crate) fn abandon_recv_waker(&self, waker: &<F::Recv as Registry>::W...
function get_async_backoff (line 293) | pub(crate) fn get_async_backoff(&self) -> Option<Backoff> {
method try_select (line 309) | fn try_select(&self, final_check: bool) -> Option<Token> {
method reg_waker (line 320) | fn reg_waker(&self, channel_id: usize, waker: &Arc<SelectWaker>) -> bool {
method cancel_waker (line 325) | fn cancel_waker(&self, waker: &Arc<SelectWaker>) {
function check_timeout (line 332) | pub fn check_timeout(deadline: Option<Instant>) -> Result<Option<Duratio...
FILE: src/sink.rs
type AsyncSink (line 9) | pub struct AsyncSink<F: Flavor> {
function fmt (line 15) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 21) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function new (line 28) | pub fn new(tx: AsyncTx<F>) -> Self {
type Target (line 34) | type Target = AsyncTx<F>;
method deref (line 37) | fn deref(&self) -> &Self::Target {
function from (line 44) | fn from(tx: AsyncTx<F>) -> Self {
function from (line 51) | fn from(tx: MAsyncTx<F>) -> Self {
function poll_send (line 85) | pub fn poll_send(
method drop (line 103) | fn drop(&mut self) {
FILE: src/spsc.rs
type List (line 51) | pub type List<T> = FlavorWrap<crate::flavor::List<T>, RegistryDummy, Reg...
type One (line 54) | pub type One<T> = FlavorWrap<crate::flavor::OneSpsc<T>, RegistrySingle, ...
type Array (line 58) | pub enum Array<T> {
function new (line 65) | pub fn new(size: usize) -> Self {
type Item (line 84) | type Item = T;
method new_with_bound (line 98) | fn new_with_bound(size: usize) -> Self {
type Send (line 104) | type Send = RegistrySingle;
type Recv (line 105) | type Recv = RegistrySingle;
function new (line 121) | pub fn new<F, S, R>() -> (S, R)
function build (line 143) | pub fn build<F, S, R>(flavor: F) -> (S, R)
function unbounded_new (line 154) | fn unbounded_new<T, R>() -> (Tx<List<T>>, R)
function unbounded_blocking (line 163) | pub fn unbounded_blocking<T>() -> (Tx<List<T>>, Rx<List<T>>)
function unbounded_async (line 171) | pub fn unbounded_async<T>() -> (Tx<List<T>>, AsyncRx<List<T>>)
function bounded_new (line 178) | fn bounded_new<T, S, R>(size: usize) -> (S, R)
function bounded_blocking (line 191) | pub fn bounded_blocking<T>(size: usize) -> (Tx<Array<T>>, Rx<Array<T>>)
function bounded_async (line 202) | pub fn bounded_async<T>(size: usize) -> (AsyncTx<Array<T>>, AsyncRx<Arra...
function bounded_blocking_async (line 213) | pub fn bounded_blocking_async<T>(size: usize) -> (Tx<Array<T>>, AsyncRx<...
function bounded_async_blocking (line 224) | pub fn bounded_async_blocking<T>(size: usize) -> (AsyncTx<Array<T>>, Rx<...
FILE: src/stream.rs
type AsyncStream (line 12) | pub struct AsyncStream<F: Flavor> {
function fmt (line 19) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 25) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function new (line 32) | pub fn new(rx: AsyncRx<F>) -> Self {
function poll_item (line 61) | pub fn poll_item(&mut self, ctx: &mut Context) -> Poll<Option<F::Item>> {
type Target (line 76) | type Target = AsyncRx<F>;
method deref (line 79) | fn deref(&self) -> &Self::Target {
type Item (line 85) | type Item = F::Item;
function poll_next (line 88) | fn poll_next(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<Sel...
function is_terminated (line 107) | fn is_terminated(&self) -> bool {
method drop (line 113) | fn drop(&mut self) {
function from (line 122) | fn from(rx: AsyncRx<F>) -> Self {
function from (line 129) | fn from(rx: MAsyncRx<F>) -> Self {
FILE: src/waitgroup.rs
type WaitGroupInline (line 151) | pub struct WaitGroupInline<const THRESHOLD: usize = 0> {
function new (line 156) | pub fn new() -> Self {
function get_left_seqcst (line 163) | pub fn get_left_seqcst(&self) -> usize {
function get_left (line 169) | pub fn get_left(&self) -> usize {
function add (line 175) | pub fn add(&self) {
function add_many (line 181) | pub fn add_many(&self, count: usize) {
function done (line 193) | pub unsafe fn done(&self) -> bool {
function done_many (line 205) | pub unsafe fn done_many(&self, count: usize) -> bool {
function try_wait (line 213) | pub fn try_wait(&self) -> Result<(), ()> {
function wait_async (line 228) | pub unsafe fn wait_async<'a>(&'a self) -> WaitGroupFuture<'a, ()> {
function wait_async_timeout (line 240) | pub unsafe fn wait_async_timeout<'a>(
function wait_async_timeout (line 255) | pub unsafe fn wait_async_timeout<'a>(
function wait_async_with_timer (line 268) | pub unsafe fn wait_async_with_timer<'a, FR, R>(
function wait (line 283) | pub unsafe fn wait(&self) {
function wait_timeout (line 293) | pub unsafe fn wait_timeout(&self, timeout: Duration) -> Result<(), ()> {
type WaitGroup (line 320) | pub struct WaitGroup<T> {
function new (line 330) | pub fn new(inner: T, threshold: usize) -> Self {
function set_threshold (line 347) | pub fn set_threshold(&mut self, threshold: usize) {
function get_inner (line 353) | fn get_inner(&self) -> &WaitGroupInner<T> {
function get_left_seqcst (line 359) | pub fn get_left_seqcst(&self) -> usize {
function get_left (line 366) | pub fn get_left(&self) -> usize {
function add_guard (line 373) | pub fn add_guard(&self) -> WaitGroupGuard<T> {
function try_wait (line 380) | pub fn try_wait(&self) -> Result<(), ()> {
function wait_async (line 395) | pub fn wait_async<'a>(&'a self) -> WaitGroupFuture<'a, T>
function wait_async_timeout (line 411) | pub fn wait_async_timeout<'a>(
function wait_async_timeout (line 429) | pub fn wait_async_timeout<'a>(
function wait_async_with_timer (line 445) | pub fn wait_async_with_timer<'a, FR, R>(
function wait (line 462) | pub fn wait(&self) {
function wait_timeout (line 472) | pub fn wait_timeout(&self, timeout: Duration) -> Result<(), ()> {
method drop (line 479) | fn drop(&mut self) {
type Target (line 487) | type Target = T;
method deref (line 489) | fn deref(&self) -> &T {
type WaitGroupGuard (line 504) | pub struct WaitGroupGuard<T> {
method drop (line 514) | fn drop(&mut self) {
method clone (line 523) | fn clone(&self) -> Self {
type Target (line 531) | type Target = T;
method deref (line 533) | fn deref(&self) -> &T {
type WaitGroupInner (line 538) | struct WaitGroupInner<T> {
function new (line 549) | fn new(inner: T, init_count: usize) -> Self {
function count (line 554) | fn count(&self, order: Ordering) -> usize {
function get_waker (line 559) | fn get_waker(&self) -> &mut Option<ThinWaker> {
function add (line 564) | fn add(&self, count: usize) {
function destroy (line 572) | unsafe fn destroy(p: NonNull<Self>) -> bool {
function done_ptr (line 596) | unsafe fn done_ptr(p: NonNull<Self>, count: usize, threshold: usize) -> ...
function done (line 608) | fn done<const OWNER_SHIP: bool>(this: *const Self, count: usize, thresho...
function try_set_waker (line 673) | fn try_set_waker(&self, waker: ThinWaker, threshold: usize, may_skip: bo...
function wait_blocking (line 716) | fn wait_blocking(&self, deadline: Option<Instant>, threshold: usize) -> ...
function poll_async (line 753) | fn poll_async(
type WaitGroupFuture (line 795) | pub struct WaitGroupFuture<'a, T> {
type Output (line 805) | type Output = ();
method poll (line 807) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
type WaitGroupTimeoutFuture (line 816) | pub struct WaitGroupTimeoutFuture<'a, T, FR, R>
type Output (line 832) | type Output = Result<(), ()>;
method poll (line 834) | fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
constant WAKER_FLAG_SET (line 848) | const WAKER_FLAG_SET: usize = 1 << (usize::BITS - 1);
constant WAKER_FLAG_LOCK (line 849) | const WAKER_FLAG_LOCK: usize = 1 << (usize::BITS - 2);
constant WAKER_FLAG_MASK (line 850) | const WAKER_FLAG_MASK: usize = WAKER_FLAG_SET | WAKER_FLAG_LOCK;
constant COUNT_MASK (line 851) | const COUNT_MASK: usize = !WAKER_FLAG_MASK;
type State (line 867) | struct State(usize);
method new (line 871) | fn new(state: usize) -> Self {
method count (line 876) | fn count(&self) -> usize {
method waker_flag (line 881) | fn waker_flag(&self) -> usize {
method is_locked (line 886) | fn is_locked(&self) -> bool {
method has_waker (line 891) | fn has_waker(&self) -> bool {
method try_lock (line 896) | fn try_lock(&self) -> usize {
method is_last (line 902) | fn is_last(&self, delta: usize) -> bool {
method try_done (line 911) | fn try_done(&mut self, delta: usize, threshold: usize) -> bool {
method to_usize (line 931) | fn to_usize(&self) -> usize {
function test_waitgroup_inner_count (line 943) | fn test_waitgroup_inner_count() {
function test_waitgroup_state (line 957) | fn test_waitgroup_state() {
function test_waitgroup_ptr (line 996) | fn test_waitgroup_ptr() {
function test_waitgroup_inner (line 1039) | fn test_waitgroup_inner() {
FILE: src/waker.rs
type WakerState (line 15) | pub enum WakerState {
type WakeResult (line 26) | pub enum WakeResult {
method is_done (line 35) | pub fn is_done(&self) -> bool {
type ArcWaker (line 42) | pub struct ArcWaker<P>(Arc<WakerInner<P>>);
function fmt (line 45) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function fmt (line 51) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
type Target (line 57) | type Target = WakerInner<P>;
method deref (line 59) | fn deref(&self) -> &Self::Target {
function new_async (line 66) | pub fn new_async(ctx: &Context, payload: P) -> Self {
function new_blocking (line 76) | pub fn new_blocking(payload: P) -> Self {
function from_arc (line 88) | pub fn from_arc(inner: Arc<WakerInner<P>>) -> Self {
function to_arc (line 94) | pub fn to_arc(self) -> Arc<WakerInner<P>> {
function weak (line 99) | pub fn weak(&self) -> Weak<WakerInner<P>> {
type ThinWaker (line 105) | pub(crate) enum ThinWaker {
method wake_by_ref (line 112) | pub fn wake_by_ref(&self) {
method wake (line 121) | pub fn wake(self) {
method will_wake (line 129) | pub fn will_wake(&self, ctx: &mut Context) -> bool {
type WakerInner (line 142) | pub struct WakerInner<P> {
function get_waker (line 155) | fn get_waker(&self) -> &ThinWaker {
function get_waker_mut (line 160) | fn get_waker_mut(&self) -> &mut ThinWaker {
function get_payload_mut (line 165) | fn get_payload_mut(&self) -> &mut P {
function reset (line 170) | pub fn reset(&self, payload: P) {
function get_seq (line 178) | pub fn get_seq(&self) -> u32 {
function set_seq (line 183) | pub fn set_seq(&self, seq: u32) {
function update_thread_handle (line 188) | fn update_thread_handle(&self) {
function commit_waiting (line 194) | pub fn commit_waiting(&self) -> u8 {
function try_change_state (line 203) | pub fn try_change_state(&self, cur: WakerState, new_state: WakerState) -...
function reset_init (line 214) | pub fn reset_init(&self) {
function abandon (line 225) | pub fn abandon(&self) -> Result<(), u8> {
function close_wake (line 235) | pub fn close_wake(&self) -> bool {
function change_state_smaller_eq (line 246) | pub fn change_state_smaller_eq(
function _get_state (line 273) | pub fn _get_state(&self, order: Ordering) -> u8 {
function get_state (line 278) | pub fn get_state(&self) -> u8 {
function get_state_relaxed (line 283) | pub fn get_state_relaxed(&self) -> u8 {
function wake (line 289) | pub fn wake(&self) -> WakeResult {
function will_wake (line 320) | pub fn will_wake(&self, ctx: &mut Context) -> bool {
function get_payload (line 327) | fn get_payload(&self) -> *const T {
function wake_or_copy (line 332) | pub fn wake_or_copy<F: FlavorImpl<Item = T>>(&self, flavor: &F) -> WakeR...
type WakerCache (line 378) | pub struct WakerCache<P: Copy>(ArcCell<WakerInner<P>>);
function new (line 382) | pub(crate) fn new() -> Self {
function new_blocking (line 387) | pub fn new_blocking(&self, payload: P) -> ArcWaker<P> {
function push (line 397) | pub(crate) fn push(&self, waker: ArcWaker<P>) {
function is_empty (line 407) | pub(crate) fn is_empty(&self) -> bool {
function test_waker_size (line 418) | fn test_waker_size() {
FILE: src/waker_registry.rs
type RegistryMultiSend (line 20) | pub(crate) type RegistryMultiSend<T> = RegistryMulti<*const T>;
type RegistryMultiRecv (line 21) | pub(crate) type RegistryMultiRecv = RegistryMulti<()>;
type Registry (line 23) | pub(crate) trait Registry: Send + Sync + 'static {
method get_waker_state (line 26) | fn get_waker_state(&self, o_waker: &Option<Self::Waker>, order: Orderi...
method clear_wakers (line 29) | fn clear_wakers(&self, _waker: &Self::Waker) {}
method close (line 31) | fn close(&self);
method len (line 34) | fn len(&self) -> usize {
method commit_waiting (line 39) | fn commit_waiting(&self, _o_waker: &Option<Self::Waker>) -> u8 {
method cancel_waker (line 44) | fn cancel_waker(&self, o_waker: &mut Option<Self::Waker>) {
method abandon_waker (line 49) | fn abandon_waker(&self, _waker: &Self::Waker) -> Result<(), u8> {
type Waker (line 139) | type Waker = ();
method get_waker_state (line 142) | fn get_waker_state(&self, _o_waker: &Option<Self::Waker>, _order: Orde...
method close (line 147) | fn close(&self) {}
type Waker (line 201) | type Waker = SingleWaker;
method get_waker_state (line 204) | fn get_waker_state(&self, _o_waker: &Option<SingleWaker>, _order: Orde...
method close (line 213) | fn close(&self) {
type Waker (line 566) | type Waker = ArcWaker<P>;
method get_waker_state (line 569) | fn get_waker_state(&self, o_waker: &Option<ArcWaker<P>>, order: Orderi...
method clear_wakers (line 579) | fn clear_wakers(&self, waker: &ArcWaker<P>) {
method close (line 584) | fn close(&self) {
method len (line 600) | fn len(&self) -> usize {
method commit_waiting (line 606) | fn commit_waiting(&self, o_waker: &Option<ArcWaker<P>>) -> u8 {
method abandon_waker (line 616) | fn abandon_waker(&self, waker: &ArcWaker<P>) -> Result<(), u8> {
method cancel_waker (line 630) | fn cancel_waker(&self, o_waker: &mut Option<ArcWaker<P>>) {
type Waker (line 833) | type Waker = ArcWaker<()>;
method get_waker_state (line 836) | fn get_waker_state(&self, _o_waker: &Option<ArcWaker<()>>, _order: Ord...
method close (line 841) | fn close(&self) {
type RegistrySend (line 54) | pub(crate) trait RegistrySend<T>: Registry {
method new (line 55) | fn new() -> Self;
method use_direct_copy (line 58) | fn use_direct_copy(&self) -> bool {
method reg_waker_blocking (line 63) | fn reg_waker_blocking(
method reg_waker_async (line 71) | fn reg_waker_async(
method cancel_reuse_waker (line 84) | fn cancel_reuse_waker(
method fire (line 92) | fn fire<F>(&self, _flavor: &F) -> WakeResult
method cache_waker (line 100) | fn cache_waker(
type RegistryRecv (line 106) | pub(crate) trait RegistryRecv: Registry {
method new (line 107) | fn new() -> Self;
method fire (line 110) | fn fire(&self) {}
method reg_waker_blocking (line 113) | fn reg_waker_blocking(
method reg_waker_async (line 120) | fn reg_waker_async(
method cache_waker (line 127) | fn cache_waker(&self, _o_waker: Option<<Self as Registry>::Waker>, _ca...
method reg_select_waker (line 129) | fn reg_select_waker(&self, channel_id: usize, waker: &Arc<SelectWaker>...
method cancel_select_waker (line 132) | fn cancel_select_waker(&self, _waker: &Arc<SelectWaker>) {}
method new (line 252) | fn new() -> Self {
method fire (line 258) | fn fire(&self) {
method reg_waker_blocking (line 263) | fn reg_waker_blocking(&self, o_waker: &mut Option<SingleWaker>, _cache...
method reg_waker_async (line 268) | fn reg_waker_async(
method reg_select_waker (line 276) | fn reg_select_waker(&self, _channel_id: usize, waker: &Arc<SelectWaker...
method new (line 738) | fn new() -> Self {
method reg_waker_blocking (line 743) | fn reg_waker_blocking(&self, o_waker: &mut Option<ArcWaker<()>>, cache...
method reg_waker_async (line 748) | fn reg_waker_async(
method fire (line 755) | fn fire(&self) {
method cache_waker (line 784) | fn cache_waker(&self, o_waker: Option<ArcWaker<()>>, cache: &WakerCach...
method reg_select_waker (line 789) | fn reg_select_waker(&self, channel_id: usize, waker: &Arc<SelectWaker>...
method cancel_select_waker (line 800) | fn cancel_select_waker(&self, waker: &Arc<SelectWaker>) {
method new (line 850) | fn new() -> Self {
method fire (line 855) | fn fire(&self) {
method reg_select_waker (line 859) | fn reg_select_waker(&self, _channel_id: usize, _waker: &Arc<SelectWake...
type RegistryDummy (line 136) | pub struct RegistryDummy();
method new (line 152) | fn new() -> Self {
type SingleWaker (line 157) | type SingleWaker = ArcWaker<()>;
type RegistrySingle (line 160) | pub struct RegistrySingle {
method _fire (line 170) | fn _fire(&self) {
method _reg_waker_async (line 178) | fn _reg_waker_async(&self, ctx: &mut Context, o_waker: &mut Option<Sin...
method _reg_waker_blocking (line 190) | fn _reg_waker_blocking(&self, o_waker: &mut Option<SingleWaker>) {
method new (line 220) | fn new() -> Self {
method fire (line 226) | fn fire<F>(&self, _flavor: &F) -> WakeResult
method reg_waker_blocking (line 235) | fn reg_waker_blocking(
method reg_waker_async (line 242) | fn reg_waker_async(
type RegistryMultiInner (line 283) | struct RegistryMultiInner<P> {
function new (line 291) | fn new() -> Self {
function check_select (line 297) | fn check_select(&self) -> u8 {
function check_waker (line 307) | fn check_waker(&self) -> u8 {
constant MULTI_EMPTY (line 316) | const MULTI_EMPTY: u8 = 0;
constant MULTI_HAS_SELECT (line 317) | const MULTI_HAS_SELECT: u8 = 1;
constant MULTI_HAS_WAKER (line 318) | const MULTI_HAS_WAKER: u8 = 2;
type RegistryMulti (line 320) | pub struct RegistryMulti<P> {
function reg_waker (line 328) | fn reg_waker(&self, waker: &ArcWaker<P>) {
function _reg_waker_async (line 343) | fn _reg_waker_async(
function _reg_waker_blocking (line 394) | fn _reg_waker_blocking(
function pop_first (line 415) | fn pop_first(&self) -> Option<(ArcWaker<P>, Option<u32>)> {
function pop_again (line 458) | fn pop_again(&self) -> Option<ArcWaker<P>> {
function _clear_wakers (line 489) | fn _clear_wakers(&self, old_waker: &ArcWaker<P>, oneshot: bool) {
function _cache_waker (line 555) | fn _cache_waker(_o_waker: Option<ArcWaker<P>>, _cache: &WakerCache<P>) {
function new (line 643) | fn new() -> Self {
function use_direct_copy (line 648) | fn use_direct_copy(&self) -> bool {
function reg_waker_blocking (line 653) | fn reg_waker_blocking(
function reg_waker_async (line 661) | fn reg_waker_async(
function cancel_reuse_waker (line 674) | fn cancel_reuse_waker(
function fire (line 698) | fn fire<F>(&self, _flavor: &F) -> WakeResult
function cache_waker (line 731) | fn cache_waker(&self, o_waker: Option<ArcWaker<*const T>>, cache: &Waker...
type SelectWakerWrapper (line 813) | pub struct SelectWakerWrapper(Arc<SelectWaker>, usize);
method wake (line 817) | pub(crate) fn wake(&self) {
method eq (line 826) | pub(crate) fn eq(&self, waker: &Arc<SelectWaker>) -> bool {
type SelectWaker (line 864) | pub(crate) struct SelectWaker {
method new (line 878) | pub fn new() -> Self {
method init_blocking (line 888) | pub fn init_blocking(&self) {
method init_async (line 904) | pub fn init_async(&self, ctx: &mut Context) {
method get_waker (line 913) | fn get_waker(&self) -> &mut Option<ArcWaker<()>> {
method clone_weak (line 918) | fn clone_weak(&self) -> Weak<WakerInner<()>> {
method add_opened (line 923) | pub fn add_opened(&self) {
method get_opened_count (line 928) | pub fn get_opened_count(&self) -> usize {
method to_wrapper (line 933) | pub fn to_wrapper(self: Arc<SelectWaker>, idx: usize) -> SelectWakerWr...
method get_hint (line 938) | pub fn get_hint(&self) -> usize {
method close (line 944) | pub fn close(&self) {
method get_waker_state (line 949) | pub fn get_waker_state(&self, order: Ordering) -> u8 {
function print_waker_registry_size (line 962) | fn print_waker_registry_size() {
function test_registry_multi_pop (line 971) | fn test_registry_multi_pop() {
function test_registry_multi_clear_waiting (line 1004) | fn test_registry_multi_clear_waiting() {
function test_registry_multi_clear_oneshot (line 1027) | fn test_registry_multi_clear_oneshot() {
function test_registry_multi_clear (line 1050) | fn test_registry_multi_clear() {
function test_registry_multi_close (line 1076) | fn test_registry_multi_close() {
FILE: src/weak.rs
type WeakTx (line 9) | pub struct WeakTx<F: Flavor + FlavorMP>(pub(crate) Arc<ChannelShared<F>>);
function upgrade (line 28) | pub fn upgrade<S: SenderType<Flavor = F>>(&self) -> Option<S> {
function get_tx_count (line 37) | pub fn get_tx_count(&self) -> usize {
function get_rx_count (line 42) | pub fn get_rx_count(&self) -> usize {
FILE: test-suite/benches/async_channel.rs
function _async_channel_unbounded_async (line 8) | async fn _async_channel_unbounded_async(tx_count: usize, rx_count: usize...
function _async_channel_bounded_async (line 63) | async fn _async_channel_bounded_async(
function bench_async_channel_unbounded_async (line 120) | fn bench_async_channel_unbounded_async(c: &mut Criterion) {
function bench_async_channel_bounded_async (line 142) | fn bench_async_channel_bounded_async(c: &mut Criterion) {
FILE: test-suite/benches/common.rs
constant ONE_MILLION (line 7) | pub const ONE_MILLION: usize = 1000000;
constant TEN_THOUSAND (line 9) | pub const TEN_THOUSAND: usize = 10000;
type Concurrency (line 12) | pub struct Concurrency {
method fmt (line 18) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
type BenchExecutor (line 23) | pub struct BenchExecutor();
method block_on (line 26) | fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
function n_n (line 99) | pub fn n_n() -> Vec<(usize, usize)> {
function n_1 (line 104) | pub fn n_1() -> Vec<usize> {
FILE: test-suite/benches/crossbeam.rs
function _crossbeam_bounded_sync (line 10) | fn _crossbeam_bounded_sync(bound: usize, tx_count: usize, rx_count: usiz...
function _crossbeam_unbounded_sync (line 65) | fn _crossbeam_unbounded_sync(tx_count: usize, rx_count: usize, msg_count...
function _crossbeam_select_mpsc (line 120) | fn _crossbeam_select_mpsc(num_channels: usize, bound: usize, total_msgs:...
function bench_crossbeam_bounded_sync (line 167) | fn bench_crossbeam_bounded_sync(c: &mut Criterion) {
function bench_crossbeam_unbounded_sync (line 197) | fn bench_crossbeam_unbounded_sync(c: &mut Criterion) {
function bench_crossbeam_select_mpsc (line 218) | fn bench_crossbeam_select_mpsc(c: &mut Criterion) {
function bench_crossbeam_wait_group (line 243) | fn bench_crossbeam_wait_group(c: &mut Criterion) {
FILE: test-suite/benches/crossfire.rs
function init_logger (line 12) | fn init_logger() {
function _crossfire_blocking (line 148) | fn _crossfire_blocking<T: BlockingTxTrait<usize>, R: BlockingRxTrait<usi...
function _crossfire_blocking_async (line 214) | async fn _crossfire_blocking_async<T: BlockingTxTrait<usize>, R: AsyncRx...
function _crossfire_bounded_async (line 271) | async fn _crossfire_bounded_async<T: AsyncTxTrait<usize>, R: AsyncRxTrai...
function crossfire_bounded_1_blocking_1_1 (line 327) | fn crossfire_bounded_1_blocking_1_1(c: &mut Criterion) {
function crossfire_bounded_1_blocking_n_1 (line 337) | fn crossfire_bounded_1_blocking_n_1(c: &mut Criterion) {
function crossfire_bounded_1_blocking_n_n (line 370) | fn crossfire_bounded_1_blocking_n_n(c: &mut Criterion) {
function crossfire_bounded_100_blocking_1_1 (line 390) | fn crossfire_bounded_100_blocking_1_1(c: &mut Criterion) {
function crossfire_bounded_100_blocking_n_1 (line 400) | fn crossfire_bounded_100_blocking_n_1(c: &mut Criterion) {
function crossfire_bounded_100_blocking_n_n (line 413) | fn crossfire_bounded_100_blocking_n_n(c: &mut Criterion) {
function crossfire_bounded_1_async_1_1 (line 431) | fn crossfire_bounded_1_async_1_1(c: &mut Criterion) {
function crossfire_bounded_1_async_n_1 (line 441) | fn crossfire_bounded_1_async_n_1(c: &mut Criterion) {
function crossfire_bounded_1_async_n_n (line 474) | fn crossfire_bounded_1_async_n_n(c: &mut Criterion) {
function crossfire_bounded_100_async_1_1 (line 494) | fn crossfire_bounded_100_async_1_1(c: &mut Criterion) {
function crossfire_bounded_100_async_n_1 (line 504) | fn crossfire_bounded_100_async_n_1(c: &mut Criterion) {
function crossfire_bounded_100_async_n_n (line 518) | fn crossfire_bounded_100_async_n_n(c: &mut Criterion) {
function crossfire_unbounded_blocking_1_1 (line 536) | fn crossfire_unbounded_blocking_1_1(c: &mut Criterion) {
function crossfire_unbounded_blocking_n_1 (line 546) | fn crossfire_unbounded_blocking_n_1(c: &mut Criterion) {
function crossfire_unbounded_blocking_n_n (line 559) | fn crossfire_unbounded_blocking_n_n(c: &mut Criterion) {
function crossfire_unbounded_async_1_1 (line 576) | fn crossfire_unbounded_async_1_1(c: &mut Criterion) {
function crossfire_unbounded_async_mpsc (line 586) | fn crossfire_unbounded_async_mpsc(c: &mut Criterion) {
function crossfire_unbounded_async_mpmc (line 599) | fn crossfire_unbounded_async_mpmc(c: &mut Criterion) {
function crossfire_oneshot_blocking (line 609) | fn crossfire_oneshot_blocking(c: &mut Criterion) {
function crossfire_oneshot_async (line 637) | fn crossfire_oneshot_async(c: &mut Criterion) {
function bench_crossfire_wait_group (line 666) | fn bench_crossfire_wait_group(c: &mut Criterion) {
FILE: test-suite/benches/crossfire_select.rs
function init_logger (line 15) | fn init_logger() {
constant NUM_CHANNELS (line 44) | const NUM_CHANNELS: usize = 4;
constant BOUND (line 45) | const BOUND: usize = 100;
function spawn_senders (line 47) | fn spawn_senders<T>(txs: Vec<T>, total_msgs: usize) -> Vec<thread::JoinH...
function run_select (line 63) | fn run_select(mode: SelectMode, total_msgs: usize) {
function run_multiplex (line 103) | fn run_multiplex(total_msgs: usize) {
function bench_select (line 126) | fn bench_select(c: &mut Criterion) {
function bench_multiplex (line 140) | fn bench_multiplex(c: &mut Criterion) {
FILE: test-suite/benches/extra.rs
function bench_async_oneshot_async (line 6) | fn bench_async_oneshot_async(c: &mut Criterion) {
function bench_oneshot_async (line 32) | fn bench_oneshot_async(c: &mut Criterion) {
function bench_oneshot_thread (line 58) | fn bench_oneshot_thread(c: &mut Criterion) {
FILE: test-suite/benches/flume.rs
function _flume_bounded_sync (line 9) | fn _flume_bounded_sync(bound: usize, tx_count: usize, rx_count: usize, m...
function _flume_unbounded_sync (line 66) | fn _flume_unbounded_sync(tx_count: usize, rx_count: usize, msg_count: us...
function _flume_unbounded_async (line 123) | async fn _flume_unbounded_async(tx_count: usize, rx_count: usize, msg_co...
function _flume_bounded_async (line 178) | async fn _flume_bounded_async(bound: usize, tx_count: usize, rx_count: u...
function bench_flume_bounded_sync (line 233) | fn bench_flume_bounded_sync(c: &mut Criterion) {
function bench_flume_unbounded_async (line 264) | fn bench_flume_unbounded_async(c: &mut Criterion) {
function bench_flume_bounded_async (line 286) | fn bench_flume_bounded_async(c: &mut Criterion) {
function bench_flume_unbounded_sync (line 317) | fn bench_flume_unbounded_sync(c: &mut Criterion) {
FILE: test-suite/benches/kanal.rs
function _kanal_bounded_blocking (line 9) | fn _kanal_bounded_blocking(bound: usize, tx_count: usize, rx_count: usiz...
function _kanal_unbounded_blocking (line 64) | fn _kanal_unbounded_blocking(tx_count: usize, rx_count: usize, msg_count...
function _kanal_bounded_async (line 119) | async fn _kanal_bounded_async(bound: usize, tx_count: usize, rx_count: u...
function _kanal_unbounded_async (line 174) | async fn _kanal_unbounded_async(tx_count: usize, rx_count: usize, msg_co...
function bench_kanal_bounded_blocking (line 230) | fn bench_kanal_bounded_blocking(c: &mut Criterion) {
function bench_kanal_unbounded_blocking (line 257) | fn bench_kanal_unbounded_blocking(c: &mut Criterion) {
function bench_kanal_bounded_async (line 277) | fn bench_kanal_bounded_async(c: &mut Criterion) {
function bench_kanal_unbounded_async (line 308) | fn bench_kanal_unbounded_async(c: &mut Criterion) {
FILE: test-suite/benches/tokio.rs
function _tokio_bounded_mpsc (line 8) | async fn _tokio_bounded_mpsc(bound: usize, tx_count: usize, msg_count: u...
function _tokio_unbounded_mpsc (line 30) | async fn _tokio_unbounded_mpsc(tx_count: usize, msg_count: usize) {
function bench_tokio_bounded (line 52) | fn bench_tokio_bounded(c: &mut Criterion) {
function bench_tokio_unbounded (line 66) | fn bench_tokio_unbounded(c: &mut Criterion) {
function bench_tokio_oneshot (line 80) | fn bench_tokio_oneshot(c: &mut Criterion) {
FILE: test-suite/src/lib.rs
constant ROUND (line 27) | pub const ROUND: usize = 10000;
constant ROUND (line 29) | pub const ROUND: usize = 20;
function _setup_log (line 40) | pub fn _setup_log() {
type TestDropMsg (line 177) | pub trait TestDropMsg: Unpin + Send + 'static {
method new (line 178) | fn new(v: usize) -> Self;
method get_value (line 180) | fn get_value(&self) -> usize;
method new (line 192) | fn new(v: usize) -> Self {
method get_value (line 196) | fn get_value(&self) -> usize {
method new (line 204) | fn new(v: usize) -> Self {
method get_value (line 208) | fn get_value(&self) -> usize {
type SmallMsg (line 183) | pub struct SmallMsg(pub usize);
method drop (line 186) | fn drop(&mut self) {
type LargeMsg (line 201) | pub struct LargeMsg([usize; 4]);
method drop (line 214) | fn drop(&mut self) {
function get_drop_counter (line 219) | pub fn get_drop_counter() -> usize {
function reset_drop_counter (line 223) | pub fn reset_drop_counter() {
function sleep (line 228) | pub async fn sleep(duration: std::time::Duration) {
function timeout (line 253) | pub async fn timeout<F, T>(duration: std::time::Duration, future: F) -> ...
function spawn_named_thread (line 282) | pub fn spawn_named_thread<F, T>(name: &str, f: F) -> std::thread::JoinHa...
FILE: test-suite/src/test_async.rs
function setup_log (line 21) | fn setup_log() {
function test_basic_weak (line 27) | fn test_basic_weak(setup_log: ()) {
function test_basic_bounded_empty_full_drop_rx (line 48) | fn test_basic_bounded_empty_full_drop_rx<T: AsyncTxTrait<usize>, R: Asyn...
function test_basic_bounded_empty_full_drop_tx (line 73) | fn test_basic_bounded_empty_full_drop_tx<T: AsyncTxTrait<usize>, R: Asyn...
function test_basic_compile_bounded_empty_full (line 95) | fn test_basic_compile_bounded_empty_full() {
function test_sync (line 114) | fn test_sync() {
function test_basic_bounded_rx_drop (line 200) | fn test_basic_bounded_rx_drop<T: AsyncTxTrait<usize>, R: AsyncRxTrait<us...
function test_basic_unbounded_rx_drop (line 225) | fn test_basic_unbounded_rx_drop<T: BlockingTxTrait<usize>, R: AsyncRxTra...
function test_basic_bounded_1_thread (line 251) | fn test_basic_bounded_1_thread<T: AsyncTxTrait<usize>, R: AsyncRxTrait<u...
function test_basic_unbounded_1_thread (line 297) | fn test_basic_unbounded_1_thread<T: BlockingTxTrait<usize>, R: AsyncRxTr...
function test_basic_unbounded_idle_select (line 342) | fn test_basic_unbounded_idle_select<T: BlockingTxTrait<usize>, R: AsyncR...
function test_basic_bounded_recv_after_sender_close (line 386) | fn test_basic_bounded_recv_after_sender_close<T: AsyncTxTrait<usize>, R:...
function test_basic_unbounded_recv_after_sender_close (line 418) | fn test_basic_unbounded_recv_after_sender_close<
function test_basic_timeout_recv_async_waker (line 452) | fn test_basic_timeout_recv_async_waker<T: AsyncTxTrait<usize>, R: AsyncR...
function test_basic_unbounded_recv_timeout_async (line 490) | fn test_basic_unbounded_recv_timeout_async<T: BlockingTxTrait<usize>, R:...
function test_basic_send_timeout_async (line 523) | fn test_basic_send_timeout_async<T: AsyncTxTrait<usize>, R: AsyncRxTrait...
function test_pressure_bounded_timeout_async (line 575) | fn test_pressure_bounded_timeout_async<F: Flavor<Item = usize> + 'static>(
function test_pressure_bounded_async_1_1 (line 702) | fn test_pressure_bounded_async_1_1<T: AsyncTxTrait<usize>, R: AsyncRxTra...
function test_pressure_bounded_async_multi_1 (line 753) | fn test_pressure_bounded_async_multi_1<
function test_pressure_bounded_async_multi (line 815) | fn test_pressure_bounded_async_multi<F: Flavor<Item = usize> + 'static>(
function test_pressure_bounded_mixed_async_blocking_conversion (line 879) | fn test_pressure_bounded_mixed_async_blocking_conversion<F: Flavor<Item ...
function test_conversion (line 955) | fn test_conversion() {
type SpuriousTx (line 966) | struct SpuriousTx<F: Flavor> {
type Output (line 973) | type Output = Result<usize, usize>;
method poll (line 975) | fn poll(self: Pin<&mut Self>, ctx: &mut std::task::Context) -> Poll<Self...
type SpuriousRx (line 998) | struct SpuriousRx<F: Flavor> {
type Output (line 1005) | type Output = Result<usize, usize>;
method poll (line 1007) | fn poll(self: Pin<&mut Self>, ctx: &mut std::task::Context) -> Poll<Self...
function test_spurious_sink (line 1031) | fn test_spurious_sink(setup_log: ()) {
function test_spurious_stream (line 1081) | fn test_spurious_stream(setup_log: ()) {
function test_basic_into_stream_1_1 (line 1130) | fn test_basic_into_stream_1_1<T: AsyncTxTrait<usize>, R: AsyncRxTrait<us...
function test_pressure_stream_multi (line 1165) | fn test_pressure_stream_multi<F: Flavor<Item = usize> + 'static>(
function test_pressure_stream_multi_idle (line 1214) | fn test_pressure_stream_multi_idle<F: Flavor<Item = usize> + 'static>(
function test_async_drop_small_msg (line 1265) | fn test_async_drop_small_msg<T: AsyncTxTrait<SmallMsg>, R: AsyncRxTrait<...
function test_async_drop_large_msg (line 1281) | fn test_async_drop_large_msg<T: AsyncTxTrait<LargeMsg>, R: AsyncRxTrait<...
function _test_async_drop_msg (line 1288) | fn _test_async_drop_msg<M: TestDropMsg, T: AsyncTxTrait<M>, R: AsyncRxTr...
FILE: test-suite/src/test_async_blocking.rs
function setup_log (line 11) | fn setup_log() {
function test_basic_bounded_empty_full_drop_rx (line 20) | fn test_basic_bounded_empty_full_drop_rx<T: AsyncTxTrait<usize>, R: Bloc...
function test_basic_bounded_empty_full_drop_tx (line 43) | fn test_basic_bounded_empty_full_drop_tx<T: AsyncTxTrait<usize>, R: Bloc...
function test_basic_compile_bounded_empty_full (line 63) | fn test_basic_compile_bounded_empty_full() {
function test_basic_1_tx_async_1_rx_blocking (line 85) | fn test_basic_1_tx_async_1_rx_blocking<T: AsyncTxTrait<usize>, R: Blocki...
function test_basic_multi_tx_async_1_rx_blocking (line 139) | fn test_basic_multi_tx_async_1_rx_blocking<
function test_pressure_1_tx_async_1_rx_blocking (line 219) | fn test_pressure_1_tx_async_1_rx_blocking<T: AsyncTxTrait<usize>, R: Blo...
function test_pressure_multi_tx_async_1_rx_blocking (line 273) | fn test_pressure_multi_tx_async_1_rx_blocking<
function test_pressure_multi_tx_async_multi_rx_blocking (line 333) | fn test_pressure_multi_tx_async_multi_rx_blocking<F: Flavor<Item = usize...
FILE: test-suite/src/test_blocking_async.rs
function setup_log (line 10) | fn setup_log() {
function test_basic_bounded_empty_full_drop_rx (line 19) | fn test_basic_bounded_empty_full_drop_rx<T: BlockingTxTrait<usize>, R: A...
function test_basic_bounded_empty_full_drop_tx (line 42) | fn test_basic_bounded_empty_full_drop_tx<T: BlockingTxTrait<usize>, R: A...
function test_basic_unbounded_empty_drop_tx (line 65) | fn test_basic_unbounded_empty_drop_tx<T: BlockingTxTrait<usize>, R: Asyn...
function test_basic_compile_bounded_empty_full (line 83) | fn test_basic_compile_bounded_empty_full() {
function test_basic_1_tx_blocking_1_rx_async (line 104) | fn test_basic_1_tx_blocking_1_rx_async<T: BlockingTxTrait<usize>, R: Asy...
function test_pressure_1_tx_blocking_1_rx_async (line 154) | fn test_pressure_1_tx_blocking_1_rx_async<T: BlockingTxTrait<usize>, R: ...
function test_pressure_tx_multi_blocking_1_rx_async (line 209) | fn test_pressure_tx_multi_blocking_1_rx_async<
function test_pressure_tx_multi_blocking_multi_rx_async (line 279) | fn test_pressure_tx_multi_blocking_multi_rx_async<F: Flavor<Item = usize...
FILE: test-suite/src/test_blocking_context.rs
function setup_log (line 11) | fn setup_log() {
function test_basic_bounded_empty_full_drop_rx (line 20) | fn test_basic_bounded_empty_full_drop_rx<T: BlockingTxTrait<usize>, R: B...
function test_basic_bounded_empty_full_drop_tx (line 57) | fn test_basic_bounded_empty_full_drop_tx<T: BlockingTxTrait<usize>, R: B...
function test_basic_unbounded_empty_drop_rx (line 94) | fn test_basic_unbounded_empty_drop_rx<T: BlockingTxTrait<usize>, R: Bloc...
function test_basic_unbounded_empty_drop_tx (line 128) | fn test_basic_unbounded_empty_drop_tx<T: BlockingTxTrait<usize>, R: Bloc...
function test_basic_bounded_1_thread (line 161) | fn test_basic_bounded_1_thread<T: BlockingTxTrait<i32>, R: BlockingRxTra...
function test_basic_unbounded_1_thread (line 207) | fn test_basic_unbounded_1_thread<T: BlockingTxTrait<i32>, R: BlockingRxT...
function test_basic_recv_after_sender_close (line 253) | fn test_basic_recv_after_sender_close<T: BlockingTxTrait<i32>, R: Blocki...
function test_pressure_bounded_blocking_1_1 (line 295) | fn test_pressure_bounded_blocking_1_1<T: BlockingTxTrait<usize>, R: Bloc...
function test_pressure_bounded_blocking_multi_1 (line 356) | fn test_pressure_bounded_blocking_multi_1<
function test_pressure_bounded_blocking_multi (line 418) | fn test_pressure_bounded_blocking_multi<F: Flavor<Item = usize> + 'static>(
function test_pressure_bounded_timeout_blocking (line 486) | fn test_pressure_bounded_timeout_blocking<F: Flavor<Item = usize> + 'sta...
function test_conversion (line 598) | fn test_conversion() {
function test_drop_small_msg (line 613) | fn test_drop_small_msg<T: BlockingTxTrait<SmallMsg>, R: BlockingRxTrait<...
function test_drop_large_msg (line 629) | fn test_drop_large_msg<T: BlockingTxTrait<LargeMsg>, R: BlockingRxTrait<...
function _test_drop_msg (line 636) | fn _test_drop_msg<M: TestDropMsg, T: BlockingTxTrait<M>, R: BlockingRxTr...
FILE: test-suite/src/test_oneshot.rs
function setup_log (line 10) | fn setup_log() {
function test_oneshot_blocking_basic (line 16) | fn test_oneshot_blocking_basic(setup_log: ()) {
function test_oneshot_blocking_drop_tx (line 34) | fn test_oneshot_blocking_drop_tx(setup_log: ()) {
function test_oneshot_blocking_drop_rx (line 51) | fn test_oneshot_blocking_drop_rx(setup_log: ()) {
function test_oneshot_blocking_leak (line 61) | fn test_oneshot_blocking_leak(setup_log: ()) {
function test_oneshot_blocking_drop_after_recv (line 73) | fn test_oneshot_blocking_drop_after_recv(setup_log: ()) {
function test_oneshot_async_basic (line 90) | fn test_oneshot_async_basic(setup_log: ()) {
function test_oneshot_async_drop_tx (line 110) | fn test_oneshot_async_drop_tx(setup_log: ()) {
function test_oneshot_async_pressure (line 129) | fn test_oneshot_async_pressure(setup_log: ()) {
function test_oneshot_blocking_batch (line 157) | fn test_oneshot_blocking_batch(setup_log: ()) {
function test_oneshot_async_batch (line 178) | fn test_oneshot_async_batch(setup_log: ()) {
function test_oneshot_blocking_concurrent (line 201) | fn test_oneshot_blocking_concurrent(setup_log: ()) {
function test_oneshot_async_concurrent (line 229) | fn test_oneshot_async_concurrent(setup_log: ()) {
function test_oneshot_blocking_with_sleep (line 259) | fn test_oneshot_blocking_with_sleep(setup_log: ()) {
function test_oneshot_async_with_sleep (line 291) | fn test_oneshot_async_with_sleep(setup_log: ()) {
function test_oneshot_async_batch_with_interval (line 325) | fn test_oneshot_async_batch_with_interval(setup_log: ()) {
function test_oneshot_blocking_timeout_fail (line 363) | fn test_oneshot_blocking_timeout_fail(setup_log: ()) {
function test_oneshot_blocking_timeout_success (line 374) | fn test_oneshot_blocking_timeout_success(setup_log: ()) {
function test_oneshot_blocking_timeout_disconnected (line 389) | fn test_oneshot_blocking_timeout_disconnected(setup_log: ()) {
function test_oneshot_async_timeout_fail (line 404) | fn test_oneshot_async_timeout_fail(setup_log: ()) {
function test_oneshot_async_timeout_disconnected (line 419) | fn test_oneshot_async_timeout_disconnected(setup_log: ()) {
function test_oneshot_async_timeout_success (line 436) | fn test_oneshot_async_timeout_success(setup_log: ()) {
FILE: test-suite/src/test_select_async.rs
function setup_log (line 9) | fn setup_log() {
function test_mpmc_null_async_close (line 16) | fn test_mpmc_null_async_close(setup_log: ()) {
function test_mpsc_null_async_close (line 35) | fn test_mpsc_null_async_close(setup_log: ()) {
function test_mpmc_null_select (line 54) | fn test_mpmc_null_select(setup_log: ()) {
function test_mpsc_null_select (line 81) | fn test_mpsc_null_select(setup_log: ()) {
function test_null_select_timeout (line 108) | fn test_null_select_timeout(setup_log: ()) {
function test_null_mixed_with_active_channel (line 132) | fn test_null_mixed_with_active_channel(setup_log: ()) {
function test_null_mixed_trigger (line 156) | fn test_null_mixed_trigger(setup_log: ()) {
FILE: test-suite/src/test_select_blocking.rs
function setup_log (line 12) | fn setup_log() {
function test_select_basic (line 18) | fn test_select_basic(setup_log: ()) {
function test_select_basic_timeout (line 45) | fn test_select_basic_timeout(setup_log: ()) {
function test_select_basic_disconnect_before_park (line 62) | fn test_select_basic_disconnect_before_park(setup_log: ()) {
function test_select_basic_disconnect_after_park (line 90) | fn test_select_basic_disconnect_after_park(setup_log: ()) {
function test_select_basic_loop (line 125) | fn test_select_basic_loop(setup_log: ()) {
function test_select_remove_mid (line 270) | fn test_select_remove_mid(setup_log: ()) {
function test_select_mixed_flavors (line 309) | fn test_select_mixed_flavors(setup_log: ()) {
function test_select_pressure (line 348) | fn test_select_pressure(setup_log: (), #[case] producers: usize) {
function test_select_null (line 475) | fn test_select_null(setup_log: ()) {
function test_select_pressure_concurrent (line 517) | fn test_select_pressure_concurrent(setup_log: ()) {
function test_multiplex_basic (line 583) | fn test_multiplex_basic(setup_log: ()) {
function test_multiplex_timeout (line 614) | fn test_multiplex_timeout(setup_log: ()) {
function test_multiplex_try_recv (line 623) | fn test_multiplex_try_recv(setup_log: ()) {
function test_multiplex_basic_array_blocking (line 634) | fn test_multiplex_basic_array_blocking(setup_log: ()) {
function test_multiplex_basic_list_blocking (line 665) | fn test_multiplex_basic_list_blocking(setup_log: ()) {
function test_multiplex_sender_close (line 715) | fn test_multiplex_sender_close(setup_log: ()) {
function test_multiplex_basic_drop_on_sender_blocked (line 738) | fn test_multiplex_basic_drop_on_sender_blocked(
function test_pressure_multiplex_array (line 793) | fn test_pressure_multiplex_array(setup_log: (), #[case] producers: usize...
function test_pressure_multiplex_array_mp (line 835) | fn test_pressure_multiplex_array_mp(setup_log: (), #[case] producers: us...
function test_pressure_multiplex_list (line 884) | fn test_pressure_multiplex_list(setup_log: (), #[case] producers: usize) {
function test_multiplex_weighted_round_robin (line 931) | fn test_multiplex_weighted_round_robin(setup_log: ()) {
function test_multiplex_weighted_skip_empty (line 963) | fn test_multiplex_weighted_skip_empty(setup_log: ()) {
FILE: test-suite/src/test_type_switch.rs
function setup_log (line 9) | fn setup_log() {
function test_bounded_async_with_sync_receiver_switch_buffered (line 29) | fn test_bounded_async_with_sync_receiver_switch_buffered<
function test_mpmc_bounded_async_with_sync_receiver_switch_buffered (line 114) | fn test_mpmc_bounded_async_with_sync_receiver_switch_buffered<F: Flavor<...
function test_spsc_bounded_blocking_with_async_sender_switch (line 193) | fn test_spsc_bounded_blocking_with_async_sender_switch<F: Flavor<Item = ...
function test_mpsc_bounded_blocking_with_async_sender_switch (line 256) | fn test_mpsc_bounded_blocking_with_async_sender_switch<F: Flavor<Item = ...
function test_mpmc_bounded_blocking_with_async_sender_switch (line 317) | fn test_mpmc_bounded_blocking_with_async_sender_switch<F: Flavor<Item = ...
function test_spsc_bounded_blocking_with_async_receiver_switch (line 377) | fn test_spsc_bounded_blocking_with_async_receiver_switch<F: Flavor<Item ...
function test_mpsc_bounded_blocking_with_async_receiver_switch (line 449) | fn test_mpsc_bounded_blocking_with_async_receiver_switch<F: Flavor<Item ...
function test_mpmc_bounded_blocking_with_async_receiver_switch (line 521) | fn test_mpmc_bounded_blocking_with_async_receiver_switch<F: Flavor<Item ...
function test_multi_producer_sender_switch (line 588) | fn test_multi_producer_sender_switch<
function test_spsc_bounded_async_with_blocking_sender_switch (line 647) | fn test_spsc_bounded_async_with_blocking_sender_switch<F: Flavor<Item = ...
function test_mpsc_bounded_async_with_blocking_sender_switch (line 704) | fn test_mpsc_bounded_async_with_blocking_sender_switch<F: Flavor<Item = ...
function test_mpmc_bounded_async_with_blocking_sender_switch (line 761) | fn test_mpmc_bounded_async_with_blocking_sender_switch<F: Flavor<Item = ...
FILE: test-suite/src/test_waitgroup.rs
function setup_log (line 10) | fn setup_log() {
function test_basic_wg_try_wait (line 20) | fn test_basic_wg_try_wait(setup_log: ()) {
function test_waitgroup_with_state (line 44) | fn test_waitgroup_with_state(setup_log: ()) {
function test_basic_wg_timeout_blocking (line 62) | fn test_basic_wg_timeout_blocking(setup_log: ()) {
function test_basic_no_wait_async (line 86) | fn test_basic_no_wait_async(setup_log: ()) {
function test_basic_wg_one_guard_async (line 98) | fn test_basic_wg_one_guard_async(setup_log: ()) {
function test_basic_wg_multi_guards_async (line 118) | fn test_basic_wg_multi_guards_async(setup_log: ()) {
function test_basic_wg_timeout_async (line 151) | fn test_basic_wg_timeout_async(setup_log: ()) {
function test_pressure_wg_blocking_spawn_sleep (line 187) | fn test_pressure_wg_blocking_spawn_sleep(setup_log: ()) {
function test_pressure_wg_async_channel (line 222) | fn test_pressure_wg_async_channel(
function test_pressure_wg_async_channel_sleep (line 282) | fn test_pressure_wg_async_channel_sleep(
function test_pressure_wg_blocking_channel (line 348) | fn test_pressure_wg_blocking_channel(
function test_waitgroup_inline (line 403) | fn test_waitgroup_inline(setup_log: ()) {
function test_waitgroup_inline_underflow (line 435) | fn test_waitgroup_inline_underflow() {
Condensed preview — 94 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (791K chars).
[
{
"path": ".github/workflows/cron_2.0_arm.yml",
"chars": 530,
"preview": "name: cron-2.0-arm\n\non:\n schedule: [cron: \"30 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n"
},
{
"path": ".github/workflows/cron_2.0_x86.yml",
"chars": 533,
"preview": "name: cron-2.0-x86\n\non:\n schedule: [cron: \"30 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n"
},
{
"path": ".github/workflows/cron_dev.yml",
"chars": 645,
"preview": "name: cron-dev\n\non:\n schedule: [cron: \"0 */6 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n bui"
},
{
"path": ".github/workflows/cron_dev_arm.yml",
"chars": 1199,
"preview": "name: cron-dev-arm\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n "
},
{
"path": ".github/workflows/cron_dev_arm_trace.yml",
"chars": 1355,
"preview": "name: cron-dev-arm-trace\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n\nj"
},
{
"path": ".github/workflows/cron_master_async_std_arm.yml",
"chars": 716,
"preview": "name: cron-master-async_std-arm\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: al"
},
{
"path": ".github/workflows/cron_master_async_std_x86.yml",
"chars": 719,
"preview": "name: cron-master-async_std-x86\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: al"
},
{
"path": ".github/workflows/cron_master_compio_arm.yml",
"chars": 1307,
"preview": "name: cron-master-compio-arm\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: alway"
},
{
"path": ".github/workflows/cron_master_compio_x86.yml",
"chars": 1310,
"preview": "name: cron-master-compio-x86\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: alway"
},
{
"path": ".github/workflows/cron_master_smol_arm.yml",
"chars": 681,
"preview": "name: cron-master-smol-arm\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n"
},
{
"path": ".github/workflows/cron_master_smol_x86.yml",
"chars": 684,
"preview": "name: cron-master-smol-x86\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n"
},
{
"path": ".github/workflows/cron_master_threaded_arm.yml",
"chars": 455,
"preview": "name: cron-master-threaded-arm\n\non:\n schedule: [cron: \"20 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: al"
},
{
"path": ".github/workflows/cron_master_threaded_x86.yml",
"chars": 458,
"preview": "name: cron-master-threaded-x86\n\non:\n schedule: [cron: \"20 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: al"
},
{
"path": ".github/workflows/cron_master_tokio_arm.yml",
"chars": 1525,
"preview": "name: cron-master-tokio-arm\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always"
},
{
"path": ".github/workflows/cron_master_tokio_x86.yml",
"chars": 689,
"preview": "name: cron-master-tokio-x86\n\non:\n schedule: [cron: \"0 */5 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always"
},
{
"path": ".github/workflows/fast.yml",
"chars": 752,
"preview": "name: fast-validation\n\non:\n push:\n branches: [ \"master\" ]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build_and_test:\n"
},
{
"path": ".github/workflows/leak.yml",
"chars": 437,
"preview": "name: leak\n\non:\n schedule: [cron: \"30 */10 * * *\"]\n workflow_dispatch:\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n build"
},
{
"path": ".github/workflows/miri_dev.yml",
"chars": 787,
"preview": "name: miri-dev\n\non:\n workflow_dispatch:\n schedule: [cron: \"20 */7 * * *\"]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n te"
},
{
"path": ".github/workflows/miri_dev_log.yml",
"chars": 789,
"preview": "name: miri-dev-log\n\non:\n workflow_dispatch:\n schedule: [cron: \"30 */8 * * *\"]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n"
},
{
"path": ".github/workflows/miri_tokio.yml",
"chars": 655,
"preview": "name: miri-tokio\n\non:\n workflow_dispatch:\n schedule: [cron: \"20 */6 * * *\"]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n "
},
{
"path": ".github/workflows/miri_tokio_cur.yml",
"chars": 682,
"preview": "name: miri-tokio-cur\n\non:\n workflow_dispatch:\n schedule: [cron: \"50 */7 * * *\"]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs"
},
{
"path": ".github/workflows/miri_tokio_cur_log.yml",
"chars": 871,
"preview": "name: miri-tokio-cur-log\n\non:\n workflow_dispatch:\n schedule: [cron: \"20 */9 * * *\"]\n\nenv:\n CARGO_TERM_COLOR: always\n\n"
},
{
"path": ".github/workflows/miri_tokio_log.yml",
"chars": 859,
"preview": "name: miri-tokio-log\n\non:\n workflow_dispatch:\n schedule: [cron: \"10 */7 * * *\"]\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs"
},
{
"path": ".github/workflows/pr.yml",
"chars": 587,
"preview": "name: pr-validation\n\non:\n pull_request:\n types:\n - opened\n - synchronize\n - ready_for_review\n\nenv:\n "
},
{
"path": ".github/workflows/typos.yml",
"chars": 410,
"preview": "name: Typo checker\non:\n pull_request:\n types:\n - opened\n - synchronize\n - ready_for_review\n push:\n "
},
{
"path": ".gitignore",
"chars": 30,
"preview": "/target\nCargo.lock\ntags\n*.sw*\n"
},
{
"path": "AGENTS.md",
"chars": 1068,
"preview": "# General\n- All comments and documents must be in English.\n- Omit unnecessary obvious comments during coding.\n- Document"
},
{
"path": "CHANGELOG.md",
"chars": 11678,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "CONTRIBUTION",
"chars": 125,
"preview": "Original Author:\n- Plan (frostyplanet at gmail.com)\nThanks:\n- Zach Schoenberger\n- MathisWellmann\n- lisovskiy\n- Sherlock-"
},
{
"path": "Cargo.toml",
"chars": 1863,
"preview": "[workspace]\nmembers = [\"test-suite\"]\n\n[package]\nname = \"crossfire\"\nversion = \"3.1.10\"\nauthors = [\"plan <frostyplanet@gma"
},
{
"path": "LICENSE",
"chars": 11378,
"preview": "Apache License\n Version 2.0, January 2004\n http://www.apache.org/licens"
},
{
"path": "Makefile",
"chars": 5090,
"preview": "PRIMARY_TARGET := $(firstword $(MAKECMDGOALS))\nARGS := $(filter-out $(PRIMARY_TARGET), $(MAKECMDGOALS))\n\nRUN_TEST_CASE ="
},
{
"path": "README.md",
"chars": 14923,
"preview": "# Crossfire\n\n[](\nhttps://github.co"
},
{
"path": "benches/inner.rs",
"chars": 12698,
"preview": "use criterion::*;\nuse crossbeam_queue::{ArrayQueue, SegQueue};\nuse crossbeam_utils::Backoff;\nuse crossfire::collections:"
},
{
"path": "git-hooks/pre-commit",
"chars": 207,
"preview": "#!/bin/bash\n\nmake fmt || exit 1\n\n# re add the files since changed by fmt\nfiles=$(git diff --cached --name-only --diff-fi"
},
{
"path": "rustfmt.toml",
"chars": 149,
"preview": "edition = \"2021\"\nfn_params_layout = \"Compressed\"\nnewline_style = \"Unix\"\nuse_small_heuristics = \"Max\"\nmax_width = 100\nuse"
},
{
"path": "src/async_rx.rs",
"chars": 24354,
"preview": "use crate::flavor::{FlavorMC, FlavorSelect};\nuse crate::select::SelectResult;\nuse crate::stream::AsyncStream;\n#[cfg(feat"
},
{
"path": "src/async_tx.rs",
"chars": 26059,
"preview": "use crate::flavor::FlavorMP;\nuse crate::sink::AsyncSink;\n#[cfg(feature = \"trace_log\")]\nuse crate::tokio_task_id;\nuse cra"
},
{
"path": "src/backoff.rs",
"chars": 5870,
"preview": "use core::num::NonZero;\nuse std::mem::transmute;\nuse std::sync::atomic::{AtomicBool, AtomicU32, Ordering};\nuse std::thre"
},
{
"path": "src/blocking_rx.rs",
"chars": 15991,
"preview": "use crate::backoff::*;\nuse crate::flavor::{FlavorMC, FlavorSelect};\nuse crate::select::SelectResult;\nuse crate::{shared:"
},
{
"path": "src/blocking_tx.rs",
"chars": 18829,
"preview": "use crate::backoff::*;\nuse crate::flavor::FlavorMP;\nuse crate::weak::WeakTx;\nuse crate::{shared::*, trace_log, AsyncTx, "
},
{
"path": "src/collections.rs",
"chars": 4338,
"preview": "use std::ptr;\nuse std::sync::{\n atomic::{AtomicPtr, Ordering},\n Arc, Weak,\n};\n\npub struct ArcCell<T> {\n ptr: At"
},
{
"path": "src/compat.rs",
"chars": 14788,
"preview": "//! compatible layer for V2.0 API\n//!\n//! # Migration from v2.* to v3\n//!\n//! If you want to migrate to v3 API, you may "
},
{
"path": "src/crossbeam/array_queue.rs",
"chars": 17045,
"preview": "//! Modify by frostyplanet@gmail.com for the crossfire crate:\n//!\n//! - Optimise for single consumer scenario;\n//! -"
},
{
"path": "src/crossbeam/array_queue_mpsc.rs",
"chars": 12976,
"preview": "//! Modify by frostyplanet@gmail.com for the crossfire crate:\n//!\n//! - Optimise for MPSC scenario;\n//! - Pack head/"
},
{
"path": "src/crossbeam/array_queue_spsc.rs",
"chars": 13766,
"preview": "//! Modify by frostyplanet@gmail.com for the crossfire crate:\n//!\n//! - Modify for SPSC, remove the `stamp` field\n//! "
},
{
"path": "src/crossbeam/err.rs",
"chars": 9651,
"preview": "//! The MIT License (MIT)\n//!\n//! Copyright (c) 2019 The Crossbeam Project Developers\n//!\n//! Permission is hereby grant"
},
{
"path": "src/crossbeam/mod.rs",
"chars": 105,
"preview": "pub mod array_queue;\npub mod array_queue_mpsc;\npub mod array_queue_spsc;\npub mod err;\npub mod seg_queue;\n"
},
{
"path": "src/crossbeam/seg_queue.rs",
"chars": 20052,
"preview": "//! Modify by frostyplanet@gmail.com for the crossfire crate:\n//!\n//! - Modify for select according to crossbeam-chann"
},
{
"path": "src/flavor/array.rs",
"chars": 3008,
"preview": "use super::{FlavorBounded, FlavorImpl, FlavorSelect, Queue, Token};\nuse crate::crossbeam::array_queue::ArrayQueue;\nuse s"
},
{
"path": "src/flavor/array_mpsc.rs",
"chars": 2917,
"preview": "use super::{FlavorBounded, FlavorImpl, FlavorSelect, Queue, Token};\nuse crate::crossbeam::array_queue_mpsc::ArrayQueueMp"
},
{
"path": "src/flavor/array_spsc.rs",
"chars": 2721,
"preview": "use super::{FlavorBounded, FlavorImpl, FlavorSelect, Queue, Token};\nuse crate::crossbeam::array_queue_spsc::ArrayQueueSp"
},
{
"path": "src/flavor/list.rs",
"chars": 1890,
"preview": "use super::{FlavorImpl, FlavorNew, FlavorSelect, Queue, Token};\nuse crate::crossbeam::seg_queue::SegQueue;\nuse std::mem:"
},
{
"path": "src/flavor/mod.rs",
"chars": 7955,
"preview": "use crate::waker_registry::*;\nuse std::marker::PhantomData;\nuse std::mem::MaybeUninit;\nuse std::ops::Deref;\n\npub mod arr"
},
{
"path": "src/flavor/one.rs",
"chars": 7631,
"preview": "use super::{FlavorImpl, FlavorNew, FlavorSelect, Queue, Token};\nuse crate::backoff::*;\nuse core::cell::UnsafeCell;\nuse c"
},
{
"path": "src/flavor/one_mpsc.rs",
"chars": 6869,
"preview": "use super::{FlavorImpl, FlavorNew, FlavorSelect, Queue, Token};\nuse crate::backoff::*;\nuse core::cell::UnsafeCell;\nuse c"
},
{
"path": "src/flavor/one_spmc.rs",
"chars": 10570,
"preview": "use super::{FlavorImpl, FlavorNew, FlavorSelect, Queue, Token};\nuse core::cell::UnsafeCell;\nuse core::mem::{needs_drop, "
},
{
"path": "src/lib.rs",
"chars": 13314,
"preview": "#![cfg_attr(docsrs, feature(doc_cfg))]\n#![cfg_attr(docsrs, allow(unused_attributes))]\n\n//! # Crossfire\n//!\n//! High-perf"
},
{
"path": "src/mpmc.rs",
"chars": 7394,
"preview": "//! Multiple producers, multiple consumers.\n//!\n//! The optimization assumes multiple consumers. The waker registration "
},
{
"path": "src/mpsc.rs",
"chars": 6992,
"preview": "//! Multiple producers, single consumer.\n//!\n//! The optimization assumes a single consumer. The waker registration of t"
},
{
"path": "src/null.rs",
"chars": 4509,
"preview": "//! A null flavor type that use to notify thread/future to close\n//!\n//! It's common practice we use `()` channel in asy"
},
{
"path": "src/oneshot.rs",
"chars": 19969,
"preview": "//! OneShot channel support both thread and async\n//!\n//! NOTE: In order to reduce initialization and teardown cost, thi"
},
{
"path": "src/select/mod.rs",
"chars": 737,
"preview": "//! # Selection between channels\n//!\n//! This module provides:\n//! - [Select]: Allows selecting from multiple borrowed r"
},
{
"path": "src/select/multiplex.rs",
"chars": 16994,
"preview": "use crate::backoff::*;\nuse crate::flavor::{Flavor, FlavorBounded, FlavorImpl, FlavorNew, FlavorWrap};\nuse crate::shared:"
},
{
"path": "src/select/select.rs",
"chars": 15700,
"preview": "// Internal Implementation Details:\n//\n// Since mixing send and receive operations is rare, and the waker types for send"
},
{
"path": "src/shared.rs",
"chars": 10648,
"preview": "use crate::backoff::*;\npub(crate) use crate::crossbeam::err::*;\npub(crate) use crate::flavor::{Flavor, FlavorSelect, Tok"
},
{
"path": "src/sink.rs",
"chars": 3391,
"preview": "use crate::shared::*;\nuse crate::{flavor::FlavorMP, AsyncTx, MAsyncTx, TrySendError};\nuse std::fmt;\nuse std::mem::MaybeU"
},
{
"path": "src/spsc.rs",
"chars": 6437,
"preview": "//! Single producer, single consumer.\n//!\n//! The optimization assumes a single producer and consumer, so waker registra"
},
{
"path": "src/stream.rs",
"chars": 3933,
"preview": "use crate::shared::*;\nuse crate::{AsyncRx, MAsyncRx};\nuse futures_core::stream;\nuse std::fmt;\nuse std::ops::Deref;\nuse s"
},
{
"path": "src/waitgroup.rs",
"chars": 37888,
"preview": "//! This module provides two waitgroup implementation, works in blocking & async context.\n//! The implementation is low-"
},
{
"path": "src/waker.rs",
"chars": 12432,
"preview": "use crate::collections::ArcCell;\nuse crate::flavor::FlavorImpl;\nuse std::cell::UnsafeCell;\nuse std::fmt;\nuse std::ops::D"
},
{
"path": "src/waker_registry.rs",
"chars": 35020,
"preview": "#[allow(unused_imports)]\nuse crate::collections::WeakCell;\n#[allow(unused_imports)]\nuse crate::flavor::{Flavor, FlavorIm"
},
{
"path": "src/weak.rs",
"chars": 1378,
"preview": "use crate::flavor::FlavorMP;\nuse crate::{shared::*, SenderType};\nuse std::sync::Arc;\n\n/// A weak reference of SenderType"
},
{
"path": "test-suite/Cargo.toml",
"chars": 1728,
"preview": "[package]\nname = \"crossfire-test\"\nversion = \"0.0.1\"\nauthors = [\"plan <frostyplanet@gmail.com>\"]\nedition = \"2021\"\nlicense"
},
{
"path": "test-suite/benches/async_channel.rs",
"chars": 5675,
"preview": "use criterion::*;\nuse std::time::Duration;\n\n#[allow(unused_imports)]\nmod common;\nuse common::*;\n\nasync fn _async_channel"
},
{
"path": "test-suite/benches/common.rs",
"chars": 2547,
"preview": "use std::fmt;\nuse std::future::Future;\n\nuse criterion::async_executor::AsyncExecutor;\n\n#[allow(dead_code)]\npub const ONE"
},
{
"path": "test-suite/benches/crossbeam.rs",
"chars": 8601,
"preview": "use criterion::*;\nuse crossbeam_utils::sync::WaitGroup;\nuse std::thread;\nuse std::time::Duration;\n\n#[allow(unused_import"
},
{
"path": "test-suite/benches/crossfire.rs",
"chars": 22365,
"preview": "use criterion::*;\nuse crossfire::waitgroup::{WaitGroup, WaitGroupGuard};\nuse crossfire::*;\nuse std::thread;\nuse std::tim"
},
{
"path": "test-suite/benches/crossfire_select.rs",
"chars": 4608,
"preview": "use criterion::*;\nuse crossfire::{\n mpsc::Array,\n select::{Multiplex, Mux, Select, SelectMode},\n *,\n};\nuse std:"
},
{
"path": "test-suite/benches/extra.rs",
"chars": 2675,
"preview": "use criterion::*;\nuse std::thread;\nmod common;\nuse common::*;\n\nfn bench_async_oneshot_async(c: &mut Criterion) {\n let"
},
{
"path": "test-suite/benches/flume.rs",
"chars": 10939,
"preview": "use criterion::*;\nuse std::thread;\nuse std::time::Duration;\n\n#[allow(unused_imports)]\nmod common;\nuse common::*;\n\nfn _fl"
},
{
"path": "test-suite/benches/kanal.rs",
"chars": 10751,
"preview": "use criterion::*;\nuse std::thread;\nuse std::time::Duration;\n\n#[allow(unused_imports)]\nmod common;\nuse common::*;\n\nfn _ka"
},
{
"path": "test-suite/benches/tokio.rs",
"chars": 3449,
"preview": "use criterion::*;\nuse std::time::Duration;\n\n#[allow(unused_imports)]\nmod common;\nuse common::*;\n\nasync fn _tokio_bounded"
},
{
"path": "test-suite/scripts/miri.sh",
"chars": 746,
"preview": "#!/bin/bash\n# -Zmiri-no-short-fd-operations is to prevent short write perform by miri, which breaks to atomic appending "
},
{
"path": "test-suite/src/lib.rs",
"chars": 7601,
"preview": "#[cfg(test)]\nmod test_async;\n#[cfg(test)]\nmod test_async_blocking;\n#[cfg(test)]\nmod test_blocking_async;\n#[cfg(test)]\nmo"
},
{
"path": "test-suite/src/test_async.rs",
"chars": 41484,
"preview": "use crate::*;\nuse captains_log::{logfn, *};\nuse crossfire::flavor::Flavor;\nuse crossfire::tokio_task_id;\nuse crossfire::"
},
{
"path": "test-suite/src/test_async_blocking.rs",
"chars": 11309,
"preview": "use crate::*;\nuse captains_log::{logfn, *};\nuse crossfire::flavor::Flavor;\nuse crossfire::tokio_task_id;\nuse crossfire::"
},
{
"path": "test-suite/src/test_blocking_async.rs",
"chars": 10128,
"preview": "use crate::*;\nuse captains_log::{logfn, *};\nuse crossfire::flavor::Flavor;\nuse crossfire::tokio_task_id;\nuse crossfire::"
},
{
"path": "test-suite/src/test_blocking_context.rs",
"chars": 22004,
"preview": "use crate::*;\nuse captains_log::{logfn, *};\nuse crossfire::flavor::Flavor;\nuse crossfire::*;\nuse rstest::*;\nuse std::syn"
},
{
"path": "test-suite/src/test_oneshot.rs",
"chars": 12288,
"preview": "use crate::*;\nuse captains_log::logfn;\nuse crossfire::*;\nuse fastrand;\nuse rstest::*;\nuse std::thread;\nuse std::time::Du"
},
{
"path": "test-suite/src/test_select_async.rs",
"chars": 4453,
"preview": "use crate::*;\nuse captains_log::logfn;\nuse crossfire::{mpmc, mpsc};\nuse futures_util::{select, FutureExt};\nuse rstest::*"
},
{
"path": "test-suite/src/test_select_blocking.rs",
"chars": 28928,
"preview": "use crate::*;\nuse captains_log::logfn;\nuse crossfire::select::{Multiplex, Mux, Select};\nuse crossfire::*;\nuse rstest::*;"
},
{
"path": "test-suite/src/test_type_switch.rs",
"chars": 31219,
"preview": "use crate::*;\nuse captains_log::{logfn, *};\nuse crossfire::flavor::Flavor;\nuse crossfire::*;\nuse rstest::*;\nuse std::tim"
},
{
"path": "test-suite/src/test_waitgroup.rs",
"chars": 12325,
"preview": "use crate::*;\nuse crossfire::waitgroup::{WaitGroup, WaitGroupInline};\nuse crossfire::*;\nuse fastrand;\nuse rstest::*;\nuse"
}
]
About this extraction
This page contains the full source code of the frostyplanet/crossfire-rs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 94 files (741.0 KB), approximately 194.0k tokens, and a symbol index with 1428 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.