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 May 20, 2022
1 parent a76aed6 commit ce8f5a2
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 64 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ features = [
[target.'cfg(unix)'.dependencies]
rustix = "0.34.0"

[target.'cfg(windows)'.dependencies]
io-lifetimes = "0.6.0"

[dev-dependencies]
tempfile = "3.0.8"
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)]
#![cfg_attr(doc, warn(missing_docs, rustdoc::missing_doc_code_examples))]
#![cfg_attr(io_lifetimes_use_std, feature(io_safety))]

mod read_guard;
mod rw_lock;
Expand Down
8 changes: 4 additions & 4 deletions src/read_guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ use crate::sys;
/// Dropping this type may panic if the lock fails to unlock.
#[must_use = "if unused the RwLock will immediately unlock"]
#[derive(Debug)]
pub struct RwLockReadGuard<'lock, T: sys::AsRaw> {
pub struct RwLockReadGuard<'lock, T: sys::AsOpenFile> {
guard: sys::RwLockReadGuard<'lock, T>,
}

impl<'lock, T: sys::AsRaw> RwLockReadGuard<'lock, T> {
impl<'lock, T: sys::AsOpenFile> RwLockReadGuard<'lock, T> {
pub(crate) fn new(guard: sys::RwLockReadGuard<'lock, T>) -> Self {
Self { guard }
}
}

impl<T: sys::AsRaw> ops::Deref for RwLockReadGuard<'_, T> {
impl<T: sys::AsOpenFile> ops::Deref for RwLockReadGuard<'_, T> {
type Target = T;

#[inline]
Expand All @@ -37,7 +37,7 @@ impl<T: sys::AsRaw> ops::Deref for RwLockReadGuard<'_, T> {
}

/// Release the lock.
impl<T: sys::AsRaw> Drop for RwLockReadGuard<'_, T> {
impl<T: sys::AsOpenFile> Drop for RwLockReadGuard<'_, T> {
#[inline]
fn drop(&mut self) {}
}
4 changes: 2 additions & 2 deletions src/rw_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use std::io;
/// underlying data (exclusive access) and the read portion of this lock typically
/// allows for read-only access (shared access).
#[derive(Debug)]
pub struct RwLock<T: sys::AsRaw> {
pub struct RwLock<T: sys::AsOpenFile> {
lock: sys::RwLock<T>,
}

impl<T: sys::AsRaw> RwLock<T> {
impl<T: sys::AsOpenFile> RwLock<T> {
/// Create a new instance.
///
/// # Examples
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::fd::AsFd as AsOpenFile;
} 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 AsOpenFile;
} else {
mod unsupported;
pub use unsupported;
Expand Down
12 changes: 6 additions & 6 deletions src/sys/unix/read_guard.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use rustix::fd::AsFd;
use rustix::fs::{flock, FlockOperation};
use std::ops;
use std::os::unix::io::AsRawFd;

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 @@ -24,9 +24,9 @@ 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 _ = flock(&self.lock.as_fd(), FlockOperation::Unlock).ok();
let _ = flock(&self.lock.inner.as_fd(), FlockOperation::Unlock).ok();
}
}
37 changes: 14 additions & 23 deletions src/sys/unix/rw_lock.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
use rustix::fd::BorrowedFd;
use rustix::fd::AsFd;
use rustix::fs::{flock, FlockOperation};
use std::io::{self, Error, ErrorKind};
use std::os::unix::io::AsRawFd;

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>> {
flock(&self.as_fd(), FlockOperation::LockExclusive)?;
flock(&self.inner.as_fd(), FlockOperation::LockExclusive)?;
Ok(RwLockWriteGuard::new(self))
}

#[inline]
pub fn try_write(&mut self) -> Result<RwLockWriteGuard<'_, T>, Error> {
flock(&self.as_fd(), FlockOperation::NonBlockingLockExclusive).map_err(|err| match err
.kind()
{
flock(
&self.inner.as_fd(),
FlockOperation::NonBlockingLockExclusive,
)
.map_err(|err| match err.kind() {
ErrorKind::AlreadyExists => ErrorKind::WouldBlock.into(),
_ => Error::from(err),
})?;
Expand All @@ -35,18 +36,18 @@ impl<T: AsRawFd> RwLock<T> {

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

#[inline]
pub fn try_read(&self) -> Result<RwLockReadGuard<'_, T>, Error> {
flock(&self.as_fd(), FlockOperation::NonBlockingLockShared).map_err(|err| {
match err.kind() {
flock(&self.inner.as_fd(), FlockOperation::NonBlockingLockShared).map_err(
|err| match err.kind() {
ErrorKind::AlreadyExists => ErrorKind::WouldBlock.into(),
_ => Error::from(err),
}
})?;
},
)?;
Ok(RwLockReadGuard::new(self))
}

Expand All @@ -57,14 +58,4 @@ impl<T: AsRawFd> RwLock<T> {
{
self.inner
}

#[inline]
pub(crate) fn as_fd(&self) -> BorrowedFd<'_> {
// Safety: We assume that `self.inner`'s file descriptor is valid for
// at least the lifetime of `self`.
//
// Once I/O safety is stablized in std, we can switch the public API to
// use `AsFd` instead of `AsRawFd` and eliminate this `unsafe` block.
unsafe { BorrowedFd::borrow_raw(self.inner.as_raw_fd()) }
}
}
14 changes: 7 additions & 7 deletions src/sys/unix/write_guard.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
use rustix::fd::AsFd;
use rustix::fs::{flock, FlockOperation};
use std::ops;
use std::os::unix::io::AsRawFd;

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 @@ -24,16 +24,16 @@ 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 _ = flock(&self.lock.as_fd(), FlockOperation::Unlock).ok();
let _ = flock(&self.lock.inner.as_fd(), FlockOperation::Unlock).ok();
}
}
9 changes: 5 additions & 4 deletions src/sys/windows/read_guard.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use windows_sys::Win32::Foundation::HANDLE;
use io_lifetimes::AsHandle;
use windows_sys::Win32::Storage::FileSystem::UnlockFile;

use std::ops;
Expand All @@ -8,11 +9,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 @@ -21,10 +22,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() as HANDLE;
let handle = self.lock.inner.as_handle().as_raw_handle() as 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 @@ -10,11 +11,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 @@ -23,7 +24,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() as HANDLE;
let handle = self.inner.as_handle().as_raw_handle() as HANDLE;
let overlapped = Overlapped::zero();
let flags = 0;
syscall(unsafe { LockFileEx(handle, flags, 0, 1, 0, overlapped.raw()) })?;
Expand All @@ -32,7 +33,7 @@ impl<T: AsRawHandle> RwLock<T> {

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

Expand All @@ -44,7 +45,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() as HANDLE;
let handle = self.inner.as_handle().as_raw_handle() as HANDLE;
let overlapped = Overlapped::zero();
let flags = LOCKFILE_EXCLUSIVE_LOCK;
syscall(unsafe { LockFileEx(handle, flags, 0, 1, 0, overlapped.raw()) })?;
Expand All @@ -53,7 +54,7 @@ impl<T: AsRawHandle> RwLock<T> {

#[inline]
pub fn try_write(&mut self) -> io::Result<RwLockWriteGuard<'_, T>> {
let handle = self.inner.as_raw_handle() as HANDLE;
let handle = self.inner.as_handle().as_raw_handle() as 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,4 +1,5 @@
use windows_sys::Win32::Foundation::HANDLE;
use io_lifetimes::AsHandle;
use windows_sys::Win32::Storage::FileSystem::UnlockFile;

use std::ops;
Expand All @@ -8,11 +9,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 @@ -21,17 +22,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() as HANDLE;
let handle = self.lock.inner.as_handle().as_raw_handle() as HANDLE;
syscall(unsafe { UnlockFile(handle, 0, 0, 1, 0) })
.expect("Could not unlock the file descriptor");
}
Expand Down
Loading

0 comments on commit ce8f5a2

Please sign in to comment.