From 61e3b3db2ed0bee56d383582592e7c91c2df4342 Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Wed, 14 Jun 2023 16:22:51 +0100 Subject: [PATCH 1/3] feat: Procedural overhaul Signed-off-by: Jonathan Woollett-Light --- .buildkite/custom-tests.json | 24 -- Cargo.toml | 18 +- README.md | 179 ++--------- benches/main.rs | 300 ++++++++++++++---- coverage_config_aarch64.json | 6 +- coverage_config_x86_64.json | 6 +- docs/DESIGN.md | 148 +-------- docs/DEVELOPMENT.md | 29 +- docs/event-manager.png | Bin 68017 -> 0 bytes rust-vmm-ci | 2 +- src/endpoint.rs | 187 ------------ src/epoll.rs | 89 ------ src/events.rs | 322 -------------------- src/lib.rs | 570 +++++++++++++++++++++-------------- src/manager.rs | 483 ----------------------------- src/subscribers.rs | 54 ---- src/utilities/mod.rs | 18 -- src/utilities/subscribers.rs | 328 -------------------- tests/basic_event_manager.rs | 111 ------- tests/endpoint.rs | 143 --------- tests/multi_threaded.rs | 150 --------- tests/negative_tests.rs | 105 ------- tests/regressions.rs | 24 -- 23 files changed, 626 insertions(+), 2670 deletions(-) delete mode 100644 docs/event-manager.png delete mode 100644 src/endpoint.rs delete mode 100644 src/epoll.rs delete mode 100644 src/events.rs delete mode 100644 src/manager.rs delete mode 100644 src/subscribers.rs delete mode 100644 src/utilities/mod.rs delete mode 100644 src/utilities/subscribers.rs delete mode 100644 tests/basic_event_manager.rs delete mode 100644 tests/endpoint.rs delete mode 100644 tests/multi_threaded.rs delete mode 100644 tests/negative_tests.rs delete mode 100644 tests/regressions.rs diff --git a/.buildkite/custom-tests.json b/.buildkite/custom-tests.json index 40da081..468dfbf 100644 --- a/.buildkite/custom-tests.json +++ b/.buildkite/custom-tests.json @@ -1,29 +1,5 @@ { "tests": [ - { - "test_name": "build-gnu-remote_endpoint", - "command": "cargo build --release --features=remote_endpoint", - "platform": [ - "x86_64", - "aarch64" - ] - }, - { - "test_name": "build-musl-remote_endpoint", - "command": "cargo build --release --features=remote_endpoint --target {target_platform}-unknown-linux-musl", - "platform": [ - "x86_64", - "aarch64" - ] - }, - { - "test_name": "check-warnings-remote_endpoint", - "command": "RUSTFLAGS=\"-D warnings\" cargo check --features=remote_endpoint", - "platform": [ - "x86_64", - "aarch64" - ] - }, { "test_name": "performance", "command": "pytest -s rust-vmm-ci/integration_tests/test_benchmark.py", diff --git a/Cargo.toml b/Cargo.toml index c892e13..9556b4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,22 +11,12 @@ edition = "2021" [dependencies] vmm-sys-util = "0.11.0" -libc = "0.2.39" +libc = { version = "0.2.39", features = ["extra_traits"] } [dev-dependencies] -criterion = "0.3.5" - -[features] -remote_endpoint = [] -test_utilities = [] +criterion = "0.5.1" +rand = "0.8.5" [[bench]] name = "main" -harness = false - -[lib] -bench = false # https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options - -[profile.bench] -lto = true -codegen-units = 1 +harness = false \ No newline at end of file diff --git a/README.md b/README.md index 375d36a..9eb7a10 100644 --- a/README.md +++ b/README.md @@ -10,160 +10,43 @@ 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. - -![](docs/event-manager.png) +This crate offers an abstraction (`EventManager`) over [epoll](https://man7.org/linux/man-pages/man7/epoll.7.html) that +allows for more ergonomic usage with many file descriptors. + +The `EventManager` allows adding and removing file descriptors with a callback closure. The +`EventManager` interest list can also be modified within these callback closures. + +A typical event-based application: + +1. Creates the `EventManager` (`EventManager::default()`). +2. Registers file descriptors with (`EventManager::add`). +3. Calls `EventManager::wait` in a loop. Read more in the [design document](docs/DESIGN.md). -## 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`](https://www.man7.org/linux/man-pages/man2/epoll_ctl.2.html). - - -## 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` and `Rc` (for any `T: EventSubscriber +?Sized`), -together with automatic implementations of `MutEventSubscriber` for `Mutex` -and `RefCell` (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](tests). - -### Basic Single Thread Subscriber - -#### Implementing a Basic Subscriber - -```rust -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 - -```rust -struct App { - event_manager: EventManager, - subscribers_id: Vec, -} - -impl App { - fn new() -> Self { - Self { - event_manager: EventManager::::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); - } -} -``` +## Implementing an event + +Like `epoll` a file descriptor only monitors specific events. + +The events ars specified when calling `EventManager::add` with `vmm_sys_util::epoll::EventSet`. + +When an event becomes ready, the event manager will call the file descriptors callback closure. + +The `epoll` events `EPOLLRDHUP`, `EPOLLERR` and `EPOLLHUP` (which correspond to +`EventSet::READ_HANG_UP`, `EventSet::ERROR` and `EventSet::HANG_UP` respectively) are documented to +always report, even when not specified by the user. + +> epoll_wait(2) will always report for this event; it is not +> necessary to set it in events when calling epoll_ctl(). + +*https://man7.org/linux/man-pages/man2/epoll_ctl.2.html* + +As such it is best practice to always handle the cases where the `EventSet` passed to the file +descriptor callback closure is `EventSet::READ_HANG_UP`, `EventSet::ERROR` or `EventSet::HANG_UP`. ## Development and Testing -The `event-manager` is tested using unit tests, Rust integration tests and -performance benchmarks. It leverages -[`rust-vmm-ci`](https://github.com/rust-vmm/rust-vmm-ci) for continuous +`event-manager` uses [`rust-vmm-ci`](https://github.com/rust-vmm/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 diff --git a/benches/main.rs b/benches/main.rs index a845621..9761f87 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -3,71 +3,154 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use event_manager::utilities::subscribers::{ - CounterInnerMutSubscriber, CounterSubscriber, CounterSubscriberWithData, -}; -use event_manager::{EventManager, EventSubscriber, MutEventSubscriber, SubscriberOps}; +use event_manager::{BufferedEventManager, EventManager}; +use std::os::fd::AsFd; +use std::os::fd::FromRawFd; +use std::os::fd::OwnedFd; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; +use vmm_sys_util::epoll::EventSet; // Test the performance of event manager when it manages a single subscriber type. // The performance is assessed under stress, all added subscribers have active events. fn run_basic_subscriber(c: &mut Criterion) { - let no_of_subscribers = 200; + let no_of_subscribers = 200i32; - let mut event_manager = EventManager::::new().unwrap(); - for _ in 0..no_of_subscribers { - let mut counter_subscriber = CounterSubscriber::default(); - counter_subscriber.trigger_event(); - event_manager.add_subscriber(counter_subscriber); - } + let mut event_manager = + BufferedEventManager::with_capacity(false, no_of_subscribers as usize).unwrap(); + + let subscribers = (0..no_of_subscribers).map(|_| { + // Create an eventfd that is initialized with 1 waiting event. + // SAFETY: Always safe. + let event_fd = unsafe { + let raw_fd = libc::eventfd(1,0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }; + + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + match event_set { + EventSet::IN => (), + EventSet::ERROR => { + eprintln!("Got error on the monitored event."); + }, + EventSet::HANG_UP => { + panic!("Cannot continue execution. Associated fd was closed."); + }, + _ => { + eprintln!("Received spurious event from the event manager {event_set:#?}."); + } + } + })).unwrap(); + + event_fd + }).collect::>(); c.bench_function("process_basic", |b| { b.iter(|| { - let ev_count = event_manager.run().unwrap(); - assert_eq!(ev_count, no_of_subscribers) + assert_eq!(event_manager.wait(Some(0)), Ok(no_of_subscribers)); }) }); + + drop(subscribers); } // Test the performance of event manager when the subscribers are wrapped in an Arc. // The performance is assessed under stress, all added subscribers have active events. fn run_arc_mutex_subscriber(c: &mut Criterion) { - let no_of_subscribers = 200; + let no_of_subscribers = 200i32; + + let mut event_manager = + BufferedEventManager::with_capacity(false, no_of_subscribers as usize).unwrap(); + + let subscribers = (0..no_of_subscribers).map(|_| { + // Create an eventfd that is initialized with 1 waiting event. + // SAFETY: Always safe. + let event_fd = unsafe { + let raw_fd = libc::eventfd(1,0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }; + let counter = Arc::new(Mutex::new(0u64)); + let counter_clone = counter.clone(); + + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + match event_set { + EventSet::IN => { + *counter_clone.lock().unwrap() += 1; + }, + EventSet::ERROR => { + eprintln!("Got error on the monitored event."); + }, + EventSet::HANG_UP => { + panic!("Cannot continue execution. Associated fd was closed."); + }, + _ => { + eprintln!("Received spurious event from the event manager {event_set:#?}."); + } + } + })).unwrap(); - let mut event_manager = EventManager::>>::new().unwrap(); - for _ in 0..no_of_subscribers { - let counter_subscriber = Arc::new(Mutex::new(CounterSubscriber::default())); - counter_subscriber.lock().unwrap().trigger_event(); - event_manager.add_subscriber(counter_subscriber); - } + (event_fd,counter) + }).collect::>(); c.bench_function("process_with_arc_mutex", |b| { b.iter(|| { - let ev_count = event_manager.run().unwrap(); - assert_eq!(ev_count, no_of_subscribers) + assert_eq!(event_manager.wait(Some(0)), Ok(no_of_subscribers)); }) }); + + drop(subscribers); } // Test the performance of event manager when the subscribers are wrapped in an Arc, and they // leverage inner mutability to update their internal state. // The performance is assessed under stress, all added subscribers have active events. fn run_subscriber_with_inner_mut(c: &mut Criterion) { - let no_of_subscribers = 200; + let no_of_subscribers = 200i32; + + let mut event_manager = + BufferedEventManager::with_capacity(false, no_of_subscribers as usize).unwrap(); + + let subscribers = (0..no_of_subscribers).map(|_| { + // Create an eventfd that is initialized with 1 waiting event. + // SAFETY: Always safe. + let event_fd = unsafe { + let raw_fd = libc::eventfd(1,0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }; + let counter = Arc::new(AtomicU64::new(0)); + let counter_clone = counter.clone(); + + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + match event_set { + EventSet::IN => { + counter_clone.fetch_add(1, Ordering::SeqCst); + }, + EventSet::ERROR => { + eprintln!("Got error on the monitored event."); + }, + EventSet::HANG_UP => { + panic!("Cannot continue execution. Associated fd was closed."); + }, + _ => { + eprintln!("Received spurious event from the event manager {event_set:#?}."); + } + } + })).unwrap(); - let mut event_manager = EventManager::>::new().unwrap(); - for _ in 0..no_of_subscribers { - let counter_subscriber = CounterInnerMutSubscriber::default(); - counter_subscriber.trigger_event(); - event_manager.add_subscriber(Arc::new(counter_subscriber)); - } + (event_fd,counter) + }).collect::>(); c.bench_function("process_with_inner_mut", |b| { b.iter(|| { - let ev_count = event_manager.run().unwrap(); - assert_eq!(ev_count, no_of_subscribers) + assert_eq!(event_manager.wait(Some(0)), Ok(no_of_subscribers)); }) }); + + drop(subscribers); } // Test the performance of event manager when it manages subscribers of different types, that are @@ -76,63 +159,152 @@ fn run_subscriber_with_inner_mut(c: &mut Criterion) { // The performance is assessed under stress, all added subscribers have active events, and the // CounterSubscriberWithData subscribers have multiple active events. fn run_multiple_subscriber_types(c: &mut Criterion) { - let no_of_subscribers = 100; + let no_of_subscribers = 100i32; + + let total = no_of_subscribers + (no_of_subscribers * i32::try_from(EVENTS).unwrap()); + + let mut event_manager = + BufferedEventManager::with_capacity(false, usize::try_from(total).unwrap()).unwrap(); - let mut event_manager = EventManager::>>::new() - .expect("Cannot create event manager."); + let subscribers = (0..no_of_subscribers).map(|_| { + // Create an eventfd that is initialized with 1 waiting event. + // SAFETY: Always safe. + let event_fd = unsafe { + let raw_fd = libc::eventfd(1,0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }; + let counter = Arc::new(AtomicU64::new(0)); + let counter_clone = counter.clone(); - for i in 0..no_of_subscribers { - // The `CounterSubscriberWithData` expects to receive a number as a parameter that - // represents the number it can use as its inner Events data. - let mut data_subscriber = CounterSubscriberWithData::new(i * no_of_subscribers); - data_subscriber.trigger_all_counters(); - event_manager.add_subscriber(Arc::new(Mutex::new(data_subscriber))); + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + match event_set { + EventSet::IN => { + counter_clone.fetch_add(1, Ordering::SeqCst); + }, + EventSet::ERROR => { + eprintln!("Got error on the monitored event."); + }, + EventSet::HANG_UP => { + panic!("Cannot continue execution. Associated fd was closed."); + }, + _ => { + eprintln!("Received spurious event from the event manager {event_set:#?}."); + } + } + })).unwrap(); - let mut counter_subscriber = CounterSubscriber::default(); - counter_subscriber.trigger_event(); - event_manager.add_subscriber(Arc::new(Mutex::new(counter_subscriber))); - } + (event_fd,counter) + }).collect::>(); + + const EVENTS: usize = 3; + + let subscribers_with_data = (0..no_of_subscribers) + .map(|_| { + let data = Arc::new([AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0)]); + assert_eq!(data.len(), EVENTS); + + // Create eventfd's that are initialized with 1 waiting event. + let inner_subscribers = (0..EVENTS) + .map(|_| + // SAFETY: Always safe. + unsafe { + let raw_fd = libc::eventfd(1, 0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }) + .collect::>(); + + for i in 0..EVENTS { + let data_clone = data.clone(); + + event_manager + .add( + inner_subscribers[i].as_fd(), + EventSet::IN | EventSet::ERROR | EventSet::HANG_UP, + Box::new( + move |_: &mut EventManager, event_set: EventSet| match event_set { + EventSet::IN => { + data_clone[i].fetch_add(1, Ordering::SeqCst); + } + EventSet::ERROR => { + eprintln!("Got error on the monitored event."); + } + EventSet::HANG_UP => { + panic!("Cannot continue execution. Associated fd was closed."); + } + _ => {} + }, + ), + ) + .unwrap(); + } + + (inner_subscribers, data) + }) + .collect::>(); c.bench_function("process_dynamic_dispatch", |b| { b.iter(|| { - let _ = event_manager.run().unwrap(); + assert_eq!(event_manager.wait(Some(0)), Ok(total)); }) }); + + drop(subscribers); + drop(subscribers_with_data); } // Test the performance of event manager when it manages a single subscriber type. // Just a few of the events are active in this test scenario. fn run_with_few_active_events(c: &mut Criterion) { - let no_of_subscribers = 200; + let no_of_subscribers = 200i32; + let active = 1 + no_of_subscribers / 23; + + let mut event_manager = + BufferedEventManager::with_capacity(false, no_of_subscribers as usize).unwrap(); - let mut event_manager = EventManager::::new().unwrap(); + let subscribers = (0..no_of_subscribers).map(|i| { + // Create an eventfd that is initialized with 1 waiting event. + // SAFETY: Always safe. + let event_fd = unsafe { + let raw_fd = libc::eventfd((i % 23 == 0) as u8 as u32,0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }; - for i in 0..no_of_subscribers { - let mut counter_subscriber = CounterSubscriber::default(); - // Let's activate the events for a few subscribers (i.e. only the ones that are - // divisible by 23). 23 is a random number that I just happen to like. - if i % 23 == 0 { - counter_subscriber.trigger_event(); - } - event_manager.add_subscriber(counter_subscriber); - } + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + match event_set { + EventSet::IN => (), + EventSet::ERROR => { + eprintln!("Got error on the monitored event."); + }, + EventSet::HANG_UP => { + panic!("Cannot continue execution. Associated fd was closed."); + }, + _ => { + eprintln!("Received spurious event from the event manager {event_set:#?}."); + } + } + })).unwrap(); + + event_fd + }).collect::>(); c.bench_function("process_dispatch_few_events", |b| { b.iter(|| { - let _ = event_manager.run().unwrap(); + assert_eq!(event_manager.wait(Some(0)), Ok(active)); }) }); + + drop(subscribers); } -criterion_group! { +criterion_group!( name = benches; config = Criterion::default() .sample_size(200) .measurement_time(std::time::Duration::from_secs(40)); targets = run_basic_subscriber, run_arc_mutex_subscriber, run_subscriber_with_inner_mut, - run_multiple_subscriber_types, run_with_few_active_events -} - -criterion_main! { - benches -} + run_multiple_subscriber_types, run_with_few_active_events +); +criterion_main!(benches); diff --git a/coverage_config_aarch64.json b/coverage_config_aarch64.json index 57d151c..10814d0 100644 --- a/coverage_config_aarch64.json +++ b/coverage_config_aarch64.json @@ -1,5 +1,5 @@ { - "coverage_score": 93.2, - "exclude_path": "tests/,benches/,utilities/", - "crate_features": "remote_endpoint,test_utilities" + "coverage_score": 89.59, + "exclude_path": "benches/", + "crate_features": "" } diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index a2ca601..10814d0 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 85.6, - "exclude_path": "tests/,benches/,utilities/", - "crate_features": "remote_endpoint,test_utilities" + "coverage_score": 89.59, + "exclude_path": "benches/", + "crate_features": "" } diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 623076f..f60c47c 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -1,147 +1,17 @@ # Event Manager Design -## Interest List Updates - -Subscribers can update their interest list when the `EventManager` calls -their `process` function. The EventManager crates a specialized `EventOps` -object. `EventOps` limits the operations that the subscribers may call to the -ones that are related to the interest list as follows: -- Adding a new event that the subscriber is interested in. -- Modifying an existing event (for example: update an event to be - edge-triggered instead of being level-triggered or update the user data - associated with an event). -- Remove an existing event. - -The subscriber is responsible for handling the errors returned from calling -`add`, `modify` or `remove`. - -The `EventManager` knows how to associate these actions to a registered -subscriber because it adds the corresponding `SubscriberId` when it creates the -`EventOps` object. - -## Events - -By default, `Events` wrap a file descriptor, and a bit mask of events -(for example `EPOLLIN | EPOLLOUT`). The `Events` can optionally contain user -defined data. - -The `Events` are used in `add`, `remove` and `modify` functions -in [`EventOps`](../src/events.rs). While their semantic is very similar to that -of `libc::epoll_event`, they come with an additional requirement. When -creating `Events` objects, the subscribers must specify the file descriptor -associated with the event mask. There are a few reasons behind this choice: -- Reducing the number of parameters on the `EventOps` functions. Instead of - always passing the file descriptor along with an `epoll_event` object, the - user only needs to pass `Events`. -- Backing the file descriptor in `Events` provides a simple mapping from a file - descriptor to the subscriber that is watching events on that particular file - descriptor. - -Storing the file descriptor in all `Events` means that there are 32 bits left -for custom user data. -A file descriptor can be registered only once (it can be associated with only -one subscriber). - -### Using Events With Custom Data - -The 32-bits in custom data can be used to map events to internal callbacks -based on user-defined numeric values instead of file descriptors. In the -below example, the user defined values are consecutive so that the match -statement can be optimized to a jump table. - -```rust - struct Painter {} - const PROCESS_GREEN:u32 = 0; - const PROCESS_RED: u32 = 1; - const PROCESS_BLUE: u32 = 2; - - impl Painter { - fn process_green(&self, event: Events) {} - fn process_red(&self, event: Events) {} - fn process_blue(&self, events: Events) {} - } - impl MutEventSubscriber for Painter { - fn init(&mut self, ops: &mut EventOps) { - let green_eventfd = EventFd::new(0).unwrap(); - let ev_for_green = Events::with_data(&green_eventfd, PROCESS_GREEN, EventSet::IN); - ops.add(ev_for_green).unwrap(); +`EventManager` is a wrapper over [epoll](https://man7.org/linux/man-pages/man7/epoll.7.html) that +allows for more ergonomic usage with many events. - let red_eventfd = EventFd::new(0).unwrap(); - let ev_for_red = Events::with_data(&red_eventfd, PROCESS_RED, EventSet::IN); - ops.add(ev_for_red).unwrap(); - - let blue_eventfd = EventFd::new(0).unwrap(); - let ev_for_blue = Events::with_data(&blue_eventfd, PROCESS_BLUE, EventSet::IN); - ops.add(ev_for_blue).unwrap(); - } - - fn process(&mut self, events: Events, ops: &mut EventOps) { - match events.data() { - PROCESS_GREEN => self.process_green(events), - PROCESS_RED => self.process_red(events), - PROCESS_BLUE => self.process_blue(events), - _ => error!("spurious event"), - }; - } - } -``` - -## Remote Endpoint - -A manager remote endpoint allows users to interact with the `EventManger` -(as a `SubscriberOps` trait object) from a different thread of execution. -This is particularly useful when the `EventManager` owns the subscriber object -the user wants to interact with, and the communication happens from a separate -thread. This functionality is gated behind the `remote_endpoint` feature. - -The current implementation relies on passing boxed closures to the manager and -getting back a boxed result. The manager is notified about incoming invocation -requests via an [`EventFd`](https://docs.rs/vmm-sys-util/latest/vmm_sys_util/eventfd/struct.EventFd.html) -which is added by the manager to its internal run loop. The manager runs each -closure to completion, and then returns the boxed result using a sender object -that is part of the initial message that also included the closure. The -following example uses the previously defined `Painter` subscriber type. - -```rust -fn main() { - // Create an event manager object. - let mut event_manager = EventManager::::new().unwrap(); - - // Obtain a remote endpoint object. - let endpoint = event_manager.remote_endpoint(); - - // Move the event manager to a new thread and start running the event loop there. - let thread_handle = thread::spawn(move || loop { - event_manager.run().unwrap(); - }); - - let subscriber = Painter {}; - - // Add the subscriber using the remote endpoint. The subscriber is moved to the event - // manager thread, and is now owned by the manager. In return, we get the subscriber id, - // which can be used to identify the subscriber for subsequent operations. - let id = endpoint - .call_blocking(move |sub_ops| -> Result { - Ok(sub_ops.add_subscriber(subscriber)) - }) - .unwrap(); - // ... +## Interest List Updates - // Add a new event to the subscriber, using fd 1 as an example. - let events = Events::new_raw(1, EventSet::OUT); - endpoint - .call_blocking(move |sub_ops| -> Result<()> { sub_ops.event_ops(id)?.add(events) }) - .unwrap(); +Event actions are represented by a closure of the type `Box`. - // ... +The mutable reference to the `EventManager` can be used to: - thread_handle.join(); -} -``` +- Add a new event with `EventManager::add`. +- Remove an existing event with `EventManager::del`. +- Wait on further events with `EventManager::wait`. -The `call_blocking` invocation sends a message over a channel to the event manager on the -other thread, and then blocks until a response is received. The event manager detects the -presence of such messages as with any other event, and handles them as part of the event -loop. This can lead to deadlocks if, for example, `call_blocking` is invoked in the `process` -implmentation of a subscriber to the same event manager. \ No newline at end of file +The `EventSet` communicates the specific event/s that fired. \ No newline at end of file diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index f38a4a2..a8fa63f 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -2,31 +2,8 @@ ## Testing -The `event-manager` is tested using: -- unit tests - defined in their corresponding modules -- Rust integration tests - defined in the [tests](../tests) directory -- performance tests - defined in the [benches](../benches) directory - -The integration and performance tests share subscribers implementations -which can be found under the [src/utilities](../src/utilities) module. - -The `utilities` module is compiled only when using the `test_utilities` -feature. To run unit tests, integration tests, and performance tests, the user -needs to specify the `test_utilities` feature; otherwise the build fails. - -```bash -cargo test --features test_utilities -cargo bench --features test_utilities -``` - -We recommend running all the tests before submitting a PR as follows: +The `event-manager` is tested using unit tests. ```bash -cargo test --all-features -``` - -Performance tests are implemented using -[criterion](https://docs.rs/crate/criterion/). Running the performance tests -locally should work, but only when they're run as part of the CI performance -improvements/degradations can be noticed. More details about performance tests -[here](https://github.com/rust-vmm/rust-vmm-ci#performance-tests). +cargo test +``` \ No newline at end of file diff --git a/docs/event-manager.png b/docs/event-manager.png deleted file mode 100644 index 5e8808a24cf6c38a5cf12b08b9be543cbf8bee58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68017 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 in order to have a single concrete type definition for the messages that -// contain closures to be executed (the `FnMsg` defined below). The actual return type is -// recovered by the initiator of the remote call (more details in the implementation of -// `RemoteEndpoint::call_blocking` below). The `Send` bound is required to send back the boxed -// result over the return channel. -type ErasedResult = Box; - -// Type alias for the boxed closures received by the manager. The `Send` bound at the end applies -// to the type of the closure, and is required to send the box over the channel. -type FnOnceBox = Box) -> ErasedResult + Send>; - -// The type of the messages received by the manager over its receive mpsc channel. -pub(crate) struct FnMsg { - // The closure to execute. - pub(crate) fnbox: FnOnceBox, - // The sending endpoint of the channel used by the remote called to wait for the result. - pub(crate) sender: Option>, -} - -// Used by the `EventManager` to keep state associated with the channel. -pub(crate) struct EventManagerChannel { - // A clone of this is given to every `RemoteEndpoint` and used to signal the presence of - // an new message on the channel. - pub(crate) event_fd: Arc, - // A clone of this sender is given to every `RemoteEndpoint` and used to send `FnMsg` objects - // to the `EventManager` over the channel. - pub(crate) sender: Sender>, - // The receiving half of the channel, used to receive incoming `FnMsg` objects. - pub(crate) receiver: Receiver>, -} - -impl EventManagerChannel { - pub(crate) fn new() -> Result { - let (sender, receiver) = channel(); - Ok(EventManagerChannel { - event_fd: Arc::new( - EventFd::new(EFD_NONBLOCK).map_err(|e| Error::EventFd(Errno::from(e)))?, - ), - sender, - receiver, - }) - } - - pub(crate) fn fd(&self) -> RawFd { - self.event_fd.as_raw_fd() - } - - pub(crate) fn remote_endpoint(&self) -> RemoteEndpoint { - RemoteEndpoint { - msg_sender: self.sender.clone(), - event_fd: self.event_fd.clone(), - } - } -} - -/// Enables interactions with an `EventManager` that runs on a different thread of execution. -pub struct RemoteEndpoint { - // A sender associated with `EventManager` channel requests are sent over. - msg_sender: Sender>, - // Used to notify the `EventManager` about the arrival of a new request. - event_fd: Arc, -} - -impl Clone for RemoteEndpoint { - fn clone(&self) -> Self { - RemoteEndpoint { - msg_sender: self.msg_sender.clone(), - event_fd: self.event_fd.clone(), - } - } -} - -impl RemoteEndpoint { - // Send a message to the remote EventManger and raise a notification. - fn send(&self, msg: FnMsg) -> Result<()> { - self.msg_sender.send(msg).map_err(|_| Error::ChannelSend)?; - self.event_fd - .write(1) - .map_err(|e| Error::EventFd(Errno::from(e)))?; - Ok(()) - } - - /// Call the specified closure on the associated remote `EventManager` (provided as a - /// `SubscriberOps` trait object), and return the result. This method blocks until the result - /// is received, and calling it from the same thread where the event loop runs leads to - /// a deadlock. - pub fn call_blocking(&self, f: F) -> result::Result - where - F: FnOnce(&mut dyn SubscriberOps) -> result::Result + Send + 'static, - O: Send + 'static, - E: From + Send + 'static, - { - // Create a temporary channel used to get back the result. We keep the receiving end, - // and put the sending end into the message we pass to the remote `EventManager`. - let (sender, receiver) = channel(); - - // We erase the return type of `f` by moving and calling it inside another closure which - // hides the result as an `ErasedResult`. This allows using the same channel to send - // closures with different signatures (and thus different types) to the remote - // `EventManager`. - let fnbox = Box::new( - move |ops: &mut dyn SubscriberOps| -> ErasedResult { Box::new(f(ops)) }, - ); - - // Send the message requesting the closure invocation. - self.send(FnMsg { - fnbox, - sender: Some(sender), - })?; - - // Block until a response is received. We can use unwrap because the downcast cannot fail, - // since the signature of F (more specifically, the return value) constrains the concrete - // type that's in the box. - let result_box = receiver - .recv() - .map_err(|_| Error::ChannelRecv)? - .downcast() - .unwrap(); - - // Turns out the dereference operator has a special behaviour for boxed objects; if we - // own a `b: Box` and call `*b`, the box goes away and we get the `T` inside. - *result_box - } - - /// Call the specified closure on the associated local/remote `EventManager` (provided as a - /// `SubscriberOps` trait object), and discard the result. This method only fires - /// the request but does not wait for result, so it may be called from the same thread where - /// the event loop runs. - pub fn fire(&self, f: F) -> Result<()> - where - F: FnOnce(&mut dyn SubscriberOps) + Send + 'static, - { - // We erase the return type of `f` by moving and calling it inside another closure which - // hides the result as an `ErasedResult`. This allows using the same channel send closures - // with different signatures (and thus different types) to the remote `EventManager`. - let fnbox = Box::new( - move |ops: &mut dyn SubscriberOps| -> ErasedResult { - f(ops); - Box::new(()) - }, - ); - - // Send the message requesting the closure invocation. - self.send(FnMsg { - fnbox, - sender: None, - }) - } - - /// Kick the worker thread to wake up from the epoll event loop. - pub fn kick(&self) -> Result<()> { - self.event_fd - .write(1) - .map(|_| ()) - .map_err(|e| Error::EventFd(Errno::from(e))) - } -} diff --git a/src/epoll.rs b/src/epoll.rs deleted file mode 100644 index b174f8d..0000000 --- a/src/epoll.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use std::collections::HashMap; -use std::os::unix::io::RawFd; - -use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent}; - -use super::{Errno, Error, EventOps, Result, SubscriberId}; - -// Internal use structure that keeps the epoll related state of an EventManager. -pub(crate) struct EpollWrapper { - // The epoll wrapper. - pub(crate) epoll: Epoll, - // Records the id of the subscriber associated with the given RawFd. The event event_manager - // does not currently support more than one subscriber being associated with an fd. - pub(crate) fd_dispatch: HashMap, - // Records the set of fds that are associated with the subscriber that has the given id. - // This is used to keep track of all fds associated with a subscriber. - pub(crate) subscriber_watch_list: HashMap>, - // A scratch buffer to avoid allocating/freeing memory on each poll iteration. - pub(crate) ready_events: Vec, -} - -impl EpollWrapper { - pub(crate) fn new(ready_events_capacity: usize) -> Result { - Ok(EpollWrapper { - epoll: Epoll::new().map_err(|e| Error::Epoll(Errno::from(e)))?, - fd_dispatch: HashMap::new(), - subscriber_watch_list: HashMap::new(), - ready_events: vec![EpollEvent::default(); ready_events_capacity], - }) - } - - // Poll the underlying epoll fd for pending IO events. - pub(crate) fn poll(&mut self, milliseconds: i32) -> Result { - let event_count = match self.epoll.wait(milliseconds, &mut self.ready_events[..]) { - Ok(ev) => ev, - // EINTR is not actually an error that needs to be handled. The documentation - // for epoll.run specifies that run exits when it for an event, on timeout, or - // on interrupt. - Err(e) if e.raw_os_error() == Some(libc::EINTR) => return Ok(0), - Err(e) => return Err(Error::Epoll(Errno::from(e))), - }; - - Ok(event_count) - } - - // Remove the fds associated with the provided subscriber id from the epoll set and the - // other structures. The subscriber id must be valid. - pub(crate) fn remove(&mut self, subscriber_id: SubscriberId) { - let fds = self - .subscriber_watch_list - .remove(&subscriber_id) - .unwrap_or_default(); - for fd in fds { - // We ignore the result of the operation since there's nothing we can't do, and its - // not a significant error condition at this point. - let _ = self - .epoll - .ctl(ControlOperation::Delete, fd, EpollEvent::default()); - self.remove_event(fd); - } - } - - // Flush and stop receiving IO events associated with the file descriptor. - pub(crate) fn remove_event(&mut self, fd: RawFd) { - self.fd_dispatch.remove(&fd); - for event in self.ready_events.iter_mut() { - if event.fd() == fd { - // It's a little complex to remove the entry from the Vec, so do soft removal - // by setting it to default value. - *event = EpollEvent::default(); - } - } - } - - // Gets the id of the subscriber associated with the provided fd (if such an association - // exists). - pub(crate) fn subscriber_id(&self, fd: RawFd) -> Option { - self.fd_dispatch.get(&fd).copied() - } - - // Creates and returns an EventOps object for the subscriber associated with the provided - // id. The subscriber id must be valid. - pub(crate) fn ops_unchecked(&mut self, subscriber_id: SubscriberId) -> EventOps { - EventOps::new(self, subscriber_id) - } -} diff --git a/src/events.rs b/src/events.rs deleted file mode 100644 index d8dd8c7..0000000 --- a/src/events.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use std::os::unix::io::{AsRawFd, RawFd}; - -use super::{Errno, Error, Result, SubscriberId}; -use crate::epoll::EpollWrapper; -use vmm_sys_util::epoll::{ControlOperation, EpollEvent, EventSet}; - -/// Wrapper over an `epoll::EpollEvent` object. -/// -/// When working directly with epoll related methods, the user associates an `u64` wide -/// epoll_data_t object with every event. We want to use fds as identifiers, but at the same time -/// keep the ability to associate opaque data with an event. An `Events` object always contains an -/// fd and an `u32` data member that can be supplied by the user. When registering events with the -/// inner epoll event set, the fd and data members of `Events` are used together to generate the -/// underlying `u64` member of the epoll_data union. -#[derive(Clone, Copy, Debug)] -pub struct Events { - inner: EpollEvent, -} - -impl PartialEq for Events { - fn eq(&self, other: &Events) -> bool { - self.fd() == other.fd() - && self.data() == other.data() - && self.event_set() == other.event_set() - } -} - -impl Events { - pub(crate) fn with_inner(inner: EpollEvent) -> Self { - Self { inner } - } - - /// Create an empty event set associated with `source`. - /// - /// No explicit events are monitored for the associated file descriptor. - /// Nevertheless, [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and - /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are implicitly - /// monitored. - /// - /// # Arguments - /// - /// * source: object that wraps a file descriptor to be associated with `events` - /// - /// # Example - /// - /// ```rust - /// # use event_manager::Events; - /// # use vmm_sys_util::eventfd::EventFd; - /// let eventfd = EventFd::new(0).unwrap(); - /// let ev_set = Events::empty(&eventfd); - /// ``` - pub fn empty(source: &T) -> Self { - Self::empty_raw(source.as_raw_fd()) - } - - /// Create an empty event set associated with the supplied `RawFd` value. - /// - /// No explicit events are monitored for the associated file descriptor. - /// Nevertheless, [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and - /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are implicitly - /// monitored. - /// - /// # Example - /// - /// ```rust - /// # use event_manager::Events; - /// # use std::os::unix::io::AsRawFd; - /// # use vmm_sys_util::eventfd::EventFd; - /// let eventfd = EventFd::new(0).unwrap(); - /// let ev_set = Events::empty_raw(eventfd.as_raw_fd()); - /// ``` - pub fn empty_raw(fd: RawFd) -> Self { - Self::new_raw(fd, EventSet::empty()) - } - - /// Create an event with `source` and the associated `events` for monitoring. - /// - /// # Arguments - /// - /// * source: object that wraps a file descriptor to be associated with `events` - /// * events: events to monitor on the provided `source`; - /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and - /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are - /// always monitored and don't need to be explicitly added to the list. - /// - /// # Example - /// - /// ```rust - /// # use event_manager::{Events, EventSet}; - /// # use vmm_sys_util::eventfd::EventFd; - /// let eventfd = EventFd::new(0).unwrap(); - /// let event_set = EventSet::IN; - /// let ev_set = Events::new(&eventfd, event_set); - /// ``` - pub fn new(source: &T, events: EventSet) -> Self { - Self::new_raw(source.as_raw_fd(), events) - } - - /// Create an event with the supplied `RawFd` value and `events` for monitoring. - /// - /// # Arguments - /// - /// * source: file descriptor on which to monitor the `events` - /// * events: events to monitor on the provided `source`; - /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and - /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are - /// always monitored and don't need to be explicitly added to the list. - /// # Example - /// - /// ```rust - /// # use event_manager::{Events, EventSet}; - /// # use vmm_sys_util::eventfd::EventFd; - /// # use std::os::unix::io::AsRawFd; - /// let eventfd = EventFd::new(0).unwrap(); - /// let event_set = EventSet::IN; - /// let ev_set = Events::new_raw(eventfd.as_raw_fd(), event_set); - /// ``` - pub fn new_raw(source: RawFd, events: EventSet) -> Self { - Self::with_data_raw(source, 0, events) - } - - /// Create an event set associated with the underlying file descriptor of the source, active - /// events, and data. - /// - /// # Arguments - /// * source: object that wraps a file descriptor to be associated with `events` - /// * data: custom user data associated with the file descriptor; the data can be used for - /// uniquely identify monitored events instead of using the file descriptor. - /// * events: events to monitor on the provided `source`; - /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and - /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are - /// always monitored and don't need to be explicitly added to the list. - /// - /// # Examples - /// - /// ```rust - /// # use event_manager::{Events, EventSet}; - /// # use vmm_sys_util::eventfd::EventFd; - /// let eventfd = EventFd::new(0).unwrap(); - /// let event_set = EventSet::IN; - /// let custom_data = 42; - /// let ev_set = Events::with_data(&eventfd, custom_data, event_set); - /// ``` - pub fn with_data(source: &T, data: u32, events: EventSet) -> Self { - Self::with_data_raw(source.as_raw_fd(), data, events) - } - - /// Create an event set associated with the supplied `RawFd` value, active events, and data. - /// - /// # Arguments - /// * source: file descriptor to be associated with `events` - /// * data: custom user data associated with the file descriptor; the data can be used for - /// uniquely identify monitored events instead of using the file descriptor. - /// * events: events to monitor on the provided `source`; - /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and - /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are - /// always monitored and don't need to be explicitly added to the list. - /// - /// # Examples - /// - /// ```rust - /// # use event_manager::{Events, EventSet}; - /// # use std::os::unix::io::AsRawFd; - /// # use vmm_sys_util::eventfd::EventFd; - /// let eventfd = EventFd::new(0).unwrap(); - /// let event_set = EventSet::IN; - /// let custom_data = 42; - /// let ev_set = Events::with_data_raw(eventfd.as_raw_fd(), custom_data, event_set); - /// ``` - pub fn with_data_raw(source: RawFd, data: u32, events: EventSet) -> Self { - let inner_data = ((data as u64) << 32) + (source as u64); - Events { - inner: EpollEvent::new(events, inner_data), - } - } - - /// Return the inner fd value. - pub fn fd(&self) -> RawFd { - self.inner.data() as RawFd - } - - /// Return the inner data value. - pub fn data(&self) -> u32 { - (self.inner.data() >> 32) as u32 - } - - /// Return the active event set. - pub fn event_set(&self) -> EventSet { - self.inner.event_set() - } - - /// Return the inner `EpollEvent`. - pub fn epoll_event(&self) -> EpollEvent { - self.inner - } -} - -/// Opaque object associated with an `EventSubscriber` that allows the addition, modification, and -/// removal of events in the watchlist. -// Right now this is a concrete object, but going further it can be turned into a trait and -// passed around as a trait object. -pub struct EventOps<'a> { - // Mutable reference to the EpollContext of an EventManager. - epoll_wrapper: &'a mut EpollWrapper, - // The id of the event subscriber this object stands for. - subscriber_id: SubscriberId, -} - -impl<'a> EventOps<'a> { - pub(crate) fn new(epoll_wrapper: &'a mut EpollWrapper, subscriber_id: SubscriberId) -> Self { - EventOps { - epoll_wrapper, - subscriber_id, - } - } - - // Apply the provided control operation for the given events on the inner epoll wrapper. - fn ctl(&self, op: ControlOperation, events: Events) -> Result<()> { - self.epoll_wrapper - .epoll - .ctl(op, events.fd(), events.epoll_event()) - .map_err(|e| Error::Epoll(Errno::from(e))) - } - - /// Add the provided events to the inner epoll event set. - pub fn add(&mut self, events: Events) -> Result<()> { - let fd = events.fd(); - if self.epoll_wrapper.fd_dispatch.contains_key(&fd) { - return Err(Error::FdAlreadyRegistered); - } - - self.ctl(ControlOperation::Add, events)?; - - self.epoll_wrapper - .fd_dispatch - .insert(fd, self.subscriber_id); - - self.epoll_wrapper - .subscriber_watch_list - .entry(self.subscriber_id) - .or_insert_with(Vec::new) - .push(fd); - - Ok(()) - } - - /// Submit the provided changes to the inner epoll event set. - pub fn modify(&self, events: Events) -> Result<()> { - self.ctl(ControlOperation::Modify, events) - } - - /// Remove the specified events from the inner epoll event set. - pub fn remove(&mut self, events: Events) -> Result<()> { - // TODO: Add some more checks here? - self.ctl(ControlOperation::Delete, events)?; - self.epoll_wrapper.remove_event(events.fd()); - - if let Some(watch_list) = self - .epoll_wrapper - .subscriber_watch_list - .get_mut(&self.subscriber_id) - { - if let Some(index) = watch_list.iter().position(|&x| x == events.fd()) { - watch_list.remove(index); - } - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use vmm_sys_util::eventfd::EventFd; - - #[test] - fn test_empty_events() { - let event_fd = EventFd::new(0).unwrap(); - - let events_raw = Events::empty_raw(event_fd.as_raw_fd()); - let events = Events::empty(&event_fd); - - assert_eq!(events, events_raw); - - assert_eq!(events.event_set(), EventSet::empty()); - assert_eq!(events.data(), 0); - assert_eq!(events.fd(), event_fd.as_raw_fd()); - } - - #[test] - fn test_events_no_data() { - let event_fd = EventFd::new(0).unwrap(); - let event_set = EventSet::IN; - - let events_raw = Events::new_raw(event_fd.as_raw_fd(), event_set); - let events = Events::new(&event_fd, event_set); - - assert_eq!(events_raw, events); - - assert_eq!(events.data(), 0); - assert_eq!(events.fd(), event_fd.as_raw_fd()); - assert_eq!(events.event_set(), event_set); - } - - #[test] - fn test_events_data() { - let event_fd = EventFd::new(0).unwrap(); - let event_set = EventSet::IN; - - let events_raw = Events::with_data_raw(event_fd.as_raw_fd(), 42, event_set); - let events = Events::with_data(&event_fd, 43, event_set); - - assert_ne!(events_raw, events); - - assert_eq!(events.data(), 43); - assert_eq!(events_raw.data(), 42); - } -} diff --git a/src/lib.rs b/src/lib.rs index feaaf43..8eba029 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,282 +1,384 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -//! Event Manager traits and implementation. -#![deny(missing_docs)] - -use std::cell::RefCell; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; -use std::result; -use std::sync::{Arc, Mutex}; - -use vmm_sys_util::errno::Error as Errno; - -/// The type of epoll events we can monitor a file descriptor for. -pub use vmm_sys_util::epoll::EventSet; - -mod epoll; -mod events; -mod manager; -mod subscribers; -#[doc(hidden)] -#[cfg(feature = "test_utilities")] -pub mod utilities; - -pub use events::{EventOps, Events}; -pub use manager::{EventManager, MAX_READY_EVENTS_CAPACITY}; - -#[cfg(feature = "remote_endpoint")] -mod endpoint; -#[cfg(feature = "remote_endpoint")] -pub use endpoint::RemoteEndpoint; - -/// Error conditions that may appear during `EventManager` related operations. -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - #[cfg(feature = "remote_endpoint")] - /// Cannot send message on channel. - ChannelSend, - #[cfg(feature = "remote_endpoint")] - /// Cannot receive message on channel. - ChannelRecv, - #[cfg(feature = "remote_endpoint")] - /// Operation on `eventfd` failed. - EventFd(Errno), - /// Operation on `libc::epoll` failed. - Epoll(Errno), - // TODO: should we allow fds to be registered multiple times? - /// The fd is already associated with an existing subscriber. - FdAlreadyRegistered, - /// The Subscriber ID does not exist or is no longer associated with a Subscriber. - InvalidId, - /// The ready list capacity passed to `EventManager::new` is invalid. - InvalidCapacity, -} +#![warn(missing_debug_implementations)] -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - #[cfg(feature = "remote_endpoint")] - Error::ChannelSend => write!( - f, - "event_manager: failed to send message to remote endpoint" - ), - #[cfg(feature = "remote_endpoint")] - Error::ChannelRecv => write!( - f, - "event_manager: failed to receive message from remote endpoint" - ), - #[cfg(feature = "remote_endpoint")] - Error::EventFd(e) => write!( - f, - "event_manager: failed to manage EventFd file descriptor: {}", - e - ), - Error::Epoll(e) => write!( - f, - "event_manager: failed to manage epoll file descriptor: {}", - e - ), - Error::FdAlreadyRegistered => write!( - f, - "event_manager: file descriptor has already been registered" - ), - Error::InvalidId => write!(f, "event_manager: invalid subscriber Id"), - Error::InvalidCapacity => write!(f, "event_manager: invalid ready_list capacity"), - } - } +use std::collections::HashMap; +use std::os::unix::io::{AsRawFd, RawFd}; + +use vmm_sys_util::epoll::EventSet; + +/// The function thats runs when an event occurs. +type Action = Box; + +fn errno() -> i32 { + // SAFETY: Always safe. + unsafe { *libc::__errno_location() } } -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - #[cfg(feature = "remote_endpoint")] - Error::ChannelSend => None, - #[cfg(feature = "remote_endpoint")] - Error::ChannelRecv => None, - #[cfg(feature = "remote_endpoint")] - Error::EventFd(e) => Some(e), - Error::Epoll(e) => Some(e), - Error::FdAlreadyRegistered => None, - Error::InvalidId => None, - Error::InvalidCapacity => None, - } - } +#[derive(Debug)] +pub struct BufferedEventManager { + event_manager: EventManager, + // TODO The length is always unused, a custom type could thus save `size_of::()` bytes. + buffer: Vec, } -/// Generic result type that may return `EventManager` errors. -pub type Result = result::Result; - -/// Opaque object that uniquely represents a subscriber registered with an `EventManager`. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct SubscriberId(u64); - -/// Allows the interaction between an `EventManager` and different event subscribers that do not -/// require a `&mut self` borrow to perform `init` and `process`. -/// -/// Any type implementing this also trivially implements `MutEventSubscriber`. The main role of -/// `EventSubscriber` is to allow wrappers such as `Arc` and `Rc` to implement `EventSubscriber` -/// themselves when the inner type is also an implementor. -pub trait EventSubscriber { - /// Process `events` triggered in the event manager loop. +impl BufferedEventManager { + /// Add an entry to the interest list of the epoll file descriptor. /// - /// Optionally, the subscriber can use `ops` to update the events it monitors. - fn process(&self, events: Events, ops: &mut EventOps); - - /// Initialization called by the [EventManager](struct.EventManager.html) when the subscriber - /// is registered. + /// # Errors /// - /// The subscriber is expected to use `ops` to register the events it wants to monitor. - fn init(&self, ops: &mut EventOps); -} + /// When [`libc::epoll_ctl`] returns `-1`. + pub fn add(&mut self, fd: T, events: EventSet, f: Action) -> Result<(), i32> { + let res = self.event_manager.add(fd, events, f); + self.buffer.reserve(self.event_manager.events.len()); + res + } -/// Allows the interaction between an `EventManager` and different event subscribers. Methods -/// are invoked with a mutable `self` borrow. -pub trait MutEventSubscriber { - /// Process `events` triggered in the event manager loop. + /// Remove (deregister) the target file descriptor fd from the interest list. /// - /// Optionally, the subscriber can use `ops` to update the events it monitors. - fn process(&mut self, events: Events, ops: &mut EventOps); - - /// Initialization called by the [EventManager](struct.EventManager.html) when the subscriber - /// is registered. + /// Returns `Ok(true)` when the given `fd` was present and `Ok(false)` when it wasn't. /// - /// The subscriber is expected to use `ops` to register the events it wants to monitor. - fn init(&mut self, ops: &mut EventOps); -} - -/// API that allows users to add, remove, and interact with registered subscribers. -pub trait SubscriberOps { - /// Subscriber type for which the operations apply. - type Subscriber: MutEventSubscriber; + /// # Errors + /// + /// When [`libc::epoll_ctl`] returns `-1`. + pub fn del(&mut self, fd: T) -> Result { + self.event_manager.del(fd) + } - /// Registers a new subscriber and returns the ID associated with it. + /// Waits until an event fires then triggers the respective action returning `Ok(x)`. If + /// timeout is `Some(_)` it may also return after the given number of milliseconds with + /// `Ok(0)`. /// - /// # Panics + /// # Errors /// - /// This function might panic if the subscriber is already registered. Whether a panic - /// is triggered depends on the implementation of - /// [Subscriber::init()](trait.EventSubscriber.html#tymethod.init). + /// When [`libc::epoll_wait`] returns `-1`. /// - /// Typically, in the `init` function, the subscriber adds fds to its interest list. The same - /// fd cannot be added twice and the `EventManager` will return - /// [Error::FdAlreadyRegistered](enum.Error.html). Using `unwrap` in init in this situation - /// triggers a panic. - fn add_subscriber(&mut self, subscriber: Self::Subscriber) -> SubscriberId; - - /// Removes the subscriber corresponding to `subscriber_id` from the watch list. - fn remove_subscriber(&mut self, subscriber_id: SubscriberId) -> Result; - - /// Returns a mutable reference to the subscriber corresponding to `subscriber_id`. - fn subscriber_mut(&mut self, subscriber_id: SubscriberId) -> Result<&mut Self::Subscriber>; - - /// Creates an event operations wrapper for the subscriber corresponding to `subscriber_id`. + /// # Panics /// - /// The event operations can be used to update the events monitored by the subscriber. - fn event_ops(&mut self, subscriber_id: SubscriberId) -> Result; -} - -impl EventSubscriber for Arc { - fn process(&self, events: Events, ops: &mut EventOps) { - self.deref().process(events, ops); - } - - fn init(&self, ops: &mut EventOps) { - self.deref().init(ops); + /// When the value given in timeout does not fit within an `i32` e.g. + /// `timeout.map(|u| i32::try_from(u).unwrap())`. + pub fn wait(&mut self, timeout: Option) -> Result { + // SAFETY: `EventManager::wait` initializes N element from the start of the slice and only + // accesses these, thus it will never access uninitialized memory, making this safe. + unsafe { + self.buffer.set_len(self.buffer.capacity()); + } + self.event_manager.wait(timeout, &mut self.buffer) } -} -impl MutEventSubscriber for Arc { - fn process(&mut self, events: Events, ops: &mut EventOps) { - self.deref().process(events, ops); + /// Creates new event manager. + /// + /// # Errors + /// + /// When [`libc::epoll_create1`] returns `-1`. + pub fn new(close_exec: bool) -> Result { + Ok(BufferedEventManager { + event_manager: EventManager::new(close_exec)?, + buffer: Vec::new(), + }) } - - fn init(&mut self, ops: &mut EventOps) { - self.deref().init(ops); + pub fn with_capacity(close_exec: bool, capacity: usize) -> Result { + Ok(BufferedEventManager { + event_manager: EventManager::new(close_exec)?, + buffer: Vec::with_capacity(capacity), + }) } } -impl EventSubscriber for Rc { - fn process(&self, events: Events, ops: &mut EventOps) { - self.deref().process(events, ops); - } - - fn init(&self, ops: &mut EventOps) { - self.deref().init(ops); +impl Default for BufferedEventManager { + fn default() -> Self { + Self::new(false).unwrap() } } -impl MutEventSubscriber for Rc { - fn process(&mut self, events: Events, ops: &mut EventOps) { - self.deref().process(events, ops); - } - - fn init(&mut self, ops: &mut EventOps) { - self.deref().init(ops); - } +pub struct EventManager { + epfd: RawFd, + events: HashMap, } -impl EventSubscriber for RefCell { - fn process(&self, events: Events, ops: &mut EventOps) { - self.borrow_mut().process(events, ops); - } - - fn init(&self, ops: &mut EventOps) { - self.borrow_mut().init(ops); +impl std::fmt::Debug for EventManager { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EventManager") + .field("epfd", &self.epfd) + .field( + "events", + &self + .events + .iter() + .map(|(k, v)| (*k, v as *const _ as usize)) + .collect::>(), + ) + .finish() } } -impl MutEventSubscriber for RefCell { - fn process(&mut self, events: Events, ops: &mut EventOps) { - self.borrow_mut().process(events, ops); +impl EventManager { + /// Add an entry to the interest list of the epoll file descriptor. + /// + /// # Errors + /// + /// When [`libc::epoll_ctl`] returns `-1`. + pub fn add(&mut self, fd: T, events: EventSet, f: Action) -> Result<(), i32> { + let mut event = libc::epoll_event { + events: events.bits(), + r#u64: u64::try_from(fd.as_raw_fd()).unwrap(), + }; + // SAFETY: Safe when `fd` is a valid file descriptor. + match unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd.as_raw_fd(), &mut event) } + { + 0 => { + self.events.insert(fd.as_raw_fd(), f); + Ok(()) + } + -1 => Err(errno()), + _ => unreachable!(), + } } - fn init(&mut self, ops: &mut EventOps) { - self.borrow_mut().init(ops); + /// Remove (deregister) the target file descriptor fd from the interest list. + /// + /// Returns `Ok(true)` when the given `fd` was present and `Ok(false)` when it wasn't. + /// + /// # Errors + /// + /// When [`libc::epoll_ctl`] returns `-1`. + pub fn del(&mut self, fd: T) -> Result { + match self.events.remove(&fd.as_raw_fd()) { + Some(_) => { + // SAFETY: Safe when `fd` is a valid file descriptor. + match unsafe { + libc::epoll_ctl( + self.epfd, + libc::EPOLL_CTL_DEL, + fd.as_raw_fd(), + std::ptr::null_mut(), + ) + } { + 0 => Ok(true), + -1 => Err(errno()), + _ => unreachable!(), + } + } + None => Ok(false), + } } -} -impl EventSubscriber for Mutex { - fn process(&self, events: Events, ops: &mut EventOps) { - self.lock().unwrap().process(events, ops); + /// Waits until an event fires then triggers the respective action returning `Ok(x)`. If + /// timeout is `Some(_)` it may also return after the given number of milliseconds with + /// `Ok(0)`. + /// + /// # Errors + /// + /// When [`libc::epoll_wait`] returns `-1`. + /// + /// # Panics + /// + /// When the value given in timeout does not fit within an `i32` e.g. + /// `timeout.map(|u| i32::try_from(u).unwrap())`. + pub fn wait( + &mut self, + timeout: Option, + buffer: &mut [libc::epoll_event], + ) -> Result { + // SAFETY: Always safe. + match unsafe { + libc::epoll_wait( + self.epfd, + buffer.as_mut_ptr(), + buffer.len().try_into().unwrap(), + timeout.map_or(-1i32, |u| i32::try_from(u).unwrap()), + ) + } { + -1 => Err(errno()), + // SAFETY: `x` elements are initialized by `libc::epoll_wait`. + n @ 0.. => unsafe { + #[allow(clippy::needless_range_loop)] + for i in 0..usize::try_from(n).unwrap_unchecked() { + let event = buffer[i]; + // For all events which can fire there exists an entry within `self.events` thus + // it is safe to unwrap here. + let f: *const dyn Fn(&mut EventManager, EventSet) = self + .events + .get(&i32::try_from(event.u64).unwrap_unchecked()) + .unwrap_unchecked(); + (*f)(self, EventSet::from_bits_unchecked(event.events)); + } + Ok(n) + }, + _ => unreachable!(), + } } - fn init(&self, ops: &mut EventOps) { - self.lock().unwrap().init(ops); + /// Creates new event manager. + /// + /// # Errors + /// + /// When [`libc::epoll_create1`] returns `-1`. + pub fn new(close_exec: bool) -> Result { + // SAFETY: Always safe. + match unsafe { libc::epoll_create1(if close_exec { libc::EPOLL_CLOEXEC } else { 0 }) } { + -1 => Err(errno()), + epfd => Ok(Self { + epfd, + events: HashMap::new(), + }), + } } } -impl MutEventSubscriber for Mutex { - fn process(&mut self, events: Events, ops: &mut EventOps) { - // If another user of this mutex panicked while holding the mutex, then - // we terminate the process. - self.get_mut().unwrap().process(events, ops); - } - - fn init(&mut self, ops: &mut EventOps) { - // If another user of this mutex panicked while holding the mutex, then - // we terminate the process. - self.get_mut().unwrap().init(ops); +impl Default for EventManager { + fn default() -> Self { + Self::new(false).unwrap() } } -impl EventSubscriber for Box { - fn process(&self, events: Events, ops: &mut EventOps) { - self.deref().process(events, ops); +#[cfg(test)] +mod tests { + use super::*; + use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd}; + use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; + use std::sync::Arc; + + #[test] + fn delete() { + static COUNT: AtomicBool = AtomicBool::new(false); + let mut manager = BufferedEventManager::default(); + // We set value to 1 so it will trigger on a read event. + // SAFETY: Always safe. + let event_fd = unsafe { + let fd = libc::eventfd(1, 0); + assert_ne!(fd, -1); + fd + }; + manager + .add( + event_fd, + EventSet::IN, + // A closure which will flip the atomic boolean then remove the event fd from the + // interest list. + Box::new(move |x: &mut EventManager, _: EventSet| { + // Flips the atomic. + let cur = COUNT.load(Ordering::SeqCst); + COUNT.store(!cur, Ordering::SeqCst); + // Calls `EventManager::del` which removes the target file descriptor fd from + // the interest list of the inner epoll. + x.del(event_fd).unwrap(); + }), + ) + .unwrap(); + + // Assert the initial state of the atomic boolean. + assert!(!COUNT.load(Ordering::SeqCst)); + + // The file descriptor has been pre-armed, this will immediately call the respective + // closure. + assert_eq!(manager.wait(Some(10)), Ok(1)); + // As the closure will flip the atomic boolean we assert it has flipped correctly. + assert!(COUNT.load(Ordering::SeqCst)); + + // At this point we have called the closure, since the closure removes the event fd from the + // interest list of the inner epoll, calling this again should timeout as there are no event + // fd in the inner epolls interest list which could trigger. + assert_eq!(manager.wait(Some(10)), Ok(0)); + // As the `EventManager::wait` should timeout the value of the atomic boolean should not be + // flipped. + assert!(COUNT.load(Ordering::SeqCst)); } - fn init(&self, ops: &mut EventOps) { - self.deref().init(ops); + #[test] + fn flip() { + static COUNT: AtomicBool = AtomicBool::new(false); + let mut manager = BufferedEventManager::default(); + // We set value to 1 so it will trigger on a read event. + // SAFETY: Always safe. + let event_fd = unsafe { + let fd = libc::eventfd(1, 0); + assert_ne!(fd, -1); + fd + }; + manager + .add( + event_fd, + EventSet::IN, + Box::new(|_: &mut EventManager, _: EventSet| { + // Flips the atomic. + let cur = COUNT.load(Ordering::SeqCst); + COUNT.store(!cur, Ordering::SeqCst); + }), + ) + .unwrap(); + + // Assert the initial state of the atomic boolean. + assert!(!COUNT.load(Ordering::SeqCst)); + + // As the closure will flip the atomic boolean we assert it has flipped correctly. + assert_eq!(manager.wait(Some(10)), Ok(1)); + // As the closure will flip the atomic boolean we assert it has flipped correctly. + assert!(COUNT.load(Ordering::SeqCst)); + + // The file descriptor has been pre-armed, this will immediately call the respective + // closure. + assert_eq!(manager.wait(Some(10)), Ok(1)); + // As the closure will flip the atomic boolean we assert it has flipped correctly. + assert!(!COUNT.load(Ordering::SeqCst)); } -} -impl MutEventSubscriber for Box { - fn process(&mut self, events: Events, ops: &mut EventOps) { - self.deref_mut().process(events, ops); - } + #[allow(clippy::assertions_on_constants)] + #[test] + fn counters() { + const SUBSCRIBERS: usize = 100; + const FIRING: usize = 4; + + assert!(FIRING <= SUBSCRIBERS); + + let mut manager = BufferedEventManager::default(); + + // Setup eventfd's and counters. + let subscribers = (0..100) + .map(|_| { + // SAFETY: Always safe. + let event_fd = unsafe { + let raw_fd = libc::eventfd(0, 0); + assert_ne!(raw_fd, -1); + OwnedFd::from_raw_fd(raw_fd) + }; + let counter = Arc::new(AtomicU64::new(0)); + let counter_clone = counter.clone(); + + manager + .add( + event_fd.as_fd(), + EventSet::IN, + Box::new(move |_: &mut EventManager, _: EventSet| { + counter_clone.fetch_add(1, Ordering::SeqCst); + }), + ) + .unwrap(); + + (event_fd, counter) + }) + .collect::>(); + + // Arm random subscribers + let mut rng = rand::thread_rng(); + let set = rand::seq::index::sample(&mut rng, SUBSCRIBERS, FIRING).into_vec(); + for i in &set { + assert_ne!( + // SAFETY: Always safe. + unsafe { + libc::write( + subscribers[*i].0.as_raw_fd(), + &1u64 as *const u64 as *const libc::c_void, + std::mem::size_of::(), + ) + }, + -1 + ); + } - fn init(&mut self, ops: &mut EventOps) { - self.deref_mut().init(ops); + // Check counter are the correct values + let n = i32::try_from(FIRING).unwrap(); + assert_eq!(manager.wait(None), Ok(n)); + for i in set { + assert_eq!(subscribers[i].1.load(Ordering::SeqCst), 1); + } } } diff --git a/src/manager.rs b/src/manager.rs deleted file mode 100644 index 99bab03..0000000 --- a/src/manager.rs +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use std::mem::size_of; - -use vmm_sys_util::epoll::EpollEvent; -#[cfg(feature = "remote_endpoint")] -use vmm_sys_util::epoll::{ControlOperation, EventSet}; - -#[cfg(feature = "remote_endpoint")] -use super::endpoint::{EventManagerChannel, RemoteEndpoint}; -use super::epoll::EpollWrapper; -use super::subscribers::Subscribers; -#[cfg(feature = "remote_endpoint")] -use super::Errno; -use super::{Error, EventOps, Events, MutEventSubscriber, Result, SubscriberId, SubscriberOps}; - -/// Allows event subscribers to be registered, connected to the event loop, and later removed. -pub struct EventManager { - subscribers: Subscribers, - epoll_context: EpollWrapper, - - #[cfg(feature = "remote_endpoint")] - channel: EventManagerChannel, -} - -/// Maximum capacity of ready events that can be passed when initializing the `EventManager`. -// This constant is not defined inside the `EventManager` implementation because it would -// make it really weird to use as the `EventManager` uses generics (S: MutEventSubscriber). -// That means that when using this const, you could not write -// EventManager::MAX_READY_EVENTS_CAPACITY because the type `S` could not be inferred. -// -// This value is taken from: https://elixir.bootlin.com/linux/latest/source/fs/eventpoll.c#L101 -pub const MAX_READY_EVENTS_CAPACITY: usize = i32::MAX as usize / size_of::(); - -impl SubscriberOps for EventManager { - type Subscriber = T; - - /// Register a subscriber with the event event_manager and returns the associated ID. - fn add_subscriber(&mut self, subscriber: T) -> SubscriberId { - let subscriber_id = self.subscribers.add(subscriber); - self.subscribers - .get_mut_unchecked(subscriber_id) - // The index is valid because we've just added the subscriber. - .init(&mut self.epoll_context.ops_unchecked(subscriber_id)); - subscriber_id - } - - /// Unregisters and returns the subscriber associated with the provided ID. - fn remove_subscriber(&mut self, subscriber_id: SubscriberId) -> Result { - let subscriber = self - .subscribers - .remove(subscriber_id) - .ok_or(Error::InvalidId)?; - self.epoll_context.remove(subscriber_id); - Ok(subscriber) - } - - /// Return a mutable reference to the subscriber associated with the provided id. - fn subscriber_mut(&mut self, subscriber_id: SubscriberId) -> Result<&mut T> { - if self.subscribers.contains(subscriber_id) { - return Ok(self.subscribers.get_mut_unchecked(subscriber_id)); - } - Err(Error::InvalidId) - } - - /// Returns a `EventOps` object for the subscriber associated with the provided ID. - fn event_ops(&mut self, subscriber_id: SubscriberId) -> Result { - // Check if the subscriber_id is valid. - if self.subscribers.contains(subscriber_id) { - // The index is valid because the result of `find` was not `None`. - return Ok(self.epoll_context.ops_unchecked(subscriber_id)); - } - Err(Error::InvalidId) - } -} - -impl EventManager { - const DEFAULT_READY_EVENTS_CAPACITY: usize = 256; - - /// Create a new `EventManger` object. - pub fn new() -> Result { - Self::new_with_capacity(Self::DEFAULT_READY_EVENTS_CAPACITY) - } - - /// Creates a new `EventManger` object with specified event capacity. - /// - /// # Arguments - /// - /// * `ready_events_capacity`: maximum number of ready events to be - /// processed a single `run`. The maximum value of this - /// parameter is `EventManager::MAX_READY_EVENTS_CAPACITY`. - pub fn new_with_capacity(ready_events_capacity: usize) -> Result { - if ready_events_capacity > MAX_READY_EVENTS_CAPACITY { - return Err(Error::InvalidCapacity); - } - - let manager = EventManager { - subscribers: Subscribers::new(), - epoll_context: EpollWrapper::new(ready_events_capacity)?, - #[cfg(feature = "remote_endpoint")] - channel: EventManagerChannel::new()?, - }; - - #[cfg(feature = "remote_endpoint")] - manager - .epoll_context - .epoll - .ctl( - ControlOperation::Add, - manager.channel.fd(), - EpollEvent::new(EventSet::IN, manager.channel.fd() as u64), - ) - .map_err(|e| Error::Epoll(Errno::from(e)))?; - Ok(manager) - } - - /// Run the event loop blocking until events are triggered or an error is returned. - /// Calls [subscriber.process()](trait.EventSubscriber.html#tymethod.process) for each event. - /// - /// On success, it returns number of dispatched events or 0 when interrupted by a signal. - pub fn run(&mut self) -> Result { - self.run_with_timeout(-1) - } - - /// Wait for events for a maximum timeout of `miliseconds` or until an error is returned. - /// Calls [subscriber.process()](trait.EventSubscriber.html#tymethod.process) for each event. - /// - /// On success, it returns number of dispatched events or 0 when interrupted by a signal. - pub fn run_with_timeout(&mut self, milliseconds: i32) -> Result { - let event_count = self.epoll_context.poll(milliseconds)?; - self.dispatch_events(event_count); - - Ok(event_count) - } - - fn dispatch_events(&mut self, event_count: usize) { - // EpollEvent doesn't implement Eq or PartialEq, so... - let default_event: EpollEvent = EpollEvent::default(); - - // Used to record whether there's an endpoint event that needs to be handled. - #[cfg(feature = "remote_endpoint")] - let mut endpoint_event = None; - - for ev_index in 0..event_count { - let event = self.epoll_context.ready_events[ev_index]; - let fd = event.fd(); - - // Check whether this event has been discarded. - // EpollWrapper::remove_event() discards an IO event by setting it to default value. - if event.events() == default_event.events() && fd == default_event.fd() { - continue; - } - - if let Some(subscriber_id) = self.epoll_context.subscriber_id(fd) { - self.subscribers.get_mut_unchecked(subscriber_id).process( - Events::with_inner(event), - // The `subscriber_id` is valid because we checked it before. - &mut self.epoll_context.ops_unchecked(subscriber_id), - ); - } else { - #[cfg(feature = "remote_endpoint")] - { - // If we got here, there's a chance the event was triggered by the remote - // endpoint fd. Only check for incoming endpoint events right now, and defer - // actually handling them until all subscriber events have been handled first. - // This prevents subscribers whose events are about to be handled from being - // removed by an endpoint request (or other similar situations). - if fd == self.channel.fd() { - endpoint_event = Some(event); - continue; - } - } - - // This should not occur during normal operation. - unreachable!("Received event on fd from subscriber that is not registered"); - } - } - - #[cfg(feature = "remote_endpoint")] - self.dispatch_endpoint_event(endpoint_event); - } -} - -#[cfg(feature = "remote_endpoint")] -impl EventManager { - /// Return a `RemoteEndpoint` object, that allows interacting with the `EventManager` from a - /// different thread. Using `RemoteEndpoint::call_blocking` on the same thread the event loop - /// runs on leads to a deadlock. - pub fn remote_endpoint(&self) -> RemoteEndpoint { - self.channel.remote_endpoint() - } - - fn dispatch_endpoint_event(&mut self, endpoint_event: Option) { - if let Some(event) = endpoint_event { - if event.event_set() != EventSet::IN { - // This situation is virtually impossible to occur. If it does we have - // a programming error in our code. - unreachable!(); - } - self.handle_endpoint_calls(); - } - } - - fn handle_endpoint_calls(&mut self) { - // Clear the inner event_fd. We don't do anything about an error here at this point. - let _ = self.channel.event_fd.read(); - - // Process messages. We consider only `Empty` errors can appear here; we don't check - // for `Disconnected` errors because we keep at least one clone of `channel.sender` alive - // at all times ourselves. - while let Ok(msg) = self.channel.receiver.try_recv() { - match msg.sender { - Some(sender) => { - // We call the inner closure and attempt to send back the result, but can't really do - // anything in case of error here. - let _ = sender.send((msg.fnbox)(self)); - } - None => { - // Just call the function and discard the result. - let _ = (msg.fnbox)(self); - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::super::Error; - use super::*; - - use std::os::unix::io::{AsRawFd, RawFd}; - use std::sync::{Arc, Mutex}; - - use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; - - struct DummySubscriber { - event_fd_1: EventFd, - event_fd_2: EventFd, - - // Flags used for checking that the event event_manager called the `process` - // function for ev1/ev2. - processed_ev1_out: bool, - processed_ev2_out: bool, - processed_ev1_in: bool, - - // Flags used for driving register/unregister/modify of events from - // outside of the `process` function. - register_ev2: bool, - unregister_ev1: bool, - modify_ev1: bool, - } - - impl DummySubscriber { - fn new() -> Self { - DummySubscriber { - event_fd_1: EventFd::new(0).unwrap(), - event_fd_2: EventFd::new(0).unwrap(), - processed_ev1_out: false, - processed_ev2_out: false, - processed_ev1_in: false, - register_ev2: false, - unregister_ev1: false, - modify_ev1: false, - } - } - } - - impl DummySubscriber { - fn register_ev2(&mut self) { - self.register_ev2 = true; - } - - fn unregister_ev1(&mut self) { - self.unregister_ev1 = true; - } - - fn modify_ev1(&mut self) { - self.modify_ev1 = true; - } - - fn processed_ev1_out(&self) -> bool { - self.processed_ev1_out - } - - fn processed_ev2_out(&self) -> bool { - self.processed_ev2_out - } - - fn processed_ev1_in(&self) -> bool { - self.processed_ev1_in - } - - fn reset_state(&mut self) { - self.processed_ev1_out = false; - self.processed_ev2_out = false; - self.processed_ev1_in = false; - } - - fn handle_updates(&mut self, event_manager: &mut EventOps) { - if self.register_ev2 { - event_manager - .add(Events::new(&self.event_fd_2, EventSet::OUT)) - .unwrap(); - self.register_ev2 = false; - } - - if self.unregister_ev1 { - event_manager - .remove(Events::new_raw( - self.event_fd_1.as_raw_fd(), - EventSet::empty(), - )) - .unwrap(); - self.unregister_ev1 = false; - } - - if self.modify_ev1 { - event_manager - .modify(Events::new(&self.event_fd_1, EventSet::IN)) - .unwrap(); - self.modify_ev1 = false; - } - } - - fn handle_in(&mut self, source: RawFd) { - if self.event_fd_1.as_raw_fd() == source { - self.processed_ev1_in = true; - } - } - - fn handle_out(&mut self, source: RawFd) { - match source { - _ if self.event_fd_1.as_raw_fd() == source => { - self.processed_ev1_out = true; - } - _ if self.event_fd_2.as_raw_fd() == source => { - self.processed_ev2_out = true; - } - _ => {} - } - } - } - - impl MutEventSubscriber for DummySubscriber { - fn process(&mut self, events: Events, ops: &mut EventOps) { - let source = events.fd(); - let event_set = events.event_set(); - - // We only know how to treat EPOLLOUT and EPOLLIN. - // If we received anything else just stop processing the event. - let all_but_in_out = EventSet::all() - EventSet::OUT - EventSet::IN; - if event_set.intersects(all_but_in_out) { - return; - } - - self.handle_updates(ops); - - match event_set { - EventSet::IN => self.handle_in(source), - EventSet::OUT => self.handle_out(source), - _ => {} - } - } - - fn init(&mut self, ops: &mut EventOps) { - let event = Events::new(&self.event_fd_1, EventSet::OUT); - ops.add(event).unwrap(); - } - } - - #[test] - fn test_register() { - use super::SubscriberOps; - - let mut event_manager = EventManager::>>::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager.add_subscriber(dummy_subscriber.clone()); - - dummy_subscriber.lock().unwrap().register_ev2(); - - // When running the loop the first time, ev1 should be processed, but ev2 shouldn't - // because it was just added as part of processing ev1. - event_manager.run().unwrap(); - assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); - assert!(!dummy_subscriber.lock().unwrap().processed_ev2_out()); - - // Check that both ev1 and ev2 are processed. - dummy_subscriber.lock().unwrap().reset_state(); - event_manager.run().unwrap(); - assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); - assert!(dummy_subscriber.lock().unwrap().processed_ev2_out()); - } - - #[test] - #[should_panic(expected = "FdAlreadyRegistered")] - fn test_add_invalid_subscriber() { - let mut event_manager = EventManager::>>::new().unwrap(); - let subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager.add_subscriber(subscriber.clone()); - event_manager.add_subscriber(subscriber); - } - - // Test that unregistering an event while processing another one works. - #[test] - fn test_unregister() { - let mut event_manager = EventManager::>>::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager.add_subscriber(dummy_subscriber.clone()); - - // Disable ev1. We should only receive this event once. - dummy_subscriber.lock().unwrap().unregister_ev1(); - - event_manager.run().unwrap(); - assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); - - dummy_subscriber.lock().unwrap().reset_state(); - - // We expect no events to be available. Let's run with timeout so that run exists. - event_manager.run_with_timeout(100).unwrap(); - assert!(!dummy_subscriber.lock().unwrap().processed_ev1_out()); - } - - #[test] - fn test_modify() { - let mut event_manager = EventManager::>>::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager.add_subscriber(dummy_subscriber.clone()); - - // Modify ev1 so that it waits for EPOLL_IN. - dummy_subscriber.lock().unwrap().modify_ev1(); - event_manager.run().unwrap(); - assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); - assert!(!dummy_subscriber.lock().unwrap().processed_ev2_out()); - - dummy_subscriber.lock().unwrap().reset_state(); - - // Make sure ev1 is ready for IN so that we don't loop forever. - dummy_subscriber - .lock() - .unwrap() - .event_fd_1 - .write(1) - .unwrap(); - - event_manager.run().unwrap(); - assert!(!dummy_subscriber.lock().unwrap().processed_ev1_out()); - assert!(!dummy_subscriber.lock().unwrap().processed_ev2_out()); - assert!(dummy_subscriber.lock().unwrap().processed_ev1_in()); - } - - #[test] - fn test_remove_subscriber() { - let mut event_manager = EventManager::>>::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - let subscriber_id = event_manager.add_subscriber(dummy_subscriber.clone()); - event_manager.run().unwrap(); - assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); - - dummy_subscriber.lock().unwrap().reset_state(); - - event_manager.remove_subscriber(subscriber_id).unwrap(); - - // We expect no events to be available. Let's run with timeout so that run exits. - event_manager.run_with_timeout(100).unwrap(); - assert!(!dummy_subscriber.lock().unwrap().processed_ev1_out()); - - // Removing the subscriber twice should return an error. - assert_eq!( - event_manager - .remove_subscriber(subscriber_id) - .err() - .unwrap(), - Error::InvalidId - ); - } -} diff --git a/src/subscribers.rs b/src/subscribers.rs deleted file mode 100644 index fb88aa1..0000000 --- a/src/subscribers.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use super::SubscriberId; -use std::collections::HashMap; - -// Internal structure used to keep the set of subscribers registered with an EventManger. -// This structure is a thin wrapper over a `HashMap` in which the keys are uniquely -// generated when calling `add`. -pub(crate) struct Subscribers { - // The key is the unique id of the subscriber and the entry is the `Subscriber`. - subscribers: HashMap, - // We are generating the unique ids by incrementing this counter for each added subscriber, - // and rely on the large value range of u64 to ensure each value is effectively - // unique over the runtime of any VMM. - next_id: u64, -} - -impl Subscribers { - pub(crate) fn new() -> Self { - Subscribers { - subscribers: HashMap::new(), - next_id: 1, - } - } - - // Adds a subscriber and generates an unique id to represent it. - pub(crate) fn add(&mut self, subscriber: T) -> SubscriberId { - let id = SubscriberId(self.next_id); - self.next_id += 1; - - self.subscribers.insert(id, subscriber); - - id - } - - // Remove and return the subscriber associated with the given id, if it exists. - pub(crate) fn remove(&mut self, subscriber_id: SubscriberId) -> Option { - self.subscribers.remove(&subscriber_id) - } - - // Checks whether a subscriber with `subscriber_id` is registered. - pub(crate) fn contains(&mut self, subscriber_id: SubscriberId) -> bool { - self.subscribers.contains_key(&subscriber_id) - } - - // Return a mutable reference to the subriber represented by `subscriber_id`. - // - // This method should only be called for indices that are known to be valid, otherwise - // panics can occur. - pub(crate) fn get_mut_unchecked(&mut self, subscriber_id: SubscriberId) -> &mut T { - self.subscribers.get_mut(&subscriber_id).unwrap() - } -} diff --git a/src/utilities/mod.rs b/src/utilities/mod.rs deleted file mode 100644 index c2d53d1..0000000 --- a/src/utilities/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Helper module for tests utilities. -// -// This module is only compiled with `test_utilities` feature on purpose. -// For production code, we do not want to export this functionality. -// At the same time, we need the test utilities to be public such that they can -// be used by multiple categories of tests. Two examples that deem this module -// necessary are the benchmark tests and the integration tests where the implementations -// of subscribers are shared. -// -// Having this module as a feature comes with a disadvantage that needs to be kept in mind. -// `cargo test` will only work when ran with `--feature test-utilities` (or with --all-features). A -// much nicer way to implement this would've been with a utilities crate that is used as -// `dev-dependencies`. Unfortunately, this is not possible because it would introduce a cyclic -// dependency. The `utilities` module has a dependency on `event-manager` because it needs to -// implement the `EventSubscriber` traits, and `event-manager` has a dependency on utilities so -// that they can be used in tests. -#![doc(hidden)] -pub mod subscribers; diff --git a/src/utilities/subscribers.rs b/src/utilities/subscribers.rs deleted file mode 100644 index 95ad790..0000000 --- a/src/utilities/subscribers.rs +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause -// -// Cargo thinks that some of the methods in this module are not used because -// not all of them are used in all the separate integration test modules. -// Cargo bug: https://github.com/rust-lang/rust/issues/46379 -// Let's allow dead code so that we don't get warnings all the time. -/// This module defines common subscribers that showcase the usage of the event-manager. -/// -/// 1. CounterSubscriber: -/// - a dummy subscriber that increments a counter on event -/// - only uses one event -/// - it has to be explicitly mutated; for this reason it implements `MutEventSubscriber`. -/// -/// 2. CounterSubscriberWithData: -/// - a dummy subscriber that increments a counter on events -/// - this subscriber takes care of multiple events and makes use of `Events::with_data` so -/// that in the `process` function it identifies the trigger of an event using the data -/// instead of the file descriptor -/// - it has to be explicitly mutated; for this reason it implements `MutEventSubscriber`. -/// -/// 3. CounterInnerMutSubscriber: -/// - a dummy subscriber that increments a counter on events -/// - the subscriber makes use of inner mutability; multi-threaded applications might want to -/// use inner mutability instead of having something heavy weight (i.e. Arc). -/// - this subscriber implement `EventSubscriber`. -use std::fmt::{Display, Formatter, Result}; -use std::os::unix::io::AsRawFd; -use std::sync::atomic::AtomicU64; -use std::sync::atomic::Ordering; - -use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; - -use crate::{EventOps, EventSubscriber, Events, MutEventSubscriber}; - -/// A `Counter` is a helper structure for creating subscribers that increment a value -/// each time an event is triggered. -/// The `Counter` allows users to assert and de-assert an event, and to query the counter value. -pub struct Counter { - event_fd: EventFd, - counter: u64, -} - -impl Display for Counter { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!( - f, - "(event_fd = {}, counter = {})", - self.event_fd.as_raw_fd(), - self.counter - ) - } -} - -impl Counter { - pub fn new() -> Self { - Self { - event_fd: EventFd::new(0).unwrap(), - counter: 0, - } - } - - pub fn trigger_event(&mut self) { - let _ = self.event_fd.write(1); - } - - pub fn clear_event(&self) { - let _ = self.event_fd.read(); - } - - pub fn counter(&self) -> u64 { - self.counter - } -} - -impl Default for Counter { - fn default() -> Self { - Self::new() - } -} - -// A dummy subscriber that increments a counter whenever it processes -// a new request. -pub struct CounterSubscriber(Counter); - -impl std::ops::Deref for CounterSubscriber { - type Target = Counter; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for CounterSubscriber { - fn deref_mut(&mut self) -> &mut Counter { - &mut self.0 - } -} - -impl Default for CounterSubscriber { - fn default() -> Self { - Self(Counter::new()) - } -} - -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) - .expect("Encountered error during cleanup."); - panic!("Cannot continue execution. Associated fd was closed."); - } - _ => { - eprintln!( - "Received spurious event from the event manager {:#?}.", - events.event_set() - ); - } - } - } - - fn init(&mut self, ops: &mut EventOps) { - ops.add(Events::new(&self.event_fd, EventSet::IN)) - .expect("Cannot register event."); - } -} - -// A dummy subscriber that makes use of the optional data in `Events` when -// registering & processing events. -// Using 3 counters each having associated event data to showcase the implementation of -// EventSubscriber trait with this scenario. -pub struct CounterSubscriberWithData { - counter_1: Counter, - counter_2: Counter, - counter_3: Counter, - - first_data: u32, - toggle_registry: bool, -} - -impl CounterSubscriberWithData { - // `first_data` represents the first event data that can be used by this subscriber. - pub fn new(first_data: u32) -> Self { - Self { - counter_1: Counter::new(), - counter_2: Counter::new(), - counter_3: Counter::new(), - // Using consecutive numbers for the event data helps the compiler to optimize - // match statements on counter_1_data, counter_2_data, counter_3_data using - // a jump table. - first_data, - toggle_registry: false, - } - } - - pub fn trigger_all_counters(&mut self) { - self.counter_1.trigger_event(); - self.counter_2.trigger_event(); - self.counter_3.trigger_event(); - } - - pub fn get_all_counter_values(&self) -> Vec { - vec![ - self.counter_1.counter(), - self.counter_2.counter(), - self.counter_3.counter(), - ] - } - - pub fn set_toggle_registry(&mut self, toggle: bool) { - self.toggle_registry = toggle; - } -} - -impl MutEventSubscriber for CounterSubscriberWithData { - fn process(&mut self, events: Events, ops: &mut EventOps) { - if self.toggle_registry { - self.toggle_registry = false; - - ops.remove(Events::with_data( - &self.counter_1.event_fd, - self.first_data, - EventSet::IN, - )) - .expect("Cannot remove event."); - ops.remove(Events::with_data( - &self.counter_2.event_fd, - self.first_data + 1, - EventSet::IN, - )) - .expect("Cannot remove event."); - ops.remove(Events::with_data( - &self.counter_3.event_fd, - self.first_data + 2, - EventSet::IN, - )) - .expect("Cannot remove event."); - - ops.add(Events::with_data( - &self.counter_1.event_fd, - self.first_data, - EventSet::IN, - )) - .expect("Cannot register event."); - ops.add(Events::with_data( - &self.counter_2.event_fd, - self.first_data + 1, - EventSet::IN, - )) - .expect("Cannot register event."); - ops.add(Events::with_data( - &self.counter_3.event_fd, - self.first_data + 2, - EventSet::IN, - )) - .expect("Cannot register event."); - } - match events.event_set() { - EventSet::IN => { - let event_id = events.data() - self.first_data; - match event_id { - 0 => { - self.counter_1.counter += 1; - } - 1 => { - self.counter_2.counter += 1; - } - 2 => { - self.counter_3.counter += 1; - } - _ => { - eprintln!("Received spurious event."); - } - }; - } - EventSet::ERROR => { - eprintln!("Got error on the monitored event."); - } - EventSet::HANG_UP => { - ops.remove(events) - .expect("Encountered error during cleanup."); - panic!("Cannot continue execution. Associated fd was closed."); - } - _ => {} - } - } - - fn init(&mut self, ops: &mut EventOps) { - ops.add(Events::with_data( - &self.counter_1.event_fd, - self.first_data, - EventSet::IN, - )) - .expect("Cannot register event."); - ops.add(Events::with_data( - &self.counter_2.event_fd, - self.first_data + 1, - EventSet::IN, - )) - .expect("Cannot register event."); - ops.add(Events::with_data( - &self.counter_3.event_fd, - self.first_data + 2, - EventSet::IN, - )) - .expect("Cannot register event."); - } -} - -pub struct CounterInnerMutSubscriber { - event_fd: EventFd, - counter: AtomicU64, -} - -impl Default for CounterInnerMutSubscriber { - fn default() -> Self { - Self { - event_fd: EventFd::new(0).unwrap(), - counter: AtomicU64::new(0), - } - } -} - -impl CounterInnerMutSubscriber { - pub fn trigger_event(&self) { - let _ = self.event_fd.write(1); - } - - pub fn clear_event(&self) { - let _ = self.event_fd.read(); - } - - pub fn counter(&self) -> u64 { - self.counter.load(Ordering::Relaxed) - } -} - -impl EventSubscriber for CounterInnerMutSubscriber { - fn process(&self, events: Events, ops: &mut EventOps) { - match events.event_set() { - EventSet::IN => { - self.counter.fetch_add(1, Ordering::Relaxed); - } - EventSet::ERROR => { - eprintln!("Got error on the monitored event."); - } - EventSet::HANG_UP => { - ops.remove(events) - .expect("Encountered error during cleanup."); - panic!("Cannot continue execution. Associated fd was closed."); - } - _ => {} - } - } - - fn init(&self, ops: &mut EventOps) { - ops.add(Events::new(&self.event_fd, EventSet::IN)) - .expect("Cannot register event."); - } -} diff --git a/tests/basic_event_manager.rs b/tests/basic_event_manager.rs deleted file mode 100644 index 8930ebf..0000000 --- a/tests/basic_event_manager.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause -// -// A basic, single threaded application that only needs one type of -// subscriber: `CounterSubscriber`. -// -// The application has an `EventManager` and can register multiple subscribers -// of type `CounterSubscriber`. - -use std::ops::Drop; - -use event_manager::utilities::subscribers::CounterSubscriber; -use event_manager::{EventManager, SubscriberId, SubscriberOps}; - -struct App { - event_manager: EventManager, - subscribers_id: Vec, -} - -impl App { - fn new() -> Self { - Self { - event_manager: EventManager::::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); - } - - fn inject_events_for(&mut self, subscriber_index: &[usize]) { - for i in subscriber_index { - let subscriber = self - .event_manager - .subscriber_mut(self.subscribers_id[*i]) - .unwrap(); - subscriber.trigger_event(); - } - } - - fn clear_events_for(&mut self, subscriber_indices: &[usize]) { - for i in subscriber_indices { - let subscriber = self - .event_manager - .subscriber_mut(self.subscribers_id[*i]) - .unwrap(); - subscriber.clear_event(); - } - } - - fn get_counters(&mut self) -> Vec { - let mut result = Vec::::new(); - for subscriber_id in &self.subscribers_id { - let subscriber = self.event_manager.subscriber_mut(*subscriber_id).unwrap(); - result.push(subscriber.counter()); - } - - result - } - - // Whenever the App does not need to monitor events anymore, it should explicitly call - // the `remove_subscriber` function. - fn cleanup(&mut self) { - for id in &self.subscribers_id { - let _ = self.event_manager.remove_subscriber(*id); - } - } -} - -impl Drop for App { - fn drop(&mut self) { - self.cleanup(); - } -} - -#[test] -fn test_single_threaded() { - let mut app = App::new(); - for _ in 0..100 { - app.add_subscriber(); - } - - // Random subscribers, in the sense that I randomly picked those numbers :) - let triggered_subscribers: Vec = vec![1, 3, 50, 97]; - app.inject_events_for(&triggered_subscribers); - app.run(); - - let counters = app.get_counters(); - for (i, &item) in counters.iter().enumerate() { - assert_eq!(item, 1 & (triggered_subscribers.contains(&i) as u64)); - } - - app.clear_events_for(&triggered_subscribers); - app.run(); - let counters = app.get_counters(); - for (i, &item) in counters.iter().enumerate() { - assert_eq!(item, 1 & (triggered_subscribers.contains(&i) as u64)); - } - - // Once the app does not need events anymore, the cleanup needs to be called. - // This is particularly important when the app continues the execution, but event monitoring - // is not necessary. - app.cleanup(); -} diff --git a/tests/endpoint.rs b/tests/endpoint.rs deleted file mode 100644 index 61a18a1..0000000 --- a/tests/endpoint.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -#![cfg(feature = "remote_endpoint")] - -use std::any::Any; -use std::result; -use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::sync::Arc; -use std::thread; -use std::time::Duration; - -use event_manager::utilities::subscribers::CounterSubscriber; -use event_manager::{self, EventManager, MutEventSubscriber, SubscriberId}; - -trait GenericSubscriber: MutEventSubscriber { - fn as_mut_any(&mut self) -> &mut dyn Any; -} - -impl GenericSubscriber for CounterSubscriber { - fn as_mut_any(&mut self) -> &mut dyn Any { - self - } -} - -// This test showcases the use of the three `RemoteEndpoint` methods: `call_blocking`, `fire`, -// and `kick`. The setting is we're moving an `EventManager` to a separate thread, and we want -// to register subscribers that remain fully owned by the manager (so, for example, it's not -// necessary to wrap them in a `Mutex` if event handling needs mutable access to their inner -// state). We also use the `GenericSubscriber` trait defined above to show how we can still -// obtain and interact with references to the actual types when event subscribers are registered -// as trait objects. -#[test] -fn test_endpoint() { - let mut event_manager = EventManager::>::new().unwrap(); - let endpoint = event_manager.remote_endpoint(); - let sub = Box::::default(); - - let run_count = Arc::new(AtomicU64::new(0)); - let keep_running = Arc::new(AtomicBool::new(true)); - - // These get moved into the following closure. - let run_count_clone = run_count.clone(); - let keep_running_clone = keep_running.clone(); - - // We spawn the thread that runs the event loop. - let thread_handle = thread::spawn(move || { - loop { - // The manager gets moved to the new thread here. - event_manager.run().unwrap(); - // Increment this counter every time the `run` method call above returns. Ordering - // is not that important here because the other thread only reads the value. - run_count_clone.fetch_add(1, Ordering::Relaxed); - - // When `keep_running` is no longer `true`, we break the loop and the - // thread will exit. - if !keep_running_clone.load(Ordering::Acquire) { - break; - } - } - }); - - // We're back to the main program thread from now on. - - // `run_count` should not change for now as there's no possible activity for the manager. - thread::sleep(Duration::from_millis(100)); - assert_eq!(run_count.load(Ordering::Relaxed), 0); - - // Use `call_blocking` to register a subscriber (and move it to the event loop thread under - // the ownership of the manager). `call_block` also allows us to inspect the result of the - // operation, but it blocks waiting for it and cannot be invoked from the same thread as - // the event loop. - let sub_id = endpoint - .call_blocking( - |sub_ops| -> result::Result { - Ok(sub_ops.add_subscriber(sub)) - }, - ) - .unwrap(); - - // We've added a subscriber. No subscriber events are fired yet, but the manager run - // loop went through one iteration when the endpoint message was received, so `run_count` - // has benn incremented. - thread::sleep(Duration::from_millis(100)); - assert_eq!(run_count.load(Ordering::Relaxed), 1); - - // Now let's activate the subscriber event. It's going to generate continuous activity until - // we explicitly clear it. We use `endpoint` to interact with the subscriber, because the - // latter is fully owned by the manager. Also, we make use of the `as_mut_any` method - // from our `GenericSubscriber` trait to get a reference to the actual subscriber type - // (which has been erased as a trait object from the manager's perspective). We use `fire` - // here, so we don't get a result. - // - // `fire` can also be used from the same thread as the `event_manager` runs on without causing - // a deadlock, because it doesn't get a result from the closure. For example, we can pass an - // endpoint to a subscriber, and use `fire` as part of `process` if it's helpful for that - // particular use case. - // - // Not getting a result from the closure means we have to deal with error conditions within. - // We use `unwrap` here, but that's ok because if the subscriber associated with `sub_id` is - // not present, then we have a serious error in our program logic. - endpoint - .fire(move |sub_ops| { - let sub = sub_ops.subscriber_mut(sub_id).unwrap(); - // The following `unwrap` cannot fail because we know the type is `CounterSubscriber`. - sub.as_mut_any() - .downcast_mut::() - .unwrap() - .trigger_event() - }) - .unwrap(); - - // The event will start triggering at this point, so `run_count` will increase. - thread::sleep(Duration::from_millis(100)); - assert!(run_count.load(Ordering::Relaxed) > 1); - - // Let's clear the subscriber event. Using `fire` again. - endpoint - .fire(move |sub_ops| { - let sub = sub_ops.subscriber_mut(sub_id).unwrap(); - // The following `unwrap` cannot fail because we know the actual type - // is `CounterSubscriber`. - sub.as_mut_any() - .downcast_mut::() - .unwrap() - .clear_event() - }) - .unwrap(); - - // We wait a bit more. The manager will be once more become blocked waiting for events. - thread::sleep(Duration::from_millis(100)); - - keep_running.store(false, Ordering::Release); - - // Trying to `join` the manager here would lead to a deadlock, because it will never read - // the value of `keep_running` to break the loop while stuck waiting for events. We use the - // `kick` endpoint method to force `EventManager::run()` to return. - - endpoint.kick().unwrap(); - - // We can `join` the manager thread and finalize now. - thread_handle.join().unwrap(); -} diff --git a/tests/multi_threaded.rs b/tests/multi_threaded.rs deleted file mode 100644 index 8f1d653..0000000 --- a/tests/multi_threaded.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use std::sync::{Arc, Mutex}; -use std::thread; - -use event_manager::utilities::subscribers::{ - CounterInnerMutSubscriber, CounterSubscriber, CounterSubscriberWithData, -}; -use event_manager::{EventManager, EventSubscriber, MutEventSubscriber, SubscriberOps}; - -// Showcase how you can manage subscribers of different types using the same event manager -// in a multithreaded context. -#[test] -fn test_diff_type_subscribers() { - let mut event_manager = EventManager::>>::new() - .expect("Cannot create event manager."); - - // The `CounterSubscriberWithData` expects to receive a number as a parameter that represents - // the number it can use as its inner Events data. - let data_subscriber = Arc::new(Mutex::new(CounterSubscriberWithData::new(1))); - data_subscriber.lock().unwrap().trigger_all_counters(); - - let counter_subscriber = Arc::new(Mutex::new(CounterSubscriber::default())); - counter_subscriber.lock().unwrap().trigger_event(); - - // Saving the ids allows to modify the subscribers in the event manager context (i.e. removing - // the subscribers from the event manager loop). - let ds_id = event_manager.add_subscriber(data_subscriber); - let cs_id = event_manager.add_subscriber(counter_subscriber); - - let thread_handle = thread::spawn(move || { - // In a typical application this would be an infinite loop with some break condition. - // For tests, 100 iterations should suffice. - for _ in 0..100 { - // When the event manager loop exits, it will return the number of events it - // handled. - let ev_count = event_manager.run().unwrap(); - // We are expecting the ev_count to be: - // 4 = 3 (events triggered from data_subscriber) + 1 (event from counter_subscriber). - assert_eq!(ev_count, 4); - } - - // One really important thing is to always remove the subscribers once you don't need them - // any more. - let _ = event_manager.remove_subscriber(ds_id).unwrap(); - let _ = event_manager.remove_subscriber(cs_id).unwrap(); - }); - thread_handle.join().unwrap(); -} - -// Showcase how you can manage a single type of subscriber using the same event manager -// in a multithreaded context. -#[test] -fn test_one_type_subscriber() { - let mut event_manager = - EventManager::::new().expect("Cannot create event manager"); - - // The `CounterSubscriberWithData` expects to receive the number it can use as its inner - // `Events` data as a parameter. - // Let's make sure that the inner data of the two subscribers will not overlap by keeping some - // numbers between the first_data of each subscriber. - let data_subscriber_1 = CounterSubscriberWithData::new(1); - let data_subscriber_2 = CounterSubscriberWithData::new(1999); - - // Saving the ids allows to modify the subscribers in the event manager context (i.e. removing - // the subscribers from the event manager loop). - let ds_id_1 = event_manager.add_subscriber(data_subscriber_1); - let ds_id_2 = event_manager.add_subscriber(data_subscriber_2); - - // Since we moved the ownership of the subscriber to the event manager, now we need to get - // a mutable reference from it so that we can modify them. - // Enclosing this in a code block so that the references are dropped when they're no longer - // needed. - { - let data_subscriber_1 = event_manager.subscriber_mut(ds_id_1).unwrap(); - data_subscriber_1.trigger_all_counters(); - - let data_subscriber_2 = event_manager.subscriber_mut(ds_id_2).unwrap(); - data_subscriber_2.trigger_all_counters(); - } - - let thread_handle = thread::spawn(move || { - // In a typical application this would be an infinite loop with some break condition. - // For tests, 100 iterations should suffice. - for _ in 0..100 { - // When the event manager loop exits, it will return the number of events it - // handled. - let ev_count = event_manager.run().unwrap(); - // Since we triggered all counters, we're expecting the number of events to always be - // 6 (3 from one subscriber and 3 from the other). - assert_eq!(ev_count, 6); - } - - // One really important thing is to always remove the subscribers once you don't need them - // any more. - // When calling remove_subscriber, you receive ownership of the subscriber object. - let data_subscriber_1 = event_manager.remove_subscriber(ds_id_1).unwrap(); - let data_subscriber_2 = event_manager.remove_subscriber(ds_id_2).unwrap(); - - // Let's check how the subscribers look after 100 iterations. - // Each subscriber's counter start from 0. When an event is triggered, each counter - // is incremented. So after 100 iterations when they're all triggered, we expect them - // to be 100. - let expected_subscriber_counters = vec![100, 100, 100]; - assert_eq!( - data_subscriber_1.get_all_counter_values(), - expected_subscriber_counters - ); - assert_eq!( - data_subscriber_2.get_all_counter_values(), - expected_subscriber_counters - ); - }); - - thread_handle.join().unwrap(); -} - -// Showcase how you can manage subscribers that make use of inner mutability using the event manager -// in a multithreaded context. -#[test] -fn test_subscriber_inner_mut() { - // We are using just the `CounterInnerMutSubscriber` subscriber, so we could've initialize - // the event manager as: EventManager::::new(). - // The typical application will have more than one subscriber type, so let's just pretend - // this is the case in this example as well. - let mut event_manager = EventManager::>::new() - .expect("Cannot create event manager"); - - let subscriber = Arc::new(CounterInnerMutSubscriber::default()); - subscriber.trigger_event(); - - // Let's just clone the subscriber before adding it to the event manager. This will allow us - // to use it from this thread without the need to call into EventManager::subscriber_mut(). - let subscriber_id = event_manager.add_subscriber(subscriber.clone()); - - let thread_handle = thread::spawn(move || { - for _ in 0..100 { - // When the event manager loop exits, it will return the number of events it - // handled. - let ev_count = event_manager.run().unwrap(); - assert_eq!(ev_count, 1); - } - - assert!(event_manager.remove_subscriber(subscriber_id).is_ok()); - }); - thread_handle.join().unwrap(); - - assert_eq!(subscriber.counter(), 100); -} diff --git a/tests/negative_tests.rs b/tests/negative_tests.rs deleted file mode 100644 index ec53ae9..0000000 --- a/tests/negative_tests.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use std::os::unix::{io::AsRawFd, net::UnixStream}; -use std::sync::{ - atomic::{AtomicU64, Ordering}, - Arc, -}; - -use event_manager::{ - EventManager, EventOps, EventSubscriber, Events, SubscriberOps, MAX_READY_EVENTS_CAPACITY, -}; -use vmm_sys_util::epoll::EventSet; - -#[derive(Debug)] -struct UnixStreamSubscriber { - stream: UnixStream, - rhup_count: AtomicU64, - // When this flag is used, in the process function the subscriber will - // unregister the fd where an error was received. - // The errors that need to be handled: `EventSet::HANG_UP`, `EventSet::ERROR`. - with_unregister_on_err: bool, -} - -impl UnixStreamSubscriber { - fn new(stream: UnixStream) -> UnixStreamSubscriber { - Self { - stream, - rhup_count: AtomicU64::new(0), - with_unregister_on_err: false, - } - } - - fn new_with_unregister_on_err(stream: UnixStream) -> UnixStreamSubscriber { - Self { - stream, - rhup_count: AtomicU64::new(0), - with_unregister_on_err: true, - } - } -} - -impl EventSubscriber for UnixStreamSubscriber { - fn process(&self, events: Events, ops: &mut EventOps<'_>) { - if events.event_set().contains(EventSet::HANG_UP) { - let _ = self.rhup_count.fetch_add(1, Ordering::Relaxed); - if self.with_unregister_on_err { - ops.remove(Events::empty(&self.stream)).unwrap(); - } - } - } - - fn init(&self, ops: &mut EventOps<'_>) { - ops.add(Events::new(&self.stream, EventSet::IN)).unwrap(); - } -} - -#[test] -fn test_handling_errors_in_subscriber() { - let (sock1, sock2) = UnixStream::pair().unwrap(); - - let mut event_manager = EventManager::>::new().unwrap(); - let subscriber = Arc::new(UnixStreamSubscriber::new(sock1)); - event_manager.add_subscriber(subscriber.clone()); - - // SAFETY: safe because `sock2` is a valid Unix socket, as asserted by the `unwrap` above. - unsafe { libc::close(sock2.as_raw_fd()) }; - - event_manager.run_with_timeout(100).unwrap(); - event_manager.run_with_timeout(100).unwrap(); - event_manager.run_with_timeout(100).unwrap(); - - // Since the subscriber did not remove the event from its watch list, the - // `EPOLLRHUP` error will continuously be a ready event each time `run` is called. - // We called `run_with_timeout` 3 times, hence we expect `rhup_count` to be 3. - assert_eq!(subscriber.rhup_count.load(Ordering::Relaxed), 3); - - let (sock1, sock2) = UnixStream::pair().unwrap(); - let subscriber_with_unregister = - Arc::new(UnixStreamSubscriber::new_with_unregister_on_err(sock1)); - event_manager.add_subscriber(subscriber_with_unregister); - - // SAFETY: safe because `sock2` is a valid Unix socket, as asserted by the `unwrap` above. - unsafe { libc::close(sock2.as_raw_fd()) }; - - let ready_list_len = event_manager.run_with_timeout(100).unwrap(); - assert_eq!(ready_list_len, 2); - // At this point the `subscriber_with_unregister` should not yield events anymore. - // We expect the number of ready fds to be 1. - let ready_list_len = event_manager.run_with_timeout(100).unwrap(); - assert_eq!(ready_list_len, 1); -} - -#[test] -fn test_max_ready_list_size() { - assert!( - EventManager::>::new_with_capacity(MAX_READY_EVENTS_CAPACITY) - .is_ok() - ); - assert!(EventManager::>::new_with_capacity( - MAX_READY_EVENTS_CAPACITY + 1 - ) - .is_err()); - assert!(EventManager::>::new_with_capacity(usize::MAX).is_err()) -} diff --git a/tests/regressions.rs b/tests/regressions.rs deleted file mode 100644 index 92c83ec..0000000 --- a/tests/regressions.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2020 Alibaba Cloud. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause - -use event_manager::utilities::subscribers::CounterSubscriberWithData; -use event_manager::{EventManager, SubscriberOps}; - -// Test for the race condition reported by: Karthik.n -// FYI: https://github.com/rust-vmm/event-manager/issues/41 -#[test] -fn test_reuse_file_descriptor() { - let mut event_manager = EventManager::::new().unwrap(); - let mut counter_subscriber = CounterSubscriberWithData::new(0); - - // Set flag to toggle the registration of all three fds on the first epoll event, so the final - // event counter should be 1. - counter_subscriber.set_toggle_registry(true); - counter_subscriber.trigger_all_counters(); - let id = event_manager.add_subscriber(counter_subscriber); - - event_manager.run().unwrap(); - let c_ref = event_manager.subscriber_mut(id).unwrap(); - let counters = c_ref.get_all_counter_values(); - assert_eq!(counters[0] + counters[1] + counters[2], 1); -} From ebb93dc730b7776b4bffa803fa69f0145abcfb6a Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Wed, 14 Jun 2023 17:31:00 +0100 Subject: [PATCH 2/3] feat: Return values Allowing a return value for the event closures among many things allows for proper error propagation. Signed-off-by: Jonathan Woollett-Light --- benches/main.rs | 27 +++++++----- src/lib.rs | 115 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 99 insertions(+), 43 deletions(-) diff --git a/benches/main.rs b/benches/main.rs index 9761f87..d6fbf5c 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -29,7 +29,7 @@ fn run_basic_subscriber(c: &mut Criterion) { OwnedFd::from_raw_fd(raw_fd) }; - event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + event_manager.add(event_fd.as_fd(), EventSet::IN | EventSet::ERROR | EventSet::HANG_UP, Box::new(move |_:&mut EventManager<()>, event_set: EventSet| { match event_set { EventSet::IN => (), EventSet::ERROR => { @@ -47,9 +47,10 @@ fn run_basic_subscriber(c: &mut Criterion) { event_fd }).collect::>(); + let expected = vec![();usize::try_from(no_of_subscribers).unwrap()]; c.bench_function("process_basic", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(no_of_subscribers)); + assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); }) }); @@ -75,7 +76,7 @@ fn run_arc_mutex_subscriber(c: &mut Criterion) { let counter = Arc::new(Mutex::new(0u64)); let counter_clone = counter.clone(); - event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager<()>, event_set: EventSet| { match event_set { EventSet::IN => { *counter_clone.lock().unwrap() += 1; @@ -95,9 +96,10 @@ fn run_arc_mutex_subscriber(c: &mut Criterion) { (event_fd,counter) }).collect::>(); + let expected = vec![();usize::try_from(no_of_subscribers).unwrap()]; c.bench_function("process_with_arc_mutex", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(no_of_subscribers)); + assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); }) }); @@ -124,7 +126,7 @@ fn run_subscriber_with_inner_mut(c: &mut Criterion) { let counter = Arc::new(AtomicU64::new(0)); let counter_clone = counter.clone(); - event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager<()>, event_set: EventSet| { match event_set { EventSet::IN => { counter_clone.fetch_add(1, Ordering::SeqCst); @@ -144,9 +146,10 @@ fn run_subscriber_with_inner_mut(c: &mut Criterion) { (event_fd,counter) }).collect::>(); + let expected = vec![();usize::try_from(no_of_subscribers).unwrap()]; c.bench_function("process_with_inner_mut", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(no_of_subscribers)); + assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); }) }); @@ -177,7 +180,7 @@ fn run_multiple_subscriber_types(c: &mut Criterion) { let counter = Arc::new(AtomicU64::new(0)); let counter_clone = counter.clone(); - event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager<()>, event_set: EventSet| { match event_set { EventSet::IN => { counter_clone.fetch_add(1, Ordering::SeqCst); @@ -223,7 +226,7 @@ fn run_multiple_subscriber_types(c: &mut Criterion) { inner_subscribers[i].as_fd(), EventSet::IN | EventSet::ERROR | EventSet::HANG_UP, Box::new( - move |_: &mut EventManager, event_set: EventSet| match event_set { + move |_: &mut EventManager<()>, event_set: EventSet| match event_set { EventSet::IN => { data_clone[i].fetch_add(1, Ordering::SeqCst); } @@ -244,9 +247,10 @@ fn run_multiple_subscriber_types(c: &mut Criterion) { }) .collect::>(); + let expected = vec![();usize::try_from(total).unwrap()]; c.bench_function("process_dynamic_dispatch", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(total)); + assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); }) }); @@ -272,7 +276,7 @@ fn run_with_few_active_events(c: &mut Criterion) { OwnedFd::from_raw_fd(raw_fd) }; - event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager, event_set: EventSet| { + event_manager.add(event_fd.as_fd(),EventSet::IN | EventSet::ERROR | EventSet::HANG_UP,Box::new(move |_:&mut EventManager<()>, event_set: EventSet| { match event_set { EventSet::IN => (), EventSet::ERROR => { @@ -290,9 +294,10 @@ fn run_with_few_active_events(c: &mut Criterion) { event_fd }).collect::>(); + let expected = vec![();usize::try_from(active).unwrap()]; c.bench_function("process_dispatch_few_events", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(active)); + assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); }) }); diff --git a/src/lib.rs b/src/lib.rs index 8eba029..4df6727 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use vmm_sys_util::epoll::EventSet; /// The function thats runs when an event occurs. -type Action = Box; +type Action = Box, EventSet) -> T>; fn errno() -> i32 { // SAFETY: Always safe. @@ -14,21 +14,24 @@ fn errno() -> i32 { } #[derive(Debug)] -pub struct BufferedEventManager { - event_manager: EventManager, +pub struct BufferedEventManager { + event_manager: EventManager, // TODO The length is always unused, a custom type could thus save `size_of::()` bytes. buffer: Vec, + // TODO The length is always unused, a custom type could thus save `size_of::()` bytes. + output_buffer: Vec, } -impl BufferedEventManager { +impl BufferedEventManager { /// Add an entry to the interest list of the epoll file descriptor. /// /// # Errors /// /// When [`libc::epoll_ctl`] returns `-1`. - pub fn add(&mut self, fd: T, events: EventSet, f: Action) -> Result<(), i32> { + pub fn add(&mut self, fd: Fd, events: EventSet, f: Action) -> Result<(), i32> { let res = self.event_manager.add(fd, events, f); self.buffer.reserve(self.event_manager.events.len()); + self.output_buffer.reserve(self.event_manager.events.len()); res } @@ -39,7 +42,7 @@ impl BufferedEventManager { /// # Errors /// /// When [`libc::epoll_ctl`] returns `-1`. - pub fn del(&mut self, fd: T) -> Result { + pub fn del(&mut self, fd: Fd) -> Result { self.event_manager.del(fd) } @@ -55,13 +58,21 @@ impl BufferedEventManager { /// /// When the value given in timeout does not fit within an `i32` e.g. /// `timeout.map(|u| i32::try_from(u).unwrap())`. - pub fn wait(&mut self, timeout: Option) -> Result { + pub fn wait(&mut self, timeout: Option) -> Result<&[T], i32> { // SAFETY: `EventManager::wait` initializes N element from the start of the slice and only // accesses these, thus it will never access uninitialized memory, making this safe. unsafe { self.buffer.set_len(self.buffer.capacity()); + self.output_buffer.set_len(self.output_buffer.capacity()); + } + let n = self + .event_manager + .wait(timeout, &mut self.buffer, &mut self.output_buffer)?; + unsafe { + Ok(self + .output_buffer + .get_unchecked(..usize::try_from(n).unwrap_unchecked())) } - self.event_manager.wait(timeout, &mut self.buffer) } /// Creates new event manager. @@ -72,29 +83,31 @@ impl BufferedEventManager { pub fn new(close_exec: bool) -> Result { Ok(BufferedEventManager { event_manager: EventManager::new(close_exec)?, - buffer: Vec::new(), + buffer: Vec::with_capacity(0), + output_buffer: Vec::with_capacity(0), }) } pub fn with_capacity(close_exec: bool, capacity: usize) -> Result { Ok(BufferedEventManager { event_manager: EventManager::new(close_exec)?, buffer: Vec::with_capacity(capacity), + output_buffer: Vec::with_capacity(capacity), }) } } -impl Default for BufferedEventManager { +impl Default for BufferedEventManager { fn default() -> Self { Self::new(false).unwrap() } } -pub struct EventManager { +pub struct EventManager { epfd: RawFd, - events: HashMap, + events: HashMap>, } -impl std::fmt::Debug for EventManager { +impl std::fmt::Debug for EventManager { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("EventManager") .field("epfd", &self.epfd) @@ -110,13 +123,13 @@ impl std::fmt::Debug for EventManager { } } -impl EventManager { +impl EventManager { /// Add an entry to the interest list of the epoll file descriptor. /// /// # Errors /// /// When [`libc::epoll_ctl`] returns `-1`. - pub fn add(&mut self, fd: T, events: EventSet, f: Action) -> Result<(), i32> { + pub fn add(&mut self, fd: Fd, events: EventSet, f: Action) -> Result<(), i32> { let mut event = libc::epoll_event { events: events.bits(), r#u64: u64::try_from(fd.as_raw_fd()).unwrap(), @@ -140,7 +153,7 @@ impl EventManager { /// # Errors /// /// When [`libc::epoll_ctl`] returns `-1`. - pub fn del(&mut self, fd: T) -> Result { + pub fn del(&mut self, fd: Fd) -> Result { match self.events.remove(&fd.as_raw_fd()) { Some(_) => { // SAFETY: Safe when `fd` is a valid file descriptor. @@ -177,13 +190,14 @@ impl EventManager { &mut self, timeout: Option, buffer: &mut [libc::epoll_event], + output_buffer: &mut [T], ) -> Result { // SAFETY: Always safe. match unsafe { libc::epoll_wait( self.epfd, buffer.as_mut_ptr(), - buffer.len().try_into().unwrap(), + buffer.len().try_into().unwrap_unchecked(), timeout.map_or(-1i32, |u| i32::try_from(u).unwrap()), ) } { @@ -195,11 +209,11 @@ impl EventManager { let event = buffer[i]; // For all events which can fire there exists an entry within `self.events` thus // it is safe to unwrap here. - let f: *const dyn Fn(&mut EventManager, EventSet) = self + let f: *const dyn Fn(&mut EventManager, EventSet) -> T = self .events .get(&i32::try_from(event.u64).unwrap_unchecked()) .unwrap_unchecked(); - (*f)(self, EventSet::from_bits_unchecked(event.events)); + output_buffer[i] = (*f)(self, EventSet::from_bits_unchecked(event.events)); } Ok(n) }, @@ -224,7 +238,7 @@ impl EventManager { } } -impl Default for EventManager { +impl Default for EventManager { fn default() -> Self { Self::new(false).unwrap() } @@ -241,6 +255,7 @@ mod tests { fn delete() { static COUNT: AtomicBool = AtomicBool::new(false); let mut manager = BufferedEventManager::default(); + // We set value to 1 so it will trigger on a read event. // SAFETY: Always safe. let event_fd = unsafe { @@ -248,13 +263,14 @@ mod tests { assert_ne!(fd, -1); fd }; + manager .add( event_fd, EventSet::IN, // A closure which will flip the atomic boolean then remove the event fd from the // interest list. - Box::new(move |x: &mut EventManager, _: EventSet| { + Box::new(move |x: &mut EventManager<()>, _| { // Flips the atomic. let cur = COUNT.load(Ordering::SeqCst); COUNT.store(!cur, Ordering::SeqCst); @@ -270,14 +286,17 @@ mod tests { // The file descriptor has been pre-armed, this will immediately call the respective // closure. - assert_eq!(manager.wait(Some(10)), Ok(1)); + let vec = vec![()]; + assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); + // As the closure will flip the atomic boolean we assert it has flipped correctly. assert!(COUNT.load(Ordering::SeqCst)); // At this point we have called the closure, since the closure removes the event fd from the // interest list of the inner epoll, calling this again should timeout as there are no event // fd in the inner epolls interest list which could trigger. - assert_eq!(manager.wait(Some(10)), Ok(0)); + let vec = vec![]; + assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); // As the `EventManager::wait` should timeout the value of the atomic boolean should not be // flipped. assert!(COUNT.load(Ordering::SeqCst)); @@ -298,7 +317,7 @@ mod tests { .add( event_fd, EventSet::IN, - Box::new(|_: &mut EventManager, _: EventSet| { + Box::new(|_, _| { // Flips the atomic. let cur = COUNT.load(Ordering::SeqCst); COUNT.store(!cur, Ordering::SeqCst); @@ -310,13 +329,15 @@ mod tests { assert!(!COUNT.load(Ordering::SeqCst)); // As the closure will flip the atomic boolean we assert it has flipped correctly. - assert_eq!(manager.wait(Some(10)), Ok(1)); + let vec = vec![()]; + assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); // As the closure will flip the atomic boolean we assert it has flipped correctly. assert!(COUNT.load(Ordering::SeqCst)); // The file descriptor has been pre-armed, this will immediately call the respective // closure. - assert_eq!(manager.wait(Some(10)), Ok(1)); + let vec = vec![()]; + assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); // As the closure will flip the atomic boolean we assert it has flipped correctly. assert!(!COUNT.load(Ordering::SeqCst)); } @@ -332,7 +353,7 @@ mod tests { let mut manager = BufferedEventManager::default(); // Setup eventfd's and counters. - let subscribers = (0..100) + let subscribers = (0..SUBSCRIBERS) .map(|_| { // SAFETY: Always safe. let event_fd = unsafe { @@ -347,9 +368,7 @@ mod tests { .add( event_fd.as_fd(), EventSet::IN, - Box::new(move |_: &mut EventManager, _: EventSet| { - counter_clone.fetch_add(1, Ordering::SeqCst); - }), + Box::new(move |_, _| counter_clone.fetch_add(1, Ordering::SeqCst)), ) .unwrap(); @@ -375,10 +394,42 @@ mod tests { } // Check counter are the correct values - let n = i32::try_from(FIRING).unwrap(); - assert_eq!(manager.wait(None), Ok(n)); + let arr = [0; FIRING]; + assert_eq!(manager.wait(None), Ok(arr.as_slice())); for i in set { assert_eq!(subscribers[i].1.load(Ordering::SeqCst), 1); } } + + #[test] + fn results() { + let mut manager = BufferedEventManager::default(); + + // We set value to 1 so it will trigger on a read event. + // SAFETY: Always safe. + let event_fd = unsafe { + let fd = libc::eventfd(1, 0); + assert_ne!(fd, -1); + fd + }; + + manager + .add(event_fd, EventSet::IN, Box::new(|_, _| Ok(()))) + .unwrap(); + + // We set value to 1 so it will trigger on a read event. + // SAFETY: Always safe. + let event_fd = unsafe { + let fd = libc::eventfd(1, 0); + assert_ne!(fd, -1); + fd + }; + + manager + .add(event_fd, EventSet::IN, Box::new(|_, _| Err(()))) + .unwrap(); + + let arr = [Ok(()), Err(())]; + assert_eq!(manager.wait(None), Ok(arr.as_slice())); + } } From 5fd3a0208f6e45da9337b5afa93bce47bc1be168 Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Wed, 14 Jun 2023 19:16:37 +0100 Subject: [PATCH 3/3] feat: Lazy iterator `wait` now returns an iterator that allows lazily evaluating event closures. This allows for returning an error when the 1st closure returns an error, rather than always needing to evaluate every closure. Signed-off-by: Jonathan Woollett-Light --- benches/main.rs | 48 +++++++++++++----- src/lib.rs | 132 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 128 insertions(+), 52 deletions(-) diff --git a/benches/main.rs b/benches/main.rs index d6fbf5c..0763732 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -47,10 +47,14 @@ fn run_basic_subscriber(c: &mut Criterion) { event_fd }).collect::>(); - let expected = vec![();usize::try_from(no_of_subscribers).unwrap()]; + let n = usize::try_from(no_of_subscribers).unwrap(); c.bench_function("process_basic", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); + let mut iter = event_manager.wait(Some(0)).unwrap(); + for _ in 0..n { + assert_eq!(iter.next(), Some(&mut ())); + } + assert_eq!(iter.next(), None); }) }); @@ -96,10 +100,14 @@ fn run_arc_mutex_subscriber(c: &mut Criterion) { (event_fd,counter) }).collect::>(); - let expected = vec![();usize::try_from(no_of_subscribers).unwrap()]; + let n = usize::try_from(no_of_subscribers).unwrap(); c.bench_function("process_with_arc_mutex", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); + let mut iter = event_manager.wait(Some(0)).unwrap(); + for _ in 0..n { + assert_eq!(iter.next(), Some(&mut ())); + } + assert_eq!(iter.next(), None); }) }); @@ -146,10 +154,14 @@ fn run_subscriber_with_inner_mut(c: &mut Criterion) { (event_fd,counter) }).collect::>(); - let expected = vec![();usize::try_from(no_of_subscribers).unwrap()]; + let n = usize::try_from(no_of_subscribers).unwrap(); c.bench_function("process_with_inner_mut", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); + let mut iter = event_manager.wait(Some(0)).unwrap(); + for _ in 0..n { + assert_eq!(iter.next(), Some(&mut ())); + } + assert_eq!(iter.next(), None); }) }); @@ -225,8 +237,8 @@ fn run_multiple_subscriber_types(c: &mut Criterion) { .add( inner_subscribers[i].as_fd(), EventSet::IN | EventSet::ERROR | EventSet::HANG_UP, - Box::new( - move |_: &mut EventManager<()>, event_set: EventSet| match event_set { + Box::new(move |_: &mut EventManager<()>, event_set: EventSet| { + match event_set { EventSet::IN => { data_clone[i].fetch_add(1, Ordering::SeqCst); } @@ -237,8 +249,8 @@ fn run_multiple_subscriber_types(c: &mut Criterion) { panic!("Cannot continue execution. Associated fd was closed."); } _ => {} - }, - ), + } + }), ) .unwrap(); } @@ -247,10 +259,14 @@ fn run_multiple_subscriber_types(c: &mut Criterion) { }) .collect::>(); - let expected = vec![();usize::try_from(total).unwrap()]; + let n = usize::try_from(total).unwrap(); c.bench_function("process_dynamic_dispatch", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); + let mut iter = event_manager.wait(Some(0)).unwrap(); + for _ in 0..n { + assert_eq!(iter.next(), Some(&mut ())); + } + assert_eq!(iter.next(), None); }) }); @@ -294,10 +310,14 @@ fn run_with_few_active_events(c: &mut Criterion) { event_fd }).collect::>(); - let expected = vec![();usize::try_from(active).unwrap()]; + let n = usize::try_from(active).unwrap(); c.bench_function("process_dispatch_few_events", |b| { b.iter(|| { - assert_eq!(event_manager.wait(Some(0)), Ok(expected.as_slice())); + let mut iter = event_manager.wait(Some(0)).unwrap(); + for _ in 0..n { + assert_eq!(iter.next(), Some(&mut ())); + } + assert_eq!(iter.next(), None); }) }); diff --git a/src/lib.rs b/src/lib.rs index 4df6727..dc60944 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,21 +58,15 @@ impl BufferedEventManager { /// /// When the value given in timeout does not fit within an `i32` e.g. /// `timeout.map(|u| i32::try_from(u).unwrap())`. - pub fn wait(&mut self, timeout: Option) -> Result<&[T], i32> { + pub fn wait(&mut self, timeout: Option) -> Result, i32> { // SAFETY: `EventManager::wait` initializes N element from the start of the slice and only // accesses these, thus it will never access uninitialized memory, making this safe. unsafe { self.buffer.set_len(self.buffer.capacity()); self.output_buffer.set_len(self.output_buffer.capacity()); } - let n = self - .event_manager - .wait(timeout, &mut self.buffer, &mut self.output_buffer)?; - unsafe { - Ok(self - .output_buffer - .get_unchecked(..usize::try_from(n).unwrap_unchecked())) - } + self.event_manager + .wait(timeout, &mut self.buffer, &mut self.output_buffer) } /// Creates new event manager. @@ -123,6 +117,62 @@ impl std::fmt::Debug for EventManager { } } +#[derive(Debug)] +pub struct Iter<'a, T> { + event_manager: &'a mut EventManager, + buffer: &'a [libc::epoll_event], + output_buffer: &'a mut [T], + index: usize, +} +impl<'a, T> Iter<'a, T> { + /// Returns a mutable slice of all the items previously returned by [`Iter::next`]. + pub fn as_mut_slice(&'a mut self) -> &'a mut [T] { + &mut self.output_buffer[..self.index] + } + /// Returns a slice of all the items previously returned by [`Iter::next`]. + pub fn as_slice(&'a self) -> &'a [T] { + &self.output_buffer[..self.index] + } +} +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a mut T; + fn next(&mut self) -> Option { + debug_assert_eq!(self.buffer.len(), self.output_buffer.len()); + + if self.index >= self.buffer.len() { + return None; + } + unsafe { + let event = self.buffer.get_unchecked(self.index); + // For all events which can fire there exists an entry within `self.events` thus + // it is safe to unwrap here. + let f: *const dyn Fn(&mut EventManager, EventSet) -> T = self + .event_manager + .events + .get(&i32::try_from(event.u64).unwrap_unchecked()) + .unwrap_unchecked(); + self.output_buffer[self.index] = (*f)( + self.event_manager, + EventSet::from_bits_unchecked(event.events), + ); + + // SAFETY: This is always safe. This is required as the current standard library trait + // doesn't support lending iteraor semantics. + let temp = Some(std::mem::transmute(&mut self.output_buffer[self.index])); + + self.index += 1; + + temp + } + } + + /// O(1) + fn size_hint(&self) -> (usize, Option) { + let n = self.buffer.len() - self.index; + (n, Some(n)) + } +} + impl EventManager { /// Add an entry to the interest list of the epoll file descriptor. /// @@ -186,12 +236,12 @@ impl EventManager { /// /// When the value given in timeout does not fit within an `i32` e.g. /// `timeout.map(|u| i32::try_from(u).unwrap())`. - pub fn wait( - &mut self, + pub fn wait<'a>( + &'a mut self, timeout: Option, - buffer: &mut [libc::epoll_event], - output_buffer: &mut [T], - ) -> Result { + buffer: &'a mut [libc::epoll_event], + output_buffer: &'a mut [T], + ) -> Result, i32> { // SAFETY: Always safe. match unsafe { libc::epoll_wait( @@ -204,18 +254,13 @@ impl EventManager { -1 => Err(errno()), // SAFETY: `x` elements are initialized by `libc::epoll_wait`. n @ 0.. => unsafe { - #[allow(clippy::needless_range_loop)] - for i in 0..usize::try_from(n).unwrap_unchecked() { - let event = buffer[i]; - // For all events which can fire there exists an entry within `self.events` thus - // it is safe to unwrap here. - let f: *const dyn Fn(&mut EventManager, EventSet) -> T = self - .events - .get(&i32::try_from(event.u64).unwrap_unchecked()) - .unwrap_unchecked(); - output_buffer[i] = (*f)(self, EventSet::from_bits_unchecked(event.events)); - } - Ok(n) + let n = usize::try_from(n).unwrap_unchecked(); + Ok(Iter { + event_manager: self, + buffer: &mut buffer[..n], + output_buffer: &mut output_buffer[..n], + index: 0, + }) }, _ => unreachable!(), } @@ -286,8 +331,9 @@ mod tests { // The file descriptor has been pre-armed, this will immediately call the respective // closure. - let vec = vec![()]; - assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); + let mut iter = manager.wait(Some(10)).unwrap(); + assert_eq!(iter.next(), Some(&mut ())); + assert_eq!(iter.next(), None); // As the closure will flip the atomic boolean we assert it has flipped correctly. assert!(COUNT.load(Ordering::SeqCst)); @@ -295,8 +341,9 @@ mod tests { // At this point we have called the closure, since the closure removes the event fd from the // interest list of the inner epoll, calling this again should timeout as there are no event // fd in the inner epolls interest list which could trigger. - let vec = vec![]; - assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); + let mut iter = manager.wait(Some(10)).unwrap(); + assert_eq!(iter.next(), None); + // As the `EventManager::wait` should timeout the value of the atomic boolean should not be // flipped. assert!(COUNT.load(Ordering::SeqCst)); @@ -329,15 +376,18 @@ mod tests { assert!(!COUNT.load(Ordering::SeqCst)); // As the closure will flip the atomic boolean we assert it has flipped correctly. - let vec = vec![()]; - assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); + let mut iter = manager.wait(Some(10)).unwrap(); + assert_eq!(iter.next(), Some(&mut ())); + assert_eq!(iter.next(), None); + // As the closure will flip the atomic boolean we assert it has flipped correctly. assert!(COUNT.load(Ordering::SeqCst)); // The file descriptor has been pre-armed, this will immediately call the respective // closure. - let vec = vec![()]; - assert_eq!(manager.wait(Some(10)), Ok(vec.as_slice())); + let mut iter = manager.wait(Some(10)).unwrap(); + assert_eq!(iter.next(), Some(&mut ())); + assert_eq!(iter.next(), None); // As the closure will flip the atomic boolean we assert it has flipped correctly. assert!(!COUNT.load(Ordering::SeqCst)); } @@ -394,8 +444,12 @@ mod tests { } // Check counter are the correct values - let arr = [0; FIRING]; - assert_eq!(manager.wait(None), Ok(arr.as_slice())); + let mut iter = manager.wait(None).unwrap(); + for _ in 0..FIRING { + assert_eq!(iter.next(), Some(&mut 0)); + } + assert_eq!(iter.next(), None); + for i in set { assert_eq!(subscribers[i].1.load(Ordering::SeqCst), 1); } @@ -429,7 +483,9 @@ mod tests { .add(event_fd, EventSet::IN, Box::new(|_, _| Err(()))) .unwrap(); - let arr = [Ok(()), Err(())]; - assert_eq!(manager.wait(None), Ok(arr.as_slice())); + let mut iter = manager.wait(None).unwrap(); + assert_eq!(iter.next(), Some(&mut Ok(()))); + assert_eq!(iter.next(), Some(&mut Err(()))); + assert_eq!(iter.next(), None); } }