42个版本

0.14.0 2024年6月4日
0.13.0 2024年3月1日
0.12.4 2024年1月15日
0.12.3 2023年10月10日
0.4.2 2018年11月15日

#41 in 异步

Download history 72222/week @ 2024-05-03 79774/week @ 2024-05-10 78009/week @ 2024-05-17 76121/week @ 2024-05-24 86665/week @ 2024-05-31 90743/week @ 2024-06-07 92923/week @ 2024-06-14 90570/week @ 2024-06-21 83097/week @ 2024-06-28 88393/week @ 2024-07-05 97929/week @ 2024-07-12 114328/week @ 2024-07-19 108867/week @ 2024-07-26 103249/week @ 2024-08-02 127919/week @ 2024-08-09 108050/week @ 2024-08-16

468,070 每月下载量
用于 1,202 个crates (17 直接)

MIT 许可证

290KB
6K SLoC

crates.io docs.rs Coverage Status

calloop

Calloop,一个基于回调的事件循环

该crate提供了一种名为EventLoop的类型,它是在轮询系统上的一个小型抽象。与其他传统的Rust事件循环相比,此crate的主要区别在于它基于回调:您可以注册多个事件源,每个事件源都与一个回调闭包相关联,该闭包将在关联的事件源生成事件时被调用。

因此,此事件循环的主要用途是用于那些预期将大部分时间用于等待事件,并希望以便宜和方便的方式进行的应用程序。它不适合大规模高性能IO。

如何使用

以下是一个快速使用calloop的示例。有关更深入的教程,请参阅calloop book

对于简单的使用,您可以直接将事件源和回调添加到事件循环中。例如,以下是一个在五秒后退出的可运行程序

use calloop::{timer::{Timer, TimeoutAction}, EventLoop, LoopSignal};

fn main() {
    // Create the event loop. The loop is parameterised by the kind of shared
    // data you want the callbacks to use. In this case, we want to be able to
    // stop the loop when the timer fires, so we provide the loop with a
    // LoopSignal, which has the ability to stop the loop from within events. We
    // just annotate the type here; the actual data is provided later in the
    // run() call.
    let mut event_loop: EventLoop<LoopSignal> =
        EventLoop::try_new().expect("Failed to initialize the event loop!");

    // Retrieve a handle. It is used to insert new sources into the event loop
    // It can be cloned, allowing you to insert sources from within source
    // callbacks.
    let handle = event_loop.handle();

    // Create our event source, a timer, that will expire in 2 seconds
    let source = Timer::from_duration(std::time::Duration::from_secs(2));

    // Inserting an event source takes this general form. It can also be done
    // from within the callback of another event source.
    handle
        .insert_source(
            // a type which implements the EventSource trait
            source,
            // a callback that is invoked whenever this source generates an event
            |event, _metadata, shared_data| {
                // This callback is given 3 values:
                // - the event generated by the source (in our case, timer events are the Instant
                //   representing the deadline for which it has fired)
                // - &mut access to some metadata, specific to the event source (in our case, a
                //   timer handle)
                // - &mut access to the global shared data that was passed to EventLoop::run or
                //   EventLoop::dispatch (in our case, a LoopSignal object to stop the loop)
                //
                // The return type is just () because nothing uses it. Some
                // sources will expect a Result of some kind instead.
                println!("Timeout for {:?} expired!", event);
                // notify the event loop to stop running using the signal in the shared data
                // (see below)
                shared_data.stop();
                // The timer event source requires us to return a TimeoutAction to
                // specify if the timer should be rescheduled. In our case we just drop it.
                TimeoutAction::Drop
            },
        )
        .expect("Failed to insert event source!");

    // Create the shared data for our loop.
    let mut shared_data = event_loop.get_signal();

    // Actually run the event loop. This will dispatch received events to their
    // callbacks, waiting at most 20ms for new events between each invocation of
    // the provided callback (pass None for the timeout argument if you want to
    // wait indefinitely between events).
    //
    // This is where we pass the *value* of the shared data, as a mutable
    // reference that will be forwarded to all your callbacks, allowing them to
    // share some state
    event_loop
        .run(
            std::time::Duration::from_millis(20),
            &mut shared_data,
            |_shared_data| {
                // Finally, this is where you can insert the processing you need
                // to do do between each waiting event eg. drawing logic if
                // you're doing a GUI app.
            },
        )
        .expect("Error during event loop!");
}

事件源类型

事件循环由操作系统提供的轮询选择器(Linux上的epoll)支持。

此crate还提供了一些常见事件源的适配器,例如

  • MPSC通道
  • 定时器
  • Linux上的Unix信号

以及由文件描述符支持的通用对象。

还可以插入“空闲”回调。这些回调代表需要在某个时候执行的计算,但不如处理事件紧急。这些回调被存储,然后在EventLoop::dispatch期间执行,一旦处理完所有来自源的事件。

Async/Await兼容性

calloop可以与futures一起使用,既可以用作执行器,也可以用于监控异步IO。

激活 executor 货物功能将添加 futures 模块,该模块提供了一个可以将作为另一个 EventSource 插入到 EventLoop 的未来执行器。

可以通过 LoopHandle::adapt_io 方法使 IO 对象异步感知。使用这些对象唤醒未来的处理由相关的 EventLoop 直接处理。

自定义事件源

您可以通过实现 EventSource 特性来创建自定义事件源,并将其插入事件循环。这可以通过直接从感兴趣源的文件描述符执行,或者通过包装其他事件源并进一步处理其事件来实现。一个 EventSource 可以注册多个文件描述符并将它们聚合。

平台支持

目前,calloop 在 Linux、FreeBSD 和 macOS 上进行了测试。

以下平台在编译时也启用了,但未进行测试:Android、NetBSD、OpenBSD、DragonFlyBSD。

基于它们与测试平台具有相同的轮询机制的事实,这些平台 应该 能够正常工作,但仍可能存在一些细微的错误。

最小安全 Rust 版本

此存储库当前的最小安全 Rust 版本 (MSRV) 是 Rust 1.63。然而,当启用 signals 功能时,它将升级到 nix 的 MSRV。在撰写本文时,这是 Rust 1.69

calloop v1.0 之前,MSRV 的升级将导致修订版本的升级。一旦发布 calloop v1.0,MSRV 的升级将导致次要版本的升级。简而言之,MSRV 的升级不被视为破坏性更改。

作为一个 暂定的 策略,基本 MSRV 不会超过由 Debian Stable 提供的当前 Rust 版本[链接]。在撰写本文时,此 Rust 版本为 1.63。然而,在主要生态系统发生重大变化或存在安全漏洞的情况下,MSRV 可能会进一步升级。

许可证:MIT

依赖项

~2–11MB
~128K SLoC