use std::collections::BTreeMap;
use std::fmt;
use std::fmt::Debug;
use std::io::Error;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Barrier, Mutex, OnceLock, RwLock};
use std::time::{Duration, Instant};
use anyhow::{anyhow, Context, Result};
use libc::{c_void, read, EFD_CLOEXEC, EFD_NONBLOCK};
use log::{error, warn};
use nix::errno::Errno;
use nix::{
poll::{ppoll, PollFd, PollFlags},
sys::time::TimeSpec,
};
use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet};
use vmm_sys_util::eventfd::EventFd;
use crate::clock::{get_current_time, ClockState};
use crate::thread_pool::ThreadPool;
use crate::UtilError;
const READY_EVENT_MAX: usize = 256;
const AIO_PRFETCH_CYCLE_TIME: usize = 100;
#[derive(Debug)]
pub enum NotifierOperation {
AddExclusion = 1,
AddShared = 2,
Modify = 4,
Delete = 8,
Park = 16,
Resume = 32,
AddEvents = 64,
DeleteEvents = 128,
}
#[derive(Debug, PartialEq)]
enum EventStatus {
Alive = 0,
Parked = 1,
Removed = 2,
}
pub type NotifierCallback = dyn Fn(EventSet, RawFd) -> Option<Vec<EventNotifier>>;
pub struct EventNotifier {
raw_fd: i32,
op: NotifierOperation,
parked_fd: Option<i32>,
event: EventSet,
handlers: Vec<Rc<NotifierCallback>>,
pub handler_poll: Option<Box<NotifierCallback>>,
status: Arc<Mutex<EventStatus>>,
}
unsafe impl Sync for EventNotifier {}
unsafe impl Send for EventNotifier {}
impl fmt::Debug for EventNotifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventNotifier")
.field("raw_fd", &self.raw_fd)
.field("op", &self.op)
.field("parked_fd", &self.parked_fd)
.field("event", &self.event)
.field("status", &self.status)
.field("io_poll", &self.handler_poll.is_some())
.finish()
}
}
impl EventNotifier {
pub fn new(
op: NotifierOperation,
raw_fd: i32,
parked_fd: Option<i32>,
event: EventSet,
handlers: Vec<Rc<NotifierCallback>>,
) -> Self {
EventNotifier {
raw_fd,
op,
parked_fd,
event,
handlers,
handler_poll: None,
status: Arc::new(Mutex::new(EventStatus::Alive)),
}
}
}
pub trait EventNotifierHelper {
fn internal_notifiers(_: Arc<Mutex<Self>>) -> Vec<EventNotifier>;
}
pub fn get_notifiers_fds(notifiers: &[EventNotifier]) -> Vec<RawFd> {
let mut fds = Vec::with_capacity(notifiers.len());
for notifier in notifiers {
fds.push(notifier.raw_fd);
}
fds
}
pub fn gen_delete_notifiers(fds: &[RawFd]) -> Vec<EventNotifier> {
let mut notifiers = Vec::with_capacity(fds.len());
for fd in fds {
notifiers.push(EventNotifier::new(
NotifierOperation::Delete,
*fd,
None,
EventSet::IN,
Vec::new(),
));
}
notifiers
}
pub fn create_new_eventfd() -> Result<EventFd, Error> {
EventFd::new(EFD_NONBLOCK | EFD_CLOEXEC)
}
pub trait EventLoopManager: Send + Sync {
fn loop_should_exit(&self) -> bool;
fn loop_cleanup(&self) -> Result<()>;
}
struct Timer {
func: Box<dyn Fn()>,
expire_time: Instant,
id: u64,
}
unsafe impl Sync for Timer {}
unsafe impl Send for Timer {}
impl Timer {
pub fn new(func: Box<dyn Fn()>, delay: Duration, id: u64) -> Self {
let expire_time = get_current_time() + delay;
Timer {
func,
expire_time,
id,
}
}
}
#[allow(clippy::vec_box)]
pub struct EventLoopContext {
epoll: Epoll,
manager: OnceLock<Arc<Mutex<dyn EventLoopManager>>>,
kick_event: EventFd,
kick_me: AtomicBool,
kicked: AtomicBool,
events: Arc<RwLock<BTreeMap<RawFd, Box<EventNotifier>>>>,
gc: Arc<RwLock<Vec<Box<EventNotifier>>>>,
ready_events: Arc<Mutex<Vec<EpollEvent>>>,
timers: Arc<Mutex<Vec<Box<Timer>>>>,
timer_next_id: AtomicU64,
pub thread_pool: Arc<ThreadPool>,
pub clock_state: Arc<Mutex<ClockState>>,
pub thread_exit_barrier: Arc<Barrier>,
}
impl Drop for EventLoopContext {
fn drop(&mut self) {
self.thread_pool
.cancel()
.unwrap_or_else(|e| error!("Thread pool cancel error: {:?}", e));
}
}
impl EventLoopContext {
pub fn new(thread_exit_barrier: Arc<Barrier>) -> Self {
let mut ctx = EventLoopContext {
epoll: Epoll::new().unwrap(),
manager: OnceLock::new(),
kick_event: create_new_eventfd().unwrap(),
kick_me: AtomicBool::new(false),
kicked: AtomicBool::new(false),
events: Arc::new(RwLock::new(BTreeMap::new())),
gc: Arc::new(RwLock::new(Vec::new())),
ready_events: Arc::new(Mutex::new(vec![EpollEvent::default(); READY_EVENT_MAX])),
timers: Arc::new(Mutex::new(Vec::new())),
timer_next_id: AtomicU64::new(0),
thread_pool: Arc::new(ThreadPool::default()),
clock_state: Arc::new(Mutex::new(ClockState::default())),
thread_exit_barrier,
};
ctx.init_kick();
ctx
}
fn init_kick(&mut self) {
let kick_handler: Rc<NotifierCallback> = Rc::new(|_, fd| {
read_fd(fd);
None
});
self.add_event(EventNotifier::new(
NotifierOperation::AddExclusion,
self.kick_event.as_raw_fd(),
None,
EventSet::IN,
vec![kick_handler],
))
.unwrap();
}
pub fn kick(&self) {
self.kicked.store(true, Ordering::SeqCst);
if self.kick_me.load(Ordering::SeqCst) {
if let Err(e) = self.kick_event.write(1) {
warn!("Failed to kick eventloop, {:?}", e);
}
}
}
pub fn set_manager(&self, manager: Arc<Mutex<dyn EventLoopManager>>) {
if self.manager.set(manager).is_err() {
warn!("EventLoopManager has been set");
}
}
fn clear_gc(&self) {
let max_cnt = self.gc.write().unwrap().len();
let mut pop_cnt: usize = 0;
loop {
if pop_cnt >= max_cnt {
break;
}
self.gc.write().unwrap().remove(0);
pop_cnt += 1;
}
}
fn add_event(&self, mut event: EventNotifier) -> Result<()> {
let mut events_map = self.events.write().unwrap();
if let Some(notifier) = events_map.get_mut(&event.raw_fd) {
if let NotifierOperation::AddExclusion = event.op {
return Err(anyhow!(UtilError::BadNotifierOperation));
}
if notifier.event != event.event {
self.epoll.ctl(
ControlOperation::Modify,
notifier.raw_fd,
EpollEvent::new(notifier.event | event.event, &**notifier as *const _ as u64),
)?;
notifier.event |= event.event;
}
notifier.handlers.append(&mut event.handlers);
if *notifier.status.lock().unwrap() == EventStatus::Parked {
warn!("Parked event updated!");
}
return Ok(());
}
let event = Box::new(event);
self.epoll.ctl(
ControlOperation::Add,
event.raw_fd,
EpollEvent::new(event.event, &*event as *const _ as u64),
)?;
let parked_fd = event.parked_fd;
events_map.insert(event.raw_fd, event);
if let Some(parked_fd) = parked_fd {
if let Some(parked) = events_map.get_mut(&parked_fd) {
self.epoll
.ctl(ControlOperation::Delete, parked_fd, EpollEvent::default())?;
*parked.status.lock().unwrap() = EventStatus::Parked;
} else {
return Err(anyhow!(UtilError::NoParkedFd(parked_fd)));
}
}
Ok(())
}
fn rm_event(&self, event: &EventNotifier) -> Result<()> {
let mut events_map = self.events.write().unwrap();
match events_map.get(&event.raw_fd) {
Some(notifier) => {
if *notifier.status.lock().unwrap() == EventStatus::Alive {
if let Err(error) = self.epoll.ctl(
ControlOperation::Delete,
notifier.raw_fd,
EpollEvent::default(),
) {
let error_num = error.raw_os_error().unwrap();
if error_num != libc::EBADF
&& error_num != libc::ENOENT
&& error_num != libc::EPERM
{
return Err(anyhow!(UtilError::BadSyscall(error)));
} else {
warn!("epoll ctl failed: {}", error);
}
}
}
let parked_fd = notifier.parked_fd;
let event = events_map.remove(&event.raw_fd).unwrap();
*event.status.lock().unwrap() = EventStatus::Removed;
self.gc.write().unwrap().push(event);
if let Some(parked_fd) = parked_fd {
if let Some(parked) = events_map.get_mut(&parked_fd) {
self.epoll.ctl(
ControlOperation::Add,
parked_fd,
EpollEvent::new(parked.event, &**parked as *const _ as u64),
)?;
*parked.status.lock().unwrap() = EventStatus::Alive;
} else {
return Err(anyhow!(UtilError::NoParkedFd(parked_fd)));
}
}
}
_ => {
return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd)));
}
}
Ok(())
}
fn modify_event(&self, mut event: EventNotifier) -> Result<()> {
let mut events_map = self.events.write().unwrap();
match events_map.get_mut(&event.raw_fd) {
Some(notifier) => {
let events_specified = !event.event.is_empty();
if events_specified && event.event != notifier.event {
self.epoll.ctl(
ControlOperation::Modify,
notifier.raw_fd,
EpollEvent::new(event.event, &**notifier as *const _ as u64),
)?;
notifier.event = event.event;
}
let handlers_specified = !event.handlers.is_empty();
if handlers_specified {
notifier.handlers.clear();
notifier.handlers.append(&mut event.handlers);
}
}
_ => {
return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd)));
}
}
Ok(())
}
fn park_event(&self, event: &EventNotifier) -> Result<()> {
let mut events_map = self.events.write().unwrap();
match events_map.get_mut(&event.raw_fd) {
Some(notifier) => {
self.epoll
.ctl(
ControlOperation::Delete,
notifier.raw_fd,
EpollEvent::default(),
)
.with_context(|| {
format!("Failed to park event, event fd:{}", notifier.raw_fd)
})?;
*notifier.status.lock().unwrap() = EventStatus::Parked;
}
_ => {
return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd)));
}
}
Ok(())
}
fn resume_event(&self, event: &EventNotifier) -> Result<()> {
let mut events_map = self.events.write().unwrap();
match events_map.get_mut(&event.raw_fd) {
Some(notifier) => {
self.epoll
.ctl(
ControlOperation::Add,
notifier.raw_fd,
EpollEvent::new(notifier.event, &**notifier as *const _ as u64),
)
.with_context(|| {
format!("Failed to resume event, event fd: {}", notifier.raw_fd)
})?;
*notifier.status.lock().unwrap() = EventStatus::Alive;
}
_ => {
return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd)));
}
}
Ok(())
}
fn update_events_for_fd(&self, event: &EventNotifier, add: bool) -> Result<()> {
let mut events_map = self.events.write().unwrap();
match events_map.get_mut(&event.raw_fd) {
Some(notifier) => {
let new_events = if add {
event.event | notifier.event
} else {
!event.event & notifier.event
};
if new_events != notifier.event {
self.epoll
.ctl(
ControlOperation::Modify,
notifier.raw_fd,
EpollEvent::new(new_events, &**notifier as *const _ as u64),
)
.with_context(|| {
format!("Failed to add events, event fd: {}", notifier.raw_fd)
})?;
notifier.event = new_events;
}
}
_ => {
return Err(anyhow!(UtilError::NoRegisterFd(event.raw_fd)));
}
}
Ok(())
}
pub fn update_events(&self, notifiers: Vec<EventNotifier>) -> Result<()> {
for en in notifiers {
trace::update_event(&en.raw_fd, &en.op);
match en.op {
NotifierOperation::AddExclusion | NotifierOperation::AddShared => {
self.add_event(en)?;
}
NotifierOperation::Modify => {
self.modify_event(en)?;
}
NotifierOperation::Delete => {
self.rm_event(&en)?;
}
NotifierOperation::Park => {
self.park_event(&en)?;
}
NotifierOperation::Resume => {
self.resume_event(&en)?;
}
NotifierOperation::AddEvents => {
self.update_events_for_fd(&en, true)?;
}
NotifierOperation::DeleteEvents => {
self.update_events_for_fd(&en, false)?;
}
}
}
self.kick();
Ok(())
}
pub fn run(&self) -> Result<bool> {
if let Some(manager) = self.manager.get() {
if manager.lock().unwrap().loop_should_exit() {
manager.lock().unwrap().loop_cleanup()?;
return Ok(false);
}
}
self.epoll_wait_manager(self.timers_min_duration())?;
Ok(true)
}
pub fn iothread_run(&self) -> Result<()> {
let min_timeout_ns = self.timers_min_duration();
if min_timeout_ns.is_none() {
for _i in 0..AIO_PRFETCH_CYCLE_TIME {
for notifier in self.events.read().unwrap().values() {
let status_locked = notifier.status.lock().unwrap();
if *status_locked != EventStatus::Alive || notifier.handler_poll.is_none() {
continue;
}
let handler_poll = notifier.handler_poll.as_ref().unwrap();
if handler_poll(EventSet::empty(), notifier.raw_fd).is_some() {
break;
}
}
}
}
self.epoll_wait_manager(min_timeout_ns)?;
Ok(())
}
pub fn timer_add(&self, func: Box<dyn Fn()>, delay: Duration) -> u64 {
let mut timers = self.timers.lock().unwrap();
let timer_id = self.timer_next_id.fetch_add(1, Ordering::SeqCst);
let timer = Box::new(Timer::new(func, delay, timer_id));
let mut index = timers.len();
for (i, t) in timers.iter().enumerate() {
if timer.expire_time < t.expire_time {
index = i;
break;
}
}
trace::timer_add(&timer.id, &timer.expire_time);
timers.insert(index, timer);
drop(timers);
self.kick();
timer_id
}
pub fn timer_del(&self, timer_id: u64) {
let mut timers = self.timers.lock().unwrap();
for (i, t) in timers.iter().enumerate() {
if timer_id == t.id {
trace::timer_del(&t.id, &t.expire_time);
timers.remove(i);
break;
}
}
}
pub fn timers_min_duration(&self) -> Option<Duration> {
self.kicked.store(false, Ordering::SeqCst);
let timers = self.timers.lock().unwrap();
if timers.is_empty() {
return None;
}
Some(
timers[0]
.expire_time
.saturating_duration_since(get_current_time()),
)
}
pub fn run_timers(&self) {
let now = get_current_time();
let mut expired_nr: usize = 0;
let mut timers = self.timers.lock().unwrap();
for timer in timers.iter() {
if timer.expire_time > now {
break;
}
expired_nr += 1;
}
let expired_timers: Vec<Box<Timer>> = timers.drain(0..expired_nr).collect();
drop(timers);
for timer in expired_timers {
trace::timer_run(&timer.id);
(timer.func)();
}
}
fn epoll_wait_manager(&self, mut time_out: Option<Duration>) -> Result<()> {
let need_kick = !(time_out.is_some() && *time_out.as_ref().unwrap() == Duration::ZERO);
if need_kick {
self.kick_me.store(true, Ordering::SeqCst);
if self.kicked.load(Ordering::SeqCst) {
time_out = Some(Duration::ZERO);
}
}
if time_out.is_some() && *time_out.as_ref().unwrap() != Duration::ZERO {
let time_out_spec = Some(TimeSpec::from_duration(*time_out.as_ref().unwrap()));
let pollflags = PollFlags::POLLIN | PollFlags::POLLOUT | PollFlags::POLLHUP;
let mut pollfds: [PollFd; 1] = [PollFd::new(self.epoll.as_raw_fd(), pollflags)];
match ppoll(&mut pollfds, time_out_spec, None) {
Ok(_) => time_out = Some(Duration::ZERO),
Err(Errno::EINTR) => time_out = Some(Duration::ZERO),
Err(e) => return Err(anyhow!(UtilError::EpollWait(e.into()))),
};
}
let mut ready_events = self.ready_events.lock().unwrap();
let time_out_ms = match time_out {
Some(t) => i32::try_from(t.as_millis()).unwrap_or(i32::MAX),
None => -1,
};
let ev_count = match self.epoll.wait(time_out_ms, &mut ready_events[..]) {
Ok(ev_count) => ev_count,
Err(e) if e.raw_os_error() == Some(libc::EINTR) => 0,
Err(e) => return Err(anyhow!(UtilError::EpollWait(e))),
};
if need_kick {
self.kick_me.store(false, Ordering::SeqCst);
}
for i in 0..ev_count {
let event = unsafe {
let event_ptr = ready_events[i].data() as *const EventNotifier;
&*event_ptr as &EventNotifier
};
let mut notifiers = Vec::new();
if *event.status.lock().unwrap() == EventStatus::Alive {
for handler in event.handlers.iter() {
match handler(ready_events[i].event_set(), event.raw_fd) {
None => {}
Some(mut notifier) => {
notifiers.append(&mut notifier);
}
}
}
}
if let Err(e) = self.update_events(notifiers) {
error!("update event failed: {}", e);
}
}
self.run_timers();
self.clear_gc();
Ok(())
}
pub fn clean_event_loop(&self) -> Result<()> {
if let Some(manager) = self.manager.get() {
manager.lock().unwrap().loop_cleanup()?;
}
Ok(())
}
}
pub fn read_fd(fd: RawFd) -> u64 {
let mut value: u64 = 0;
let buf = &mut value as *mut u64 as *mut c_void;
let count = std::mem::size_of::<u64>();
let ret = unsafe { read(fd, buf, count) };
if ret == -1 {
warn!("Failed to read fd");
}
value
}
#[cfg(test)]
mod test {
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::Barrier;
use vmm_sys_util::{epoll::EventSet, eventfd::EventFd};
use super::*;
impl EventLoopContext {
fn check_existence(&self, fd: RawFd) -> Option<bool> {
let events_map = self.events.read().unwrap();
events_map
.get(&fd)
.map(|notifier| *notifier.status.lock().unwrap() == EventStatus::Alive)
}
fn create_event(&mut self) -> i32 {
let fd = EventFd::new(EFD_NONBLOCK).unwrap();
let result = fd.as_raw_fd();
let event = EventNotifier::new(
NotifierOperation::AddShared,
fd.as_raw_fd(),
None,
EventSet::OUT,
Vec::new(),
);
self.update_events(vec![event]).unwrap();
result
}
}
fn generate_handler(related_fd: i32) -> Rc<NotifierCallback> {
Rc::new(move |_, _| {
let mut notifiers = Vec::new();
let event = EventNotifier::new(
NotifierOperation::AddShared,
related_fd,
None,
EventSet::IN,
Vec::new(),
);
notifiers.push(event);
Some(notifiers)
})
}
#[test]
fn basic_test() {
let mainloop = EventLoopContext::new(Arc::new(Barrier::new(1)));
let mut notifiers = Vec::new();
let fd1 = EventFd::new(EFD_NONBLOCK).unwrap();
let fd1_related = EventFd::new(EFD_NONBLOCK).unwrap();
let handler1 = generate_handler(fd1_related.as_raw_fd());
let handlers = vec![handler1];
let event1 = EventNotifier::new(
NotifierOperation::AddShared,
fd1.as_raw_fd(),
None,
EventSet::OUT,
handlers,
);
notifiers.push(event1);
mainloop.update_events(notifiers).unwrap();
mainloop.run().unwrap();
assert!(mainloop.check_existence(fd1.as_raw_fd()).unwrap());
assert!(mainloop.check_existence(fd1_related.as_raw_fd()).unwrap());
}
#[test]
fn parked_event_test() {
let mainloop = EventLoopContext::new(Arc::new(Barrier::new(1)));
let mut notifiers = Vec::new();
let fd1 = EventFd::new(EFD_NONBLOCK).unwrap();
let fd2 = EventFd::new(EFD_NONBLOCK).unwrap();
let event1 = EventNotifier::new(
NotifierOperation::AddShared,
fd1.as_raw_fd(),
None,
EventSet::OUT,
Vec::new(),
);
let event2 = EventNotifier::new(
NotifierOperation::AddShared,
fd2.as_raw_fd(),
Some(fd1.as_raw_fd()),
EventSet::OUT,
Vec::new(),
);
notifiers.push(event1);
notifiers.push(event2);
mainloop.update_events(notifiers).unwrap();
mainloop.run().unwrap();
assert!(!mainloop.check_existence(fd1.as_raw_fd()).unwrap());
assert!(mainloop.check_existence(fd2.as_raw_fd()).unwrap());
let event2_remove = EventNotifier::new(
NotifierOperation::Delete,
fd2.as_raw_fd(),
Some(fd1.as_raw_fd()),
EventSet::OUT,
Vec::new(),
);
mainloop.update_events(vec![event2_remove]).unwrap();
assert!(mainloop.check_existence(fd1.as_raw_fd()).unwrap());
assert!(mainloop.check_existence(fd2.as_raw_fd()).is_none());
}
#[test]
fn event_handler_test() {
let mainloop = EventLoopContext::new(Arc::new(Barrier::new(1)));
let mut notifiers = Vec::new();
let fd1 = EventFd::new(EFD_NONBLOCK).unwrap();
let fd1_related = EventFd::new(EFD_NONBLOCK).unwrap();
let fd1_related_update = EventFd::new(EFD_NONBLOCK).unwrap();
let handler1 = generate_handler(fd1_related.as_raw_fd());
let handler1_update = generate_handler(fd1_related_update.as_raw_fd());
let event1 = EventNotifier::new(
NotifierOperation::AddShared,
fd1.as_raw_fd(),
None,
EventSet::OUT,
vec![handler1],
);
let event1_update = EventNotifier::new(
NotifierOperation::AddShared,
fd1.as_raw_fd(),
None,
EventSet::OUT,
vec![handler1_update],
);
notifiers.push(event1);
notifiers.push(event1_update);
mainloop.update_events(notifiers).unwrap();
mainloop.run().unwrap();
assert!(mainloop.check_existence(fd1_related.as_raw_fd()).unwrap());
assert!(mainloop
.check_existence(fd1_related_update.as_raw_fd())
.unwrap());
}
#[test]
fn error_operation_test() {
let mainloop = EventLoopContext::new(Arc::new(Barrier::new(1)));
let fd1 = EventFd::new(EFD_NONBLOCK).unwrap();
let leisure_fd = EventFd::new(EFD_NONBLOCK).unwrap();
let event1 = EventNotifier::new(
NotifierOperation::Delete,
fd1.as_raw_fd(),
None,
EventSet::OUT,
Vec::new(),
);
assert!(mainloop.update_events(vec![event1]).is_err());
let event1 = EventNotifier::new(
NotifierOperation::AddShared,
fd1.as_raw_fd(),
Some(leisure_fd.as_raw_fd()),
EventSet::OUT,
Vec::new(),
);
assert!(mainloop.update_events(vec![event1]).is_err());
let event1_delete = EventNotifier::new(
NotifierOperation::Delete,
fd1.as_raw_fd(),
Some(leisure_fd.as_raw_fd()),
EventSet::OUT,
Vec::new(),
);
assert!(mainloop.update_events(vec![event1_delete]).is_err());
}
#[test]
fn error_parked_operation_test() {
let mainloop = EventLoopContext::new(Arc::new(Barrier::new(1)));
let fd1 = EventFd::new(EFD_NONBLOCK).unwrap();
let fd2 = EventFd::new(EFD_NONBLOCK).unwrap();
let event1 = EventNotifier::new(
NotifierOperation::AddShared,
fd1.as_raw_fd(),
None,
EventSet::OUT,
Vec::new(),
);
mainloop.update_events(vec![event1]).unwrap();
let event2 = EventNotifier::new(
NotifierOperation::AddShared,
fd2.as_raw_fd(),
Some(fd1.as_raw_fd()),
EventSet::OUT,
Vec::new(),
);
mainloop.update_events(vec![event2]).unwrap();
let event1 = EventNotifier::new(
NotifierOperation::Delete,
fd1.as_raw_fd(),
None,
EventSet::OUT,
Vec::new(),
);
assert!(mainloop.update_events(vec![event1]).is_ok());
}
#[test]
fn fd_released_test() {
let mut mainloop = EventLoopContext::new(Arc::new(Barrier::new(1)));
let fd = mainloop.create_event();
let event = EventNotifier::new(
NotifierOperation::Delete,
fd,
None,
EventSet::OUT,
Vec::new(),
);
assert!(mainloop.update_events(vec![event]).is_ok());
}
}