Rename OptionFileHandle to HandleOrInvalid and make it just wrap an Option<OwnedHandle>

The name (and updated documentation) make the FFI-only usage clearer, and wrapping Option<OwnedHandle> avoids the need to write a separate Drop or Debug impl.

Co-authored-by: Josh Triplett <josh@joshtriplett.org>
This commit is contained in:
Dan Gohman 2021-07-29 13:36:44 -07:00
parent 18a9f4628a
commit 1ae1eeec25
2 changed files with 38 additions and 80 deletions

View File

@ -45,7 +45,7 @@ pub struct BorrowedHandle<'handle> {
/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is
/// sometimes a valid handle value. See [here] for the full story. For APIs
/// like `CreateFileW` which report errors with `INVALID_HANDLE_VALUE` instead
/// of null, use [`OptionFileHandle`] instead of `Option<OwnedHandle>`.
/// of null, use [`HandleOrInvalid`] instead of `Option<OwnedHandle>`.
///
/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such,
/// it must not be used with handles to open registry keys which need to be
@ -61,25 +61,21 @@ pub struct OwnedHandle {
handle: NonNull<c_void>,
}
/// Similar to `Option<OwnedHandle>`, but intended for use in FFI interfaces
/// where `INVALID_HANDLE_VALUE` is used as the sentry value, and null values
/// are not used at all, such as in the return value of `CreateFileW`.
/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used
/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such
/// FFI declarations.
///
/// The main thing you can do with an `OptionFileHandle` is to convert it into
/// an `OwnedHandle` using its [`TryFrom`] implementation, and this conversion
/// takes care of the check for `INVALID_HANDLE_VALUE`.
/// The only thing you can usefully do with a `HandleOrInvalid` is to convert it into an
/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for
/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
/// checking for `INVALID_HANDLE_VALUE` first.
///
/// If this holds an owned handle, it closes the handle on drop.
///
/// This uses `repr(transparent)` and has the representation of a host handle,
/// so it can be used in FFI in places where a non-null handle is passed as a
/// consumed argument or returned as an owned value, or it is
/// `INVALID_HANDLE_VALUE` indicating an error or an otherwise absent value.
/// If this holds a valid handle, it will close the handle on drop.
#[repr(transparent)]
#[unstable(feature = "io_safety", issue = "87074")]
pub struct OptionFileHandle {
handle: RawHandle,
}
#[derive(Debug)]
pub struct HandleOrInvalid(Option<OwnedHandle>);
// The Windows [`HANDLE`] type may be transferred across and shared between
// thread boundaries (despite containing a `*mut void`, which in general isn't
@ -87,10 +83,10 @@ pub struct OptionFileHandle {
//
// [`HANDLE`]: std::os::windows::raw::HANDLE
unsafe impl Send for OwnedHandle {}
unsafe impl Send for OptionFileHandle {}
unsafe impl Send for HandleOrInvalid {}
unsafe impl Send for BorrowedHandle<'_> {}
unsafe impl Sync for OwnedHandle {}
unsafe impl Sync for OptionFileHandle {}
unsafe impl Sync for HandleOrInvalid {}
unsafe impl Sync for BorrowedHandle<'_> {}
impl BorrowedHandle<'_> {
@ -114,54 +110,31 @@ impl BorrowedHandle<'_> {
}
}
impl OptionFileHandle {
/// Return an empty `OptionFileHandle` with no resource.
#[inline]
#[unstable(feature = "io_safety", issue = "87074")]
pub const fn none() -> Self {
Self { handle: c::INVALID_HANDLE_VALUE }
}
}
impl TryFrom<OptionFileHandle> for OwnedHandle {
impl TryFrom<HandleOrInvalid> for OwnedHandle {
type Error = ();
#[inline]
fn try_from(option: OptionFileHandle) -> Result<Self, ()> {
let handle = option.handle;
forget(option);
if let Some(non_null) = NonNull::new(handle) {
if non_null.as_ptr() != c::INVALID_HANDLE_VALUE {
Ok(Self { handle: non_null })
} else {
Err(())
}
fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> {
// In theory, we ought to be able to assume that the pointer here is
// never null, use `OwnedHandle` rather than `Option<OwnedHandle>`, and
// obviate the the panic path here. Unfortunately, Win32 documentation
// doesn't explicitly guarantee this anywhere.
//
// APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
// null handle indicates an absent value, which wouldn't work if null
// were a valid handle value, so it seems very unlikely that it could
// ever return null. But who knows?
//
// [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
let owned_handle = handle_or_invalid.0.expect("A `HandleOrInvalid` was null!");
if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE {
Err(())
} else {
// In theory, we ought to be able to assume that the pointer here
// is never null, change `option.handle` to `NonNull`, and obviate
// the the panic path here. Unfortunately, Win32 documentation
// doesn't explicitly guarantee this anywhere.
//
// APIs like [`CreateFileW`] itself have `HANDLE` arguments where a
// null handle indicates an absent value, which wouldn't work if
// null were a valid handle value, so it seems very unlikely that
// it could ever return null. But who knows?
//
// [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
panic!("An `OptionFileHandle` was null!");
Ok(owned_handle)
}
}
}
impl From<OwnedHandle> for OptionFileHandle {
#[inline]
fn from(owned: OwnedHandle) -> Self {
let handle = owned.handle;
forget(owned);
Self { handle: handle.as_ptr() }
}
}
impl AsRawHandle for BorrowedHandle<'_> {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
@ -188,7 +161,7 @@ impl IntoRawHandle for OwnedHandle {
impl FromRawHandle for OwnedHandle {
/// Constructs a new instance of `Self` from the given raw handle.
///
/// Use `OptionFileHandle` instead of `Option<OwnedHandle>` for APIs that
/// Use `HandleOrInvalid` instead of `Option<OwnedHandle>` for APIs that
/// use `INVALID_HANDLE_VALUE` to indicate failure.
///
/// # Safety
@ -212,12 +185,12 @@ impl FromRawHandle for OwnedHandle {
}
}
impl FromRawHandle for OptionFileHandle {
/// Constructs a new instance of `Self` from the given raw handle returned
impl FromRawHandle for HandleOrInvalid {
/// Constructs a new instance of `Self` from the given `RawHandle` returned
/// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate
/// failure, such as `CreateFileW`.
///
/// Use `Option<OwnedHandle>` instead of `OptionFileHandle` for APIs that
/// Use `Option<OwnedHandle>` instead of `HandleOrInvalid` for APIs that
/// use null to indicate failure.
///
/// # Safety
@ -230,8 +203,8 @@ impl FromRawHandle for OptionFileHandle {
/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
assert!(!handle.is_null());
Self { handle }
// We require non-null here to catch errors earlier.
Self(Some(OwnedHandle::from_raw_handle(handle)))
}
}
@ -244,15 +217,6 @@ impl Drop for OwnedHandle {
}
}
impl Drop for OptionFileHandle {
#[inline]
fn drop(&mut self) {
unsafe {
let _ = c::CloseHandle(self.handle);
}
}
}
impl fmt::Debug for BorrowedHandle<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedHandle").field("handle", &self.handle).finish()
@ -265,12 +229,6 @@ impl fmt::Debug for OwnedHandle {
}
}
impl fmt::Debug for OptionFileHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OptionFileHandle").field("handle", &self.handle).finish()
}
}
/// A trait to borrow the handle from an underlying object.
#[unstable(feature = "io_safety", issue = "87074")]
pub trait AsHandle {

View File

@ -34,7 +34,7 @@ pub mod prelude {
#[stable(feature = "rust1", since = "1.0.0")]
pub use super::io::{
AsHandle, AsSocket, BorrowedHandle, BorrowedSocket, FromRawHandle, FromRawSocket,
IntoRawHandle, IntoRawSocket, OptionFileHandle, OwnedHandle, OwnedSocket,
HandleOrInvalid, IntoRawHandle, IntoRawSocket, OwnedHandle, OwnedSocket,
};
#[doc(no_inline)]
#[stable(feature = "rust1", since = "1.0.0")]