use std::io;
use std::path::Path;
use std::time::{Duration, SystemTime};
use super::defs::*;
use crate::{account::model::*, support::error::Error};
impl Account {
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
pub async fn idle(
&mut self,
mailbox: &mut Mailbox,
) -> Result<PollResponse, Error> {
self.idle_poll(mailbox).await
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
pub async fn idle(
&mut self,
mailbox: &mut Mailbox,
) -> Result<PollResponse, Error> {
use std::os::unix::io::RawFd;
use tokio::io::unix::AsyncFd;
#[cfg(target_os = "linux")]
fn init(
metadb_path: &Path,
deliverydb_path: &Path,
) -> io::Result<(nix::sys::inotify::Inotify, AsyncFd<RawFd>)> {
use nix::sys::inotify;
use std::os::fd::{AsFd, AsRawFd};
let handle = inotify::Inotify::init(
inotify::InitFlags::IN_CLOEXEC
| inotify::InitFlags::IN_NONBLOCK,
)?;
handle.add_watch(metadb_path, inotify::AddWatchFlags::IN_MODIFY)?;
handle.add_watch(
deliverydb_path,
inotify::AddWatchFlags::IN_MODIFY,
)?;
let asyncfd = tokio::io::unix::AsyncFd::with_interest(
handle.as_fd().as_raw_fd(),
tokio::io::Interest::READABLE,
)
.unwrap();
Ok((handle, asyncfd))
}
#[cfg(target_os = "linux")]
fn clear_events(inotify: &nix::sys::inotify::Inotify) {
while inotify.read_events().ok().is_some_and(|v| !v.is_empty()) {}
}
#[cfg(target_os = "freebsd")]
#[allow(dead_code)]
struct Handle {
kqueue: nix::sys::event::Kqueue,
metadb: std::fs::File,
deliverydb: std::fs::File,
}
#[cfg(target_os = "freebsd")]
fn init(
metadb_path: &Path,
deliverydb_path: &Path,
) -> io::Result<(Handle, AsyncFd<RawFd>)> {
use nix::sys::event;
use std::mem;
use std::os::fd::AsRawFd;
let metadb = std::fs::File::open(metadb_path)?;
let deliverydb = std::fs::File::open(deliverydb_path)?;
let kqueue = event::Kqueue::new()?;
kqueue.kevent(
&[
event::KEvent::new(
metadb.as_raw_fd() as usize,
event::EventFilter::EVFILT_VNODE,
event::EventFlag::EV_ADD
| event::EventFlag::EV_ENABLE
| event::EventFlag::EV_CLEAR,
event::FilterFlag::NOTE_EXTEND
| event::FilterFlag::NOTE_WRITE,
0, 0, ),
event::KEvent::new(
deliverydb.as_raw_fd() as usize,
event::EventFilter::EVFILT_VNODE,
event::EventFlag::EV_ADD
| event::EventFlag::EV_ENABLE
| event::EventFlag::EV_CLEAR,
event::FilterFlag::NOTE_EXTEND
| event::FilterFlag::NOTE_WRITE,
0, 0, ),
],
&mut [],
Some(nix::libc::timespec {
tv_sec: 0,
tv_nsec: 0,
}),
)?;
let fd: RawFd = unsafe {
assert_eq!(
mem::size_of::<RawFd>(),
mem::size_of::<event::Kqueue>(),
);
assert_eq!(
mem::align_of::<RawFd>(),
mem::align_of::<event::Kqueue>(),
);
mem::transmute_copy(&kqueue)
};
let asyncfd = tokio::io::unix::AsyncFd::with_interest(
fd,
tokio::io::Interest::READABLE,
)
.unwrap();
Ok((
Handle {
kqueue,
metadb,
deliverydb,
},
asyncfd,
))
}
#[cfg(target_os = "freebsd")]
fn clear_events(handle: &Handle) {
use nix::sys::event;
let mut buf: [event::KEvent; 4] = unsafe { std::mem::zeroed() };
let zero = nix::libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
while handle
.kqueue
.kevent(&[], &mut buf, Some(zero))
.ok()
.is_some_and(|n| n > 0)
{}
}
let (handle, asyncfd) = init(&self.metadb_path, &self.deliverydb_path)?;
loop {
self.drain_deliveries();
let poll = self.poll(mailbox)?;
if PollResponse::default() != poll {
return Ok(poll);
}
let mut readable = asyncfd.readable().await?;
clear_events(&handle);
readable.clear_ready();
}
}
#[allow(dead_code)]
async fn idle_poll(
&mut self,
mailbox: &mut Mailbox,
) -> Result<PollResponse, Error> {
let mut last_metadb = SystemTime::UNIX_EPOCH;
let mut last_deliverydb = SystemTime::UNIX_EPOCH;
loop {
let metadb = self.metadb_mtime()?;
let deliverydb = self.deliverydb_mtime()?;
if (last_metadb, last_deliverydb) != (metadb, deliverydb) {
last_metadb = metadb;
last_deliverydb = deliverydb;
self.drain_deliveries();
let poll = self.poll(mailbox)?;
if PollResponse::default() != poll {
return Ok(poll);
}
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
fn metadb_mtime(&self) -> Result<SystemTime, Error> {
Ok(self.metadb_path.metadata()?.modified()?)
}
fn deliverydb_mtime(&self) -> Result<SystemTime, Error> {
Ok(self.deliverydb_path.metadata()?.modified()?)
}
}