Moved raw fd holding into a seperate inner structure to handle fd closing and ownership.

Fixed returning `(rx, tx)` instead of `(tx, rx)` from `unix_pipe()`.

Fortune for comfork's current commit: Small curse − 小凶
master
Avril 3 years ago
parent a2f208bc6b
commit 76fe936970
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -18,36 +18,110 @@ pub enum ErrorKind
Unknown, Unknown,
} }
/// A raw unix pipe /// A raw unix pipe.
// TODO: Change uses of `i32` for socket fds. Or change this to a struct (see below.) ///
pub type RawPipe = i32; /// Encapsulates a raw fd, proventing arbitrary operations on it while it is owned by the instance.
/// The fd is closed when this instance is dropped.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct RawPipe(i32);
impl io::Read for RawPipe
{
#[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok(read_raw(self.0, buf)?)
}
}
impl io::Write for RawPipe
{
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(write_raw(self.0, buf)?)
}
#[inline] fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl From<RawPipe> for i32
{
#[inline] fn from(from: RawPipe) -> Self
{
from.take()
}
}
impl RawPipe
{
/// Transfer ownership of the internal fd to the caller
#[inline] pub fn take(self) -> i32
{
let i = self.0;
std::mem::forget(self);
i
}
/// Transfer ownership of an fd into a new managed instance.
///
/// # Safety
/// The caller should ensure the fd is valid, or all operations on the instance returned will fail.
#[inline] pub const unsafe fn new_unchecked(int: i32) -> Self
{
Self(int)
}
/// Get a raw handle to the fd
///
/// # Safety
/// This operation is unsafe as the caller could close the fd while its still owned by the instance.
/// If you want to transfer ownership of the fd to yourself, use `take()` or `Into::into()`.
#[inline] pub const unsafe fn as_raw_fd(&self) -> i32
{
self.0
}
/// Transfer ownership of an fd into a new managed instance.
///
/// # Panics
/// If the fd value was below 0.
pub fn new(int: i32) -> Self
{
assert!(int > 0, "invalid fd");
Self(int)
}
}
/// Create a raw pipe pair. impl Drop for RawPipe
pub(crate) fn unix_pipe() -> Result<(i32, i32), Error> {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}
/// Create a raw pipe pair. Returns `(tx, rx)`: writer then reader.
pub(crate) fn unix_pipe() -> Result<(RawPipe, RawPipe), Error>
{ {
use libc::pipe; use libc::pipe;
let mut pipe_fds: [libc::c_int; 2] = [-1;2]; let mut pipe_fds: [libc::c_int; 2] = [-1;2];
if unsafe{pipe(pipe_fds.as_mut_ptr())} == 0 { if unsafe{pipe(pipe_fds.as_mut_ptr())} == 0 {
Ok((i32::from(pipe_fds[0]), i32::from(pipe_fds[1]))) Ok((RawPipe(i32::from(pipe_fds[1])), RawPipe(i32::from(pipe_fds[0]))))
} else { } else {
Err(ErrorKind::Create.into_error()) Err(ErrorKind::Create.into_error())
} }
} }
//TODO: Factor out the `i32` raw sockets in these types below into a seperate `RawPipe` struct, which has the Drop impl and has functions for transfering ownership of the raw socket (it should hide the raw fd when not explicitly needed ideally.)
// This will prevent us from having to ensure we `forget()` the outer type each time we want to move the inner one around without closing it.
/// A writer for a bi-directinoal unix pipe /// A writer for a bi-directinoal unix pipe
/// ///
/// Created by splitting `Pipe`, data written to this is available to be read from its `ReadHalf` counterpart. /// Created by splitting `Pipe`, data written to this is available to be read from its `ReadHalf` counterpart.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct WriteHalf(i32); pub struct WriteHalf(RawPipe);
/// A reader for a bi-directional unix pipe. /// A reader for a bi-directional unix pipe.
/// ///
/// Created by splitting `Pipe`, data read from this is data that was sent to its `WriteHalf` counterpart. /// Created by splitting `Pipe`, data read from this is data that was sent to its `WriteHalf` counterpart.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct ReadHalf(i32); pub struct ReadHalf(RawPipe);
/// A bi-drectional unix pipe /// A bi-drectional unix pipe
/// ///
@ -55,11 +129,10 @@ pub struct ReadHalf(i32);
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct Pipe pub struct Pipe
{ {
tx: i32, tx: RawPipe,
rx: i32, rx: RawPipe,
} }
// Make sure when operating on consumers of Pipe of Read/WriteHalf, the pipe structures are `mem::forget`ed before returning, since the Drop impl of those types closes the raw socket.
impl Pipe impl Pipe
{ {
/// Split this pipe into a read and write half. /// Split this pipe into a read and write half.
@ -67,9 +140,7 @@ impl Pipe
/// Data written to the `WriteHalf` is sent to the `ReadHalf` (unless the pipe is mismatched.) /// Data written to the `WriteHalf` is sent to the `ReadHalf` (unless the pipe is mismatched.)
#[inline] pub fn split(self) -> (WriteHalf, ReadHalf) #[inline] pub fn split(self) -> (WriteHalf, ReadHalf)
{ {
let rv = (WriteHalf(self.tx), ReadHalf(self.rx)); (WriteHalf(self.tx), ReadHalf(self.rx))
std::mem::forget(self);
rv
} }
/// Create a `Pipe` from two halves. /// Create a `Pipe` from two halves.
@ -79,16 +150,14 @@ impl Pipe
/// If they are not, then writing to the `Pipe` will send the data to its original receiver, and reading from it will take the data from its original sender. /// If they are not, then writing to the `Pipe` will send the data to its original receiver, and reading from it will take the data from its original sender.
#[inline] pub fn unsplit(tx: WriteHalf, rx: ReadHalf) -> Self #[inline] pub fn unsplit(tx: WriteHalf, rx: ReadHalf) -> Self
{ {
let v = Self::new_from_raw((tx.0, rx.0)); Self::new_from_raw((tx.0, rx.0))
std::mem::forget((tx, rx));
v
} }
/// Create a new `Pipe` from a pair of raw file descriptors. /// Create a new `Pipe` from a pair of raw file descriptors.
/// ///
/// # Safety /// # Safety
/// Does not check that the integers provided are valid and open file descriptors. /// Does not check that the integers provided are valid and open file descriptors.
#[inline(always)] fn new_from_raw((tx, rx): (i32, i32)) -> Self #[inline(always)] fn new_from_raw((tx, rx): (RawPipe, RawPipe)) -> Self
{ {
Self{tx,rx} Self{tx,rx}
} }
@ -98,17 +167,15 @@ impl Pipe
/// # Safety /// # Safety
/// The caller must check that `tx` and `rx` are valid, open file descriptors /// The caller must check that `tx` and `rx` are valid, open file descriptors
// TODO: When writing the async version make sure to state that the fds need to be non-blocking. // TODO: When writing the async version make sure to state that the fds need to be non-blocking.
#[inline] pub unsafe fn from_raw(tx: i32, rx: i32) -> Self #[inline] pub unsafe fn from_raw(tx: RawPipe, rx: RawPipe) -> Self
{ {
Self::new_from_raw((tx, rx)) Self::new_from_raw((tx, rx))
} }
/// Transfer the ownership of the socket's inner Tx and Rx fds. /// Transfer the ownership of the socket's inner Tx and Rx fds.
pub fn into_raw(self) -> (i32, i32) pub fn into_raw(self) -> (RawPipe, RawPipe)
{ {
let txrx = (self.tx, self.rx); (self.tx, self.rx)
std::mem::forget(self);
txrx
} }
/// Create a new `Pipe` from two new file descriptors that stream to eachother. /// Create a new `Pipe` from two new file descriptors that stream to eachother.
@ -163,59 +230,32 @@ fn read_raw(sock: i32, data: &mut [u8]) -> Result<usize, Errno>
impl io::Read for Pipe impl io::Read for Pipe
{ {
#[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok(read_raw(self.rx, buf)?) self.rx.read(buf)
} }
} }
impl io::Write for Pipe impl io::Write for Pipe
{ {
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(write_raw(self.tx, buf)?) self.tx.write(buf)
} }
#[inline] fn flush(&mut self) -> io::Result<()> { #[inline] fn flush(&mut self) -> io::Result<()> {
Ok(()) self.tx.flush()
} }
} }
impl io::Read for ReadHalf impl io::Read for ReadHalf
{ {
#[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
Ok(read_raw(self.0, buf)?) self.0.read(buf)
} }
} }
impl io::Write for WriteHalf impl io::Write for WriteHalf
{ {
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(write_raw(self.0, buf)?) self.0.write(buf)
} }
#[inline] fn flush(&mut self) -> io::Result<()> { #[inline] fn flush(&mut self) -> io::Result<()> {
Ok(()) self.0.flush()
}
}
impl Drop for ReadHalf
{
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}
impl Drop for WriteHalf
{
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}
impl Drop for Pipe
{
fn drop(&mut self) {
unsafe {
libc::close(self.tx);
libc::close(self.rx);
}
} }
} }
@ -323,3 +363,19 @@ impl fmt::Display for Error
} }
} }
#[cfg(test)]
mod tests
{
use std::io::{Read, Write};
#[test]
fn pipe_single_rw()
{
const DATA: &'static [u8] = b"hello world";
let mut pipe = super::Pipe::new().expect("Creation");
print!("Pipe: {:?}", pipe);
pipe.write_all(DATA).expect("write (ex)");
let mut buffer = [0u8; DATA.len()];
pipe.read_exact(&mut buffer[..]).expect("read (ex)");
assert_eq!(&buffer, DATA, "data different");
}
}

Loading…
Cancel
Save