4 个版本 (2 个重大更新)

0.4.0 2024 年 1 月 3 日
0.3.0 2022 年 11 月 21 日
0.2.1 2021 年 2 月 25 日
0.2.0 2020 年 9 月 25 日
0.1.0 2020 年 8 月 12 日

#54 in Unix API

Download history 16933/week @ 2024-03-14 10559/week @ 2024-03-21 8575/week @ 2024-03-28 10803/week @ 2024-04-04 5790/week @ 2024-04-11 9548/week @ 2024-04-18 8992/week @ 2024-04-25 8239/week @ 2024-05-02 7875/week @ 2024-05-09 7322/week @ 2024-05-16 7102/week @ 2024-05-23 9029/week @ 2024-05-30 11501/week @ 2024-06-06 11019/week @ 2024-06-13 4007/week @ 2024-06-20 3155/week @ 2024-06-27

31,583 每月下载量
用于 4 个 crate(通过 dbs-utils

Apache-2.0 OR BSD-3-Clause

95KB
1K SLoC

event-manager

crates.io docs.rs

event-manager 为实现基于事件的系统提供抽象。目前,此 crate 仅适用于 Linux,并使用 epoll API 提供处理 I/O 通知的机制。

设计

此 crate 以两个抽象为基础构建

  • 事件管理器
  • 事件订阅者

订阅者定义并注册一个兴趣列表给事件管理器。兴趣列表表示订阅者想要监控的事件。

事件管理器允许添加和删除订阅者,并通过 API 提供更新订阅者兴趣列表的接口。这些操作通过 SubscriberOps 特性进行抽象。

为了与事件管理器接口,事件订阅者需要提供一个初始化函数和一个当兴趣列表中的事件就绪时的回调。在处理就绪事件时,订阅者可以更新他们的兴趣列表。这些操作通过 EventSubscriberMutEventSubscriber 特性进行抽象。它们包含相同的方法,但前者只需要不可变 self 借用,而后者需要可变借用。任何实现 EventSubscriber 的类型也会自动实现 MutEventSubscriber

典型的基于事件的应用创建事件管理器,注册订阅者,然后在循环中调用事件管理器的 run 函数。幕后,事件管理器调用 epoll::wait 并将就绪列表中的文件描述符映射到其管理的订阅者。事件管理器调用订阅者的 process 函数(其已注册的回调)。在分发事件时,事件管理器创建一个专用对象并将其传递给回调函数,以便订阅者可以使用它来修改其兴趣列表。

更多详情请参阅 设计文档

实现事件订阅者

事件订阅者对其监控的事件拥有完全控制权。事件需要作为 init 函数的一部分添加到事件管理器的循环中。将事件添加到循环可能会返回错误,并且订阅者有责任处理这些错误。

同样,事件订阅者完全控制就绪事件。当事件就绪时,事件管理器将调用订阅者的 process 函数。订阅者应该处理以下事件,这些事件在发生时总是返回(它们不需要注册)

  • EventSet::ERROR - 监视文件描述符上发生了错误。
  • EventSet::HANG_UP - 关联的文件描述符发生了挂起。
  • EventSet::READ_HANG_UP - 在注册的事件是边缘触发时发生挂起。

有关错误情况的更多详细信息,您可以查看 epoll_ctl 文档

初始化事件管理器

EventManager 使用一个泛型类型参数,表示订阅者类型。该包为 Arc<T>Rc<T>(对于任何 T: EventSubscriber +?Sized)提供了自动实现,同时为 Mutex<T>RefCell<T>(对于任何 T: MutEventSubscriber + ?Sized)提供了自动实现。泛型类型参数允许静态或动态分发。

此包没有默认功能。可选的 remote_endpoint 功能允许在不需要更多侵入式同步的情况下从不同的线程与 EventManager 交互。

示例

有关更接近现实世界的用例,请查看 测试中的示例

基本单线程订阅者

实现基本订阅者

use event_manager::{EventOps, Events, MutEventSubscriber};
use vmm_sys_util::{eventfd::EventFd, epoll::EventSet};

use std::os::unix::io::AsRawFd;
use std::fmt::{Display, Formatter, Result};

pub struct CounterSubscriber {
    event_fd: EventFd,
    counter: u64,
}

impl CounterSubscriber {
    pub fn new() -> Self {
        Self {
            event_fd: EventFd::new(0).unwrap(),
            counter: 0,
        }
    }
}

impl MutEventSubscriber for CounterSubscriber {
    fn process(&mut self, events: Events, event_ops: &mut EventOps) {
        match events.event_set() {
            EventSet::IN => {
                self.counter += 1;
            }
            EventSet::ERROR => {
                eprintln!("Got error on the monitored event.");
            }
            EventSet::HANG_UP => {
                event_ops.remove(events).unwrap_or(
                    eprintln!("Encountered error during cleanup")
                );
                panic!("Cannot continue execution. Associated fd was closed.");
            }
            _ => {}
        }
    }

    fn init(&mut self, ops: &mut EventOps) {
        ops.add(Events::new(&self.event_fd, EventSet::IN)).expect("Cannot register event.");
    }
}

将订阅者添加到事件管理器

struct App {
    event_manager: EventManager<CounterSubscriber>,
    subscribers_id: Vec<SubscriberId>,
}

impl App {
    fn new() -> Self {
        Self {
            event_manager: EventManager::<CounterSubscriber>::new().unwrap(),
            subscribers_id: vec![]
        }
    }

    fn add_subscriber(&mut self) {
        let counter_subscriber = CounterSubscriber::default();
        let id = self.event_manager.add_subscriber(counter_subscriber);
        self.subscribers_id.push(id);
    }

    fn run(&mut self) {
        let _ = self.event_manager.run_with_timeout(100);
    }
}

开发和测试

event-manager 使用单元测试、Rust 集成测试和性能基准进行测试。它利用 rust-vmm-ci 进行持续测试。所有测试都在 rustvmm/dev 容器中运行。

有关运行测试的更多详细信息,请参阅 开发 文档。

许可协议

本项目采用以下任意一种许可协议:

依赖关系

~310KB