Skip to content

Commit f199fa8

Browse files
author
Jonathan Woollett-Light
committed
feat: Procedural overhaul
Signed-off-by: Jonathan Woollett-Light <[email protected]>
1 parent 6970de9 commit f199fa8

23 files changed

+662
-2664
lines changed

.buildkite/custom-tests.json

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,5 @@
11
{
22
"tests": [
3-
{
4-
"test_name": "build-gnu-remote_endpoint",
5-
"command": "cargo build --release --features=remote_endpoint",
6-
"platform": [
7-
"x86_64",
8-
"aarch64"
9-
]
10-
},
11-
{
12-
"test_name": "build-musl-remote_endpoint",
13-
"command": "cargo build --release --features=remote_endpoint --target {target_platform}-unknown-linux-musl",
14-
"platform": [
15-
"x86_64",
16-
"aarch64"
17-
]
18-
},
19-
{
20-
"test_name": "check-warnings-remote_endpoint",
21-
"command": "RUSTFLAGS=\"-D warnings\" cargo check --features=remote_endpoint",
22-
"platform": [
23-
"x86_64",
24-
"aarch64"
25-
]
26-
},
273
{
284
"test_name": "performance",
295
"command": "pytest -s rust-vmm-ci/integration_tests/test_benchmark.py",

Cargo.toml

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,12 @@ edition = "2021"
1111

1212
[dependencies]
1313
vmm-sys-util = "0.11.0"
14-
libc = "0.2.39"
14+
libc = { version = "0.2.39", features = ["extra_traits"] }
1515

1616
[dev-dependencies]
17-
criterion = "0.3.5"
18-
19-
[features]
20-
remote_endpoint = []
21-
test_utilities = []
17+
criterion = "0.5.1"
18+
rand = "0.8.5"
2219

2320
[[bench]]
2421
name = "main"
25-
harness = false
26-
27-
[lib]
28-
bench = false # https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options
29-
30-
[profile.bench]
31-
lto = true
32-
codegen-units = 1
22+
harness = false

README.md

Lines changed: 31 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -10,160 +10,43 @@ mechanism for handling I/O notifications.
1010

1111
## Design
1212

13-
This crate is built around two abstractions:
14-
- Event Manager
15-
- Event Subscriber
16-
17-
The subscriber defines and registers an interest list with the event manager.
18-
The interest list represents the events that the subscriber wants to monitor.
19-
20-
The Event Manager allows adding and removing subscribers, and provides
21-
APIs through which the subscribers can be updated in terms of events in their
22-
interest list. These actions are abstracted through the `SubscriberOps` trait.
23-
24-
To interface with the Event Manager, the Event Subscribers need to provide an
25-
initialization function, and a callback for when events in the
26-
interest list become ready. The subscribers can update their interest list
27-
when handling ready events. These actions are abstracted through the
28-
`EventSubscriber` and `MutEventSubscriber` traits. They contain the same
29-
methods, but the former only requires immutable `self` borrows, whereas the
30-
latter requires mutable borrows. Any type implementing `EventSubscriber`
31-
automatically implements `MutEventSubscriber` as well.
32-
33-
A typical event-based application creates the event manager, registers
34-
subscribers, and then calls into the event manager's `run` function in a loop.
35-
Behind the scenes, the event manager calls into `epoll::wait` and maps the file
36-
descriptors in the ready list to the subscribers it manages. The event manager
37-
calls the subscriber's `process` function (its registered callback). When
38-
dispatching the events, the event manager creates a specialized object and
39-
passes it in the callback function so that the subscribers can use it to alter
40-
their interest list.
41-
42-
![](docs/event-manager.png)
13+
This crate offers an abstraction (`EventManager`) over [epoll](https://man7.org/linux/man-pages/man7/epoll.7.html) that
14+
allows for more ergonomic usage with many file descriptors.
15+
16+
The `EventManager` allows adding and removing file descriptors with a callback closure. The
17+
`EventManager` interest list can also be modified within these callback closures.
18+
19+
A typical event-based application:
20+
21+
1. Creates the `EventManager` (`EventManager::default()`).
22+
2. Registers file descriptors with (`EventManager::add`).
23+
3. Calls `EventManager::wait` in a loop.
4324

4425
Read more in the [design document](docs/DESIGN.md).
4526

46-
## Implementing an Event Subscriber
47-
48-
The event subscriber has full control over the events that it monitors.
49-
The events need to be added to the event manager's loop as part of the
50-
`init` function. Adding events to the loop can return errors, and it is
51-
the responsibility of the subscriber to handle them.
52-
53-
Similarly, the event subscriber is in full control of the ready events.
54-
When an event becomes ready, the event manager will call into the subscriber
55-
`process` function. The subscriber SHOULD handle the following events which
56-
are always returned when they occur (they don't need to be registered):
57-
- `EventSet::ERROR` - an error occurred on the monitor file descriptor.
58-
- `EventSet::HANG_UP` - hang up happened on the associated fd.
59-
- `EventSet::READ_HANG_UP` - hang up when the registered event is edge
60-
triggered.
61-
62-
For more details about the error cases, you can check the
63-
[`epoll_ctl documentation`](https://www.man7.org/linux/man-pages/man2/epoll_ctl.2.html).
64-
65-
66-
## Initializing the Event Manager
67-
68-
The `EventManager` uses a generic type parameter which represents the
69-
subscriber type. The crate provides automatic implementations of
70-
`EventSubscriber` for `Arc<T>` and `Rc<T>` (for any `T: EventSubscriber +?Sized`),
71-
together with automatic implementations of `MutEventSubscriber` for `Mutex<T>`
72-
and `RefCell<T>` (for any `T: MutEventSubscriber + ?Sized`). The generic type
73-
parameter enables either static or dynamic dispatch.
74-
75-
This crate has no default features. The optional `remote_endpoint`
76-
feature enables interactions with the `EventManager` from different threads
77-
without the need of more intrusive synchronization.
78-
79-
## Examples
80-
81-
For closer to real life use cases, please check the examples in
82-
[tests](tests).
83-
84-
### Basic Single Thread Subscriber
85-
86-
#### Implementing a Basic Subscriber
87-
88-
```rust
89-
use event_manager::{EventOps, Events, MutEventSubscriber};
90-
use vmm_sys_util::{eventfd::EventFd, epoll::EventSet};
91-
92-
use std::os::unix::io::AsRawFd;
93-
use std::fmt::{Display, Formatter, Result};
94-
95-
pub struct CounterSubscriber {
96-
event_fd: EventFd,
97-
counter: u64,
98-
}
99-
100-
impl CounterSubscriber {
101-
pub fn new() -> Self {
102-
Self {
103-
event_fd: EventFd::new(0).unwrap(),
104-
counter: 0,
105-
}
106-
}
107-
}
108-
109-
impl MutEventSubscriber for CounterSubscriber {
110-
fn process(&mut self, events: Events, event_ops: &mut EventOps) {
111-
match events.event_set() {
112-
EventSet::IN => {
113-
self.counter += 1;
114-
}
115-
EventSet::ERROR => {
116-
eprintln!("Got error on the monitored event.");
117-
}
118-
EventSet::HANG_UP => {
119-
event_ops.remove(events).unwrap_or(
120-
eprintln!("Encountered error during cleanup")
121-
);
122-
panic!("Cannot continue execution. Associated fd was closed.");
123-
}
124-
_ => {}
125-
}
126-
}
127-
128-
fn init(&mut self, ops: &mut EventOps) {
129-
ops.add(Events::new(&self.event_fd, EventSet::IN)).expect("Cannot register event.");
130-
}
131-
}
132-
```
133-
134-
#### Adding Subscribers to the Event Manager
135-
136-
```rust
137-
struct App {
138-
event_manager: EventManager<CounterSubscriber>,
139-
subscribers_id: Vec<SubscriberId>,
140-
}
141-
142-
impl App {
143-
fn new() -> Self {
144-
Self {
145-
event_manager: EventManager::<CounterSubscriber>::new().unwrap(),
146-
subscribers_id: vec![]
147-
}
148-
}
149-
150-
fn add_subscriber(&mut self) {
151-
let counter_subscriber = CounterSubscriber::default();
152-
let id = self.event_manager.add_subscriber(counter_subscriber);
153-
self.subscribers_id.push(id);
154-
}
155-
156-
fn run(&mut self) {
157-
let _ = self.event_manager.run_with_timeout(100);
158-
}
159-
}
160-
```
27+
## Implementing an event
28+
29+
Like `epoll` a file descriptor only monitors specific events.
30+
31+
The events ars specified when calling `EventManager::add` with `vmm_sys_util::epoll::EventSet`.
32+
33+
When an event becomes ready, the event manager will call the file descriptors callback closure.
34+
35+
The `epoll` events `EPOLLRDHUP`, `EPOLLERR` and `EPOLLHUP` (which correspond to
36+
`EventSet::READ_HANG_UP`, `EventSet::ERROR` and `EventSet::HANG_UP` respectively) are documented to
37+
always report, even when not specified by the user.
38+
39+
> epoll_wait(2) will always report for this event; it is not
40+
> necessary to set it in events when calling epoll_ctl().
41+
42+
*https://man7.org/linux/man-pages/man2/epoll_ctl.2.html*
43+
44+
As such it is best practice to always handle the cases where the `EventSet` passed to the file
45+
descriptor callback closure is `EventSet::READ_HANG_UP`, `EventSet::ERROR` or `EventSet::HANG_UP`.
16146

16247
## Development and Testing
16348

164-
The `event-manager` is tested using unit tests, Rust integration tests and
165-
performance benchmarks. It leverages
166-
[`rust-vmm-ci`](https://github.com/rust-vmm/rust-vmm-ci) for continuous
49+
`event-manager` uses [`rust-vmm-ci`](https://github.com/rust-vmm/rust-vmm-ci) for continuous
16750
testing. All tests are run in the `rustvmm/dev` container.
16851

16952
More details on running the tests can be found in the

0 commit comments

Comments
 (0)