4 个版本 (2 个稳定版)
1.0.1 | 2024年4月26日 |
---|---|
1.0.0 | 2024年4月23日 |
1.0.0-pre.1 | 2024年3月3日 |
#706 in 嵌入式开发
每月 226次下载
180KB
1.5K SLoC
lilos的handoff结构
实现了同步 rendezvous 结构,允许任务在不进行额外复制或预留存储空间的情况下将值传递给另一个任务。
这原本是 lilos
核心API的一部分,但在最终确定 lilos
1.0 版本的过程中被提取出来。它目前与 lilos
分离,因为它的API不是取消安全的。
尽管不是取消安全的,但仍然非常有用。请参阅模块文档以获取更多详细信息。
lib.rs
:
从一项任务向另一项任务传递数据的最小化复制机制。
这个crate为 lilos
提供了 Handoff
抽象。
Handoff
有两个方面:发送者和接收者。当发送者和接收者都准备好时,一个单一的 T
会从发送者的所有权传递到接收者。在这种情况下,“准备好”意味着发送者或接收者已经因为等待对方而被阻塞,当对方到达时——当两个任务都在handoff处等待时,我们可以复制数据然后解除两个任务的阻塞。
因为我们不需要任何 T
的复制区域,所以 Handoff
非常小——大约是两个指针的大小。
在计算机科学中,这被称为 rendezvous,但拼写起来比handoff困难。
创建和使用 Handoff
Handoff
本身不包含任何存储,因此它们在栈上创建很便宜。然后你需要将它们 split
成它们的 Pusher
和 Popper
端——这两个都 borrow 了 Handoff
,所以你需要保留它。然后你可以将端点传递给其他future。一个典型的用例如下
let mut handoff = Handoff::new();
let (push, pop) = handoff.split();
join!(data_producer(push), data_consumer(pop));
如果您只想在 rendezvous 点同步两个任务,且不需要移动数据,请使用 Handoff<()>
。它能够正确地完成工作。
注意事项和替代方案
一次只能存在一个 Pusher
和 Popper
-- 编译器确保了这一点。这极大地简化了实现过程,但这也意味着如果您需要多方的 rendezvous,这不是合适的工具。
如果您希望能够推送数据并继续处理,而不必等待其被弹出,您需要一个队列而不是 handoff。请参阅 lilos::spsc
模块。
请注意,这些类型都不是 Send
或 Sync
-- 它们绝对不是线程安全的,因此可以在 async
任务中自由使用,但不能与中断处理程序共享。出于同样的原因,您可能不想尝试将其存储在 static
中 -- 即使使用足够的 unsafe
,结果也将无济于事!在 spsc
中提供的队列没有这个限制,但代价是设置起来更麻烦。
取消安全性
Handoff
并非严格取消安全,与 lilos
中的大多数内容不同。具体来说,在 push
或 pop
future 解决之前取消它们,最多会导致丢失一个数据项。
虽然技术上是非取消安全的,但由于实际使用 handoff 的方式,这通常是可以接受的。请仔细阅读 Pusher::push
和 Popper::pop
的文档,否则您可能会丢失数据。
如果 handoff 的推送和弹出端是“长期存在”的,由不会取消的任务(如 lilos
中的顶层任务)持有,并且永远不会在可能会取消未来的上下文中使用(如 with_timeout
),那么您不需要担心这个问题。尽管这并不是编译器可以检查的性质,所以再次提醒 -- 小心行事。
依赖关系
~1–1.5MB
~24K SLoC