Struct duct::ReaderHandle

source ·
pub struct ReaderHandle { /* private fields */ }
Expand description

An incremental reader created with the Expression::reader method.

When this reader reaches EOF, it automatically calls wait on the inner handle. If the child returns a non-zero exit status, the read at EOF will return an error, unless you use unchecked.

If the reader is dropped before reaching EOF, it calls kill in its destructor.

Both ReaderHandle and &ReaderHandle implement std::io::Read. That makes it possible for one thread to kill the ReaderHandle while another thread is reading it. That can be useful for effectively canceling the read and unblocking the reader thread. However, note that killed child processes return a non-zero exit status, which is an error for the reader by default, unless you use unchecked.

Example

use duct::cmd;
use duct::ReaderHandle;
use std::sync::Arc;
use std::io::prelude::*;

// This child process prints a single byte and then sleeps.
//
// CAUTION: Using Bash for this example would probably hang, because Bash
// would spawn a `sleep` grandchild processes, and that grandchild wouldn't
// receive the kill signal.
let python_child = "\
import sys
import time
print()
sys.stdout.flush()
time.sleep(24 * 60 * 60)
";
let reader: ReaderHandle = cmd!("python3", "-c", python_child)
    .unchecked()
    .reader()?;

// Spawn two threads that both try to read the single byte. Whichever one
// succeeds then calls kill() to unblock the other.
let arc_reader: Arc<ReaderHandle> = Arc::new(reader);
let mut threads = Vec::new();
for _ in 0..2 {
    let arc_reader = arc_reader.clone();
    threads.push(std::thread::spawn(move || -> std::io::Result<()> {
        let mut single_byte = [0u8];
        (&*arc_reader).read(&mut single_byte)?;
        arc_reader.kill()?;
        Ok(())
    }));
}

// Join both threads. Because of the kill() above, both threads will exit
// quickly.
for thread in threads {
    thread.join().unwrap()?;
}

Implementations§

source§

impl ReaderHandle

source

pub fn try_wait(&self) -> Result<Option<&Output>>

Check whether the underlying expression is finished. This is equivalent to Handle::try_wait. If the ReaderHandle has indicated EOF successfully, then it’s guaranteed that this method will return Ok(Some(_)).

Note that the stdout field of the returned Output will always be empty, because the ReaderHandle itself owns the child’s stdout pipe.

source

pub fn kill(&self) -> Result<()>

Kill the underlying expression and await all the child processes.

Any errors that would normally result from a non-zero exit status are ignored during this wait, as with Handle::kill.

Note that as with std::process::Child::kill, this does not kill any grandchild processes that the children have spawned on their own. It only kills the child processes that Duct spawned itself. This is especially relevant for ReaderHandle, because if you’re using kill to unblock another thread that’s reading, an unkilled grandchild process might keep the child’s stdout pipe open and keep your reader thread blocked. For that use case, you need to ensure that any grandchild processes your child might spawn are going to be short-lived. See gotchas.md for an extensive discussion of these issues.

source

pub fn pids(&self) -> Vec<u32>

Return a Vec<u32> containing the PIDs of all of the child processes. The PIDs are given in pipeline order, from left to right.

Trait Implementations§

source§

impl Debug for ReaderHandle

source§

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

Formats the value using the given formatter. Read more
source§

impl Drop for ReaderHandle

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl<'a> Read for &'a ReaderHandle

source§

fn read(&mut self, buf: &mut [u8]) -> Result<usize>

Note that if you don’t use unchecked, and the child returns a non-zero exit status, the final call to read will return an error, just as run would.

1.36.0 · source§

fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize, Error>

Like read, except that it reads into a slice of buffers. Read more
source§

fn is_read_vectored(&self) -> bool

🔬This is a nightly-only experimental API. (can_vector)
Determines if this Reader has an efficient read_vectored implementation. Read more
1.0.0 · source§

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error>

Read all bytes until EOF in this source, placing them into buf. Read more
1.0.0 · source§

fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Error>

Read all bytes until EOF in this source, appending them to buf. Read more
1.6.0 · source§

fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>

Read the exact number of bytes required to fill buf. Read more
source§

fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<(), Error>

🔬This is a nightly-only experimental API. (read_buf)
Pull some bytes from this source into the specified buffer. Read more
source§

fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<(), Error>

🔬This is a nightly-only experimental API. (read_buf)
Read the exact number of bytes required to fill cursor. Read more
1.0.0 · source§

fn by_ref(&mut self) -> &mut Self
where Self: Sized,

Creates a “by reference” adaptor for this instance of Read. Read more
1.0.0 · source§

fn bytes(self) -> Bytes<Self>
where Self: Sized,

Transforms this Read instance to an Iterator over its bytes. Read more
1.0.0 · source§

fn chain<R>(self, next: R) -> Chain<Self, R>
where R: Read, Self: Sized,

Creates an adapter which will chain this stream with another. Read more
1.0.0 · source§

fn take(self, limit: u64) -> Take<Self>
where Self: Sized,

Creates an adapter which will read at most limit bytes from it. Read more
source§

impl Read for ReaderHandle

source§

fn read(&mut self, buf: &mut [u8]) -> Result<usize>

Note that if you don’t use unchecked, and the child returns a non-zero exit status, the final call to read will return an error, just as run would.

1.36.0 · source§

fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize, Error>

Like read, except that it reads into a slice of buffers. Read more
source§

fn is_read_vectored(&self) -> bool

🔬This is a nightly-only experimental API. (can_vector)
Determines if this Reader has an efficient read_vectored implementation. Read more
1.0.0 · source§

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error>

Read all bytes until EOF in this source, placing them into buf. Read more
1.0.0 · source§

fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Error>

Read all bytes until EOF in this source, appending them to buf. Read more
1.6.0 · source§

fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>

Read the exact number of bytes required to fill buf. Read more
source§

fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<(), Error>

🔬This is a nightly-only experimental API. (read_buf)
Pull some bytes from this source into the specified buffer. Read more
source§

fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<(), Error>

🔬This is a nightly-only experimental API. (read_buf)
Read the exact number of bytes required to fill cursor. Read more
1.0.0 · source§

fn by_ref(&mut self) -> &mut Self
where Self: Sized,

Creates a “by reference” adaptor for this instance of Read. Read more
1.0.0 · source§

fn bytes(self) -> Bytes<Self>
where Self: Sized,

Transforms this Read instance to an Iterator over its bytes. Read more
1.0.0 · source§

fn chain<R>(self, next: R) -> Chain<Self, R>
where R: Read, Self: Sized,

Creates an adapter which will chain this stream with another. Read more
1.0.0 · source§

fn take(self, limit: u64) -> Take<Self>
where Self: Sized,

Creates an adapter which will read at most limit bytes from it. 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>,

§

type Error = Infallible

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>,

§

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.