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
impl BlockingEndpoint
Sourcepub fn new(endpoint: UnassociatedEndpoint) -> Result<Self, TryFromEndpointError>
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.
Sourcepub fn handle(&self) -> BorrowedHandle<'_>
pub fn handle(&self) -> BorrowedHandle<'_>
Borrow the underlying handle for issuing native operations.
Sourcepub unsafe fn run<P, F>(
&self,
operation: &mut Operation<P>,
issue: F,
) -> Result<usize>
pub unsafe fn run<P, F>( &self, operation: &mut Operation<P>, issue: F, ) -> Result<usize>
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
impl BlockingEndpoint
Sourcepub unsafe fn ioctl(
&mut self,
code: u32,
input: &[u8],
output: &mut [u8],
) -> Result<usize>
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
impl BlockingEndpoint
Sourcepub fn read(&mut self, buffer: &mut [u8], offset: u64) -> Result<usize>
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);
}Sourcepub fn write(&mut self, data: &[u8], offset: u64) -> Result<usize>
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
impl BlockingEndpoint
Sourcepub fn read_scatter(
&mut self,
buffers: &mut PageBuffers,
offset: u64,
) -> Result<usize>
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.
Sourcepub fn write_gather(
&mut self,
buffers: &PageBuffers,
offset: u64,
) -> Result<usize>
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.