Rust in Everything
基于 crossbeam 0.8.5

源码地图

主线位置:三条主线经过的 crate 和文件。主线 1(一条消息)全在 crossbeam-channel 里,主线 2(一个任务)在 crossbeam-deque,主线 3(一块内存的回收)在 crossbeam-epoch;三条线都踩在 crossbeam-utils 上。
flowchart BT
  utils["crossbeam-utils 0.8.23"]
  epoch["crossbeam-epoch 0.9.21"] --> utils
  deque["crossbeam-deque 0.8.8"] --> epoch
  deque --> utils
  chan["crossbeam-channel 0.5.17"] --> utils
  queue["crossbeam-queue 0.3.14"] --> utils
  skip["crossbeam-skiplist 0.1.3(不在发布分支)"] --> epoch
  skip --> utils
  facade["crossbeam 0.8.5(门面)"] --> chan
  facade --> deque
  facade --> epoch
  facade --> queue
  facade --> utils

crate 与依赖

依赖只有一个方向:utils 在最底下,不依赖任何 crossbeam crate;epoch 依赖 utils;deque 和 skiplist 依赖 epoch 和 utils;channel 和 queue 只依赖 utils。门面 crossbeam 依赖除 skiplist 外的全部。行数是 src/ 下 .rs 的总行数,含文档注释和文件内的单元测试,不含 tests/、benches/:

crate版本MSRV默认 featuresrc/ 行数本卷
crossbeam-utils0.8.231.60std416803
crossbeam-epoch0.9.211.61std(含 alloc)494504
crossbeam-deque0.8.81.61std236705
crossbeam-channel0.5.171.60std784406
crossbeam-queue0.3.141.60std(含 alloc)139207
crossbeam-skiplist(tag crossbeam-skiplist-0.1.3)0.1.31.61std(含 alloc)379207
crossbeam(门面)0.8.51.61std(含 alloc)84本页

工作区根 Cargo.toml 的 members 里,skiplist 那一行被注释掉了:

members = [
  ".",
  "crossbeam-channel",
  "crossbeam-channel/benchmarks",
  "crossbeam-deque",
  "crossbeam-epoch",
  "crossbeam-queue",
  # "crossbeam-skiplist", # Not published from this branch.
  "crossbeam-utils",
]

这就是为什么 skiplist 用的是另一个 tag:发布分支上的 skiplist 目录比 0.1.3 还旧(Cargo.toml 写 version = "0.0.0"),见 怎么读这一卷。

门面 crate

src/lib.rs 是 #![no_std],正文只有重导出:

pub use crossbeam_utils::atomic;

pub mod utils {
    pub use crossbeam_utils::Backoff;
    pub use crossbeam_utils::CachePadded;
}

#[cfg(feature = "alloc")]
#[doc(inline)]
pub use {crossbeam_epoch as epoch, crossbeam_queue as queue};

#[cfg(feature = "std")]
#[doc(inline)]
pub use {
    crossbeam_channel as channel, crossbeam_channel::select, crossbeam_deque as deque,
    crossbeam_utils::sync,
};

#[cfg(feature = "std")]
#[cfg(not(crossbeam_loom))]
pub use crossbeam_utils::thread::{self, scope};

no_std 下只剩 atomic 和 utils;开 alloc 多出 epoch、queue;std 才有 channel、select!、deque、sync 和 scope。crossbeam::scope 在 cfg(crossbeam_loom) 下不存在。

每个文件管什么

crossbeam-utils

文件行数内容
lib.rs110两份 mod primitive(loom 与否),模块声明
cache_padded.rs223CachePadded<T>
backoff.rs287Backoff
atomic/atomic_cell.rs1214AtomicCell<T>、atomic! 宏、67 把全局锁
atomic/seq_lock.rs、seq_lock_wide.rs112、155锁路径用的 seqlock;32 位平台用 wide 版
atomic/consume.rs111AtomicConsume::load_consume
sync/parker.rs415Parker、Unparker
sync/sharded_lock.rs638ShardedLock 与线程编号表
sync/wait_group.rs155WaitGroup
sync/once_lock.rs88照 std 抄的 OnceLock(MSRV 低于 1.70)
thread.rs611作用域线程

crossbeam-epoch

文件行数内容
atomic.rs1713Atomic、Owned、Shared、Pointable、标签位
internal.rs618Global、Local、Bag、pin / unpin、collect、try_advance
guard.rs523Guard、defer*、flush、repin、unprotected
collector.rs465Collector、LocalHandle(一半是测试)
sync/list.rs、sync/queue.rs488、469参与者链表(Michael 2002)、袋子队列(Michael-Scott)
deferred.rs149Deferred:内联 3 个 usize 的闭包
epoch.rs149Epoch、AtomicEpoch
default.rs93默认收集器、epoch::pin()

crossbeam-deque:deque.rs 一个文件 2261 行,Worker、Stealer、Injector、Steal;lib.rs 只有文档和重导出。

crossbeam-channel

文件行数内容
channel.rs1534bounded / unbounded / after / at / tick / never,Sender、Receiver,迭代器
select.rs1299Token、Operation、Selected、SelectHandle、run_select、Select
select_macro.rs1153select! / select_biased! 的宏展开规则
flavors/list.rs、array.rs、zero.rs788、655、486无界、有界、零容量三种可收发的 flavor
flavors/at.rs、tick.rs、never.rs197、167、110只能接收的三种定时 flavor
err.rs378错误类型
waker.rs、context.rs、counter.rs、utils.rs287、178、144、73阻塞与唤醒、线程本地上下文、收发端计数、忽略 poison 的 Mutex

crossbeam-queue:seg_queue.rs 713 行,array_queue.rs 645 行。

crossbeam-skiplist(0.1.3):base.rs 2123 行(跳表本体),map.rs 783 行、set.rs 629 行(带默认收集器的包装),lib.rs 257 行。

feature

feature在哪些 crate打开什么
std(默认)全部依赖 std 的 API。channel 和 deque 的 Cargo.toml 注明「Disabling std feature is not supported yet」,关掉后它们几乎什么都不剩
alloc(std 隐含)epoch、queue、skiplist、门面需要分配但不需要 std 的 API:epoch 整体、ArrayQueue / SegQueue
nightlyutils、epoch、queue、门面三个子 crate 的 Cargo.toml 都写着「These features are no longer used」,留着只为兼容,下个大版本删除;门面的 nightly 只是转发
loomepoch(显式)、utils(可选依赖隐含)引入 loom 0.7 依赖;还要配合 --cfg crossbeam_loom 才会生效

cfg

除 feature 外,源码里还有五个 cfg 改变行为。它们不是 feature,由 RUSTFLAGS、构建脚本或编译器给出:

cfg谁打开改了什么
crossbeam_loom手动 RUSTFLAGS="--cfg crossbeam_loom"(加 loom feature)utils、epoch 的 mod primitive 换成 loom 原语;AtomicCell、ShardedLock、thread 不编译;门面不导出 scope(怎么验证并发代码)
crossbeam_sanitize手动,CI 的 sanitizer 和 loom 任务传入epoch 的袋子从 64 个对象降到 4 个、collect 不限步数;几个计时敏感的测试跳过
crossbeam_sanitize_threadutils、epoch、deque 的 build.rs:CARGO_CFG_SANITIZE 含 thread,或目标是 x86_64-unknown-linux-gnutsan「Relaxed + fence」改写成 TSan 能理解的 Acquire / Release 访问;load_consume 退回 Acquire
crossbeam_no_atomicutils 的 build.rs:目标在 no_atomic.rs 列出的 8 个之中(armv4t-none-eabi、msp430-none-elf、thumbv4t-none-eabi 等);也可手动设假定目标没有任何原子操作:utils 的 mod primitive 不再引入原子类型,AtomicConsume 没有实现。build.rs 注释说它是「公开但不稳定」的 API
miriMiri 自动设epoch 袋子降到 4、x86 的 CAS 代替 fence 关掉、load_consume 退回 Acquire

no_atomic.rs 是 ci/no_atomic.sh 生成的:用 rustc --print all-target-specs-json 找出 max-atomic-width 为 0、或者最小原子宽度不是 8 的目标。CI 的 codegen 任务每次重新生成一遍,git diff --exit-code 确认文件没过时。

本卷只在 aarch64 macOS 上用默认 feature 编译和运行,以上 cfg 都没有打开。

MSRV

README 写的是 1.61;各 crate 的 rust-version 是 1.60(utils、channel、queue)或 1.61(epoch、deque、skiplist、门面)。0.8.23 / 0.5.17 这一轮把政策从「支持半年内的 Rust」改成「支持一年内」(#1284),理由写的是和 rayon 的 MSRV 政策对齐。CI 的 test 矩阵最低测到 1.65(Linux)和 1.71(Windows),features 任务用 cargo hack --rust-version 按各 crate 声明的 MSRV 编译(不跑测试)。

低 MSRV 解释了 utils 和 epoch 为什么各自带一份 OnceLock(std 的要 1.70 才稳定)。另有几处注释比 MSRV 还旧:x86 上 pin 那段说「stable 上用不了内联汇编」(asm! 1.59 已稳定),AtomicCell 那段说「AtomicU8 在 stable 上不可用」。

小结

  • 五个发布 crate 加门面:utils 在底层,epoch 依赖 utils,deque 依赖 epoch,channel、queue 只依赖 utils;门面只做重导出,no_std / alloc / std 三档逐级放开。
  • skiplist 不在发布分支的工作区成员里,本卷用 tag crossbeam-skiplist-0.1.3,src/ 共 3792 行。
  • feature 只有 std、alloc、nightly(已无作用)、loom;真正改行为的是五个 cfg:crossbeam_loom、crossbeam_sanitize、crossbeam_sanitize_thread、crossbeam_no_atomic、miri。
  • MSRV 1.60 / 1.61,CI 测到 1.65 / 1.71。

💬 关联讨论

发起讨论
加载中...