Added `copy_` versions of `move_write/read_value()`.

Fortune for comfork's current commit: Middle blessing − 中吉
master
Avril 3 years ago
parent 8008643bb4
commit 3aa61be7cd
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -13,15 +13,18 @@ use std::io::{
Write, Write,
}; };
use std::marker::Unpin; use std::marker::Unpin;
use std::panic::{
AssertUnwindSafe,
catch_unwind,
resume_unwind,
};
use errno::Errno; use errno::Errno;
/// THe kind of error from forking /// Information on how to set-up child process.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForkInfo
{
uid: Option<u32>,
gid: Option<u32>,
}
/// The kind of error from forking
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum ErrorKind pub enum ErrorKind
@ -38,49 +41,6 @@ pub enum ErrorKind
Pipe(pipe::ErrorKind), Pipe(pipe::ErrorKind),
} }
// TODO: Add non-`sys` type: `Channel<T>`, which can be used to send/recv typed data between parent and child.
// It should leverage the two below functions: `move_*_stream()`.
/// Move a value into a stream.
///
/// This function will return `Ok` if and only if all bytes from `val` were successfully written to the stream `to`.
/// If only a subset of them were written, then the data is corrupt and that is counted as an error.
///
/// # Safety
/// The object `val` is written into the stream `to` verbatim. Its destructor is not run.
/// The object should be read verbatim from the stream with no errors or corruption before its dropped, or it will leak resources.
///
/// This function is safe, but reading from the stream into a new object is not.
///
/// ## Unpin
/// `T` should be `Unpin` in case it contains self-referrential data, as those references will no longer be valid after being moved across a stream.
// TODO: Should we expose these to the user somehow, with an unsafe helper module maybe? They could be useful.
// Or perhaps: Add the functionality to move types to/from the child/parent into the library, similar to `mpsc`.
pub(crate) fn move_write_value<T: Unpin, S: Write>(mut to: S, val: T) -> io::Result<()>
{
let bytes = unsafe {
std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>())
};
// Catch any panics that may happen in `write`.
let res = catch_unwind(AssertUnwindSafe(move || {
to.write_all(bytes)
}));
// Ensure this happens even if the above panics.
drop(bytes); // Make sure `bytes` isn't dangling after we move `val` into `forget()`.
std::mem::forget(val);
// Resume unwind if one occoured, if not return the value
match res {
Ok(v) => v,
Err(panic) => resume_unwind(panic),
}
}
//TODO: Counterpart to `move_write_value()`. This function should be `unsafe` as it is extermely unsafe.
pub(crate) unsafe fn move_read_value<T: Unpin, S: Read>(mut from: S) -> io::Result<T>
{
todo!()
}
/// An error in either forking or communicating with the child process during its setup. /// An error in either forking or communicating with the child process during its setup.
#[derive(Debug)] #[derive(Debug)]
#[repr(transparent)] #[repr(transparent)]

@ -5,7 +5,103 @@ use super::*;
use errno::Errno; use errno::Errno;
use std::{error, fmt}; use std::{error, fmt};
use std::ops::Drop; use std::ops::Drop;
use std::io; use std::io::{
self,
Read, Write,
};
use std::panic::{
AssertUnwindSafe,
catch_unwind,
resume_unwind,
};
// TODO: Add non-`sys` type: `Channel<T>`, which can be used to send/recv typed data between parent and child.
// It should leverage the two below functions: `move_*_stream()`.
/// Move a value into a stream.
///
/// This function will return `Ok` if and only if all bytes from `val` were successfully written to the stream `to`.
/// If only a subset of them were written, then the data is corrupt and that is counted as an error.
///
/// # Safety
/// The object `val` is written into the stream `to` verbatim. Its destructor is not run.
/// The object should be read verbatim from the stream with no errors or corruption before its dropped, or it will leak resources.
///
/// This function is safe, but reading from the stream into a new object is not.
///
/// ## Unpin
/// `T` should be `Unpin` in case it contains self-referrential data, as those references will no longer be valid after being moved across a stream.
// TODO: Should we expose these to the user somehow, with an unsafe helper module maybe? They could be useful.
// Or perhaps: Add the functionality to move types to/from the child/parent into the library, similar to `mpsc`.
// XXX: This is not safe, and will cause a memory leak when moving from parent to child. If a box is passed to the child, then it will not be deep copied, nor will it be correctly freed on the parent. Idk how to handle this for complex types...
pub(crate) fn move_write_value<T: Send, S: Write>(mut to: S, val: T) -> io::Result<()>
{
let bytes = unsafe {
std::slice::from_raw_parts(&val as *const T as *const u8, std::mem::size_of::<T>())
};
// Catch any panics that may happen in `write`.
let res = catch_unwind(AssertUnwindSafe(move || {
to.write_all(bytes)
}));
// Ensure this happens even if the above panics.
drop(bytes); // Make sure `bytes` isn't dangling after we move `val` into `forget()`.
std::mem::forget(val);
// Resume unwind if one occoured, if not return the value
match res {
Ok(v) => v,
Err(panic) => resume_unwind(panic),
}
}
/// Write a value into a stream. The value can be unsized but must be `Send` as we cannot guarantee the stream will not be read on a seperate thread.
///
/// This is a safe function, but reading from the other stream is not.
/// Ownership is not transfered to the other side of the stream, so make sure that if the value is not `Copy` any resources owned by `val` are properly transfered to the read object.
// Should this require `Send` as a bound?
pub(crate) fn copy_write_value<T: Send, S: Write>(mut to: S, val: &T) -> io::Result<usize>
{
let sz = std::mem::size_of_val(val);
let bytes = unsafe {
std::slice::from_raw_parts(val as *const T as *const _, sz)
};
to.write_all(bytes)?;
Ok(sz)
}
/// Read a value from a stream.
///
/// # Safety
/// This is extremely unsafe, it reads the type `T` bitwise from the stream.
/// The caller must ensure that any pointers contained within `T` remain valid after being passed through a stream, and that the type passed through has not been corrupted within the stream.
pub(crate) unsafe fn copy_read_value<T: Send, S: Read>(mut from: S) -> io::Result<T>
{
let mut value = std::mem::MaybeUninit::<T>::uninit();
Ok({
let bytes = {
std::slice::from_raw_parts_mut(value.as_mut_ptr() as *mut _ as *mut _, std::mem::size_of::<T>())
};
from.read_exact(&mut bytes[..])?;
{
value.assume_init()
}
})
}
/// Read a value from a stream.
///
/// This is currently identical to `copy_read_value()`.
///
/// # Safety
/// This is extremely unsafe, it reads the type `T` bitwise from the stream.
/// The caller must ensure that any pointers contained within `T` remain valid after being passed through a stream, and that the type passed through has not been corrupted within the stream.
#[inline] pub(crate) unsafe fn move_read_value<T: Send, S: Read>(from: S) -> io::Result<T>
{
// These are effectively the same.
copy_read_value(from)
}
/// Kind of pipe error /// Kind of pipe error
#[derive(Debug)] #[derive(Debug)]

Loading…
Cancel
Save