├── .buildkite └── custom-tests.json ├── .cargo ├── audit.toml └── config ├── .github └── dependabot.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CODEOWNERS ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-BSD-3-CLAUSE ├── README.md ├── benches └── main.rs ├── coverage_config_aarch64.json ├── coverage_config_x86_64.json ├── docs ├── DESIGN.md ├── DEVELOPMENT.md └── event-manager.png ├── src ├── endpoint.rs ├── epoll.rs ├── events.rs ├── lib.rs ├── manager.rs ├── subscribers.rs └── utilities │ ├── mod.rs │ └── subscribers.rs └── tests ├── basic_event_manager.rs ├── endpoint.rs ├── multi_threaded.rs ├── negative_tests.rs └── regressions.rs /.buildkite/custom-tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "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 | }, 27 | { 28 | "test_name": "performance", 29 | "command": "pytest -s rust-vmm-ci/integration_tests/test_benchmark.py", 30 | "platform": [ 31 | "x86_64", 32 | "aarch64" 33 | ], 34 | "timeout_in_minutes": 15 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.cargo/audit.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | ignore = [ 3 | # serde_cbor is an unmaintained dependency introduced by criterion. 4 | # We are using criterion only for benchmarks, so we can ignore 5 | # this vulnerability until criterion is fixing this. 6 | # See https://github.com/bheisler/criterion.rs/issues/534. 7 | "RUSTSEC-2021-0127", 8 | # atty is unmaintained (the unsound problem doesn't seem to impact us). 9 | # We are ignoring this advisory because it's only used by criterion, 10 | # and we are using criterion for benchmarks. This is not a problem for 11 | # production use cases. Also, criterion did not update the dependency, 12 | # so there is not much else we can do. 13 | "RUSTSEC-2021-0145" 14 | ] 15 | -------------------------------------------------------------------------------- /.cargo/config: -------------------------------------------------------------------------------- 1 | # This workaround is needed because the linker is unable to find __addtf3, 2 | # __multf3 and __subtf3. 3 | # Related issue: https://github.com/rust-lang/compiler-builtins/issues/201 4 | [target.aarch64-unknown-linux-musl] 5 | rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc"] 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gitsubmodule 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rust-vmm-ci"] 2 | path = rust-vmm-ci 3 | url = https://github.com/rust-vmm/rust-vmm-ci.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Upcoming Version 4 | 5 | ### Changed 6 | ### Added 7 | ### Fixed 8 | ### Removed 9 | 10 | ## v0.4.1 11 | 12 | ### Changed 13 | 14 | - Updated vmm-sys-util version to 0.14.0 15 | 16 | ## v0.4.0 17 | 18 | ### Changed 19 | 20 | - Added `remote_endpoint` feature to generated docs.rs documentation. 21 | - Derived the `Debug` trait for multiple types 22 | 23 | ## v0.3.0 24 | 25 | ### Changed 26 | 27 | - Updated to Rust version 2021. 28 | - Dependencies are now specified by caret, so that they're no longer 29 | automatically updated when new ones are published. 30 | - The `Display` implementations for the `EventFd` and `Epoll` variants of the 31 | `Error` type now contain the inner error message as well. 32 | 33 | ### Added 34 | 35 | - The `Error` type now implements `Eq`. 36 | 37 | ## v0.2.1 38 | 39 | ### Changed 40 | 41 | - Updated the vmm-sys-util dependency to v0.8.0. 42 | 43 | ### Fixed 44 | 45 | - Fixed `RemoteEndpoint` `Clone` implementation. 46 | - Check the maximum capacity when calling `EventManager::new`. 47 | 48 | ## v0.2.0 49 | 50 | ### Fixed 51 | 52 | - Fixed a race condition that might lead to wrongfully call the dispatch 53 | function for an inactive event 54 | ([[#41]](https://github.com/rust-vmm/event-manager/issues/41)). 55 | 56 | ### Added 57 | 58 | - By default, the event manager can dispatch 256 events at one time. This limit 59 | can now be increased by using the `new_with_capacity` constructor 60 | ([[#37]](https://github.com/rust-vmm/event-manager/issues/37)). 61 | 62 | ## v0.1.0 63 | 64 | This is the first release of event-manager. 65 | The event-manager provides abstractions for implementing event based systems. 66 | For now, this crate only works on Linux and uses the epoll API to provide a 67 | mechanism for handling I/O notifications. 68 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Add the list of code owners here (using their GitHub username) 2 | * @alexandruag @andreeaflorescu @roypat @ShadowCurse 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "event-manager" 3 | version = "0.4.1" 4 | description = "Abstractions for implementing event based systems" 5 | keywords = ["events"] 6 | repository = "https://github.com/rust-vmm/event-manager" 7 | readme = "README.md" 8 | authors = ["rust-vmm AWS maintainers "] 9 | license = "Apache-2.0 OR BSD-3-Clause" 10 | edition = "2021" 11 | 12 | [dependencies] 13 | vmm-sys-util = ">=0.12.1,<=0.14.0" 14 | libc = "0.2.39" 15 | 16 | [dev-dependencies] 17 | criterion = "0.6.0" 18 | 19 | [package.metadata.docs.rs] 20 | all-features = true 21 | rustdoc-args = ["--cfg", "docsrs"] 22 | 23 | [features] 24 | remote_endpoint = [] 25 | test_utilities = [] 26 | 27 | [[bench]] 28 | name = "main" 29 | harness = false 30 | 31 | [lib] 32 | bench = false # https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options 33 | 34 | [profile.bench] 35 | lto = true 36 | codegen-units = 1 37 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-BSD-3-CLAUSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # event-manager 2 | 3 | [![crates.io](https://img.shields.io/crates/v/event-manager)](https://crates.io/crates/event-manager) 4 | [![docs.rs](https://img.shields.io/docsrs/event-manager)](https://docs.rs/event-manager/) 5 | 6 | The `event-manager` provides abstractions for implementing event based 7 | systems. For now, this crate only works on Linux and uses the 8 | [epoll](http://man7.org/linux/man-pages/man7/epoll.7.html) API to provide a 9 | mechanism for handling I/O notifications. 10 | 11 | ## Design 12 | 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) 43 | 44 | Read more in the [design document](docs/DESIGN.md). 45 | 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` and `Rc` (for any `T: EventSubscriber +?Sized`), 71 | together with automatic implementations of `MutEventSubscriber` for `Mutex` 72 | and `RefCell` (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, 139 | subscribers_id: Vec, 140 | } 141 | 142 | impl App { 143 | fn new() -> Self { 144 | Self { 145 | event_manager: EventManager::::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 | ``` 161 | 162 | ## Development and Testing 163 | 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 167 | testing. All tests are run in the `rustvmm/dev` container. 168 | 169 | More details on running the tests can be found in the 170 | [development](docs/DEVELOPMENT.md) document. 171 | 172 | ## License 173 | 174 | This project is licensed under either of: 175 | 176 | - [Apache License](LICENSE-APACHE), Version 2.0 177 | - [BSD-3-Clause License](LICENSE-BSD-3-CLAUSE) 178 | -------------------------------------------------------------------------------- /benches/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use criterion::{criterion_group, criterion_main, Criterion}; 5 | 6 | use event_manager::utilities::subscribers::{ 7 | CounterInnerMutSubscriber, CounterSubscriber, CounterSubscriberWithData, 8 | }; 9 | use event_manager::{EventManager, EventSubscriber, MutEventSubscriber, SubscriberOps}; 10 | use std::sync::{Arc, Mutex}; 11 | 12 | // Test the performance of event manager when it manages a single subscriber type. 13 | // The performance is assessed under stress, all added subscribers have active events. 14 | fn run_basic_subscriber(c: &mut Criterion) { 15 | let no_of_subscribers = 200; 16 | 17 | let mut event_manager = EventManager::::new().unwrap(); 18 | for _ in 0..no_of_subscribers { 19 | let mut counter_subscriber = CounterSubscriber::default(); 20 | counter_subscriber.trigger_event(); 21 | event_manager.add_subscriber(counter_subscriber); 22 | } 23 | 24 | c.bench_function("process_basic", |b| { 25 | b.iter(|| { 26 | let ev_count = event_manager.run().unwrap(); 27 | assert_eq!(ev_count, no_of_subscribers) 28 | }) 29 | }); 30 | } 31 | 32 | // Test the performance of event manager when the subscribers are wrapped in an Arc. 33 | // The performance is assessed under stress, all added subscribers have active events. 34 | fn run_arc_mutex_subscriber(c: &mut Criterion) { 35 | let no_of_subscribers = 200; 36 | 37 | let mut event_manager = EventManager::>>::new().unwrap(); 38 | for _ in 0..no_of_subscribers { 39 | let counter_subscriber = Arc::new(Mutex::new(CounterSubscriber::default())); 40 | counter_subscriber.lock().unwrap().trigger_event(); 41 | event_manager.add_subscriber(counter_subscriber); 42 | } 43 | 44 | c.bench_function("process_with_arc_mutex", |b| { 45 | b.iter(|| { 46 | let ev_count = event_manager.run().unwrap(); 47 | assert_eq!(ev_count, no_of_subscribers) 48 | }) 49 | }); 50 | } 51 | 52 | // Test the performance of event manager when the subscribers are wrapped in an Arc, and they 53 | // leverage inner mutability to update their internal state. 54 | // The performance is assessed under stress, all added subscribers have active events. 55 | fn run_subscriber_with_inner_mut(c: &mut Criterion) { 56 | let no_of_subscribers = 200; 57 | 58 | let mut event_manager = EventManager::>::new().unwrap(); 59 | for _ in 0..no_of_subscribers { 60 | let counter_subscriber = CounterInnerMutSubscriber::default(); 61 | counter_subscriber.trigger_event(); 62 | event_manager.add_subscriber(Arc::new(counter_subscriber)); 63 | } 64 | 65 | c.bench_function("process_with_inner_mut", |b| { 66 | b.iter(|| { 67 | let ev_count = event_manager.run().unwrap(); 68 | assert_eq!(ev_count, no_of_subscribers) 69 | }) 70 | }); 71 | } 72 | 73 | // Test the performance of event manager when it manages subscribers of different types, that are 74 | // wrapped in an Arc. Also, make use of `Events` with custom user data 75 | // (using CounterSubscriberWithData). 76 | // The performance is assessed under stress, all added subscribers have active events, and the 77 | // CounterSubscriberWithData subscribers have multiple active events. 78 | fn run_multiple_subscriber_types(c: &mut Criterion) { 79 | let no_of_subscribers = 100; 80 | 81 | let mut event_manager = EventManager::>>::new() 82 | .expect("Cannot create event manager."); 83 | 84 | for i in 0..no_of_subscribers { 85 | // The `CounterSubscriberWithData` expects to receive a number as a parameter that 86 | // represents the number it can use as its inner Events data. 87 | let mut data_subscriber = CounterSubscriberWithData::new(i * no_of_subscribers); 88 | data_subscriber.trigger_all_counters(); 89 | event_manager.add_subscriber(Arc::new(Mutex::new(data_subscriber))); 90 | 91 | let mut counter_subscriber = CounterSubscriber::default(); 92 | counter_subscriber.trigger_event(); 93 | event_manager.add_subscriber(Arc::new(Mutex::new(counter_subscriber))); 94 | } 95 | 96 | c.bench_function("process_dynamic_dispatch", |b| { 97 | b.iter(|| { 98 | let _ = event_manager.run().unwrap(); 99 | }) 100 | }); 101 | } 102 | 103 | // Test the performance of event manager when it manages a single subscriber type. 104 | // Just a few of the events are active in this test scenario. 105 | fn run_with_few_active_events(c: &mut Criterion) { 106 | let no_of_subscribers = 200; 107 | 108 | let mut event_manager = EventManager::::new().unwrap(); 109 | 110 | for i in 0..no_of_subscribers { 111 | let mut counter_subscriber = CounterSubscriber::default(); 112 | // Let's activate the events for a few subscribers (i.e. only the ones that are 113 | // divisible by 23). 23 is a random number that I just happen to like. 114 | if i % 23 == 0 { 115 | counter_subscriber.trigger_event(); 116 | } 117 | event_manager.add_subscriber(counter_subscriber); 118 | } 119 | 120 | c.bench_function("process_dispatch_few_events", |b| { 121 | b.iter(|| { 122 | let _ = event_manager.run().unwrap(); 123 | }) 124 | }); 125 | } 126 | 127 | criterion_group! { 128 | name = benches; 129 | config = Criterion::default() 130 | .sample_size(200) 131 | .measurement_time(std::time::Duration::from_secs(40)); 132 | targets = run_basic_subscriber, run_arc_mutex_subscriber, run_subscriber_with_inner_mut, 133 | run_multiple_subscriber_types, run_with_few_active_events 134 | } 135 | 136 | criterion_main! { 137 | benches 138 | } 139 | -------------------------------------------------------------------------------- /coverage_config_aarch64.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage_score": 93.2, 3 | "exclude_path": "utilities/", 4 | "crate_features": "remote_endpoint,test_utilities" 5 | } 6 | -------------------------------------------------------------------------------- /coverage_config_x86_64.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage_score": 83.81, 3 | "exclude_path": "utilities/", 4 | "crate_features": "remote_endpoint,test_utilities" 5 | } 6 | -------------------------------------------------------------------------------- /docs/DESIGN.md: -------------------------------------------------------------------------------- 1 | # Event Manager Design 2 | 3 | ## Interest List Updates 4 | 5 | Subscribers can update their interest list when the `EventManager` calls 6 | their `process` function. The EventManager crates a specialized `EventOps` 7 | object. `EventOps` limits the operations that the subscribers may call to the 8 | ones that are related to the interest list as follows: 9 | - Adding a new event that the subscriber is interested in. 10 | - Modifying an existing event (for example: update an event to be 11 | edge-triggered instead of being level-triggered or update the user data 12 | associated with an event). 13 | - Remove an existing event. 14 | 15 | The subscriber is responsible for handling the errors returned from calling 16 | `add`, `modify` or `remove`. 17 | 18 | The `EventManager` knows how to associate these actions to a registered 19 | subscriber because it adds the corresponding `SubscriberId` when it creates the 20 | `EventOps` object. 21 | 22 | ## Events 23 | 24 | By default, `Events` wrap a file descriptor, and a bit mask of events 25 | (for example `EPOLLIN | EPOLLOUT`). The `Events` can optionally contain user 26 | defined data. 27 | 28 | The `Events` are used in `add`, `remove` and `modify` functions 29 | in [`EventOps`](../src/events.rs). While their semantic is very similar to that 30 | of `libc::epoll_event`, they come with an additional requirement. When 31 | creating `Events` objects, the subscribers must specify the file descriptor 32 | associated with the event mask. There are a few reasons behind this choice: 33 | - Reducing the number of parameters on the `EventOps` functions. Instead of 34 | always passing the file descriptor along with an `epoll_event` object, the 35 | user only needs to pass `Events`. 36 | - Backing the file descriptor in `Events` provides a simple mapping from a file 37 | descriptor to the subscriber that is watching events on that particular file 38 | descriptor. 39 | 40 | Storing the file descriptor in all `Events` means that there are 32 bits left 41 | for custom user data. 42 | A file descriptor can be registered only once (it can be associated with only 43 | one subscriber). 44 | 45 | ### Using Events With Custom Data 46 | 47 | The 32-bits in custom data can be used to map events to internal callbacks 48 | based on user-defined numeric values instead of file descriptors. In the 49 | below example, the user defined values are consecutive so that the match 50 | statement can be optimized to a jump table. 51 | 52 | ```rust 53 | struct Painter {} 54 | const PROCESS_GREEN:u32 = 0; 55 | const PROCESS_RED: u32 = 1; 56 | const PROCESS_BLUE: u32 = 2; 57 | 58 | impl Painter { 59 | fn process_green(&self, event: Events) {} 60 | fn process_red(&self, event: Events) {} 61 | fn process_blue(&self, events: Events) {} 62 | } 63 | 64 | impl MutEventSubscriber for Painter { 65 | fn init(&mut self, ops: &mut EventOps) { 66 | let green_eventfd = EventFd::new(0).unwrap(); 67 | let ev_for_green = Events::with_data(&green_eventfd, PROCESS_GREEN, EventSet::IN); 68 | ops.add(ev_for_green).unwrap(); 69 | 70 | let red_eventfd = EventFd::new(0).unwrap(); 71 | let ev_for_red = Events::with_data(&red_eventfd, PROCESS_RED, EventSet::IN); 72 | ops.add(ev_for_red).unwrap(); 73 | 74 | let blue_eventfd = EventFd::new(0).unwrap(); 75 | let ev_for_blue = Events::with_data(&blue_eventfd, PROCESS_BLUE, EventSet::IN); 76 | ops.add(ev_for_blue).unwrap(); 77 | } 78 | 79 | fn process(&mut self, events: Events, ops: &mut EventOps) { 80 | match events.data() { 81 | PROCESS_GREEN => self.process_green(events), 82 | PROCESS_RED => self.process_red(events), 83 | PROCESS_BLUE => self.process_blue(events), 84 | _ => error!("spurious event"), 85 | }; 86 | } 87 | } 88 | ``` 89 | 90 | ## Remote Endpoint 91 | 92 | A manager remote endpoint allows users to interact with the `EventManger` 93 | (as a `SubscriberOps` trait object) from a different thread of execution. 94 | This is particularly useful when the `EventManager` owns the subscriber object 95 | the user wants to interact with, and the communication happens from a separate 96 | thread. This functionality is gated behind the `remote_endpoint` feature. 97 | 98 | The current implementation relies on passing boxed closures to the manager and 99 | getting back a boxed result. The manager is notified about incoming invocation 100 | requests via an [`EventFd`](https://docs.rs/vmm-sys-util/latest/vmm_sys_util/eventfd/struct.EventFd.html) 101 | which is added by the manager to its internal run loop. The manager runs each 102 | closure to completion, and then returns the boxed result using a sender object 103 | that is part of the initial message that also included the closure. The 104 | following example uses the previously defined `Painter` subscriber type. 105 | 106 | ```rust 107 | fn main() { 108 | // Create an event manager object. 109 | let mut event_manager = EventManager::::new().unwrap(); 110 | 111 | // Obtain a remote endpoint object. 112 | let endpoint = event_manager.remote_endpoint(); 113 | 114 | // Move the event manager to a new thread and start running the event loop there. 115 | let thread_handle = thread::spawn(move || loop { 116 | event_manager.run().unwrap(); 117 | }); 118 | 119 | let subscriber = Painter {}; 120 | 121 | // Add the subscriber using the remote endpoint. The subscriber is moved to the event 122 | // manager thread, and is now owned by the manager. In return, we get the subscriber id, 123 | // which can be used to identify the subscriber for subsequent operations. 124 | let id = endpoint 125 | .call_blocking(move |sub_ops| -> Result { 126 | Ok(sub_ops.add_subscriber(subscriber)) 127 | }) 128 | .unwrap(); 129 | // ... 130 | 131 | // Add a new event to the subscriber, using fd 1 as an example. 132 | let events = Events::new_raw(1, EventSet::OUT); 133 | endpoint 134 | .call_blocking(move |sub_ops| -> Result<()> { sub_ops.event_ops(id)?.add(events) }) 135 | .unwrap(); 136 | 137 | // ... 138 | 139 | thread_handle.join(); 140 | } 141 | ``` 142 | 143 | The `call_blocking` invocation sends a message over a channel to the event manager on the 144 | other thread, and then blocks until a response is received. The event manager detects the 145 | presence of such messages as with any other event, and handles them as part of the event 146 | loop. This can lead to deadlocks if, for example, `call_blocking` is invoked in the `process` 147 | implmentation of a subscriber to the same event manager. -------------------------------------------------------------------------------- /docs/DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development and Testing 2 | 3 | ## Testing 4 | 5 | The `event-manager` is tested using: 6 | - unit tests - defined in their corresponding modules 7 | - Rust integration tests - defined in the [tests](../tests) directory 8 | - performance tests - defined in the [benches](../benches) directory 9 | 10 | The integration and performance tests share subscribers implementations 11 | which can be found under the [src/utilities](../src/utilities) module. 12 | 13 | The `utilities` module is compiled only when using the `test_utilities` 14 | feature. To run unit tests, integration tests, and performance tests, the user 15 | needs to specify the `test_utilities` feature; otherwise the build fails. 16 | 17 | ```bash 18 | cargo test --features test_utilities 19 | cargo bench --features test_utilities 20 | ``` 21 | 22 | We recommend running all the tests before submitting a PR as follows: 23 | 24 | ```bash 25 | cargo test --all-features 26 | ``` 27 | 28 | Performance tests are implemented using 29 | [criterion](https://docs.rs/crate/criterion/). Running the performance tests 30 | locally should work, but only when they're run as part of the CI performance 31 | improvements/degradations can be noticed. More details about performance tests 32 | [here](https://github.com/rust-vmm/rust-vmm-ci#performance-tests). 33 | -------------------------------------------------------------------------------- /docs/event-manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-vmm/event-manager/f634abe86f1ca7374ca658627def5a65627c4573/docs/event-manager.png -------------------------------------------------------------------------------- /src/endpoint.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | //! A manager remote endpoint allows user to interact with the `EventManger` (as a `SubscriberOps` 5 | //! trait object) from a different thread of execution. 6 | //! 7 | //! This is particularly useful when the `EventManager` owns (via `EventManager::add_subscriber`) 8 | //! a subscriber object the user needs to work with (via `EventManager::subscriber_mut`), but the 9 | //! `EventManager` being on a different thread requires synchronized handles. 10 | //! 11 | //! Until more sophisticated methods are explored (for example making the `EventManager` offer 12 | //! interior mutability using something like an RCU mechanism), the current approach relies on 13 | //! passing boxed closures to the manager and getting back a boxed result. The manager is notified 14 | //! about incoming invocation requests via an `EventFd` which is added to the epoll event set. 15 | //! The signature of the closures as they are received is the `FnOnceBox` type alias defined 16 | //! below. The actual return type is opaque to the manager, but known to the initiator. The manager 17 | //! runs each closure to completion, and then returns the boxed result using a sender object that 18 | //! is part of the initial message that also included the closure. 19 | 20 | use std::any::Any; 21 | use std::os::unix::io::{AsRawFd, RawFd}; 22 | use std::result; 23 | use std::sync::mpsc::{channel, Receiver, Sender}; 24 | use std::sync::Arc; 25 | 26 | use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; 27 | 28 | use super::{Errno, Error, MutEventSubscriber, Result, SubscriberOps}; 29 | 30 | // The return type of the closure received by the manager is erased (by placing it into a 31 | // `Box` in order to have a single concrete type definition for the messages that 32 | // contain closures to be executed (the `FnMsg` defined below). The actual return type is 33 | // recovered by the initiator of the remote call (more details in the implementation of 34 | // `RemoteEndpoint::call_blocking` below). The `Send` bound is required to send back the boxed 35 | // result over the return channel. 36 | type ErasedResult = Box; 37 | 38 | // Type alias for the boxed closures received by the manager. The `Send` bound at the end applies 39 | // to the type of the closure, and is required to send the box over the channel. 40 | type FnOnceBox = Box) -> ErasedResult + Send>; 41 | 42 | // The type of the messages received by the manager over its receive mpsc channel. 43 | pub(crate) struct FnMsg { 44 | // The closure to execute. 45 | pub(crate) fnbox: FnOnceBox, 46 | // The sending endpoint of the channel used by the remote called to wait for the result. 47 | pub(crate) sender: Option>, 48 | } 49 | 50 | // Used by the `EventManager` to keep state associated with the channel. 51 | #[derive(Debug)] 52 | pub(crate) struct EventManagerChannel { 53 | // A clone of this is given to every `RemoteEndpoint` and used to signal the presence of 54 | // an new message on the channel. 55 | pub(crate) event_fd: Arc, 56 | // A clone of this sender is given to every `RemoteEndpoint` and used to send `FnMsg` objects 57 | // to the `EventManager` over the channel. 58 | pub(crate) sender: Sender>, 59 | // The receiving half of the channel, used to receive incoming `FnMsg` objects. 60 | pub(crate) receiver: Receiver>, 61 | } 62 | 63 | impl EventManagerChannel { 64 | pub(crate) fn new() -> Result { 65 | let (sender, receiver) = channel(); 66 | Ok(EventManagerChannel { 67 | event_fd: Arc::new( 68 | EventFd::new(EFD_NONBLOCK).map_err(|e| Error::EventFd(Errno::from(e)))?, 69 | ), 70 | sender, 71 | receiver, 72 | }) 73 | } 74 | 75 | pub(crate) fn fd(&self) -> RawFd { 76 | self.event_fd.as_raw_fd() 77 | } 78 | 79 | pub(crate) fn remote_endpoint(&self) -> RemoteEndpoint { 80 | RemoteEndpoint { 81 | msg_sender: self.sender.clone(), 82 | event_fd: self.event_fd.clone(), 83 | } 84 | } 85 | } 86 | 87 | /// Enables interactions with an `EventManager` that runs on a different thread of execution. 88 | #[derive(Debug)] 89 | pub struct RemoteEndpoint { 90 | // A sender associated with `EventManager` channel requests are sent over. 91 | msg_sender: Sender>, 92 | // Used to notify the `EventManager` about the arrival of a new request. 93 | event_fd: Arc, 94 | } 95 | 96 | impl Clone for RemoteEndpoint { 97 | fn clone(&self) -> Self { 98 | RemoteEndpoint { 99 | msg_sender: self.msg_sender.clone(), 100 | event_fd: self.event_fd.clone(), 101 | } 102 | } 103 | } 104 | 105 | impl RemoteEndpoint { 106 | // Send a message to the remote EventManger and raise a notification. 107 | fn send(&self, msg: FnMsg) -> Result<()> { 108 | self.msg_sender.send(msg).map_err(|_| Error::ChannelSend)?; 109 | self.event_fd 110 | .write(1) 111 | .map_err(|e| Error::EventFd(Errno::from(e)))?; 112 | Ok(()) 113 | } 114 | 115 | /// Call the specified closure on the associated remote `EventManager` (provided as a 116 | /// `SubscriberOps` trait object), and return the result. This method blocks until the result 117 | /// is received, and calling it from the same thread where the event loop runs leads to 118 | /// a deadlock. 119 | pub fn call_blocking(&self, f: F) -> result::Result 120 | where 121 | F: FnOnce(&mut dyn SubscriberOps) -> result::Result + Send + 'static, 122 | O: Send + 'static, 123 | E: From + Send + 'static, 124 | { 125 | // Create a temporary channel used to get back the result. We keep the receiving end, 126 | // and put the sending end into the message we pass to the remote `EventManager`. 127 | let (sender, receiver) = channel(); 128 | 129 | // We erase the return type of `f` by moving and calling it inside another closure which 130 | // hides the result as an `ErasedResult`. This allows using the same channel to send 131 | // closures with different signatures (and thus different types) to the remote 132 | // `EventManager`. 133 | let fnbox = Box::new( 134 | move |ops: &mut dyn SubscriberOps| -> ErasedResult { Box::new(f(ops)) }, 135 | ); 136 | 137 | // Send the message requesting the closure invocation. 138 | self.send(FnMsg { 139 | fnbox, 140 | sender: Some(sender), 141 | })?; 142 | 143 | // Block until a response is received. We can use unwrap because the downcast cannot fail, 144 | // since the signature of F (more specifically, the return value) constrains the concrete 145 | // type that's in the box. 146 | let result_box = receiver 147 | .recv() 148 | .map_err(|_| Error::ChannelRecv)? 149 | .downcast() 150 | .unwrap(); 151 | 152 | // Turns out the dereference operator has a special behaviour for boxed objects; if we 153 | // own a `b: Box` and call `*b`, the box goes away and we get the `T` inside. 154 | *result_box 155 | } 156 | 157 | /// Call the specified closure on the associated local/remote `EventManager` (provided as a 158 | /// `SubscriberOps` trait object), and discard the result. This method only fires 159 | /// the request but does not wait for result, so it may be called from the same thread where 160 | /// the event loop runs. 161 | pub fn fire(&self, f: F) -> Result<()> 162 | where 163 | F: FnOnce(&mut dyn SubscriberOps) + Send + 'static, 164 | { 165 | // We erase the return type of `f` by moving and calling it inside another closure which 166 | // hides the result as an `ErasedResult`. This allows using the same channel send closures 167 | // with different signatures (and thus different types) to the remote `EventManager`. 168 | let fnbox = Box::new( 169 | move |ops: &mut dyn SubscriberOps| -> ErasedResult { 170 | f(ops); 171 | Box::new(()) 172 | }, 173 | ); 174 | 175 | // Send the message requesting the closure invocation. 176 | self.send(FnMsg { 177 | fnbox, 178 | sender: None, 179 | }) 180 | } 181 | 182 | /// Kick the worker thread to wake up from the epoll event loop. 183 | pub fn kick(&self) -> Result<()> { 184 | self.event_fd 185 | .write(1) 186 | .map(|_| ()) 187 | .map_err(|e| Error::EventFd(Errno::from(e))) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/epoll.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use std::collections::HashMap; 5 | use std::os::unix::io::RawFd; 6 | 7 | use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent}; 8 | 9 | use super::{Errno, Error, EventOps, Result, SubscriberId}; 10 | 11 | // Internal use structure that keeps the epoll related state of an EventManager. 12 | #[derive(Debug)] 13 | pub(crate) struct EpollWrapper { 14 | // The epoll wrapper. 15 | pub(crate) epoll: Epoll, 16 | // Records the id of the subscriber associated with the given RawFd. The event event_manager 17 | // does not currently support more than one subscriber being associated with an fd. 18 | pub(crate) fd_dispatch: HashMap, 19 | // Records the set of fds that are associated with the subscriber that has the given id. 20 | // This is used to keep track of all fds associated with a subscriber. 21 | pub(crate) subscriber_watch_list: HashMap>, 22 | // A scratch buffer to avoid allocating/freeing memory on each poll iteration. 23 | pub(crate) ready_events: Vec, 24 | } 25 | 26 | impl EpollWrapper { 27 | pub(crate) fn new(ready_events_capacity: usize) -> Result { 28 | Ok(EpollWrapper { 29 | epoll: Epoll::new().map_err(|e| Error::Epoll(Errno::from(e)))?, 30 | fd_dispatch: HashMap::new(), 31 | subscriber_watch_list: HashMap::new(), 32 | ready_events: vec![EpollEvent::default(); ready_events_capacity], 33 | }) 34 | } 35 | 36 | // Poll the underlying epoll fd for pending IO events. 37 | pub(crate) fn poll(&mut self, milliseconds: i32) -> Result { 38 | let event_count = match self.epoll.wait(milliseconds, &mut self.ready_events[..]) { 39 | Ok(ev) => ev, 40 | // EINTR is not actually an error that needs to be handled. The documentation 41 | // for epoll.run specifies that run exits when it for an event, on timeout, or 42 | // on interrupt. 43 | Err(e) if e.raw_os_error() == Some(libc::EINTR) => return Ok(0), 44 | Err(e) => return Err(Error::Epoll(Errno::from(e))), 45 | }; 46 | 47 | Ok(event_count) 48 | } 49 | 50 | // Remove the fds associated with the provided subscriber id from the epoll set and the 51 | // other structures. The subscriber id must be valid. 52 | pub(crate) fn remove(&mut self, subscriber_id: SubscriberId) { 53 | let fds = self 54 | .subscriber_watch_list 55 | .remove(&subscriber_id) 56 | .unwrap_or_default(); 57 | for fd in fds { 58 | // We ignore the result of the operation since there's nothing we can't do, and its 59 | // not a significant error condition at this point. 60 | let _ = self 61 | .epoll 62 | .ctl(ControlOperation::Delete, fd, EpollEvent::default()); 63 | self.remove_event(fd); 64 | } 65 | } 66 | 67 | // Flush and stop receiving IO events associated with the file descriptor. 68 | pub(crate) fn remove_event(&mut self, fd: RawFd) { 69 | self.fd_dispatch.remove(&fd); 70 | for event in self.ready_events.iter_mut() { 71 | if event.fd() == fd { 72 | // It's a little complex to remove the entry from the Vec, so do soft removal 73 | // by setting it to default value. 74 | *event = EpollEvent::default(); 75 | } 76 | } 77 | } 78 | 79 | // Gets the id of the subscriber associated with the provided fd (if such an association 80 | // exists). 81 | pub(crate) fn subscriber_id(&self, fd: RawFd) -> Option { 82 | self.fd_dispatch.get(&fd).copied() 83 | } 84 | 85 | // Creates and returns an EventOps object for the subscriber associated with the provided 86 | // id. The subscriber id must be valid. 87 | pub(crate) fn ops_unchecked(&mut self, subscriber_id: SubscriberId) -> EventOps { 88 | EventOps::new(self, subscriber_id) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/events.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use std::os::unix::io::{AsRawFd, RawFd}; 5 | 6 | use super::{Errno, Error, Result, SubscriberId}; 7 | use crate::epoll::EpollWrapper; 8 | use vmm_sys_util::epoll::{ControlOperation, EpollEvent, EventSet}; 9 | 10 | /// Wrapper over an `epoll::EpollEvent` object. 11 | /// 12 | /// When working directly with epoll related methods, the user associates an `u64` wide 13 | /// epoll_data_t object with every event. We want to use fds as identifiers, but at the same time 14 | /// keep the ability to associate opaque data with an event. An `Events` object always contains an 15 | /// fd and an `u32` data member that can be supplied by the user. When registering events with the 16 | /// inner epoll event set, the fd and data members of `Events` are used together to generate the 17 | /// underlying `u64` member of the epoll_data union. 18 | #[derive(Clone, Copy, Debug)] 19 | pub struct Events { 20 | inner: EpollEvent, 21 | } 22 | 23 | impl PartialEq for Events { 24 | fn eq(&self, other: &Events) -> bool { 25 | self.fd() == other.fd() 26 | && self.data() == other.data() 27 | && self.event_set() == other.event_set() 28 | } 29 | } 30 | 31 | impl Events { 32 | pub(crate) fn with_inner(inner: EpollEvent) -> Self { 33 | Self { inner } 34 | } 35 | 36 | /// Create an empty event set associated with `source`. 37 | /// 38 | /// No explicit events are monitored for the associated file descriptor. 39 | /// Nevertheless, [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and 40 | /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are implicitly 41 | /// monitored. 42 | /// 43 | /// # Arguments 44 | /// 45 | /// * source: object that wraps a file descriptor to be associated with `events` 46 | /// 47 | /// # Example 48 | /// 49 | /// ```rust 50 | /// # use event_manager::Events; 51 | /// # use vmm_sys_util::eventfd::EventFd; 52 | /// let eventfd = EventFd::new(0).unwrap(); 53 | /// let ev_set = Events::empty(&eventfd); 54 | /// ``` 55 | pub fn empty(source: &T) -> Self { 56 | Self::empty_raw(source.as_raw_fd()) 57 | } 58 | 59 | /// Create an empty event set associated with the supplied `RawFd` value. 60 | /// 61 | /// No explicit events are monitored for the associated file descriptor. 62 | /// Nevertheless, [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and 63 | /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are implicitly 64 | /// monitored. 65 | /// 66 | /// # Example 67 | /// 68 | /// ```rust 69 | /// # use event_manager::Events; 70 | /// # use std::os::unix::io::AsRawFd; 71 | /// # use vmm_sys_util::eventfd::EventFd; 72 | /// let eventfd = EventFd::new(0).unwrap(); 73 | /// let ev_set = Events::empty_raw(eventfd.as_raw_fd()); 74 | /// ``` 75 | pub fn empty_raw(fd: RawFd) -> Self { 76 | Self::new_raw(fd, EventSet::empty()) 77 | } 78 | 79 | /// Create an event with `source` and the associated `events` for monitoring. 80 | /// 81 | /// # Arguments 82 | /// 83 | /// * source: object that wraps a file descriptor to be associated with `events` 84 | /// * events: events to monitor on the provided `source`; 85 | /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and 86 | /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are 87 | /// always monitored and don't need to be explicitly added to the list. 88 | /// 89 | /// # Example 90 | /// 91 | /// ```rust 92 | /// # use event_manager::{Events, EventSet}; 93 | /// # use vmm_sys_util::eventfd::EventFd; 94 | /// let eventfd = EventFd::new(0).unwrap(); 95 | /// let event_set = EventSet::IN; 96 | /// let ev_set = Events::new(&eventfd, event_set); 97 | /// ``` 98 | pub fn new(source: &T, events: EventSet) -> Self { 99 | Self::new_raw(source.as_raw_fd(), events) 100 | } 101 | 102 | /// Create an event with the supplied `RawFd` value and `events` for monitoring. 103 | /// 104 | /// # Arguments 105 | /// 106 | /// * source: file descriptor on which to monitor the `events` 107 | /// * events: events to monitor on the provided `source`; 108 | /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and 109 | /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are 110 | /// always monitored and don't need to be explicitly added to the list. 111 | /// # Example 112 | /// 113 | /// ```rust 114 | /// # use event_manager::{Events, EventSet}; 115 | /// # use vmm_sys_util::eventfd::EventFd; 116 | /// # use std::os::unix::io::AsRawFd; 117 | /// let eventfd = EventFd::new(0).unwrap(); 118 | /// let event_set = EventSet::IN; 119 | /// let ev_set = Events::new_raw(eventfd.as_raw_fd(), event_set); 120 | /// ``` 121 | pub fn new_raw(source: RawFd, events: EventSet) -> Self { 122 | Self::with_data_raw(source, 0, events) 123 | } 124 | 125 | /// Create an event set associated with the underlying file descriptor of the source, active 126 | /// events, and data. 127 | /// 128 | /// # Arguments 129 | /// * source: object that wraps a file descriptor to be associated with `events` 130 | /// * data: custom user data associated with the file descriptor; the data can be used for 131 | /// uniquely identify monitored events instead of using the file descriptor. 132 | /// * events: events to monitor on the provided `source`; 133 | /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and 134 | /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are 135 | /// always monitored and don't need to be explicitly added to the list. 136 | /// 137 | /// # Examples 138 | /// 139 | /// ```rust 140 | /// # use event_manager::{Events, EventSet}; 141 | /// # use vmm_sys_util::eventfd::EventFd; 142 | /// let eventfd = EventFd::new(0).unwrap(); 143 | /// let event_set = EventSet::IN; 144 | /// let custom_data = 42; 145 | /// let ev_set = Events::with_data(&eventfd, custom_data, event_set); 146 | /// ``` 147 | pub fn with_data(source: &T, data: u32, events: EventSet) -> Self { 148 | Self::with_data_raw(source.as_raw_fd(), data, events) 149 | } 150 | 151 | /// Create an event set associated with the supplied `RawFd` value, active events, and data. 152 | /// 153 | /// # Arguments 154 | /// * source: file descriptor to be associated with `events` 155 | /// * data: custom user data associated with the file descriptor; the data can be used for 156 | /// uniquely identify monitored events instead of using the file descriptor. 157 | /// * events: events to monitor on the provided `source`; 158 | /// [`EventSet::ERROR`](struct.EventSet.html#associatedconstant.ERROR) and 159 | /// [`EventSet::HANG_UP`](struct.EventSet.html#associatedconstant.HANG_UP) are 160 | /// always monitored and don't need to be explicitly added to the list. 161 | /// 162 | /// # Examples 163 | /// 164 | /// ```rust 165 | /// # use event_manager::{Events, EventSet}; 166 | /// # use std::os::unix::io::AsRawFd; 167 | /// # use vmm_sys_util::eventfd::EventFd; 168 | /// let eventfd = EventFd::new(0).unwrap(); 169 | /// let event_set = EventSet::IN; 170 | /// let custom_data = 42; 171 | /// let ev_set = Events::with_data_raw(eventfd.as_raw_fd(), custom_data, event_set); 172 | /// ``` 173 | pub fn with_data_raw(source: RawFd, data: u32, events: EventSet) -> Self { 174 | let inner_data = ((data as u64) << 32) + (source as u64); 175 | Events { 176 | inner: EpollEvent::new(events, inner_data), 177 | } 178 | } 179 | 180 | /// Return the inner fd value. 181 | pub fn fd(&self) -> RawFd { 182 | self.inner.data() as RawFd 183 | } 184 | 185 | /// Return the inner data value. 186 | pub fn data(&self) -> u32 { 187 | (self.inner.data() >> 32) as u32 188 | } 189 | 190 | /// Return the active event set. 191 | pub fn event_set(&self) -> EventSet { 192 | self.inner.event_set() 193 | } 194 | 195 | /// Return the inner `EpollEvent`. 196 | pub fn epoll_event(&self) -> EpollEvent { 197 | self.inner 198 | } 199 | } 200 | 201 | /// Opaque object associated with an `EventSubscriber` that allows the addition, modification, and 202 | /// removal of events in the watchlist. 203 | // Right now this is a concrete object, but going further it can be turned into a trait and 204 | // passed around as a trait object. 205 | #[derive(Debug)] 206 | pub struct EventOps<'a> { 207 | // Mutable reference to the EpollContext of an EventManager. 208 | epoll_wrapper: &'a mut EpollWrapper, 209 | // The id of the event subscriber this object stands for. 210 | subscriber_id: SubscriberId, 211 | } 212 | 213 | impl<'a> EventOps<'a> { 214 | pub(crate) fn new(epoll_wrapper: &'a mut EpollWrapper, subscriber_id: SubscriberId) -> Self { 215 | EventOps { 216 | epoll_wrapper, 217 | subscriber_id, 218 | } 219 | } 220 | 221 | // Apply the provided control operation for the given events on the inner epoll wrapper. 222 | fn ctl(&self, op: ControlOperation, events: Events) -> Result<()> { 223 | self.epoll_wrapper 224 | .epoll 225 | .ctl(op, events.fd(), events.epoll_event()) 226 | .map_err(|e| Error::Epoll(Errno::from(e))) 227 | } 228 | 229 | /// Add the provided events to the inner epoll event set. 230 | pub fn add(&mut self, events: Events) -> Result<()> { 231 | let fd = events.fd(); 232 | if self.epoll_wrapper.fd_dispatch.contains_key(&fd) { 233 | return Err(Error::FdAlreadyRegistered); 234 | } 235 | 236 | self.ctl(ControlOperation::Add, events)?; 237 | 238 | self.epoll_wrapper 239 | .fd_dispatch 240 | .insert(fd, self.subscriber_id); 241 | 242 | self.epoll_wrapper 243 | .subscriber_watch_list 244 | .entry(self.subscriber_id) 245 | .or_default() 246 | .push(fd); 247 | 248 | Ok(()) 249 | } 250 | 251 | /// Submit the provided changes to the inner epoll event set. 252 | pub fn modify(&self, events: Events) -> Result<()> { 253 | self.ctl(ControlOperation::Modify, events) 254 | } 255 | 256 | /// Remove the specified events from the inner epoll event set. 257 | pub fn remove(&mut self, events: Events) -> Result<()> { 258 | // TODO: Add some more checks here? 259 | self.ctl(ControlOperation::Delete, events)?; 260 | self.epoll_wrapper.remove_event(events.fd()); 261 | 262 | if let Some(watch_list) = self 263 | .epoll_wrapper 264 | .subscriber_watch_list 265 | .get_mut(&self.subscriber_id) 266 | { 267 | if let Some(index) = watch_list.iter().position(|&x| x == events.fd()) { 268 | watch_list.remove(index); 269 | } 270 | } 271 | Ok(()) 272 | } 273 | } 274 | 275 | #[cfg(test)] 276 | mod tests { 277 | use super::*; 278 | 279 | use vmm_sys_util::eventfd::EventFd; 280 | 281 | #[test] 282 | fn test_empty_events() { 283 | let event_fd = EventFd::new(0).unwrap(); 284 | 285 | let events_raw = Events::empty_raw(event_fd.as_raw_fd()); 286 | let events = Events::empty(&event_fd); 287 | 288 | assert_eq!(events, events_raw); 289 | 290 | assert_eq!(events.event_set(), EventSet::empty()); 291 | assert_eq!(events.data(), 0); 292 | assert_eq!(events.fd(), event_fd.as_raw_fd()); 293 | } 294 | 295 | #[test] 296 | fn test_events_no_data() { 297 | let event_fd = EventFd::new(0).unwrap(); 298 | let event_set = EventSet::IN; 299 | 300 | let events_raw = Events::new_raw(event_fd.as_raw_fd(), event_set); 301 | let events = Events::new(&event_fd, event_set); 302 | 303 | assert_eq!(events_raw, events); 304 | 305 | assert_eq!(events.data(), 0); 306 | assert_eq!(events.fd(), event_fd.as_raw_fd()); 307 | assert_eq!(events.event_set(), event_set); 308 | } 309 | 310 | #[test] 311 | fn test_events_data() { 312 | let event_fd = EventFd::new(0).unwrap(); 313 | let event_set = EventSet::IN; 314 | 315 | let events_raw = Events::with_data_raw(event_fd.as_raw_fd(), 42, event_set); 316 | let events = Events::with_data(&event_fd, 43, event_set); 317 | 318 | assert_ne!(events_raw, events); 319 | 320 | assert_eq!(events.data(), 43); 321 | assert_eq!(events_raw.data(), 42); 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | //! Event Manager traits and implementation. 5 | #![deny(missing_debug_implementations)] 6 | #![deny(missing_docs)] 7 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 8 | 9 | use std::cell::RefCell; 10 | use std::ops::{Deref, DerefMut}; 11 | use std::rc::Rc; 12 | use std::result; 13 | use std::sync::{Arc, Mutex}; 14 | 15 | use vmm_sys_util::errno::Error as Errno; 16 | 17 | /// The type of epoll events we can monitor a file descriptor for. 18 | pub use vmm_sys_util::epoll::EventSet; 19 | 20 | mod epoll; 21 | mod events; 22 | mod manager; 23 | mod subscribers; 24 | #[doc(hidden)] 25 | #[cfg(feature = "test_utilities")] 26 | pub mod utilities; 27 | 28 | pub use events::{EventOps, Events}; 29 | pub use manager::{EventManager, MAX_READY_EVENTS_CAPACITY}; 30 | 31 | #[cfg(feature = "remote_endpoint")] 32 | mod endpoint; 33 | #[cfg(feature = "remote_endpoint")] 34 | pub use endpoint::RemoteEndpoint; 35 | 36 | /// Error conditions that may appear during `EventManager` related operations. 37 | #[derive(Debug, Eq, PartialEq)] 38 | pub enum Error { 39 | #[cfg(feature = "remote_endpoint")] 40 | /// Cannot send message on channel. 41 | ChannelSend, 42 | #[cfg(feature = "remote_endpoint")] 43 | /// Cannot receive message on channel. 44 | ChannelRecv, 45 | #[cfg(feature = "remote_endpoint")] 46 | /// Operation on `eventfd` failed. 47 | EventFd(Errno), 48 | /// Operation on `libc::epoll` failed. 49 | Epoll(Errno), 50 | // TODO: should we allow fds to be registered multiple times? 51 | /// The fd is already associated with an existing subscriber. 52 | FdAlreadyRegistered, 53 | /// The Subscriber ID does not exist or is no longer associated with a Subscriber. 54 | InvalidId, 55 | /// The ready list capacity passed to `EventManager::new` is invalid. 56 | InvalidCapacity, 57 | } 58 | 59 | impl std::fmt::Display for Error { 60 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 61 | match self { 62 | #[cfg(feature = "remote_endpoint")] 63 | Error::ChannelSend => write!( 64 | f, 65 | "event_manager: failed to send message to remote endpoint" 66 | ), 67 | #[cfg(feature = "remote_endpoint")] 68 | Error::ChannelRecv => write!( 69 | f, 70 | "event_manager: failed to receive message from remote endpoint" 71 | ), 72 | #[cfg(feature = "remote_endpoint")] 73 | Error::EventFd(e) => write!( 74 | f, 75 | "event_manager: failed to manage EventFd file descriptor: {}", 76 | e 77 | ), 78 | Error::Epoll(e) => write!( 79 | f, 80 | "event_manager: failed to manage epoll file descriptor: {}", 81 | e 82 | ), 83 | Error::FdAlreadyRegistered => write!( 84 | f, 85 | "event_manager: file descriptor has already been registered" 86 | ), 87 | Error::InvalidId => write!(f, "event_manager: invalid subscriber Id"), 88 | Error::InvalidCapacity => write!(f, "event_manager: invalid ready_list capacity"), 89 | } 90 | } 91 | } 92 | 93 | impl std::error::Error for Error { 94 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 95 | match self { 96 | #[cfg(feature = "remote_endpoint")] 97 | Error::ChannelSend => None, 98 | #[cfg(feature = "remote_endpoint")] 99 | Error::ChannelRecv => None, 100 | #[cfg(feature = "remote_endpoint")] 101 | Error::EventFd(e) => Some(e), 102 | Error::Epoll(e) => Some(e), 103 | Error::FdAlreadyRegistered => None, 104 | Error::InvalidId => None, 105 | Error::InvalidCapacity => None, 106 | } 107 | } 108 | } 109 | 110 | /// Generic result type that may return `EventManager` errors. 111 | pub type Result = result::Result; 112 | 113 | /// Opaque object that uniquely represents a subscriber registered with an `EventManager`. 114 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 115 | pub struct SubscriberId(u64); 116 | 117 | /// Allows the interaction between an `EventManager` and different event subscribers that do not 118 | /// require a `&mut self` borrow to perform `init` and `process`. 119 | /// 120 | /// Any type implementing this also trivially implements `MutEventSubscriber`. The main role of 121 | /// `EventSubscriber` is to allow wrappers such as `Arc` and `Rc` to implement `EventSubscriber` 122 | /// themselves when the inner type is also an implementor. 123 | pub trait EventSubscriber { 124 | /// Process `events` triggered in the event manager loop. 125 | /// 126 | /// Optionally, the subscriber can use `ops` to update the events it monitors. 127 | fn process(&self, events: Events, ops: &mut EventOps); 128 | 129 | /// Initialization called by the [EventManager](struct.EventManager.html) when the subscriber 130 | /// is registered. 131 | /// 132 | /// The subscriber is expected to use `ops` to register the events it wants to monitor. 133 | fn init(&self, ops: &mut EventOps); 134 | } 135 | 136 | /// Allows the interaction between an `EventManager` and different event subscribers. Methods 137 | /// are invoked with a mutable `self` borrow. 138 | pub trait MutEventSubscriber { 139 | /// Process `events` triggered in the event manager loop. 140 | /// 141 | /// Optionally, the subscriber can use `ops` to update the events it monitors. 142 | fn process(&mut self, events: Events, ops: &mut EventOps); 143 | 144 | /// Initialization called by the [EventManager](struct.EventManager.html) when the subscriber 145 | /// is registered. 146 | /// 147 | /// The subscriber is expected to use `ops` to register the events it wants to monitor. 148 | fn init(&mut self, ops: &mut EventOps); 149 | } 150 | 151 | /// API that allows users to add, remove, and interact with registered subscribers. 152 | pub trait SubscriberOps { 153 | /// Subscriber type for which the operations apply. 154 | type Subscriber: MutEventSubscriber; 155 | 156 | /// Registers a new subscriber and returns the ID associated with it. 157 | /// 158 | /// # Panics 159 | /// 160 | /// This function might panic if the subscriber is already registered. Whether a panic 161 | /// is triggered depends on the implementation of 162 | /// [Subscriber::init()](trait.EventSubscriber.html#tymethod.init). 163 | /// 164 | /// Typically, in the `init` function, the subscriber adds fds to its interest list. The same 165 | /// fd cannot be added twice and the `EventManager` will return 166 | /// [Error::FdAlreadyRegistered](enum.Error.html). Using `unwrap` in init in this situation 167 | /// triggers a panic. 168 | fn add_subscriber(&mut self, subscriber: Self::Subscriber) -> SubscriberId; 169 | 170 | /// Removes the subscriber corresponding to `subscriber_id` from the watch list. 171 | fn remove_subscriber(&mut self, subscriber_id: SubscriberId) -> Result; 172 | 173 | /// Returns a mutable reference to the subscriber corresponding to `subscriber_id`. 174 | fn subscriber_mut(&mut self, subscriber_id: SubscriberId) -> Result<&mut Self::Subscriber>; 175 | 176 | /// Creates an event operations wrapper for the subscriber corresponding to `subscriber_id`. 177 | /// 178 | /// The event operations can be used to update the events monitored by the subscriber. 179 | fn event_ops(&mut self, subscriber_id: SubscriberId) -> Result; 180 | } 181 | 182 | impl EventSubscriber for Arc { 183 | fn process(&self, events: Events, ops: &mut EventOps) { 184 | self.deref().process(events, ops); 185 | } 186 | 187 | fn init(&self, ops: &mut EventOps) { 188 | self.deref().init(ops); 189 | } 190 | } 191 | 192 | impl MutEventSubscriber for Arc { 193 | fn process(&mut self, events: Events, ops: &mut EventOps) { 194 | self.deref().process(events, ops); 195 | } 196 | 197 | fn init(&mut self, ops: &mut EventOps) { 198 | self.deref().init(ops); 199 | } 200 | } 201 | 202 | impl EventSubscriber for Rc { 203 | fn process(&self, events: Events, ops: &mut EventOps) { 204 | self.deref().process(events, ops); 205 | } 206 | 207 | fn init(&self, ops: &mut EventOps) { 208 | self.deref().init(ops); 209 | } 210 | } 211 | 212 | impl MutEventSubscriber for Rc { 213 | fn process(&mut self, events: Events, ops: &mut EventOps) { 214 | self.deref().process(events, ops); 215 | } 216 | 217 | fn init(&mut self, ops: &mut EventOps) { 218 | self.deref().init(ops); 219 | } 220 | } 221 | 222 | impl EventSubscriber for RefCell { 223 | fn process(&self, events: Events, ops: &mut EventOps) { 224 | self.borrow_mut().process(events, ops); 225 | } 226 | 227 | fn init(&self, ops: &mut EventOps) { 228 | self.borrow_mut().init(ops); 229 | } 230 | } 231 | 232 | impl MutEventSubscriber for RefCell { 233 | fn process(&mut self, events: Events, ops: &mut EventOps) { 234 | self.borrow_mut().process(events, ops); 235 | } 236 | 237 | fn init(&mut self, ops: &mut EventOps) { 238 | self.borrow_mut().init(ops); 239 | } 240 | } 241 | 242 | impl EventSubscriber for Mutex { 243 | fn process(&self, events: Events, ops: &mut EventOps) { 244 | self.lock().unwrap().process(events, ops); 245 | } 246 | 247 | fn init(&self, ops: &mut EventOps) { 248 | self.lock().unwrap().init(ops); 249 | } 250 | } 251 | 252 | impl MutEventSubscriber for Mutex { 253 | fn process(&mut self, events: Events, ops: &mut EventOps) { 254 | // If another user of this mutex panicked while holding the mutex, then 255 | // we terminate the process. 256 | self.get_mut().unwrap().process(events, ops); 257 | } 258 | 259 | fn init(&mut self, ops: &mut EventOps) { 260 | // If another user of this mutex panicked while holding the mutex, then 261 | // we terminate the process. 262 | self.get_mut().unwrap().init(ops); 263 | } 264 | } 265 | 266 | impl EventSubscriber for Box { 267 | fn process(&self, events: Events, ops: &mut EventOps) { 268 | self.deref().process(events, ops); 269 | } 270 | 271 | fn init(&self, ops: &mut EventOps) { 272 | self.deref().init(ops); 273 | } 274 | } 275 | 276 | impl MutEventSubscriber for Box { 277 | fn process(&mut self, events: Events, ops: &mut EventOps) { 278 | self.deref_mut().process(events, ops); 279 | } 280 | 281 | fn init(&mut self, ops: &mut EventOps) { 282 | self.deref_mut().init(ops); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/manager.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use std::mem::size_of; 5 | 6 | use vmm_sys_util::epoll::EpollEvent; 7 | #[cfg(feature = "remote_endpoint")] 8 | use vmm_sys_util::epoll::{ControlOperation, EventSet}; 9 | 10 | #[cfg(feature = "remote_endpoint")] 11 | use super::endpoint::{EventManagerChannel, RemoteEndpoint}; 12 | use super::epoll::EpollWrapper; 13 | use super::subscribers::Subscribers; 14 | #[cfg(feature = "remote_endpoint")] 15 | use super::Errno; 16 | use super::{Error, EventOps, Events, MutEventSubscriber, Result, SubscriberId, SubscriberOps}; 17 | 18 | /// Allows event subscribers to be registered, connected to the event loop, and later removed. 19 | #[derive(Debug)] 20 | pub struct EventManager { 21 | subscribers: Subscribers, 22 | epoll_context: EpollWrapper, 23 | 24 | #[cfg(feature = "remote_endpoint")] 25 | channel: EventManagerChannel, 26 | } 27 | 28 | /// Maximum capacity of ready events that can be passed when initializing the `EventManager`. 29 | // This constant is not defined inside the `EventManager` implementation because it would 30 | // make it really weird to use as the `EventManager` uses generics (S: MutEventSubscriber). 31 | // That means that when using this const, you could not write 32 | // EventManager::MAX_READY_EVENTS_CAPACITY because the type `S` could not be inferred. 33 | // 34 | // This value is taken from: https://elixir.bootlin.com/linux/latest/source/fs/eventpoll.c#L101 35 | pub const MAX_READY_EVENTS_CAPACITY: usize = i32::MAX as usize / size_of::(); 36 | 37 | impl SubscriberOps for EventManager { 38 | type Subscriber = T; 39 | 40 | /// Register a subscriber with the event event_manager and returns the associated ID. 41 | fn add_subscriber(&mut self, subscriber: T) -> SubscriberId { 42 | let subscriber_id = self.subscribers.add(subscriber); 43 | self.subscribers 44 | .get_mut_unchecked(subscriber_id) 45 | // The index is valid because we've just added the subscriber. 46 | .init(&mut self.epoll_context.ops_unchecked(subscriber_id)); 47 | subscriber_id 48 | } 49 | 50 | /// Unregisters and returns the subscriber associated with the provided ID. 51 | fn remove_subscriber(&mut self, subscriber_id: SubscriberId) -> Result { 52 | let subscriber = self 53 | .subscribers 54 | .remove(subscriber_id) 55 | .ok_or(Error::InvalidId)?; 56 | self.epoll_context.remove(subscriber_id); 57 | Ok(subscriber) 58 | } 59 | 60 | /// Return a mutable reference to the subscriber associated with the provided id. 61 | fn subscriber_mut(&mut self, subscriber_id: SubscriberId) -> Result<&mut T> { 62 | if self.subscribers.contains(subscriber_id) { 63 | return Ok(self.subscribers.get_mut_unchecked(subscriber_id)); 64 | } 65 | Err(Error::InvalidId) 66 | } 67 | 68 | /// Returns a `EventOps` object for the subscriber associated with the provided ID. 69 | fn event_ops(&mut self, subscriber_id: SubscriberId) -> Result { 70 | // Check if the subscriber_id is valid. 71 | if self.subscribers.contains(subscriber_id) { 72 | // The index is valid because the result of `find` was not `None`. 73 | return Ok(self.epoll_context.ops_unchecked(subscriber_id)); 74 | } 75 | Err(Error::InvalidId) 76 | } 77 | } 78 | 79 | impl EventManager { 80 | const DEFAULT_READY_EVENTS_CAPACITY: usize = 256; 81 | 82 | /// Create a new `EventManger` object. 83 | pub fn new() -> Result { 84 | Self::new_with_capacity(Self::DEFAULT_READY_EVENTS_CAPACITY) 85 | } 86 | 87 | /// Creates a new `EventManger` object with specified event capacity. 88 | /// 89 | /// # Arguments 90 | /// 91 | /// * `ready_events_capacity`: maximum number of ready events to be 92 | /// processed a single `run`. The maximum value of this 93 | /// parameter is `EventManager::MAX_READY_EVENTS_CAPACITY`. 94 | pub fn new_with_capacity(ready_events_capacity: usize) -> Result { 95 | if ready_events_capacity > MAX_READY_EVENTS_CAPACITY { 96 | return Err(Error::InvalidCapacity); 97 | } 98 | 99 | let manager = EventManager { 100 | subscribers: Subscribers::new(), 101 | epoll_context: EpollWrapper::new(ready_events_capacity)?, 102 | #[cfg(feature = "remote_endpoint")] 103 | channel: EventManagerChannel::new()?, 104 | }; 105 | 106 | #[cfg(feature = "remote_endpoint")] 107 | manager 108 | .epoll_context 109 | .epoll 110 | .ctl( 111 | ControlOperation::Add, 112 | manager.channel.fd(), 113 | EpollEvent::new(EventSet::IN, manager.channel.fd() as u64), 114 | ) 115 | .map_err(|e| Error::Epoll(Errno::from(e)))?; 116 | Ok(manager) 117 | } 118 | 119 | /// Run the event loop blocking until events are triggered or an error is returned. 120 | /// Calls [subscriber.process()](trait.EventSubscriber.html#tymethod.process) for each event. 121 | /// 122 | /// On success, it returns number of dispatched events or 0 when interrupted by a signal. 123 | pub fn run(&mut self) -> Result { 124 | self.run_with_timeout(-1) 125 | } 126 | 127 | /// Wait for events for a maximum timeout of `miliseconds` or until an error is returned. 128 | /// Calls [subscriber.process()](trait.EventSubscriber.html#tymethod.process) for each event. 129 | /// 130 | /// On success, it returns number of dispatched events or 0 when interrupted by a signal. 131 | pub fn run_with_timeout(&mut self, milliseconds: i32) -> Result { 132 | let event_count = self.epoll_context.poll(milliseconds)?; 133 | self.dispatch_events(event_count); 134 | 135 | Ok(event_count) 136 | } 137 | 138 | fn dispatch_events(&mut self, event_count: usize) { 139 | // EpollEvent doesn't implement Eq or PartialEq, so... 140 | let default_event: EpollEvent = EpollEvent::default(); 141 | 142 | // Used to record whether there's an endpoint event that needs to be handled. 143 | #[cfg(feature = "remote_endpoint")] 144 | let mut endpoint_event = None; 145 | 146 | for ev_index in 0..event_count { 147 | let event = self.epoll_context.ready_events[ev_index]; 148 | let fd = event.fd(); 149 | 150 | // Check whether this event has been discarded. 151 | // EpollWrapper::remove_event() discards an IO event by setting it to default value. 152 | if event.events() == default_event.events() && fd == default_event.fd() { 153 | continue; 154 | } 155 | 156 | if let Some(subscriber_id) = self.epoll_context.subscriber_id(fd) { 157 | self.subscribers.get_mut_unchecked(subscriber_id).process( 158 | Events::with_inner(event), 159 | // The `subscriber_id` is valid because we checked it before. 160 | &mut self.epoll_context.ops_unchecked(subscriber_id), 161 | ); 162 | } else { 163 | #[cfg(feature = "remote_endpoint")] 164 | { 165 | // If we got here, there's a chance the event was triggered by the remote 166 | // endpoint fd. Only check for incoming endpoint events right now, and defer 167 | // actually handling them until all subscriber events have been handled first. 168 | // This prevents subscribers whose events are about to be handled from being 169 | // removed by an endpoint request (or other similar situations). 170 | if fd == self.channel.fd() { 171 | endpoint_event = Some(event); 172 | continue; 173 | } 174 | } 175 | 176 | // This should not occur during normal operation. 177 | unreachable!("Received event on fd from subscriber that is not registered"); 178 | } 179 | } 180 | 181 | #[cfg(feature = "remote_endpoint")] 182 | self.dispatch_endpoint_event(endpoint_event); 183 | } 184 | } 185 | 186 | #[cfg(feature = "remote_endpoint")] 187 | impl EventManager { 188 | /// Return a `RemoteEndpoint` object, that allows interacting with the `EventManager` from a 189 | /// different thread. Using `RemoteEndpoint::call_blocking` on the same thread the event loop 190 | /// runs on leads to a deadlock. 191 | pub fn remote_endpoint(&self) -> RemoteEndpoint { 192 | self.channel.remote_endpoint() 193 | } 194 | 195 | fn dispatch_endpoint_event(&mut self, endpoint_event: Option) { 196 | if let Some(event) = endpoint_event { 197 | if event.event_set() != EventSet::IN { 198 | // This situation is virtually impossible to occur. If it does we have 199 | // a programming error in our code. 200 | unreachable!(); 201 | } 202 | self.handle_endpoint_calls(); 203 | } 204 | } 205 | 206 | fn handle_endpoint_calls(&mut self) { 207 | // Clear the inner event_fd. We don't do anything about an error here at this point. 208 | let _ = self.channel.event_fd.read(); 209 | 210 | // Process messages. We consider only `Empty` errors can appear here; we don't check 211 | // for `Disconnected` errors because we keep at least one clone of `channel.sender` alive 212 | // at all times ourselves. 213 | while let Ok(msg) = self.channel.receiver.try_recv() { 214 | match msg.sender { 215 | Some(sender) => { 216 | // We call the inner closure and attempt to send back the result, but can't really do 217 | // anything in case of error here. 218 | let _ = sender.send((msg.fnbox)(self)); 219 | } 220 | None => { 221 | // Just call the function and discard the result. 222 | let _ = (msg.fnbox)(self); 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | #[cfg(test)] 230 | mod tests { 231 | use super::super::Error; 232 | use super::*; 233 | 234 | use std::os::unix::io::{AsRawFd, RawFd}; 235 | use std::sync::{Arc, Mutex}; 236 | 237 | use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; 238 | 239 | struct DummySubscriber { 240 | event_fd_1: EventFd, 241 | event_fd_2: EventFd, 242 | 243 | // Flags used for checking that the event event_manager called the `process` 244 | // function for ev1/ev2. 245 | processed_ev1_out: bool, 246 | processed_ev2_out: bool, 247 | processed_ev1_in: bool, 248 | 249 | // Flags used for driving register/unregister/modify of events from 250 | // outside of the `process` function. 251 | register_ev2: bool, 252 | unregister_ev1: bool, 253 | modify_ev1: bool, 254 | } 255 | 256 | impl DummySubscriber { 257 | fn new() -> Self { 258 | DummySubscriber { 259 | event_fd_1: EventFd::new(0).unwrap(), 260 | event_fd_2: EventFd::new(0).unwrap(), 261 | processed_ev1_out: false, 262 | processed_ev2_out: false, 263 | processed_ev1_in: false, 264 | register_ev2: false, 265 | unregister_ev1: false, 266 | modify_ev1: false, 267 | } 268 | } 269 | } 270 | 271 | impl DummySubscriber { 272 | fn register_ev2(&mut self) { 273 | self.register_ev2 = true; 274 | } 275 | 276 | fn unregister_ev1(&mut self) { 277 | self.unregister_ev1 = true; 278 | } 279 | 280 | fn modify_ev1(&mut self) { 281 | self.modify_ev1 = true; 282 | } 283 | 284 | fn processed_ev1_out(&self) -> bool { 285 | self.processed_ev1_out 286 | } 287 | 288 | fn processed_ev2_out(&self) -> bool { 289 | self.processed_ev2_out 290 | } 291 | 292 | fn processed_ev1_in(&self) -> bool { 293 | self.processed_ev1_in 294 | } 295 | 296 | fn reset_state(&mut self) { 297 | self.processed_ev1_out = false; 298 | self.processed_ev2_out = false; 299 | self.processed_ev1_in = false; 300 | } 301 | 302 | fn handle_updates(&mut self, event_manager: &mut EventOps) { 303 | if self.register_ev2 { 304 | event_manager 305 | .add(Events::new(&self.event_fd_2, EventSet::OUT)) 306 | .unwrap(); 307 | self.register_ev2 = false; 308 | } 309 | 310 | if self.unregister_ev1 { 311 | event_manager 312 | .remove(Events::new_raw( 313 | self.event_fd_1.as_raw_fd(), 314 | EventSet::empty(), 315 | )) 316 | .unwrap(); 317 | self.unregister_ev1 = false; 318 | } 319 | 320 | if self.modify_ev1 { 321 | event_manager 322 | .modify(Events::new(&self.event_fd_1, EventSet::IN)) 323 | .unwrap(); 324 | self.modify_ev1 = false; 325 | } 326 | } 327 | 328 | fn handle_in(&mut self, source: RawFd) { 329 | if self.event_fd_1.as_raw_fd() == source { 330 | self.processed_ev1_in = true; 331 | } 332 | } 333 | 334 | fn handle_out(&mut self, source: RawFd) { 335 | match source { 336 | _ if self.event_fd_1.as_raw_fd() == source => { 337 | self.processed_ev1_out = true; 338 | } 339 | _ if self.event_fd_2.as_raw_fd() == source => { 340 | self.processed_ev2_out = true; 341 | } 342 | _ => {} 343 | } 344 | } 345 | } 346 | 347 | impl MutEventSubscriber for DummySubscriber { 348 | fn process(&mut self, events: Events, ops: &mut EventOps) { 349 | let source = events.fd(); 350 | let event_set = events.event_set(); 351 | 352 | // We only know how to treat EPOLLOUT and EPOLLIN. 353 | // If we received anything else just stop processing the event. 354 | let all_but_in_out = EventSet::all() - EventSet::OUT - EventSet::IN; 355 | if event_set.intersects(all_but_in_out) { 356 | return; 357 | } 358 | 359 | self.handle_updates(ops); 360 | 361 | match event_set { 362 | EventSet::IN => self.handle_in(source), 363 | EventSet::OUT => self.handle_out(source), 364 | _ => {} 365 | } 366 | } 367 | 368 | fn init(&mut self, ops: &mut EventOps) { 369 | let event = Events::new(&self.event_fd_1, EventSet::OUT); 370 | ops.add(event).unwrap(); 371 | } 372 | } 373 | 374 | #[test] 375 | fn test_register() { 376 | use super::SubscriberOps; 377 | 378 | let mut event_manager = EventManager::>>::new().unwrap(); 379 | let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); 380 | 381 | event_manager.add_subscriber(dummy_subscriber.clone()); 382 | 383 | dummy_subscriber.lock().unwrap().register_ev2(); 384 | 385 | // When running the loop the first time, ev1 should be processed, but ev2 shouldn't 386 | // because it was just added as part of processing ev1. 387 | event_manager.run().unwrap(); 388 | assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); 389 | assert!(!dummy_subscriber.lock().unwrap().processed_ev2_out()); 390 | 391 | // Check that both ev1 and ev2 are processed. 392 | dummy_subscriber.lock().unwrap().reset_state(); 393 | event_manager.run().unwrap(); 394 | assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); 395 | assert!(dummy_subscriber.lock().unwrap().processed_ev2_out()); 396 | } 397 | 398 | #[test] 399 | #[should_panic(expected = "FdAlreadyRegistered")] 400 | fn test_add_invalid_subscriber() { 401 | let mut event_manager = EventManager::>>::new().unwrap(); 402 | let subscriber = Arc::new(Mutex::new(DummySubscriber::new())); 403 | 404 | event_manager.add_subscriber(subscriber.clone()); 405 | event_manager.add_subscriber(subscriber); 406 | } 407 | 408 | // Test that unregistering an event while processing another one works. 409 | #[test] 410 | fn test_unregister() { 411 | let mut event_manager = EventManager::>>::new().unwrap(); 412 | let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); 413 | 414 | event_manager.add_subscriber(dummy_subscriber.clone()); 415 | 416 | // Disable ev1. We should only receive this event once. 417 | dummy_subscriber.lock().unwrap().unregister_ev1(); 418 | 419 | event_manager.run().unwrap(); 420 | assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); 421 | 422 | dummy_subscriber.lock().unwrap().reset_state(); 423 | 424 | // We expect no events to be available. Let's run with timeout so that run exists. 425 | event_manager.run_with_timeout(100).unwrap(); 426 | assert!(!dummy_subscriber.lock().unwrap().processed_ev1_out()); 427 | } 428 | 429 | #[test] 430 | fn test_modify() { 431 | let mut event_manager = EventManager::>>::new().unwrap(); 432 | let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); 433 | 434 | event_manager.add_subscriber(dummy_subscriber.clone()); 435 | 436 | // Modify ev1 so that it waits for EPOLL_IN. 437 | dummy_subscriber.lock().unwrap().modify_ev1(); 438 | event_manager.run().unwrap(); 439 | assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); 440 | assert!(!dummy_subscriber.lock().unwrap().processed_ev2_out()); 441 | 442 | dummy_subscriber.lock().unwrap().reset_state(); 443 | 444 | // Make sure ev1 is ready for IN so that we don't loop forever. 445 | dummy_subscriber 446 | .lock() 447 | .unwrap() 448 | .event_fd_1 449 | .write(1) 450 | .unwrap(); 451 | 452 | event_manager.run().unwrap(); 453 | assert!(!dummy_subscriber.lock().unwrap().processed_ev1_out()); 454 | assert!(!dummy_subscriber.lock().unwrap().processed_ev2_out()); 455 | assert!(dummy_subscriber.lock().unwrap().processed_ev1_in()); 456 | } 457 | 458 | #[test] 459 | fn test_remove_subscriber() { 460 | let mut event_manager = EventManager::>>::new().unwrap(); 461 | let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); 462 | 463 | let subscriber_id = event_manager.add_subscriber(dummy_subscriber.clone()); 464 | event_manager.run().unwrap(); 465 | assert!(dummy_subscriber.lock().unwrap().processed_ev1_out()); 466 | 467 | dummy_subscriber.lock().unwrap().reset_state(); 468 | 469 | event_manager.remove_subscriber(subscriber_id).unwrap(); 470 | 471 | // We expect no events to be available. Let's run with timeout so that run exits. 472 | event_manager.run_with_timeout(100).unwrap(); 473 | assert!(!dummy_subscriber.lock().unwrap().processed_ev1_out()); 474 | 475 | // Removing the subscriber twice should return an error. 476 | assert_eq!( 477 | event_manager 478 | .remove_subscriber(subscriber_id) 479 | .err() 480 | .unwrap(), 481 | Error::InvalidId 482 | ); 483 | } 484 | } 485 | -------------------------------------------------------------------------------- /src/subscribers.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use super::SubscriberId; 5 | use std::collections::HashMap; 6 | 7 | // Internal structure used to keep the set of subscribers registered with an EventManger. 8 | // This structure is a thin wrapper over a `HashMap` in which the keys are uniquely 9 | // generated when calling `add`. 10 | #[derive(Debug)] 11 | pub(crate) struct Subscribers { 12 | // The key is the unique id of the subscriber and the entry is the `Subscriber`. 13 | subscribers: HashMap, 14 | // We are generating the unique ids by incrementing this counter for each added subscriber, 15 | // and rely on the large value range of u64 to ensure each value is effectively 16 | // unique over the runtime of any VMM. 17 | next_id: u64, 18 | } 19 | 20 | impl Subscribers { 21 | pub(crate) fn new() -> Self { 22 | Subscribers { 23 | subscribers: HashMap::new(), 24 | next_id: 1, 25 | } 26 | } 27 | 28 | // Adds a subscriber and generates an unique id to represent it. 29 | pub(crate) fn add(&mut self, subscriber: T) -> SubscriberId { 30 | let id = SubscriberId(self.next_id); 31 | self.next_id += 1; 32 | 33 | self.subscribers.insert(id, subscriber); 34 | 35 | id 36 | } 37 | 38 | // Remove and return the subscriber associated with the given id, if it exists. 39 | pub(crate) fn remove(&mut self, subscriber_id: SubscriberId) -> Option { 40 | self.subscribers.remove(&subscriber_id) 41 | } 42 | 43 | // Checks whether a subscriber with `subscriber_id` is registered. 44 | pub(crate) fn contains(&mut self, subscriber_id: SubscriberId) -> bool { 45 | self.subscribers.contains_key(&subscriber_id) 46 | } 47 | 48 | // Return a mutable reference to the subriber represented by `subscriber_id`. 49 | // 50 | // This method should only be called for indices that are known to be valid, otherwise 51 | // panics can occur. 52 | pub(crate) fn get_mut_unchecked(&mut self, subscriber_id: SubscriberId) -> &mut T { 53 | self.subscribers.get_mut(&subscriber_id).unwrap() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/utilities/mod.rs: -------------------------------------------------------------------------------- 1 | // Helper module for tests utilities. 2 | // 3 | // This module is only compiled with `test_utilities` feature on purpose. 4 | // For production code, we do not want to export this functionality. 5 | // At the same time, we need the test utilities to be public such that they can 6 | // be used by multiple categories of tests. Two examples that deem this module 7 | // necessary are the benchmark tests and the integration tests where the implementations 8 | // of subscribers are shared. 9 | // 10 | // Having this module as a feature comes with a disadvantage that needs to be kept in mind. 11 | // `cargo test` will only work when ran with `--feature test-utilities` (or with --all-features). A 12 | // much nicer way to implement this would've been with a utilities crate that is used as 13 | // `dev-dependencies`. Unfortunately, this is not possible because it would introduce a cyclic 14 | // dependency. The `utilities` module has a dependency on `event-manager` because it needs to 15 | // implement the `EventSubscriber` traits, and `event-manager` has a dependency on utilities so 16 | // that they can be used in tests. 17 | #![doc(hidden)] 18 | pub mod subscribers; 19 | -------------------------------------------------------------------------------- /src/utilities/subscribers.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | // 4 | // Cargo thinks that some of the methods in this module are not used because 5 | // not all of them are used in all the separate integration test modules. 6 | // Cargo bug: https://github.com/rust-lang/rust/issues/46379 7 | // Let's allow dead code so that we don't get warnings all the time. 8 | /// This module defines common subscribers that showcase the usage of the event-manager. 9 | /// 10 | /// 1. CounterSubscriber: 11 | /// - a dummy subscriber that increments a counter on event 12 | /// - only uses one event 13 | /// - it has to be explicitly mutated; for this reason it implements `MutEventSubscriber`. 14 | /// 15 | /// 2. CounterSubscriberWithData: 16 | /// - a dummy subscriber that increments a counter on events 17 | /// - this subscriber takes care of multiple events and makes use of `Events::with_data` so 18 | /// that in the `process` function it identifies the trigger of an event using the data 19 | /// instead of the file descriptor 20 | /// - it has to be explicitly mutated; for this reason it implements `MutEventSubscriber`. 21 | /// 22 | /// 3. CounterInnerMutSubscriber: 23 | /// - a dummy subscriber that increments a counter on events 24 | /// - the subscriber makes use of inner mutability; multi-threaded applications might want to 25 | /// use inner mutability instead of having something heavy weight (i.e. Arc). 26 | /// - this subscriber implement `EventSubscriber`. 27 | use std::fmt::{Display, Formatter, Result}; 28 | use std::os::unix::io::AsRawFd; 29 | use std::sync::atomic::AtomicU64; 30 | use std::sync::atomic::Ordering; 31 | 32 | use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; 33 | 34 | use crate::{EventOps, EventSubscriber, Events, MutEventSubscriber}; 35 | 36 | /// A `Counter` is a helper structure for creating subscribers that increment a value 37 | /// each time an event is triggered. 38 | /// The `Counter` allows users to assert and de-assert an event, and to query the counter value. 39 | #[derive(Debug)] 40 | pub struct Counter { 41 | event_fd: EventFd, 42 | counter: u64, 43 | } 44 | 45 | impl Display for Counter { 46 | fn fmt(&self, f: &mut Formatter<'_>) -> Result { 47 | write!( 48 | f, 49 | "(event_fd = {}, counter = {})", 50 | self.event_fd.as_raw_fd(), 51 | self.counter 52 | ) 53 | } 54 | } 55 | 56 | impl Counter { 57 | pub fn new() -> Self { 58 | Self { 59 | event_fd: EventFd::new(0).unwrap(), 60 | counter: 0, 61 | } 62 | } 63 | 64 | pub fn trigger_event(&mut self) { 65 | let _ = self.event_fd.write(1); 66 | } 67 | 68 | pub fn clear_event(&self) { 69 | let _ = self.event_fd.read(); 70 | } 71 | 72 | pub fn counter(&self) -> u64 { 73 | self.counter 74 | } 75 | } 76 | 77 | impl Default for Counter { 78 | fn default() -> Self { 79 | Self::new() 80 | } 81 | } 82 | 83 | // A dummy subscriber that increments a counter whenever it processes 84 | // a new request. 85 | #[derive(Debug)] 86 | pub struct CounterSubscriber(Counter); 87 | 88 | impl std::ops::Deref for CounterSubscriber { 89 | type Target = Counter; 90 | 91 | fn deref(&self) -> &Self::Target { 92 | &self.0 93 | } 94 | } 95 | 96 | impl std::ops::DerefMut for CounterSubscriber { 97 | fn deref_mut(&mut self) -> &mut Counter { 98 | &mut self.0 99 | } 100 | } 101 | 102 | impl Default for CounterSubscriber { 103 | fn default() -> Self { 104 | Self(Counter::new()) 105 | } 106 | } 107 | 108 | impl MutEventSubscriber for CounterSubscriber { 109 | fn process(&mut self, events: Events, event_ops: &mut EventOps) { 110 | match events.event_set() { 111 | EventSet::IN => { 112 | self.counter += 1; 113 | } 114 | EventSet::ERROR => { 115 | eprintln!("Got error on the monitored event."); 116 | } 117 | EventSet::HANG_UP => { 118 | event_ops 119 | .remove(events) 120 | .expect("Encountered error during cleanup."); 121 | panic!("Cannot continue execution. Associated fd was closed."); 122 | } 123 | _ => { 124 | eprintln!( 125 | "Received spurious event from the event manager {:#?}.", 126 | events.event_set() 127 | ); 128 | } 129 | } 130 | } 131 | 132 | fn init(&mut self, ops: &mut EventOps) { 133 | ops.add(Events::new(&self.event_fd, EventSet::IN)) 134 | .expect("Cannot register event."); 135 | } 136 | } 137 | 138 | // A dummy subscriber that makes use of the optional data in `Events` when 139 | // registering & processing events. 140 | // Using 3 counters each having associated event data to showcase the implementation of 141 | // EventSubscriber trait with this scenario. 142 | #[derive(Debug)] 143 | pub struct CounterSubscriberWithData { 144 | counter_1: Counter, 145 | counter_2: Counter, 146 | counter_3: Counter, 147 | 148 | first_data: u32, 149 | toggle_registry: bool, 150 | } 151 | 152 | impl CounterSubscriberWithData { 153 | // `first_data` represents the first event data that can be used by this subscriber. 154 | pub fn new(first_data: u32) -> Self { 155 | Self { 156 | counter_1: Counter::new(), 157 | counter_2: Counter::new(), 158 | counter_3: Counter::new(), 159 | // Using consecutive numbers for the event data helps the compiler to optimize 160 | // match statements on counter_1_data, counter_2_data, counter_3_data using 161 | // a jump table. 162 | first_data, 163 | toggle_registry: false, 164 | } 165 | } 166 | 167 | pub fn trigger_all_counters(&mut self) { 168 | self.counter_1.trigger_event(); 169 | self.counter_2.trigger_event(); 170 | self.counter_3.trigger_event(); 171 | } 172 | 173 | pub fn get_all_counter_values(&self) -> Vec { 174 | vec![ 175 | self.counter_1.counter(), 176 | self.counter_2.counter(), 177 | self.counter_3.counter(), 178 | ] 179 | } 180 | 181 | pub fn set_toggle_registry(&mut self, toggle: bool) { 182 | self.toggle_registry = toggle; 183 | } 184 | } 185 | 186 | impl MutEventSubscriber for CounterSubscriberWithData { 187 | fn process(&mut self, events: Events, ops: &mut EventOps) { 188 | if self.toggle_registry { 189 | self.toggle_registry = false; 190 | 191 | ops.remove(Events::with_data( 192 | &self.counter_1.event_fd, 193 | self.first_data, 194 | EventSet::IN, 195 | )) 196 | .expect("Cannot remove event."); 197 | ops.remove(Events::with_data( 198 | &self.counter_2.event_fd, 199 | self.first_data + 1, 200 | EventSet::IN, 201 | )) 202 | .expect("Cannot remove event."); 203 | ops.remove(Events::with_data( 204 | &self.counter_3.event_fd, 205 | self.first_data + 2, 206 | EventSet::IN, 207 | )) 208 | .expect("Cannot remove event."); 209 | 210 | ops.add(Events::with_data( 211 | &self.counter_1.event_fd, 212 | self.first_data, 213 | EventSet::IN, 214 | )) 215 | .expect("Cannot register event."); 216 | ops.add(Events::with_data( 217 | &self.counter_2.event_fd, 218 | self.first_data + 1, 219 | EventSet::IN, 220 | )) 221 | .expect("Cannot register event."); 222 | ops.add(Events::with_data( 223 | &self.counter_3.event_fd, 224 | self.first_data + 2, 225 | EventSet::IN, 226 | )) 227 | .expect("Cannot register event."); 228 | } 229 | match events.event_set() { 230 | EventSet::IN => { 231 | let event_id = events.data() - self.first_data; 232 | match event_id { 233 | 0 => { 234 | self.counter_1.counter += 1; 235 | } 236 | 1 => { 237 | self.counter_2.counter += 1; 238 | } 239 | 2 => { 240 | self.counter_3.counter += 1; 241 | } 242 | _ => { 243 | eprintln!("Received spurious event."); 244 | } 245 | }; 246 | } 247 | EventSet::ERROR => { 248 | eprintln!("Got error on the monitored event."); 249 | } 250 | EventSet::HANG_UP => { 251 | ops.remove(events) 252 | .expect("Encountered error during cleanup."); 253 | panic!("Cannot continue execution. Associated fd was closed."); 254 | } 255 | _ => {} 256 | } 257 | } 258 | 259 | fn init(&mut self, ops: &mut EventOps) { 260 | ops.add(Events::with_data( 261 | &self.counter_1.event_fd, 262 | self.first_data, 263 | EventSet::IN, 264 | )) 265 | .expect("Cannot register event."); 266 | ops.add(Events::with_data( 267 | &self.counter_2.event_fd, 268 | self.first_data + 1, 269 | EventSet::IN, 270 | )) 271 | .expect("Cannot register event."); 272 | ops.add(Events::with_data( 273 | &self.counter_3.event_fd, 274 | self.first_data + 2, 275 | EventSet::IN, 276 | )) 277 | .expect("Cannot register event."); 278 | } 279 | } 280 | 281 | #[derive(Debug)] 282 | pub struct CounterInnerMutSubscriber { 283 | event_fd: EventFd, 284 | counter: AtomicU64, 285 | } 286 | 287 | impl Default for CounterInnerMutSubscriber { 288 | fn default() -> Self { 289 | Self { 290 | event_fd: EventFd::new(0).unwrap(), 291 | counter: AtomicU64::new(0), 292 | } 293 | } 294 | } 295 | 296 | impl CounterInnerMutSubscriber { 297 | pub fn trigger_event(&self) { 298 | let _ = self.event_fd.write(1); 299 | } 300 | 301 | pub fn clear_event(&self) { 302 | let _ = self.event_fd.read(); 303 | } 304 | 305 | pub fn counter(&self) -> u64 { 306 | self.counter.load(Ordering::Relaxed) 307 | } 308 | } 309 | 310 | impl EventSubscriber for CounterInnerMutSubscriber { 311 | fn process(&self, events: Events, ops: &mut EventOps) { 312 | match events.event_set() { 313 | EventSet::IN => { 314 | self.counter.fetch_add(1, Ordering::Relaxed); 315 | } 316 | EventSet::ERROR => { 317 | eprintln!("Got error on the monitored event."); 318 | } 319 | EventSet::HANG_UP => { 320 | ops.remove(events) 321 | .expect("Encountered error during cleanup."); 322 | panic!("Cannot continue execution. Associated fd was closed."); 323 | } 324 | _ => {} 325 | } 326 | } 327 | 328 | fn init(&self, ops: &mut EventOps) { 329 | ops.add(Events::new(&self.event_fd, EventSet::IN)) 330 | .expect("Cannot register event."); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /tests/basic_event_manager.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | // 4 | // A basic, single threaded application that only needs one type of 5 | // subscriber: `CounterSubscriber`. 6 | // 7 | // The application has an `EventManager` and can register multiple subscribers 8 | // of type `CounterSubscriber`. 9 | 10 | use std::ops::Drop; 11 | 12 | use event_manager::utilities::subscribers::CounterSubscriber; 13 | use event_manager::{EventManager, SubscriberId, SubscriberOps}; 14 | 15 | struct App { 16 | event_manager: EventManager, 17 | subscribers_id: Vec, 18 | } 19 | 20 | impl App { 21 | fn new() -> Self { 22 | Self { 23 | event_manager: EventManager::::new().unwrap(), 24 | subscribers_id: vec![], 25 | } 26 | } 27 | 28 | fn add_subscriber(&mut self) { 29 | let counter_subscriber = CounterSubscriber::default(); 30 | let id = self.event_manager.add_subscriber(counter_subscriber); 31 | self.subscribers_id.push(id); 32 | } 33 | 34 | fn run(&mut self) { 35 | let _ = self.event_manager.run_with_timeout(100); 36 | } 37 | 38 | fn inject_events_for(&mut self, subscriber_index: &[usize]) { 39 | for i in subscriber_index { 40 | let subscriber = self 41 | .event_manager 42 | .subscriber_mut(self.subscribers_id[*i]) 43 | .unwrap(); 44 | subscriber.trigger_event(); 45 | } 46 | } 47 | 48 | fn clear_events_for(&mut self, subscriber_indices: &[usize]) { 49 | for i in subscriber_indices { 50 | let subscriber = self 51 | .event_manager 52 | .subscriber_mut(self.subscribers_id[*i]) 53 | .unwrap(); 54 | subscriber.clear_event(); 55 | } 56 | } 57 | 58 | fn get_counters(&mut self) -> Vec { 59 | let mut result = Vec::::new(); 60 | for subscriber_id in &self.subscribers_id { 61 | let subscriber = self.event_manager.subscriber_mut(*subscriber_id).unwrap(); 62 | result.push(subscriber.counter()); 63 | } 64 | 65 | result 66 | } 67 | 68 | // Whenever the App does not need to monitor events anymore, it should explicitly call 69 | // the `remove_subscriber` function. 70 | fn cleanup(&mut self) { 71 | for id in &self.subscribers_id { 72 | let _ = self.event_manager.remove_subscriber(*id); 73 | } 74 | } 75 | } 76 | 77 | impl Drop for App { 78 | fn drop(&mut self) { 79 | self.cleanup(); 80 | } 81 | } 82 | 83 | #[test] 84 | fn test_single_threaded() { 85 | let mut app = App::new(); 86 | for _ in 0..100 { 87 | app.add_subscriber(); 88 | } 89 | 90 | // Random subscribers, in the sense that I randomly picked those numbers :) 91 | let triggered_subscribers: Vec = vec![1, 3, 50, 97]; 92 | app.inject_events_for(&triggered_subscribers); 93 | app.run(); 94 | 95 | let counters = app.get_counters(); 96 | for (i, &item) in counters.iter().enumerate() { 97 | assert_eq!(item, 1 & (triggered_subscribers.contains(&i) as u64)); 98 | } 99 | 100 | app.clear_events_for(&triggered_subscribers); 101 | app.run(); 102 | let counters = app.get_counters(); 103 | for (i, &item) in counters.iter().enumerate() { 104 | assert_eq!(item, 1 & (triggered_subscribers.contains(&i) as u64)); 105 | } 106 | 107 | // Once the app does not need events anymore, the cleanup needs to be called. 108 | // This is particularly important when the app continues the execution, but event monitoring 109 | // is not necessary. 110 | app.cleanup(); 111 | } 112 | -------------------------------------------------------------------------------- /tests/endpoint.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | #![cfg(feature = "remote_endpoint")] 5 | 6 | use std::any::Any; 7 | use std::result; 8 | use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; 9 | use std::sync::Arc; 10 | use std::thread; 11 | use std::time::Duration; 12 | 13 | use event_manager::utilities::subscribers::CounterSubscriber; 14 | use event_manager::{self, EventManager, MutEventSubscriber, SubscriberId}; 15 | 16 | trait GenericSubscriber: MutEventSubscriber { 17 | fn as_mut_any(&mut self) -> &mut dyn Any; 18 | } 19 | 20 | impl GenericSubscriber for CounterSubscriber { 21 | fn as_mut_any(&mut self) -> &mut dyn Any { 22 | self 23 | } 24 | } 25 | 26 | // This test showcases the use of the three `RemoteEndpoint` methods: `call_blocking`, `fire`, 27 | // and `kick`. The setting is we're moving an `EventManager` to a separate thread, and we want 28 | // to register subscribers that remain fully owned by the manager (so, for example, it's not 29 | // necessary to wrap them in a `Mutex` if event handling needs mutable access to their inner 30 | // state). We also use the `GenericSubscriber` trait defined above to show how we can still 31 | // obtain and interact with references to the actual types when event subscribers are registered 32 | // as trait objects. 33 | #[test] 34 | fn test_endpoint() { 35 | let mut event_manager = EventManager::>::new().unwrap(); 36 | let endpoint = event_manager.remote_endpoint(); 37 | let sub = Box::::default(); 38 | 39 | let run_count = Arc::new(AtomicU64::new(0)); 40 | let keep_running = Arc::new(AtomicBool::new(true)); 41 | 42 | // These get moved into the following closure. 43 | let run_count_clone = run_count.clone(); 44 | let keep_running_clone = keep_running.clone(); 45 | 46 | // We spawn the thread that runs the event loop. 47 | let thread_handle = thread::spawn(move || { 48 | loop { 49 | // The manager gets moved to the new thread here. 50 | event_manager.run().unwrap(); 51 | // Increment this counter every time the `run` method call above returns. Ordering 52 | // is not that important here because the other thread only reads the value. 53 | run_count_clone.fetch_add(1, Ordering::Relaxed); 54 | 55 | // When `keep_running` is no longer `true`, we break the loop and the 56 | // thread will exit. 57 | if !keep_running_clone.load(Ordering::Acquire) { 58 | break; 59 | } 60 | } 61 | }); 62 | 63 | // We're back to the main program thread from now on. 64 | 65 | // `run_count` should not change for now as there's no possible activity for the manager. 66 | thread::sleep(Duration::from_millis(100)); 67 | assert_eq!(run_count.load(Ordering::Relaxed), 0); 68 | 69 | // Use `call_blocking` to register a subscriber (and move it to the event loop thread under 70 | // the ownership of the manager). `call_block` also allows us to inspect the result of the 71 | // operation, but it blocks waiting for it and cannot be invoked from the same thread as 72 | // the event loop. 73 | let sub_id = endpoint 74 | .call_blocking( 75 | |sub_ops| -> result::Result { 76 | Ok(sub_ops.add_subscriber(sub)) 77 | }, 78 | ) 79 | .unwrap(); 80 | 81 | // We've added a subscriber. No subscriber events are fired yet, but the manager run 82 | // loop went through one iteration when the endpoint message was received, so `run_count` 83 | // has benn incremented. 84 | thread::sleep(Duration::from_millis(100)); 85 | assert_eq!(run_count.load(Ordering::Relaxed), 1); 86 | 87 | // Now let's activate the subscriber event. It's going to generate continuous activity until 88 | // we explicitly clear it. We use `endpoint` to interact with the subscriber, because the 89 | // latter is fully owned by the manager. Also, we make use of the `as_mut_any` method 90 | // from our `GenericSubscriber` trait to get a reference to the actual subscriber type 91 | // (which has been erased as a trait object from the manager's perspective). We use `fire` 92 | // here, so we don't get a result. 93 | // 94 | // `fire` can also be used from the same thread as the `event_manager` runs on without causing 95 | // a deadlock, because it doesn't get a result from the closure. For example, we can pass an 96 | // endpoint to a subscriber, and use `fire` as part of `process` if it's helpful for that 97 | // particular use case. 98 | // 99 | // Not getting a result from the closure means we have to deal with error conditions within. 100 | // We use `unwrap` here, but that's ok because if the subscriber associated with `sub_id` is 101 | // not present, then we have a serious error in our program logic. 102 | endpoint 103 | .fire(move |sub_ops| { 104 | let sub = sub_ops.subscriber_mut(sub_id).unwrap(); 105 | // The following `unwrap` cannot fail because we know the type is `CounterSubscriber`. 106 | sub.as_mut_any() 107 | .downcast_mut::() 108 | .unwrap() 109 | .trigger_event() 110 | }) 111 | .unwrap(); 112 | 113 | // The event will start triggering at this point, so `run_count` will increase. 114 | thread::sleep(Duration::from_millis(100)); 115 | assert!(run_count.load(Ordering::Relaxed) > 1); 116 | 117 | // Let's clear the subscriber event. Using `fire` again. 118 | endpoint 119 | .fire(move |sub_ops| { 120 | let sub = sub_ops.subscriber_mut(sub_id).unwrap(); 121 | // The following `unwrap` cannot fail because we know the actual type 122 | // is `CounterSubscriber`. 123 | sub.as_mut_any() 124 | .downcast_mut::() 125 | .unwrap() 126 | .clear_event() 127 | }) 128 | .unwrap(); 129 | 130 | // We wait a bit more. The manager will be once more become blocked waiting for events. 131 | thread::sleep(Duration::from_millis(100)); 132 | 133 | keep_running.store(false, Ordering::Release); 134 | 135 | // Trying to `join` the manager here would lead to a deadlock, because it will never read 136 | // the value of `keep_running` to break the loop while stuck waiting for events. We use the 137 | // `kick` endpoint method to force `EventManager::run()` to return. 138 | 139 | endpoint.kick().unwrap(); 140 | 141 | // We can `join` the manager thread and finalize now. 142 | thread_handle.join().unwrap(); 143 | } 144 | -------------------------------------------------------------------------------- /tests/multi_threaded.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use std::sync::{Arc, Mutex}; 5 | use std::thread; 6 | 7 | use event_manager::utilities::subscribers::{ 8 | CounterInnerMutSubscriber, CounterSubscriber, CounterSubscriberWithData, 9 | }; 10 | use event_manager::{EventManager, EventSubscriber, MutEventSubscriber, SubscriberOps}; 11 | 12 | // Showcase how you can manage subscribers of different types using the same event manager 13 | // in a multithreaded context. 14 | #[test] 15 | fn test_diff_type_subscribers() { 16 | let mut event_manager = EventManager::>>::new() 17 | .expect("Cannot create event manager."); 18 | 19 | // The `CounterSubscriberWithData` expects to receive a number as a parameter that represents 20 | // the number it can use as its inner Events data. 21 | let data_subscriber = Arc::new(Mutex::new(CounterSubscriberWithData::new(1))); 22 | data_subscriber.lock().unwrap().trigger_all_counters(); 23 | 24 | let counter_subscriber = Arc::new(Mutex::new(CounterSubscriber::default())); 25 | counter_subscriber.lock().unwrap().trigger_event(); 26 | 27 | // Saving the ids allows to modify the subscribers in the event manager context (i.e. removing 28 | // the subscribers from the event manager loop). 29 | let ds_id = event_manager.add_subscriber(data_subscriber); 30 | let cs_id = event_manager.add_subscriber(counter_subscriber); 31 | 32 | let thread_handle = thread::spawn(move || { 33 | // In a typical application this would be an infinite loop with some break condition. 34 | // For tests, 100 iterations should suffice. 35 | for _ in 0..100 { 36 | // When the event manager loop exits, it will return the number of events it 37 | // handled. 38 | let ev_count = event_manager.run().unwrap(); 39 | // We are expecting the ev_count to be: 40 | // 4 = 3 (events triggered from data_subscriber) + 1 (event from counter_subscriber). 41 | assert_eq!(ev_count, 4); 42 | } 43 | 44 | // One really important thing is to always remove the subscribers once you don't need them 45 | // any more. 46 | let _ = event_manager.remove_subscriber(ds_id).unwrap(); 47 | let _ = event_manager.remove_subscriber(cs_id).unwrap(); 48 | }); 49 | thread_handle.join().unwrap(); 50 | } 51 | 52 | // Showcase how you can manage a single type of subscriber using the same event manager 53 | // in a multithreaded context. 54 | #[test] 55 | fn test_one_type_subscriber() { 56 | let mut event_manager = 57 | EventManager::::new().expect("Cannot create event manager"); 58 | 59 | // The `CounterSubscriberWithData` expects to receive the number it can use as its inner 60 | // `Events` data as a parameter. 61 | // Let's make sure that the inner data of the two subscribers will not overlap by keeping some 62 | // numbers between the first_data of each subscriber. 63 | let data_subscriber_1 = CounterSubscriberWithData::new(1); 64 | let data_subscriber_2 = CounterSubscriberWithData::new(1999); 65 | 66 | // Saving the ids allows to modify the subscribers in the event manager context (i.e. removing 67 | // the subscribers from the event manager loop). 68 | let ds_id_1 = event_manager.add_subscriber(data_subscriber_1); 69 | let ds_id_2 = event_manager.add_subscriber(data_subscriber_2); 70 | 71 | // Since we moved the ownership of the subscriber to the event manager, now we need to get 72 | // a mutable reference from it so that we can modify them. 73 | // Enclosing this in a code block so that the references are dropped when they're no longer 74 | // needed. 75 | { 76 | let data_subscriber_1 = event_manager.subscriber_mut(ds_id_1).unwrap(); 77 | data_subscriber_1.trigger_all_counters(); 78 | 79 | let data_subscriber_2 = event_manager.subscriber_mut(ds_id_2).unwrap(); 80 | data_subscriber_2.trigger_all_counters(); 81 | } 82 | 83 | let thread_handle = thread::spawn(move || { 84 | // In a typical application this would be an infinite loop with some break condition. 85 | // For tests, 100 iterations should suffice. 86 | for _ in 0..100 { 87 | // When the event manager loop exits, it will return the number of events it 88 | // handled. 89 | let ev_count = event_manager.run().unwrap(); 90 | // Since we triggered all counters, we're expecting the number of events to always be 91 | // 6 (3 from one subscriber and 3 from the other). 92 | assert_eq!(ev_count, 6); 93 | } 94 | 95 | // One really important thing is to always remove the subscribers once you don't need them 96 | // any more. 97 | // When calling remove_subscriber, you receive ownership of the subscriber object. 98 | let data_subscriber_1 = event_manager.remove_subscriber(ds_id_1).unwrap(); 99 | let data_subscriber_2 = event_manager.remove_subscriber(ds_id_2).unwrap(); 100 | 101 | // Let's check how the subscribers look after 100 iterations. 102 | // Each subscriber's counter start from 0. When an event is triggered, each counter 103 | // is incremented. So after 100 iterations when they're all triggered, we expect them 104 | // to be 100. 105 | let expected_subscriber_counters = vec![100, 100, 100]; 106 | assert_eq!( 107 | data_subscriber_1.get_all_counter_values(), 108 | expected_subscriber_counters 109 | ); 110 | assert_eq!( 111 | data_subscriber_2.get_all_counter_values(), 112 | expected_subscriber_counters 113 | ); 114 | }); 115 | 116 | thread_handle.join().unwrap(); 117 | } 118 | 119 | // Showcase how you can manage subscribers that make use of inner mutability using the event manager 120 | // in a multithreaded context. 121 | #[test] 122 | fn test_subscriber_inner_mut() { 123 | // We are using just the `CounterInnerMutSubscriber` subscriber, so we could've initialize 124 | // the event manager as: EventManager::::new(). 125 | // The typical application will have more than one subscriber type, so let's just pretend 126 | // this is the case in this example as well. 127 | let mut event_manager = EventManager::>::new() 128 | .expect("Cannot create event manager"); 129 | 130 | let subscriber = Arc::new(CounterInnerMutSubscriber::default()); 131 | subscriber.trigger_event(); 132 | 133 | // Let's just clone the subscriber before adding it to the event manager. This will allow us 134 | // to use it from this thread without the need to call into EventManager::subscriber_mut(). 135 | let subscriber_id = event_manager.add_subscriber(subscriber.clone()); 136 | 137 | let thread_handle = thread::spawn(move || { 138 | for _ in 0..100 { 139 | // When the event manager loop exits, it will return the number of events it 140 | // handled. 141 | let ev_count = event_manager.run().unwrap(); 142 | assert_eq!(ev_count, 1); 143 | } 144 | 145 | assert!(event_manager.remove_subscriber(subscriber_id).is_ok()); 146 | }); 147 | thread_handle.join().unwrap(); 148 | 149 | assert_eq!(subscriber.counter(), 100); 150 | } 151 | -------------------------------------------------------------------------------- /tests/negative_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use std::os::unix::{io::AsRawFd, net::UnixStream}; 5 | use std::sync::{ 6 | atomic::{AtomicU64, Ordering}, 7 | Arc, 8 | }; 9 | 10 | use event_manager::{ 11 | EventManager, EventOps, EventSubscriber, Events, SubscriberOps, MAX_READY_EVENTS_CAPACITY, 12 | }; 13 | use vmm_sys_util::epoll::EventSet; 14 | 15 | #[derive(Debug)] 16 | struct UnixStreamSubscriber { 17 | stream: UnixStream, 18 | rhup_count: AtomicU64, 19 | // When this flag is used, in the process function the subscriber will 20 | // unregister the fd where an error was received. 21 | // The errors that need to be handled: `EventSet::HANG_UP`, `EventSet::ERROR`. 22 | with_unregister_on_err: bool, 23 | } 24 | 25 | impl UnixStreamSubscriber { 26 | fn new(stream: UnixStream) -> UnixStreamSubscriber { 27 | Self { 28 | stream, 29 | rhup_count: AtomicU64::new(0), 30 | with_unregister_on_err: false, 31 | } 32 | } 33 | 34 | fn new_with_unregister_on_err(stream: UnixStream) -> UnixStreamSubscriber { 35 | Self { 36 | stream, 37 | rhup_count: AtomicU64::new(0), 38 | with_unregister_on_err: true, 39 | } 40 | } 41 | } 42 | 43 | impl EventSubscriber for UnixStreamSubscriber { 44 | fn process(&self, events: Events, ops: &mut EventOps<'_>) { 45 | if events.event_set().contains(EventSet::HANG_UP) { 46 | let _ = self.rhup_count.fetch_add(1, Ordering::Relaxed); 47 | if self.with_unregister_on_err { 48 | ops.remove(Events::empty(&self.stream)).unwrap(); 49 | } 50 | } 51 | } 52 | 53 | fn init(&self, ops: &mut EventOps<'_>) { 54 | ops.add(Events::new(&self.stream, EventSet::IN)).unwrap(); 55 | } 56 | } 57 | 58 | #[test] 59 | fn test_handling_errors_in_subscriber() { 60 | let (sock1, sock2) = UnixStream::pair().unwrap(); 61 | 62 | let mut event_manager = EventManager::>::new().unwrap(); 63 | let subscriber = Arc::new(UnixStreamSubscriber::new(sock1)); 64 | event_manager.add_subscriber(subscriber.clone()); 65 | 66 | // SAFETY: safe because `sock2` is a valid Unix socket, as asserted by the `unwrap` above. 67 | unsafe { libc::close(sock2.as_raw_fd()) }; 68 | std::mem::forget(sock2); 69 | 70 | event_manager.run_with_timeout(100).unwrap(); 71 | event_manager.run_with_timeout(100).unwrap(); 72 | event_manager.run_with_timeout(100).unwrap(); 73 | 74 | // Since the subscriber did not remove the event from its watch list, the 75 | // `EPOLLRHUP` error will continuously be a ready event each time `run` is called. 76 | // We called `run_with_timeout` 3 times, hence we expect `rhup_count` to be 3. 77 | assert_eq!(subscriber.rhup_count.load(Ordering::Relaxed), 3); 78 | 79 | let (sock1, sock2) = UnixStream::pair().unwrap(); 80 | let subscriber_with_unregister = 81 | Arc::new(UnixStreamSubscriber::new_with_unregister_on_err(sock1)); 82 | event_manager.add_subscriber(subscriber_with_unregister); 83 | 84 | // SAFETY: safe because `sock2` is a valid Unix socket, as asserted by the `unwrap` above. 85 | unsafe { libc::close(sock2.as_raw_fd()) }; 86 | std::mem::forget(sock2); 87 | 88 | let ready_list_len = event_manager.run_with_timeout(100).unwrap(); 89 | assert_eq!(ready_list_len, 2); 90 | // At this point the `subscriber_with_unregister` should not yield events anymore. 91 | // We expect the number of ready fds to be 1. 92 | let ready_list_len = event_manager.run_with_timeout(100).unwrap(); 93 | assert_eq!(ready_list_len, 1); 94 | } 95 | 96 | #[test] 97 | fn test_max_ready_list_size() { 98 | assert!( 99 | EventManager::>::new_with_capacity(MAX_READY_EVENTS_CAPACITY) 100 | .is_ok() 101 | ); 102 | assert!(EventManager::>::new_with_capacity( 103 | MAX_READY_EVENTS_CAPACITY + 1 104 | ) 105 | .is_err()); 106 | assert!(EventManager::>::new_with_capacity(usize::MAX).is_err()) 107 | } 108 | -------------------------------------------------------------------------------- /tests/regressions.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Alibaba Cloud. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause 3 | 4 | use event_manager::utilities::subscribers::CounterSubscriberWithData; 5 | use event_manager::{EventManager, SubscriberOps}; 6 | 7 | // Test for the race condition reported by: Karthik.n 8 | // FYI: https://github.com/rust-vmm/event-manager/issues/41 9 | #[test] 10 | fn test_reuse_file_descriptor() { 11 | let mut event_manager = EventManager::::new().unwrap(); 12 | let mut counter_subscriber = CounterSubscriberWithData::new(0); 13 | 14 | // Set flag to toggle the registration of all three fds on the first epoll event, so the final 15 | // event counter should be 1. 16 | counter_subscriber.set_toggle_registry(true); 17 | counter_subscriber.trigger_all_counters(); 18 | let id = event_manager.add_subscriber(counter_subscriber); 19 | 20 | event_manager.run().unwrap(); 21 | let c_ref = event_manager.subscriber_mut(id).unwrap(); 22 | let counters = c_ref.get_all_counter_values(); 23 | assert_eq!(counters[0] + counters[1] + counters[2], 1); 24 | } 25 | --------------------------------------------------------------------------------