#实时 #异步 #操作系统

lilos-watch

为 lilos 提供低成本的共享和变更通知

1 个不稳定版本

0.1.0 2024 年 5 月 5 日

2032嵌入式开发

MPL-2.0 许可证

180KB
1.5K SLoC

lilos 提供低成本的价值共享和变更通知

该软件包提供了 Watch<T> 数据结构,用于带有更新通知的数据共享。有关详细信息,请参阅软件包文档。


lib.rs:

一个简单的机制,用于共享一块数据并在它发生变化时接收通知。

此模块提供了 Watch<T>,这是一个数据结构,允许您共享类型为 T 的一些数据,从多个生产者那里更新它,并在它发生变化时高效地通知多个消费者。

Watch<T> 对于配置数据等特别有用,这些数据实际上是“全局”的(由许多任务共享),但可能需要特殊处理变更。

let shared_number = Watch::new(1234_u32);

您可以通过调用 subscribe 创建一个对 Watch<T>接收句柄。这将生成一个 Receiver<T>,允许其持有者检查共享数据,并在它发生变化时接收通知。

let rcvr = shared_number.subscribe();
// A receiver only tracks changes made _after_ it's created:
assert_eq!(rcvr.is_changed(), false);

您可以通过调用 sender 创建一个对 Watch<T>发送句柄。这将生成一个 Sender<T>,允许其持有者更新共享数据。

let sender = shared_number.sender();

// Update the shared data:
sender.send(4567);

// Now the receiver sees a change:
assert_eq!(rcvr.is_changed(), true);

// We can inspect it and mark it as seen:
rcvr.glimpse_and_update(|value| assert_eq!(*value, 4567));

希望监控数据变更的代码可以使用 Receiver::changed future 来实现

loop {
    rcvr.changed().await;
    rcvr.glimpse_and_update(|value| process(value));
}

重入性和恐慌

如果努力足够,可以尝试重新进入使用单个Watch<T>的句柄。实现会检查这种情况并引发恐慌。例如,尝试从传递给Receiver::glimpse的闭包的内部发送新值。

let shared = Watch::new(some_data);
let sender = shared.sender();
let rcvr = shared.subscribe();

// This line will panic at runtime.
rcvr.glimpse(|contents| sender.send(*contents));

在其中一个闭包内部发送或检查一个不同的 Watch<T>实例是绝对安全的,只是不能是同一个。

实际上,这样做几乎是不可能的,但现在你知道了。

实现

具体来说,Watch<T>包含一个更改计数。每次其内容通过任何Sender更新时,更改计数都会增加。

每个Receiver都有一个更改计数的副本,反映了它在最后一次访问共享数据时的计数。如果存储在Watch中的更改计数与存储在Receiver中的不同,那么有一个更新还没有被其所有者看到。

由于Watch<T>只存储数据的单个副本和一个计数器,因此Receiver可能无法看到数据的每个更新。如果在检查之间发生多个更新,则Receiver将始终只看到最后一个更新。这既降低了存储要求,也降低了更新的成本。

Watch<T>内部包含一个Notify,它使用它来有效地唤醒正在等待Receiver::changed的任务。

依赖项

~2MB
~42K SLoC