Coder Social home page Coder Social logo

event-manager's Introduction

event-manager

crates.io docs.rs

The event-manager provides abstractions for implementing event based systems. For now, this crate only works on Linux and uses the epoll API to provide a mechanism for handling I/O notifications.

Design

This crate is built around two abstractions:

  • Event Manager
  • Event Subscriber

The subscriber defines and registers an interest list with the event manager. The interest list represents the events that the subscriber wants to monitor.

The Event Manager allows adding and removing subscribers, and provides APIs through which the subscribers can be updated in terms of events in their interest list. These actions are abstracted through the SubscriberOps trait.

To interface with the Event Manager, the Event Subscribers need to provide an initialization function, and a callback for when events in the interest list become ready. The subscribers can update their interest list when handling ready events. These actions are abstracted through the EventSubscriber and MutEventSubscriber traits. They contain the same methods, but the former only requires immutable self borrows, whereas the latter requires mutable borrows. Any type implementing EventSubscriber automatically implements MutEventSubscriber as well.

A typical event-based application creates the event manager, registers subscribers, and then calls into the event manager's run function in a loop. Behind the scenes, the event manager calls into epoll::wait and maps the file descriptors in the ready list to the subscribers it manages. The event manager calls the subscriber's process function (its registered callback). When dispatching the events, the event manager creates a specialized object and passes it in the callback function so that the subscribers can use it to alter their interest list.

Read more in the design document.

Implementing an Event Subscriber

The event subscriber has full control over the events that it monitors. The events need to be added to the event manager's loop as part of the init function. Adding events to the loop can return errors, and it is the responsibility of the subscriber to handle them.

Similarly, the event subscriber is in full control of the ready events. When an event becomes ready, the event manager will call into the subscriber process function. The subscriber SHOULD handle the following events which are always returned when they occur (they don't need to be registered):

  • EventSet::ERROR - an error occurred on the monitor file descriptor.
  • EventSet::HANG_UP - hang up happened on the associated fd.
  • EventSet::READ_HANG_UP - hang up when the registered event is edge triggered.

For more details about the error cases, you can check the epoll_ctl documentation.

Initializing the Event Manager

The EventManager uses a generic type parameter which represents the subscriber type. The crate provides automatic implementations of EventSubscriber for Arc<T> and Rc<T> (for any T: EventSubscriber +?Sized), together with automatic implementations of MutEventSubscriber for Mutex<T> and RefCell<T> (for any T: MutEventSubscriber + ?Sized). The generic type parameter enables either static or dynamic dispatch.

This crate has no default features. The optional remote_endpoint feature enables interactions with the EventManager from different threads without the need of more intrusive synchronization.

Examples

For closer to real life use cases, please check the examples in tests.

Basic Single Thread Subscriber

Implementing a Basic Subscriber

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.");
    }
}

Adding Subscribers to the Event Manager

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);
    }
}

Development and Testing

The event-manager is tested using unit tests, Rust integration tests and performance benchmarks. It leverages rust-vmm-ci for continuous testing. All tests are run in the rustvmm/dev container.

More details on running the tests can be found in the development document.

License

This project is licensed under either of:

event-manager's People

Contributors

00xc avatar aghecenco avatar alexandruag avatar andreeaflorescu avatar catdum avatar dependabot-preview[bot] avatar dependabot[bot] avatar jiangliu avatar jkshtj avatar jonathanwoollett-light avatar karthiknedunchezhiyan avatar kzys avatar lauralt avatar roypat avatar studychao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

event-manager's Issues

Add a mechanism for logs/metrics

We should add a mechanism to log certain events and potentially record metrics as well. One possibility is to use a callback-based approach such as this proposal. Another option is to directly leverage the log crate.

patch release

As 0.1.0 version of this crate has crash possible cases and also solution merged in master, can we make a patch release?

Event Manager Example & Usage Patterns

Add end-to-end usage patterns for the EventManager. The examples can live in docs/ and they are different from the typical public function examples.

The examples should show how to use the Event Manager for:

  • single threaded apps
  • multi-threaded apps
  • other usage patterns we might have in mind

Doc improvements

Include in the documentation/design that fds can be registered only once.

Cannot Run Example

Hello, I'm having trouble running the example from the readme.

Running with cargo run --release --verbose

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.");
    }
}

gives:

error[E0433]: failed to resolve: could not find `unix` in `os`
 --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\epoll.rs:5:14
  |
5 | use std::os::unix::io::RawFd;
  |              ^^^^ could not find `unix` in `os`

error[E0432]: unresolved import `vmm_sys_util::epoll`
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\lib.rs:16:23
   |
16 | pub use vmm_sys_util::epoll::EventSet;
   |                       ^^^^^ could not find `epoll` in `vmm_sys_util`

error[E0433]: failed to resolve: could not find `unix` in `os`
 --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:4:14
  |
4 | use std::os::unix::io::{AsRawFd, RawFd};
  |              ^^^^ could not find `unix` in `os`

error[E0432]: unresolved import `vmm_sys_util::epoll`
 --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\epoll.rs:7:19
  |
7 | use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent};
  |                   ^^^^^ could not find `epoll` in `vmm_sys_util`

error[E0432]: unresolved import `vmm_sys_util::epoll`
 --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:8:19
  |
8 | use vmm_sys_util::epoll::{ControlOperation, EpollEvent, EventSet};
  |                   ^^^^^ could not find `epoll` in `vmm_sys_util`

error[E0432]: unresolved import `vmm_sys_util::epoll`
 --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\manager.rs:6:19
  |
6 | use vmm_sys_util::epoll::EpollEvent;
  |                   ^^^^^ could not find `epoll` in `vmm_sys_util`

error[E0412]: cannot find type `RawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\epoll.rs:17:37
   |
12 | pub(crate) struct EpollWrapper {
   |                               - help: you might be missing a type parameter: `<RawFd>`
...
17 |     pub(crate) fd_dispatch: HashMap<RawFd, SubscriberId>,
   |                                     ^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\epoll.rs:20:65
   |
12 | pub(crate) struct EpollWrapper {
   |                               - help: you might be missing a type parameter: `<RawFd>`
...
20 |     pub(crate) subscriber_watch_list: HashMap<SubscriberId, Vec<RawFd>>,
   |                                                                 ^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\epoll.rs:67:47
   |
67 |     pub(crate) fn remove_event(&mut self, fd: RawFd) {
   |                                               ^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\epoll.rs:80:44
   |
80 |     pub(crate) fn subscriber_id(&self, fd: RawFd) -> Option<SubscriberId> {
   |                                            ^^^^^ not found in this scope

error[E0405]: cannot find trait `AsRawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:55:21
   |
55 |     pub fn empty<T: AsRawFd>(source: &T) -> Self {
   |                     ^^^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:75:26
   |
75 |     pub fn empty_raw(fd: RawFd) -> Self {
   |                          ^^^^^ not found in this scope

error[E0405]: cannot find trait `AsRawFd` in this scope
  --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:98:19
   |
98 |     pub fn new<T: AsRawFd>(source: &T, events: EventSet) -> Self {
   |                   ^^^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
   --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:121:28
    |
121 |     pub fn new_raw(source: RawFd, events: EventSet) -> Self {
    |                            ^^^^^ not found in this scope

error[E0405]: cannot find trait `AsRawFd` in this scope
   --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:147:25
    |
147 |     pub fn with_data<T: AsRawFd>(source: &T, data: u32, events: EventSet) -> Self {
    |                         ^^^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
   --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:173:34
    |
173 |     pub fn with_data_raw(source: RawFd, data: u32, events: EventSet) -> Self {
    |                                  ^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
   --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:181:25
    |
181 |     pub fn fd(&self) -> RawFd {
    |                         ^^^^^ not found in this scope

error[E0412]: cannot find type `RawFd` in this scope
   --> C:\Users\Primary User\.cargo\registry\src\github.com-1ecc6299db9ec823\event-manager-0.2.1\src\events.rs:182:30
    |
182 |         self.inner.data() as RawFd
    |                              ^^^^^ not found in this scope

error: aborting due to 18 previous errors

Some errors have detailed explanations: E0405, E0412, E0432, E0433.
For more information about an error, try `rustc --explain E0405`.
error: could not compile `event-manager`

OS: Win10
rustup show:

installed toolchains
--------------------

stable-x86_64-pc-windows-gnu
stable-x86_64-pc-windows-msvc
nightly-x86_64-pc-windows-msvc

active toolchain
----------------

stable-x86_64-pc-windows-msvc (default)
rustc 1.52.1 (9bc8c42bb 2021-05-09)

Is this a unix-only crate?

Check that new_with_capacity accepts only values >=0 and <= EP_MAX_EVENTS

Update the new_with_capacity function such that it only accepts values >= 0 (this should be implied by the type of ready_events_capacity being an usize) and <= EP_MAX_EVENTS. In the Linux kernel, EP_MAX_EVENTS is defined as INT_MAX / sizeof(struct epoll_event)): https://elixir.bootlin.com/linux/latest/source/fs/eventpoll.c#L101

Add a test to check that initializing the event manager with a size that is bigger than EP_MAX_EVENTS returns an error.

feature: support for add/remove single flag in event set

It would be helpful if the event-manager support add/remove individual flag from the existing one (instead of whole) or support for getting previously registered flags.

Some use case is we send more data via vsock (async mode) so, we need to add EventSet::OUT flag additionally whenever we experience WouldBlock error.

manager: support for specifing event capacity

In my project, I need to handle very few events in a single loop so, adding support for specifying event capacity while creating event-manager object will solve my use-case without additional handling.

feature: provide some sort of mock "EventOps" for unit-testing

The fn process(&self, events: Events, ops: &mut EventOps) function of trait (Mut)EventSubscriber contains the core event-handling logic of any user or this crate.

It would be nice to be able to unit-test it and cover that critical functionality.

Currently, the EventOps reference prevents any outside/test calls into process() because EventOps doesn't have any public constructors, and such objects are only built by event-manager crate-internal code.

I don't have a clear idea right now on what would be the best way to allow unit-testing process(); the real EventOps object also contains other internal/non-public members so we can't just stick a public constructor and be done with it...

Any ideas? 😃

Add unit tests

Add unit tests for the uncovered code paths. TBD scoping.

Performance Tests

Add integration tests to check the performance of Event manager. The performance of handling events is on the critical path as callbacks are called when handling emulation and can impact the emulation performance.

Alternatively, we can use cargo bench.

This task probably required changes to the rust-vmm-ci as well. Should be further broke down after more scoping.

Doc updates

Include in the documentation/design that fds can be registered only once.

Gracefully handle EINTR

Right now run return libc::EINTR error. Maybe we should catch it so that not all users of the crate have to do the same error handling.

race condition causing false event callback

I have an fd (say with number 10) in the dispatch queue. before fd 10 dispatched I have dropped fd 10 (also removed via EventOps::remove) and create a new eventfd and add it via EventOps::add. There is a high chance that new eventfd will receive the same fd number 10 as kernel always assigns a lowest possible number.

In the above scenario, even though my new eventfd has not got any event, my subscriber process callback will be triggered.

quick sample code to reproduce above race condition: https://gist.github.com/KarthikNedunchezhiyan/39fbdd3eee4b97ab540bf3cb16b21313

above code produces the output like below

added fd 4
added fd 5
event received for subscriber uuid 1
removed fd 4
removed fd 5
added fd 4
added fd 5
event received for subscriber uuid 2

Even though I have not produced the event to the subscriber uuid 2, It is getting called.

Add mechanism to manually inject events

Seeing how the EventManager interface completely hides event handling from the EventManager user, it is very difficult to externally/artificially trigger an event for a given Subscriber.

A mechanism to manually/artificially inject events for a given Subscriber would be useful in the case of restoring from snapshot or live migration where post migration you'd want to artificially kick all devices.

Update License

Right now just the Apache License is specified. Add the BSD-3-Clause as well.

Cargo.toml should also be updated.

Use dependency from crates.io not github

Currently, event-manager takes a dependency on vmm-sys-util using GitHub:

vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }

Before we publish event-manager on crates.io we should also update the dependency to use crates.io as the source.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.