Merge pull request #4770 from hulxv/refactor/simplify-libc-tests/libc-socketpair
Refactor socketpair tests to use utility functions for reading/writing
This commit is contained in:
commit
ded9a2bbcf
2 changed files with 83 additions and 153 deletions
|
|
@ -9,6 +9,7 @@ use std::thread;
|
|||
|
||||
#[path = "../../utils/libc.rs"]
|
||||
mod libc_utils;
|
||||
use libc_utils::*;
|
||||
|
||||
fn main() {
|
||||
test_socketpair();
|
||||
|
|
@ -21,139 +22,89 @@ fn main() {
|
|||
|
||||
fn test_socketpair() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
|
||||
|
||||
// Read size == data available in buffer.
|
||||
let data = "abcde".as_bytes().as_ptr();
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
let mut buf: [u8; 5] = [0; 5];
|
||||
let res =
|
||||
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 5);
|
||||
assert_eq!(buf, "abcde".as_bytes());
|
||||
let data = b"abcde";
|
||||
write_all_from_slice(fds[0], data).unwrap();
|
||||
let buf = read_all_into_array::<5>(fds[1]).unwrap();
|
||||
assert_eq!(&buf, data);
|
||||
|
||||
// Read size > data available in buffer.
|
||||
let data = "abc".as_bytes();
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
let data = b"abc";
|
||||
write_all_from_slice(fds[0], data).unwrap();
|
||||
let mut buf2: [u8; 5] = [0; 5];
|
||||
let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) };
|
||||
assert!(res > 0 && res <= 3);
|
||||
let res = res as usize;
|
||||
assert_eq!(buf2[..res], data[..res]);
|
||||
if res < 3 {
|
||||
// Drain the rest from the read end.
|
||||
let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) };
|
||||
assert!(res > 0);
|
||||
}
|
||||
let (read, rest) = read_into_slice(fds[1], &mut buf2).unwrap();
|
||||
assert_eq!(read[..], data[..read.len()]);
|
||||
// Write 2 more bytes so we can exactly fill the `rest`.
|
||||
write_all_from_slice(fds[0], b"12").unwrap();
|
||||
read_all_into_slice(fds[1], rest).unwrap();
|
||||
|
||||
// Test read and write from another direction.
|
||||
// Read size == data available in buffer.
|
||||
let data = "12345".as_bytes().as_ptr();
|
||||
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
let mut buf3: [u8; 5] = [0; 5];
|
||||
let res = unsafe {
|
||||
libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t)
|
||||
};
|
||||
assert_eq!(res, 5);
|
||||
assert_eq!(buf3, "12345".as_bytes());
|
||||
let data = b"12345";
|
||||
write_all_from_slice(fds[1], data).unwrap();
|
||||
let buf3 = read_all_into_array::<5>(fds[0]).unwrap();
|
||||
assert_eq!(&buf3, data);
|
||||
|
||||
// Read size > data available in buffer.
|
||||
let data = "123".as_bytes();
|
||||
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
let data = b"123";
|
||||
write_all_from_slice(fds[1], data).unwrap();
|
||||
let mut buf4: [u8; 5] = [0; 5];
|
||||
let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) };
|
||||
assert!(res > 0 && res <= 3);
|
||||
let res = res as usize;
|
||||
assert_eq!(buf4[..res], data[..res]);
|
||||
if res < 3 {
|
||||
// Drain the rest from the read end.
|
||||
let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) };
|
||||
assert!(res > 0);
|
||||
}
|
||||
let (read, rest) = read_into_slice(fds[0], &mut buf4).unwrap();
|
||||
assert_eq!(read[..], data[..read.len()]);
|
||||
// Write 2 more bytes so we can exactly fill the `rest`.
|
||||
write_all_from_slice(fds[1], b"12").unwrap();
|
||||
read_all_into_slice(fds[0], rest).unwrap();
|
||||
|
||||
// Test when happens when we close one end, with some data in the buffer.
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
unsafe { libc::close(fds[0]) };
|
||||
write_all_from_slice(fds[0], data).unwrap();
|
||||
errno_check(unsafe { libc::close(fds[0]) });
|
||||
// Reading the other end should return that data, then EOF.
|
||||
let mut buf: [u8; 5] = [0; 5];
|
||||
let res =
|
||||
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(&buf[0..3], "123".as_bytes());
|
||||
let res =
|
||||
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 0); // 0-sized read: EOF.
|
||||
let (res, _) = read_until_eof_into_slice(fds[1], &mut buf).unwrap();
|
||||
assert_eq!(res, data);
|
||||
// Writing the other end should emit EPIPE.
|
||||
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) };
|
||||
assert_eq!(res, -1);
|
||||
assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE));
|
||||
let err = write_all_from_slice(fds[1], &mut buf).unwrap_err();
|
||||
assert_eq!(err.raw_os_error(), Some(libc::EPIPE));
|
||||
}
|
||||
|
||||
fn test_socketpair_threaded() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
|
||||
|
||||
let thread1 = thread::spawn(move || {
|
||||
let mut buf: [u8; 5] = [0; 5];
|
||||
let res: i64 = unsafe {
|
||||
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(res, 5);
|
||||
assert_eq!(buf, "abcde".as_bytes());
|
||||
let buf = read_all_into_array::<5>(fds[1]).unwrap();
|
||||
assert_eq!(&buf, b"abcde");
|
||||
});
|
||||
thread::yield_now();
|
||||
let data = "abcde".as_bytes().as_ptr();
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
write_all_from_slice(fds[0], b"abcde").unwrap();
|
||||
thread1.join().unwrap();
|
||||
|
||||
// Read and write from different direction
|
||||
let thread2 = thread::spawn(move || {
|
||||
thread::yield_now();
|
||||
let data = "12345".as_bytes().as_ptr();
|
||||
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
write_all_from_slice(fds[1], b"12345").unwrap();
|
||||
});
|
||||
let mut buf: [u8; 5] = [0; 5];
|
||||
let res =
|
||||
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 5);
|
||||
assert_eq!(buf, "12345".as_bytes());
|
||||
let buf = read_all_into_array::<5>(fds[0]).unwrap();
|
||||
assert_eq!(&buf, b"12345");
|
||||
thread2.join().unwrap();
|
||||
}
|
||||
|
||||
fn test_race() {
|
||||
static mut VAL: u8 = 0;
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
|
||||
let thread1 = thread::spawn(move || {
|
||||
let mut buf: [u8; 1] = [0; 1];
|
||||
// write() from the main thread will occur before the read() here
|
||||
// because preemption is disabled and the main thread yields after write().
|
||||
let res: i32 = unsafe {
|
||||
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(res, 1);
|
||||
assert_eq!(buf, "a".as_bytes());
|
||||
let buf = read_all_into_array::<1>(fds[1]).unwrap();
|
||||
assert_eq!(&buf, b"a");
|
||||
// The read above establishes a happens-before so it is now safe to access this global variable.
|
||||
unsafe { assert_eq!(VAL, 1) };
|
||||
});
|
||||
unsafe { VAL = 1 };
|
||||
let data = "a".as_bytes().as_ptr();
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 1) };
|
||||
assert_eq!(res, 1);
|
||||
write_all_from_slice(fds[0], b"a").unwrap();
|
||||
thread::yield_now();
|
||||
thread1.join().unwrap();
|
||||
}
|
||||
|
|
@ -161,22 +112,15 @@ fn test_race() {
|
|||
// Test the behaviour of a socketpair getting blocked on read and subsequently unblocked.
|
||||
fn test_blocking_read() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
|
||||
let thread1 = thread::spawn(move || {
|
||||
// Let this thread block on read.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe {
|
||||
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
|
||||
};
|
||||
assert_eq!(res, 3);
|
||||
assert_eq!(&buf, "abc".as_bytes());
|
||||
let buf = read_all_into_array::<3>(fds[1]).unwrap();
|
||||
assert_eq!(&buf, b"abc");
|
||||
});
|
||||
let thread2 = thread::spawn(move || {
|
||||
// Unblock thread1 by doing writing something.
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
write_all_from_slice(fds[0], b"abc").unwrap();
|
||||
});
|
||||
thread1.join().unwrap();
|
||||
thread2.join().unwrap();
|
||||
|
|
@ -185,26 +129,17 @@ fn test_blocking_read() {
|
|||
// Test the behaviour of a socketpair getting blocked on write and subsequently unblocked.
|
||||
fn test_blocking_write() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
|
||||
let arr1: [u8; 0x34000] = [1; 0x34000];
|
||||
// Exhaust the space in the buffer so the subsequent write will block.
|
||||
let res =
|
||||
unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, arr1.len()) };
|
||||
assert_eq!(res, 0x34000);
|
||||
write_all_from_slice(fds[0], &arr1).unwrap();
|
||||
let thread1 = thread::spawn(move || {
|
||||
let data = "abc".as_bytes().as_ptr();
|
||||
// The write below will be blocked because the buffer is already full.
|
||||
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) };
|
||||
assert_eq!(res, 3);
|
||||
write_all_from_slice(fds[0], b"abc").unwrap();
|
||||
});
|
||||
let thread2 = thread::spawn(move || {
|
||||
// Unblock thread1 by freeing up some space.
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let res = unsafe {
|
||||
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
|
||||
};
|
||||
assert_eq!(res, 3);
|
||||
let buf = read_all_into_array::<3>(fds[1]).unwrap();
|
||||
assert_eq!(buf, [1, 1, 1]);
|
||||
});
|
||||
thread1.join().unwrap();
|
||||
|
|
@ -215,30 +150,25 @@ fn test_blocking_write() {
|
|||
fn test_socketpair_setfl_getfl() {
|
||||
// Initialise socketpair fds.
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
|
||||
|
||||
// Test if both sides have O_RDWR.
|
||||
let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
|
||||
assert_eq!(res, libc::O_RDWR);
|
||||
let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) };
|
||||
assert_eq!(res, libc::O_RDWR);
|
||||
assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
|
||||
assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
|
||||
|
||||
// Add the O_NONBLOCK flag with F_SETFL.
|
||||
let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) };
|
||||
assert_eq!(res, 0);
|
||||
errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) });
|
||||
|
||||
// Test if the O_NONBLOCK flag is successfully added.
|
||||
let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
|
||||
assert_eq!(res, libc::O_RDWR | libc::O_NONBLOCK);
|
||||
assert_eq!(
|
||||
errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(),
|
||||
libc::O_RDWR | libc::O_NONBLOCK
|
||||
);
|
||||
|
||||
// The other side remains unchanged.
|
||||
let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) };
|
||||
assert_eq!(res, libc::O_RDWR);
|
||||
assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
|
||||
|
||||
// Test if O_NONBLOCK flag can be unset.
|
||||
let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) };
|
||||
assert_eq!(res, 0);
|
||||
let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
|
||||
assert_eq!(res, libc::O_RDWR);
|
||||
errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) });
|
||||
assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,21 +40,17 @@ pub unsafe fn read_all(
|
|||
return read_so_far as libc::ssize_t;
|
||||
}
|
||||
|
||||
/// Try to fill the given slice by reading from `fd`. Error if that many bytes could not be read.
|
||||
/// Try to fill the given slice by reading from `fd`. Panic if that many bytes could not be read.
|
||||
#[track_caller]
|
||||
pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> Result<(), libc::ssize_t> {
|
||||
let res = unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) };
|
||||
if res >= 0 {
|
||||
assert_eq!(res as usize, buf.len());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(res)
|
||||
}
|
||||
pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<()> {
|
||||
let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?;
|
||||
assert_eq!(res as usize, buf.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read exactly `N` bytes from `fd`. Error if that many bytes could not be read.
|
||||
#[track_caller]
|
||||
pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> Result<[u8; N], libc::ssize_t> {
|
||||
pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> io::Result<[u8; N]> {
|
||||
let mut buf = [0; N];
|
||||
read_all_into_slice(fd, &mut buf)?;
|
||||
Ok(buf)
|
||||
|
|
@ -63,12 +59,20 @@ pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> Result<[u8; N], l
|
|||
/// Do a single read from `fd` and return the part of the buffer that was written into,
|
||||
/// and the rest.
|
||||
#[track_caller]
|
||||
pub fn read_into_slice(
|
||||
pub fn read_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> {
|
||||
let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?;
|
||||
Ok(buf.split_at_mut(res as usize))
|
||||
}
|
||||
|
||||
/// Read from `fd` until we get EOF and return the part of the buffer that was written into,
|
||||
/// and the rest.
|
||||
#[track_caller]
|
||||
pub fn read_until_eof_into_slice(
|
||||
fd: libc::c_int,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(&mut [u8], &mut [u8]), libc::ssize_t> {
|
||||
let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) };
|
||||
if res >= 0 { Ok(buf.split_at_mut(res as usize)) } else { Err(res) }
|
||||
) -> io::Result<(&mut [u8], &mut [u8])> {
|
||||
let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?;
|
||||
Ok(buf.split_at_mut(res as usize))
|
||||
}
|
||||
|
||||
pub unsafe fn write_all(
|
||||
|
|
@ -89,16 +93,12 @@ pub unsafe fn write_all(
|
|||
return written_so_far as libc::ssize_t;
|
||||
}
|
||||
|
||||
/// Write the entire `buf` to `fd`. Error if not all bytes could be written.
|
||||
/// Write the entire `buf` to `fd`. Panic if not all bytes could be written.
|
||||
#[track_caller]
|
||||
pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> Result<(), libc::ssize_t> {
|
||||
let res = unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) };
|
||||
if res >= 0 {
|
||||
assert_eq!(res as usize, buf.len());
|
||||
Ok(())
|
||||
} else {
|
||||
Err(res)
|
||||
}
|
||||
pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> io::Result<()> {
|
||||
let res = errno_result(unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) })?;
|
||||
assert_eq!(res as usize, buf.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue