Skip to content

Commit

Permalink
Use the new I/O safety traits, using rustix
Browse files Browse the repository at this point in the history
Switch public APIs from `AsRawFd`/`AsRawHandle` to the new I/O safety
traits `AsFd`/`AsHandle`. On Unix platforms, use rustix to avoid
manipulating raw file descriptors altogether.
  • Loading branch information
sunfishcode committed Nov 17, 2021
1 parent 48de729 commit 7cab0d1
Show file tree
Hide file tree
Showing 15 changed files with 58 additions and 71 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ readme = "README.md"
edition = "2018"

[dependencies]
libc = "0.2.58"
cfg-if = "1.0.0"

[target.'cfg(unix)'.dependencies]
rustix = "0.26.2"

[target.'cfg(windows)'.dependencies]
io-lifetimes = { version = "0.3.0", default-features = false }
winapi = "0.3.7"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ $ cargo add fd-lock
```

## Safety
This crate uses `unsafe` to interface with `libc` and `winapi`. All invariants
This crate uses `unsafe` on Windows to interface with `winapi`. All invariants
have been carefully checked, and are manually enforced.

## Contributing
Expand All @@ -51,7 +51,7 @@ look at some of these issues:
## References
- [LockFile function - WDC](https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-lockfile)
- [flock(2) - Linux Man Page](https://linux.die.net/man/2/flock)
- [`libc::flock`](https://docs.rs/libc/0.2.58/libc/fn.flock.html)
- [`rustix::fs::flock`](https://docs.rs/rustix/*/rustix/fs/fn.flock.html)
- [`winapi::um::fileapi::LockFile`](https://docs.rs/winapi/0.3.7/x86_64-pc-windows-msvc/winapi/um/fileapi/fn.LockFile.html)

## License
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#![forbid(future_incompatible)]
#![deny(missing_debug_implementations, nonstandard_style)]
#![warn(missing_docs, missing_doc_code_examples)]
#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

mod read_guard;
mod rw_lock;
Expand Down
4 changes: 2 additions & 2 deletions src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ cfg_if! {
if #[cfg(unix)] {
mod unix;
pub use unix::*;
pub(crate) use std::os::unix::prelude::AsRawFd as AsRaw;
pub(crate) use rustix::io_lifetimes::AsFd as AsRaw;
} else if #[cfg(windows)] {
mod windows;
pub use windows::*;
#[doc(no_inline)]
pub(crate) use std::os::windows::prelude::AsRawHandle as AsRaw;
pub(crate) use io_lifetimes::AsHandle as AsRaw;
} else {
mod unsupported;
pub use unsupported;
Expand Down
2 changes: 0 additions & 2 deletions src/sys/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ mod read_guard;
mod rw_lock;
mod write_guard;

pub(crate) mod utils;

pub use read_guard::RwLockReadGuard;
pub use rw_lock::RwLock;
pub use write_guard::RwLockWriteGuard;
17 changes: 8 additions & 9 deletions src/sys/unix/read_guard.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
use libc::{flock, LOCK_UN};
use rustix::fs::{flock, FlockOperation};
use rustix::io_lifetimes::AsFd;
use std::ops;
use std::os::unix::io::AsRawFd;

use super::utils::syscall;
use super::RwLock;

#[derive(Debug)]
pub struct RwLockReadGuard<'lock, T: AsRawFd> {
pub struct RwLockReadGuard<'lock, T: AsFd> {
lock: &'lock RwLock<T>,
}

impl<'lock, T: AsRawFd> RwLockReadGuard<'lock, T> {
impl<'lock, T: AsFd> RwLockReadGuard<'lock, T> {
pub(crate) fn new(lock: &'lock RwLock<T>) -> Self {
Self { lock }
}
}

impl<T: AsRawFd> ops::Deref for RwLockReadGuard<'_, T> {
impl<T: AsFd> ops::Deref for RwLockReadGuard<'_, T> {
type Target = T;

#[inline]
Expand All @@ -25,10 +24,10 @@ impl<T: AsRawFd> ops::Deref for RwLockReadGuard<'_, T> {
}
}

impl<T: AsRawFd> Drop for RwLockReadGuard<'_, T> {
impl<T: AsFd> Drop for RwLockReadGuard<'_, T> {
#[inline]
fn drop(&mut self) {
let fd = self.lock.inner.as_raw_fd();
let _ = syscall(unsafe { flock(fd, LOCK_UN) });
let fd = self.lock.inner.as_fd();
let _ = flock(&fd, FlockOperation::Unlock).ok();
}
}
29 changes: 14 additions & 15 deletions src/sys/unix/rw_lock.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,50 @@
use libc::{flock, LOCK_EX, LOCK_NB, LOCK_SH};
use rustix::fs::{flock, FlockOperation};
use rustix::io_lifetimes::AsFd;
use std::io::{self, Error, ErrorKind};
use std::os::unix::io::AsRawFd;

use super::utils::syscall;
use super::{RwLockReadGuard, RwLockWriteGuard};

#[derive(Debug)]
pub struct RwLock<T: AsRawFd> {
pub struct RwLock<T: AsFd> {
pub(crate) inner: T,
}

impl<T: AsRawFd> RwLock<T> {
impl<T: AsFd> RwLock<T> {
#[inline]
pub fn new(inner: T) -> Self {
RwLock { inner }
}

#[inline]
pub fn write(&mut self) -> io::Result<RwLockWriteGuard<'_, T>> {
let fd = self.inner.as_raw_fd();
syscall(unsafe { flock(fd, LOCK_EX) })?;
let fd = self.inner.as_fd();
flock(&fd, FlockOperation::LockExclusive)?;
Ok(RwLockWriteGuard::new(self))
}

#[inline]
pub fn try_write(&mut self) -> Result<RwLockWriteGuard<'_, T>, Error> {
let fd = self.inner.as_raw_fd();
syscall(unsafe { flock(fd, LOCK_EX | LOCK_NB) }).map_err(|err| match err.kind() {
let fd = self.inner.as_fd();
flock(&fd, FlockOperation::NonBlockingLockExclusive).map_err(|err| match err.kind() {
ErrorKind::AlreadyExists => ErrorKind::WouldBlock.into(),
_ => err,
_ => Error::from(err),
})?;
Ok(RwLockWriteGuard::new(self))
}

#[inline]
pub fn read(&self) -> io::Result<RwLockReadGuard<'_, T>> {
let fd = self.inner.as_raw_fd();
syscall(unsafe { flock(fd, LOCK_SH) })?;
let fd = self.inner.as_fd();
flock(&fd, FlockOperation::LockShared)?;
Ok(RwLockReadGuard::new(self))
}

#[inline]
pub fn try_read(&self) -> Result<RwLockReadGuard<'_, T>, Error> {
let fd = self.inner.as_raw_fd();
syscall(unsafe { flock(fd, LOCK_SH | LOCK_NB) }).map_err(|err| match err.kind() {
let fd = self.inner.as_fd();
flock(&fd, FlockOperation::NonBlockingLockShared).map_err(|err| match err.kind() {
ErrorKind::AlreadyExists => ErrorKind::WouldBlock.into(),
_ => err,
_ => Error::from(err),
})?;
Ok(RwLockReadGuard::new(self))
}
Expand Down
9 changes: 0 additions & 9 deletions src/sys/unix/utils.rs

This file was deleted.

19 changes: 9 additions & 10 deletions src/sys/unix/write_guard.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
use libc::{flock, LOCK_UN};
use rustix::fs::{flock, FlockOperation};
use rustix::io_lifetimes::AsFd;
use std::ops;
use std::os::unix::io::AsRawFd;

use super::utils::syscall;
use super::RwLock;

#[derive(Debug)]
pub struct RwLockWriteGuard<'lock, T: AsRawFd> {
pub struct RwLockWriteGuard<'lock, T: AsFd> {
lock: &'lock mut RwLock<T>,
}

impl<'lock, T: AsRawFd> RwLockWriteGuard<'lock, T> {
impl<'lock, T: AsFd> RwLockWriteGuard<'lock, T> {
pub(crate) fn new(lock: &'lock mut RwLock<T>) -> Self {
Self { lock }
}
}

impl<T: AsRawFd> ops::Deref for RwLockWriteGuard<'_, T> {
impl<T: AsFd> ops::Deref for RwLockWriteGuard<'_, T> {
type Target = T;

#[inline]
Expand All @@ -25,17 +24,17 @@ impl<T: AsRawFd> ops::Deref for RwLockWriteGuard<'_, T> {
}
}

impl<T: AsRawFd> ops::DerefMut for RwLockWriteGuard<'_, T> {
impl<T: AsFd> ops::DerefMut for RwLockWriteGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.lock.inner
}
}

impl<T: AsRawFd> Drop for RwLockWriteGuard<'_, T> {
impl<T: AsFd> Drop for RwLockWriteGuard<'_, T> {
#[inline]
fn drop(&mut self) {
let fd = self.lock.inner.as_raw_fd();
let _ = syscall(unsafe { flock(fd, LOCK_UN) });
let fd = self.lock.inner.as_fd();
let _ = flock(&fd, FlockOperation::Unlock).ok();
}
}
2 changes: 0 additions & 2 deletions src/sys/unsupported/read_guard.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use libc::{flock, LOCK_UN};
use std::ops;
use std::os::unix::io::AsRawFd;

use super::utils::syscall;
use super::RwLock;

#[derive(Debug)]
Expand Down
2 changes: 0 additions & 2 deletions src/sys/unsupported/rw_lock.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use libc::{flock, LOCK_EX, LOCK_NB, LOCK_SH};
use std::io::{self, Error, ErrorKind};
use std::os::unix::io::AsRawFd;

use super::utils::syscall;
use super::{RwLockReadGuard, RwLockWriteGuard};

#[derive(Debug)]
Expand Down
2 changes: 0 additions & 2 deletions src/sys/unsupported/write_guard.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use libc::{flock, LOCK_UN};
use std::ops;
use std::os::unix::io::AsRawFd;

use super::utils::syscall;
use super::RwLock;

#[derive(Debug)]
Expand Down
9 changes: 5 additions & 4 deletions src/sys/windows/read_guard.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use io_lifetimes::AsHandle;
use winapi::um::fileapi::UnlockFile;

use std::ops;
Expand All @@ -7,11 +8,11 @@ use super::utils::syscall;
use super::RwLock;

#[derive(Debug)]
pub struct RwLockReadGuard<'lock, T: AsRawHandle> {
pub struct RwLockReadGuard<'lock, T: AsHandle> {
pub(crate) lock: &'lock RwLock<T>,
}

impl<T: AsRawHandle> ops::Deref for RwLockReadGuard<'_, T> {
impl<T: AsHandle> ops::Deref for RwLockReadGuard<'_, T> {
type Target = T;

#[inline]
Expand All @@ -20,10 +21,10 @@ impl<T: AsRawHandle> ops::Deref for RwLockReadGuard<'_, T> {
}
}

impl<T: AsRawHandle> Drop for RwLockReadGuard<'_, T> {
impl<T: AsHandle> Drop for RwLockReadGuard<'_, T> {
#[inline]
fn drop(&mut self) {
let handle = self.lock.inner.as_raw_handle();
let handle = self.lock.inner.as_handle().as_raw_handle();
syscall(unsafe { UnlockFile(handle, 0, 0, 1, 0) })
.expect("Could not unlock the file descriptor");
}
Expand Down
13 changes: 7 additions & 6 deletions src/sys/windows/rw_lock.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use io_lifetimes::AsHandle;
use std::io::{self, Error, ErrorKind};
use std::os::windows::io::AsRawHandle;

Expand All @@ -8,11 +9,11 @@ use super::utils::{syscall, Overlapped};
use super::{RwLockReadGuard, RwLockWriteGuard};

#[derive(Debug)]
pub struct RwLock<T: AsRawHandle> {
pub struct RwLock<T: AsHandle> {
pub(crate) inner: T,
}

impl<T: AsRawHandle> RwLock<T> {
impl<T: AsHandle> RwLock<T> {
#[inline]
pub fn new(inner: T) -> Self {
RwLock { inner }
Expand All @@ -21,7 +22,7 @@ impl<T: AsRawHandle> RwLock<T> {
#[inline]
pub fn read(&self) -> io::Result<RwLockReadGuard<'_, T>> {
// See: https://stackoverflow.com/a/9186532, https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
let handle = self.inner.as_raw_handle();
let handle = self.inner.as_handle().as_raw_handle();
let overlapped = Overlapped::zero();
let flags = 0;
syscall(unsafe { LockFileEx(handle, flags, 0, 1, 0, overlapped.raw()) })?;
Expand All @@ -30,7 +31,7 @@ impl<T: AsRawHandle> RwLock<T> {

#[inline]
pub fn try_read(&self) -> io::Result<RwLockReadGuard<'_, T>> {
let handle = self.inner.as_raw_handle();
let handle = self.inner.as_handle().as_raw_handle();
let overlapped = Overlapped::zero();
let flags = LOCKFILE_FAIL_IMMEDIATELY;

Expand All @@ -42,7 +43,7 @@ impl<T: AsRawHandle> RwLock<T> {
#[inline]
pub fn write(&mut self) -> io::Result<RwLockWriteGuard<'_, T>> {
// See: https://stackoverflow.com/a/9186532, https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
let handle = self.inner.as_raw_handle();
let handle = self.inner.as_handle().as_raw_handle();
let overlapped = Overlapped::zero();
let flags = LOCKFILE_EXCLUSIVE_LOCK;
syscall(unsafe { LockFileEx(handle, flags, 0, 1, 0, overlapped.raw()) })?;
Expand All @@ -51,7 +52,7 @@ impl<T: AsRawHandle> RwLock<T> {

#[inline]
pub fn try_write(&mut self) -> io::Result<RwLockWriteGuard<'_, T>> {
let handle = self.inner.as_raw_handle();
let handle = self.inner.as_handle().as_raw_handle();
let overlapped = Overlapped::zero();
let flags = LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK;

Expand Down
11 changes: 6 additions & 5 deletions src/sys/windows/write_guard.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use io_lifetimes::AsHandle;
use winapi::um::fileapi::UnlockFile;

use std::ops;
Expand All @@ -7,11 +8,11 @@ use super::utils::syscall;
use super::RwLock;

#[derive(Debug)]
pub struct RwLockWriteGuard<'lock, T: AsRawHandle> {
pub struct RwLockWriteGuard<'lock, T: AsHandle> {
pub(crate) lock: &'lock mut RwLock<T>,
}

impl<T: AsRawHandle> ops::Deref for RwLockWriteGuard<'_, T> {
impl<T: AsHandle> ops::Deref for RwLockWriteGuard<'_, T> {
type Target = T;

#[inline]
Expand All @@ -20,17 +21,17 @@ impl<T: AsRawHandle> ops::Deref for RwLockWriteGuard<'_, T> {
}
}

impl<T: AsRawHandle> ops::DerefMut for RwLockWriteGuard<'_, T> {
impl<T: AsHandle> ops::DerefMut for RwLockWriteGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.lock.inner
}
}

impl<T: AsRawHandle> Drop for RwLockWriteGuard<'_, T> {
impl<T: AsHandle> Drop for RwLockWriteGuard<'_, T> {
#[inline]
fn drop(&mut self) {
let handle = self.lock.inner.as_raw_handle();
let handle = self.lock.inner.as_handle().as_raw_handle();
syscall(unsafe { UnlockFile(handle, 0, 0, 1, 0) })
.expect("Could not unlock the file descriptor");
}
Expand Down

0 comments on commit 7cab0d1

Please sign in to comment.