use std::{
io,
os::fd::AsRawFd as _,
};
use derive_new::new;
use log::*;
use nix::{
errno::Errno,
ioctl_read,
ioctl_readwrite,
ioctl_write_ptr,
libc,
};
use crate::{
Error,
EventSources,
Fd,
NTSYNC_MAGIC,
NTSyncObjects,
NtSync,
OwnerId,
Sealed,
cold_path,
raw,
};
#[repr(C)]
#[derive(Debug, new, Default)]
#[new(visibility = "pub(crate)")]
pub struct MutexStatus {
owner: OwnerId,
#[new(value = "0")]
count: u32,
}
impl MutexStatus {
pub fn owner(&self) -> Option<OwnerId> {
if self.owner.0 == 0 {
return None;
}
Some(self.owner)
}
pub fn depth(&self) -> Option<u32> {
if self.count != 0 {
Some(self.count)
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Mutex {
pub(crate) id: Fd,
}
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}
impl From<Mutex> for EventSources {
fn from(value: Mutex) -> EventSources {
EventSources::Mutex(value)
}
}
impl Mutex {
pub fn unlock(&self, owner: OwnerId) -> crate::Result<()> {
let mut args = MutexStatus::new(owner);
match unsafe { ntsync_mutex_unlock(self.id, raw!(mut args: MutexStatus)) } {
Ok(_) => Ok(()),
Err(Errno::EBADF) => Err(Error::AlreadyClosed),
Err(errno) => {
cold_path();
match errno {
Errno::EINVAL => Err(crate::Error::InvalidValue),
Errno::EPERM => Err(crate::Error::PermissionDenied),
other => {
cold_path();
Err(crate::Error::Unknown(other as i32))
},
}
},
}
}
pub fn kill(&self, owner: OwnerId) -> crate::Result<()> {
let id = owner.0;
match unsafe { ntsync_mutex_kill(self.id, raw!(const id: u32)) } {
Ok(_) => {
error!(target: "ntsync", "Mutex {} was killed.", self.id);
Ok(())
},
Err(Errno::EBADF) => Err(Error::AlreadyClosed),
Err(errno) => {
cold_path();
error!(target: "ntsync", "Wanted to kill Mutex {}, but failed", self.id);
match errno {
Errno::EINVAL => Err(crate::Error::InvalidValue),
Errno::EPERM => Err(crate::Error::PermissionDenied),
other => {
cold_path();
Err(crate::Error::Unknown(other as i32))
},
}
},
}
}
}
impl NtSync {
pub fn new_mutex(&self) -> crate::Result<Mutex> {
let args = MutexStatus::default();
match unsafe { ntsync_create_mutex(self.inner.handle.as_raw_fd(), raw!(const args: MutexStatus)) } {
Ok(fd) => {
Ok(Mutex {
id: fd,
})
},
Err(errno) => {
cold_path();
trace!(target: "ntsync", handle=self.inner.handle.as_raw_fd(), returncode=errno as i32 ;"Failed to create Mutex");
match errno {
Errno::EBADF => Err(crate::Error::AlreadyClosed),
other => {
cold_path();
Err(crate::Error::Unknown(other as i32))
},
}
},
}
}
}
impl Sealed for Mutex {}
impl NTSyncObjects for Mutex {
type Status = MutexStatus;
fn delete(self) -> crate::Result<()> {
if unsafe { libc::close(self.id) } == -1 {
cold_path();
return match Errno::last() {
Errno::EBADF => {
trace!(target: "ntsync", handle=self.id; "tried to double close an Mutex");
Err(Error::AlreadyClosed)
},
Errno::EINTR => {
trace!(target: "ntsync", handle=self.id; "While closing the Mutex an interrupt occured");
Err(Error::Interrupt)
},
Errno::EIO => {
trace!(target: "ntsync", handle=self.id; "While closing the Mutex an IOError occured");
Err(Error::IOError(io::Error::from_raw_os_error(Errno::EIO as i32)))
},
errno => {
cold_path();
trace!(target: "ntsync", handle=self.id; "Unexpected error while closing the Mutex: {errno}");
Err(Error::Unknown(errno as i32))
},
};
}
Ok(())
}
#[allow(unused)]
fn read(&self) -> crate::Result<MutexStatus> {
let mut args = MutexStatus::default();
match unsafe { ntsync_mutex_read(self.id, raw!(mut args: MutexStatus)) } {
Ok(_) => Ok(args),
Err(errno) => {
cold_path();
match errno {
Errno::EOWNERDEAD => Err(crate::Error::OwnerDead),
Errno::EINVAL => Err(crate::Error::InvalidValue),
Errno::EBADF => Err(Error::AlreadyClosed),
other => {
cold_path();
Err(crate::Error::Unknown(other as i32))
},
}
},
}
}
}
ioctl_write_ptr!(ntsync_create_mutex, NTSYNC_MAGIC, 0x84, MutexStatus);
ioctl_readwrite!(ntsync_mutex_unlock, NTSYNC_MAGIC, 0x85, MutexStatus);
ioctl_write_ptr!(ntsync_mutex_kill, NTSYNC_MAGIC, 0x86, u32);
ioctl_read!(ntsync_mutex_read, NTSYNC_MAGIC, 0x8C, MutexStatus);