use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
pub(crate) struct GuestFdSemaphore {
initial: u64,
available: AtomicU64,
error_logged: AtomicBool,
}
pub struct GuestFile {
file: File,
sem: Arc<GuestFdSemaphore>,
}
impl GuestFdSemaphore {
pub fn new(limit: u64) -> Self {
GuestFdSemaphore {
initial: limit,
available: limit.into(),
error_logged: false.into(),
}
}
pub fn allocate(self: &Arc<Self>, file: File) -> io::Result<GuestFile> {
self.available.fetch_update(
Ordering::Relaxed,
Ordering::Relaxed,
|previous| previous.checked_sub(1),
).map_err(|_| {
if !self.error_logged.fetch_or(true, Ordering::Relaxed) {
error!(
"No more file descriptors available to the guest (0 available out of {} initially), \
consider increasing the --rlimit-nofile value",
self.initial,
);
}
io::Error::from_raw_os_error(libc::ENFILE)
})?;
Ok(GuestFile {
file,
sem: Arc::clone(self),
})
}
fn release(&self) {
let increased_to = self
.available
.fetch_add(1, Ordering::Relaxed)
.checked_add(1)
.unwrap_or_else(|| panic!("FD semaphore overflow"));
debug_assert!(increased_to <= self.initial);
}
}
impl GuestFile {
pub fn get_file(&self) -> &File {
&self.file
}
}
impl AsRawFd for GuestFile {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl Drop for GuestFile {
fn drop(&mut self) {
self.sem.release();
}
}