Skip to main content

BlockingEndpoint

Struct BlockingEndpoint 

Source
pub struct BlockingEndpoint { /* private fields */ }
Expand description

An overlapped endpoint that completes operations synchronously, one at a time, via GetOverlappedResult.

“One at a time” is enforced, not merely documented: every safe adapter on this type takes &mut self, so a second operation while one is in flight is a borrow-check error. That matters because GetOverlappedResult waits on the handle, which is signalled by whichever operation completes – with two outstanding, a call could return the other one’s result and hand back buffers the kernel is still writing into.

The type is still Send + Sync, so an endpoint can be moved between threads or shared behind a Mutex; what it cannot do is have two operations in flight, which is what the mutual exclusion buys.

One owner issuing operations in sequence is the supported shape; sharing one endpoint across threads and operating from both is rejected at compile time rather than corrupting a result at run time, since every operation method takes &mut self while an Arc hands out only &BlockingEndpoint. See the read method (available with the fs feature) for runnable examples of both – the examples live there because they call read, which the fs feature provides, so they compile in every configuration that has it.

Implementations§

Source§

impl BlockingEndpoint

Source

pub fn new(endpoint: UnassociatedEndpoint) -> Result<Self, TryFromEndpointError>

Take ownership of an overlapped endpoint for synchronous completion.

§Errors

Returns TryFromEndpointError, recoverable back into endpoint via TryFromEndpointError::into_endpoint, if endpoint has NotificationModes::skip_set_event_on_handle set (PR #20 review response). run below waits on the handle’s own internal event via GetOverlappedResult, which is exactly the notification that mode suppresses – constructing a BlockingEndpoint from such an endpoint would have no wakeup source for a genuinely pending (ERROR_IO_PENDING) operation and could block forever. Win32 offers no way to clear the mode once set (see UnassociatedEndpoint::into_handle), so this is the one place the incompatibility can be caught, and it is checked here rather than left as a documentation-only warning.

Source

pub fn handle(&self) -> BorrowedHandle<'_>

Borrow the underlying handle for issuing native operations.

Source

pub unsafe fn run<P, F>( &self, operation: &mut Operation<P>, issue: F, ) -> Result<usize>
where F: FnOnce(BorrowedHandle<'_>, *mut OVERLAPPED) -> Result<()>,

Issue one overlapped operation and block until it completes, returning the number of bytes transferred.

issue performs the native call with the operation’s OVERLAPPED pointer, returning Ok when the operation was accepted (native success or ERROR_IO_PENDING) and Err on an immediate failure.

§Safety

issue must start exactly one overlapped operation using the provided OVERLAPPED pointer and no other storage, and no other operation may be outstanding on this endpoint until this call returns. Any buffers the operation reads or writes must stay valid for the duration of the call.

This takes &self rather than &mut self so a caller driving the raw seam can hold other borrows of the endpoint; the exclusivity requirement is theirs to uphold, which is what makes this unsafe. The safe adapters built on it take &mut self instead, so they cannot violate it.

Source§

impl BlockingEndpoint

Source

pub unsafe fn ioctl( &mut self, code: u32, input: &[u8], output: &mut [u8], ) -> Result<usize>

Issue an overlapped DeviceIoControl with control code code, blocking until it completes.

input is the input buffer (empty for control codes that take none) and output is the buffer the device writes into; the return value is how many bytes it wrote. Takes plain slices and allocates nothing: this call does not return until the operation is over, so an ordinary borrow provably covers the whole time the driver is using them.

§Errors

Returns io::ErrorKind::InvalidInput if either buffer is longer than u32::MAX bytes, which the control code’s byte counts cannot express, or any error from issuing or completing the control operation.

§Safety

code’s input layout must be self-contained: the driver may read or write only the bytes inside input and the output buffer, never memory reached through a pointer embedded in input. Some control codes take an input structure that carries raw pointers to separate buffers – SCSI_PASS_THROUGH_DIRECT::DataBuffer is one – which this adapter neither owns nor keeps alive. For such a code the caller must keep every referenced buffer valid for the whole call; the adapter cannot, because it does not know the code’s layout. This is why the generic raw-code seam is unsafe even though a self-contained code (an FSCTL query, say) needs nothing more than owned buffers.

Source§

impl BlockingEndpoint

Source

pub fn read(&mut self, buffer: &mut [u8], offset: u64) -> Result<usize>

Read into buffer starting at offset, blocking until the read completes, and return the number of bytes read.

Takes a plain &mut [u8] rather than an owned buffer, and allocates nothing: this call does not return until the operation is over, so an ordinary borrow provably covers the whole time the kernel is writing. That is the difference from AssociatedEndpoint::read, which must take ownership because its operation outlives the call.

§Errors

Returns io::ErrorKind::InvalidInput if buffer is longer than u32::MAX, which the read’s byte count cannot express, or any error from issuing or completing the read.

§Examples

One owner issuing reads in sequence is the supported shape, and compiles:

use windows_overlapped_io_sys::BlockingEndpoint;

fn read_twice(endpoint: &mut BlockingEndpoint) -> std::io::Result<()> {
    let mut buffer = [0_u8; 64];
    let _first = endpoint.read(&mut buffer, 0)?;
    let _second = endpoint.read(&mut buffer, 64)?;
    Ok(())
}

Sharing one endpoint across threads and reading from both is rejected at compile time rather than corrupting a result at run time, because read takes &mut self while an Arc can only hand out &BlockingEndpoint:

use std::sync::Arc;
use windows_overlapped_io_sys::BlockingEndpoint;

fn read_from_two_threads(endpoint: BlockingEndpoint) {
    let shared = Arc::new(endpoint);
    let other = Arc::clone(&shared);
    std::thread::spawn(move || other.read(&mut [0_u8; 64], 0));
    let _ = shared.read(&mut [0_u8; 64], 64);
}
Source

pub fn write(&mut self, data: &[u8], offset: u64) -> Result<usize>

Write data starting at offset, blocking until the write completes, and return the number of bytes written.

§Errors

Returns io::ErrorKind::InvalidInput if data is longer than u32::MAX, which the write’s byte count cannot express, or any error from issuing or completing the write.

Source§

impl BlockingEndpoint

Source

pub fn read_scatter( &mut self, buffers: &mut PageBuffers, offset: u64, ) -> Result<usize>

Scatter-read into buffers starting at offset, blocking until the read completes, and return the number of bytes read.

Takes the caller’s pages by &mut and allocates nothing, matching BlockingEndpoint::write_gather; the endpoint must be opened with FILE_FLAG_NO_BUFFERING, or the native call fails.

§Errors

Returns io::ErrorKind::InvalidInput if the pages total more than u32::MAX bytes, or any error from issuing or completing the scatter-read.

Source

pub fn write_gather( &mut self, buffers: &PageBuffers, offset: u64, ) -> Result<usize>

Gather-write buffers starting at offset, blocking until the write completes, and return the number of bytes written.

The endpoint must be opened with FILE_FLAG_NO_BUFFERING; otherwise the native call fails.

§Errors

Returns io::ErrorKind::InvalidInput if the buffers total more than u32::MAX bytes, or any error from issuing or completing the gather-write.

Trait Implementations§

Source§

impl Debug for BlockingEndpoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.