use std::{cell::RefCell, future::poll_fn, io, mem, ops::Deref, rc::Rc, task::Waker};
#[cfg(unix)]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{
AsRawHandle, AsRawSocket, FromRawHandle, FromRawSocket, IntoRawHandle, IntoRawSocket, OwnedHandle, OwnedSocket,
RawHandle, RawSocket,
};
#[cfg(windows)]
use windows_sys::Win32::Foundation::HANDLE;
use crate::driver::ops::Op;
pub(crate) struct SharedIoHandle<T> {
inner: Rc<Inner<T>>,
}
impl<T> Clone for SharedIoHandle<T> {
fn clone(&self) -> Self {
Self {
inner: Rc::clone(&self.inner),
}
}
}
impl<T> SharedIoHandle<T> {
pub(crate) fn new(resource: T) -> Self {
Self {
inner: Rc::new(Inner {
resource: Some(resource),
state: RefCell::new(State::Init),
#[cfg(windows)]
association: Rc::new(RefCell::new(AssociationState::Unassociated)),
}),
}
}
pub(crate) fn with_resource<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(self
.inner
.resource
.as_ref()
.expect("SharedIoHandle used after close transferred ownership"))
}
pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.inner, &other.inner)
}
pub(crate) fn strong_count(&self) -> usize {
Rc::strong_count(&self.inner)
}
#[allow(dead_code)]
pub(crate) fn try_unwrap(self) -> Result<T, Self> {
let this = mem::ManuallyDrop::new(self);
let inner = unsafe { std::ptr::read(&this.inner) };
match Rc::try_unwrap(inner) {
Ok(inner) => {
let mut inner = mem::ManuallyDrop::new(inner);
let resource = inner.resource.take().expect("resource already taken by close");
unsafe {
std::ptr::drop_in_place(&mut inner.state);
#[cfg(windows)]
std::ptr::drop_in_place(&mut inner.association);
}
Ok(resource)
}
Err(inner) => Err(Self { inner }),
}
}
pub(crate) async fn take(mut self) -> Option<T> {
loop {
if let Some(inner) = Rc::get_mut(&mut self.inner) {
return inner.take_owned();
}
self.is_unique().await;
}
}
async fn is_unique(&self) {
use std::task::Poll;
poll_fn(|cx| {
if Rc::<Inner<T>>::strong_count(&self.inner) == 1 {
return Poll::Ready(());
}
let mut state = self.inner.state.borrow_mut();
match &mut *state {
State::Init => {
*state = State::Waiting(cx.waker().clone());
Poll::Pending
}
State::Waiting(waker) => {
if !waker.will_wake(cx.waker()) {
waker.clone_from(cx.waker());
}
Poll::Pending
}
State::Closed => Poll::Ready(()),
}
})
.await;
}
}
impl<T: IntoRawOsHandle> SharedIoHandle<T> {
pub(crate) async fn close(self) -> io::Result<()> {
match self.take().await {
Some(resource) => {
let raw = resource.into_raw_os_handle();
match Op::close(raw) {
Ok(op) => op.await,
Err(e) => {
unsafe { drop_raw_os_handle(raw) };
Err(e)
}
}
}
None => Ok(()),
}
}
}
impl<T: AsRawOsHandle> SharedIoHandle<T> {
#[allow(dead_code)] pub(crate) fn as_raw_os_handle(&self) -> OsRawHandle {
self.with_resource(|r| r.as_raw_os_handle())
}
#[cfg(windows)]
#[allow(dead_code)] pub(crate) fn raw_os_handle(&self) -> OsRawHandle {
self.as_raw_os_handle()
}
}
#[cfg(unix)]
impl<T: AsRawFd> SharedIoHandle<T> {
pub(crate) fn raw_fd(&self) -> RawFd {
self.with_resource(|r| r.as_raw_fd())
}
}
#[cfg(windows)]
impl<T: AsRawHandle> SharedIoHandle<T> {
#[allow(dead_code)]
pub(crate) fn raw_handle(&self) -> RawHandle {
self.with_resource(|r| r.as_raw_handle())
}
#[cfg(windows)]
pub(crate) fn handle_registration(&self) -> HandleRegistration {
HandleRegistration {
association: Rc::clone(&self.inner.association),
raw: WindowsRawHandle::Handle(self.raw_handle()),
}
}
}
#[cfg(windows)]
impl<T: AsRawSocket> SharedIoHandle<T> {
pub(crate) fn raw_socket(&self) -> RawSocket {
self.with_resource(|r| r.as_raw_socket())
}
#[cfg(windows)]
pub(crate) fn socket_registration(&self) -> HandleRegistration {
HandleRegistration {
association: Rc::clone(&self.inner.association),
raw: WindowsRawHandle::Socket(self.raw_socket()),
}
}
}
impl<T> Deref for SharedIoHandle<T> {
type Target = T;
fn deref(&self) -> &T {
self.inner
.resource
.as_ref()
.expect("SharedIoHandle used after close transferred ownership")
}
}
impl<T> Drop for SharedIoHandle<T> {
fn drop(&mut self) {
let mut state = self.inner.state.borrow_mut();
if let State::Waiting(_) = *state
&& let State::Waiting(waker) = mem::replace(&mut *state, State::Init)
{
waker.wake();
}
}
}
#[cfg(unix)]
impl<T: AsRawFd> AsRawFd for SharedIoHandle<T> {
fn as_raw_fd(&self) -> RawFd {
self.raw_fd()
}
}
#[cfg(unix)]
impl<T: AsFd> AsFd for SharedIoHandle<T> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner
.resource
.as_ref()
.expect("SharedIoHandle used after close transferred ownership")
.as_fd()
}
}
#[allow(dead_code)] pub(crate) trait AsRawOsHandle {
fn as_raw_os_handle(&self) -> OsRawHandle;
}
pub(crate) trait IntoRawOsHandle {
fn into_raw_os_handle(self) -> OsRawHandle;
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum OsRawHandle {
#[cfg(unix)]
Fd(RawFd),
#[cfg(windows)]
Handle(RawHandle),
#[cfg(windows)]
Socket(RawSocket),
}
#[cfg(windows)]
#[derive(Clone)]
pub(crate) struct HandleRegistration {
association: Rc<RefCell<AssociationState>>,
raw: WindowsRawHandle,
}
#[cfg(windows)]
#[derive(Clone, Copy)]
enum WindowsRawHandle {
Handle(RawHandle),
Socket(RawSocket),
}
#[cfg(windows)]
enum AssociationState {
Unassociated,
Associated(usize),
}
#[cfg(windows)]
impl HandleRegistration {
pub(crate) fn associate(
&self,
registrar: usize,
register: impl FnOnce(HANDLE) -> io::Result<()>,
) -> io::Result<()> {
let mut state = self.association.borrow_mut();
match *state {
AssociationState::Associated(current) if current == registrar => return Ok(()),
AssociationState::Associated(_) => {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"I/O handle is already associated with another karmaio driver",
));
}
AssociationState::Unassociated => {}
}
register(self.raw.as_handle())?;
*state = AssociationState::Associated(registrar);
Ok(())
}
}
#[cfg(windows)]
impl WindowsRawHandle {
#[inline]
fn as_handle(self) -> HANDLE {
match self {
Self::Handle(handle) => handle as HANDLE,
Self::Socket(socket) => socket as HANDLE,
}
}
}
pub(crate) unsafe fn drop_raw_os_handle(raw: OsRawHandle) {
match raw {
#[cfg(unix)]
OsRawHandle::Fd(fd) => drop(unsafe { OwnedFd::from_raw_fd(fd) }),
#[cfg(windows)]
OsRawHandle::Handle(h) => drop(unsafe { OwnedHandle::from_raw_handle(h) }),
#[cfg(windows)]
OsRawHandle::Socket(s) => drop(unsafe { OwnedSocket::from_raw_socket(s) }),
}
}
#[cfg(unix)]
impl AsRawOsHandle for OwnedFd {
fn as_raw_os_handle(&self) -> OsRawHandle {
OsRawHandle::Fd(self.as_raw_fd())
}
}
#[cfg(unix)]
impl IntoRawOsHandle for OwnedFd {
fn into_raw_os_handle(self) -> OsRawHandle {
OsRawHandle::Fd(self.into_raw_fd())
}
}
impl AsRawOsHandle for std::fs::File {
fn as_raw_os_handle(&self) -> OsRawHandle {
#[cfg(unix)]
{
OsRawHandle::Fd(self.as_raw_fd())
}
#[cfg(windows)]
{
OsRawHandle::Handle(self.as_raw_handle())
}
}
}
impl IntoRawOsHandle for std::fs::File {
fn into_raw_os_handle(self) -> OsRawHandle {
#[cfg(unix)]
{
OsRawHandle::Fd(self.into_raw_fd())
}
#[cfg(windows)]
{
OsRawHandle::Handle(self.into_raw_handle())
}
}
}
#[cfg(feature = "net")]
impl AsRawOsHandle for socket2::Socket {
fn as_raw_os_handle(&self) -> OsRawHandle {
#[cfg(unix)]
{
OsRawHandle::Fd(self.as_raw_fd())
}
#[cfg(windows)]
{
OsRawHandle::Socket(self.as_raw_socket())
}
}
}
#[cfg(feature = "net")]
impl IntoRawOsHandle for socket2::Socket {
fn into_raw_os_handle(self) -> OsRawHandle {
#[cfg(unix)]
{
OsRawHandle::Fd(self.into_raw_fd())
}
#[cfg(windows)]
{
OsRawHandle::Socket(self.into_raw_socket())
}
}
}
#[cfg(windows)]
impl AsRawOsHandle for OwnedHandle {
fn as_raw_os_handle(&self) -> OsRawHandle {
OsRawHandle::Handle(self.as_raw_handle())
}
}
#[cfg(windows)]
impl IntoRawOsHandle for OwnedHandle {
fn into_raw_os_handle(self) -> OsRawHandle {
OsRawHandle::Handle(self.into_raw_handle())
}
}
#[cfg(windows)]
impl AsRawOsHandle for OwnedSocket {
fn as_raw_os_handle(&self) -> OsRawHandle {
OsRawHandle::Socket(self.as_raw_socket())
}
}
#[cfg(windows)]
impl IntoRawOsHandle for OwnedSocket {
fn into_raw_os_handle(self) -> OsRawHandle {
OsRawHandle::Socket(self.into_raw_socket())
}
}
struct Inner<T> {
resource: Option<T>,
state: RefCell<State>,
#[cfg(windows)]
association: Rc<RefCell<AssociationState>>,
}
impl<T> Inner<T> {
fn take_owned(&mut self) -> Option<T> {
{
let state = RefCell::get_mut(&mut self.state);
if let State::Closed = *state {
return None;
}
*state = State::Closed;
}
self.resource.take()
}
}
enum State {
Init,
Waiting(Waker),
Closed,
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(windows)]
use std::cell::Cell;
#[cfg(unix)]
use std::task::Poll;
#[cfg(windows)]
fn test_registration(raw: WindowsRawHandle) -> HandleRegistration {
HandleRegistration {
association: Rc::new(RefCell::new(AssociationState::Unassociated)),
raw,
}
}
#[cfg(windows)]
#[test]
fn registration_associates_once_and_rejects_another_driver() {
let registration = test_registration(WindowsRawHandle::Handle(1usize as RawHandle));
let calls = Cell::new(0);
registration
.associate(7, |_| {
calls.set(calls.get() + 1);
Ok(())
})
.expect("first registration");
registration
.associate(7, |_| {
calls.set(calls.get() + 1);
Ok(())
})
.expect("same driver registration is idempotent");
assert_eq!(calls.get(), 1);
assert_eq!(
registration
.associate(8, |_| Ok(()))
.expect_err("another driver must be rejected")
.kind(),
io::ErrorKind::AlreadyExists
);
}
#[cfg(windows)]
#[test]
fn failed_registration_can_be_retried() {
let raw = usize::MAX as RawSocket;
let registration = test_registration(WindowsRawHandle::Socket(raw));
let seen = Cell::new(0usize);
assert_eq!(
registration
.associate(3, |handle| {
seen.set(handle as usize);
Err(io::Error::new(io::ErrorKind::PermissionDenied, "injected"))
})
.expect_err("injected registration failure")
.kind(),
io::ErrorKind::PermissionDenied
);
assert_eq!(seen.get(), raw as usize);
registration
.associate(3, |_| Ok(()))
.expect("failed registration must remain retryable");
}
#[cfg(unix)]
fn dev_null_handle() -> SharedIoHandle<std::fs::File> {
let file = std::fs::File::open("/dev/null").expect("/dev/null");
SharedIoHandle::new(file)
}
#[cfg(unix)]
#[test]
fn try_unwrap_succeeds_when_unique() {
let handle = dev_null_handle();
let owned = match handle.try_unwrap() {
Ok(owned) => owned,
Err(_) => panic!("unique handle should unwrap"),
};
drop(owned);
}
#[cfg(unix)]
#[test]
fn try_unwrap_fails_when_shared() {
let handle = dev_null_handle();
let clone = handle.clone();
let err = match handle.try_unwrap() {
Err(handle) => handle,
Ok(_) => panic!("shared handle must not unwrap"),
};
drop(clone);
drop(err);
}
#[cfg(unix)]
#[test]
fn close_waits_for_in_flight_clone() {
use crate::runtime::local::Runtime;
let mut runtime = Runtime::new().expect("runtime should start");
let handle = dev_null_handle();
let inflight = handle.clone();
let close_jh = runtime.spawn(async move {
handle.close().await.expect("close should succeed");
});
let drop_jh = runtime.spawn(async move {
let mut yielded = false;
std::future::poll_fn(|cx| {
if !yielded {
yielded = true;
cx.waker().wake_by_ref();
Poll::Pending
} else {
Poll::Ready(())
}
})
.await;
drop(inflight);
});
runtime.block_on(async {
drop_jh.await.expect("drop task");
close_jh.await.expect("close task");
});
}
#[cfg(unix)]
#[test]
fn close_when_already_unique() {
use crate::runtime::local::Runtime;
let mut runtime = Runtime::new().expect("runtime should start");
let handle = dev_null_handle();
runtime.block_on(async move {
handle.close().await.expect("close should succeed");
});
}
#[cfg(unix)]
#[test]
fn take_returns_owned_when_unique() {
use crate::runtime::local::Runtime;
let mut runtime = Runtime::new().expect("runtime should start");
let handle = dev_null_handle();
runtime.block_on(async move {
let owned = handle.take().await.expect("take should return owned handle");
drop(owned);
});
}
#[cfg(unix)]
#[test]
fn take_waits_for_in_flight_clone() {
use crate::runtime::local::Runtime;
let mut runtime = Runtime::new().expect("runtime should start");
let handle = dev_null_handle();
let inflight = handle.clone();
let take_jh = runtime.spawn(async move { handle.take().await.expect("take should succeed after clone drops") });
let drop_jh = runtime.spawn(async move {
let mut yielded = false;
std::future::poll_fn(|cx| {
if !yielded {
yielded = true;
cx.waker().wake_by_ref();
Poll::Pending
} else {
Poll::Ready(())
}
})
.await;
drop(inflight);
});
runtime.block_on(async {
drop_jh.await.expect("drop task");
let owned = take_jh.await.expect("take task");
drop(owned);
});
}
#[cfg(unix)]
#[test]
fn drop_closes_synchronously_without_explicit_close() {
let handle = dev_null_handle();
drop(handle);
}
#[cfg(unix)]
#[test]
fn take_after_prior_take_is_none_via_closed_state() {
use crate::runtime::local::Runtime;
let mut runtime = Runtime::new().expect("runtime should start");
let handle = dev_null_handle();
runtime.block_on(async move {
let owned = handle.take().await.expect("first take");
drop(owned);
});
}
#[cfg(unix)]
#[test]
fn as_raw_os_handle_matches_as_raw_fd() {
let handle = dev_null_handle();
let raw = handle.as_raw_os_handle();
match raw {
OsRawHandle::Fd(fd) => assert_eq!(fd, handle.as_raw_fd()),
}
}
#[cfg(unix)]
#[test]
fn try_unwrap_owned_fd_variant() {
let file = std::fs::File::open("/dev/null").expect("/dev/null");
let handle = SharedIoHandle::new(OwnedFd::from(file));
let owned = match handle.try_unwrap() {
Ok(owned) => owned,
Err(_) => panic!("unique handle should unwrap"),
};
drop(owned);
}
#[cfg(unix)]
#[test]
fn deref_exposes_resource() {
let handle = dev_null_handle();
let _ = handle.metadata().expect("metadata");
}
}