Skip to content

Commit

Permalink
Rollup merge of rust-lang#35704 - tbu-:pr_pread_pwrite, r=sfackler
Browse files Browse the repository at this point in the history
Implement `read_offset` and `write_offset`

These functions allow to read from and write to a file from multiple
threads without changing the per-file cursor, avoiding the race between
the seek and the read.
  • Loading branch information
eddyb authored Aug 23, 2016
2 parents f81abcf + 4e48924 commit cf9cf3f
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 2 deletions.
71 changes: 71 additions & 0 deletions src/libstd/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,30 @@ impl File {
inner: self.inner.duplicate()?
})
}

/// Reads a number of bytes starting from a given offset.
///
/// The offset is relative to the file start and thus independent from the
/// current cursor.
///
/// Note that similar to `File::read`, it is not an error to return a short
/// read.
#[unstable(feature = "file_offset", issue = "35918")]
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.inner.read_at(buf, offset)
}

/// Writes a number of bytes starting from a given offset.
///
/// The offset is relative to the file start and thus independent from the
/// current cursor.
///
/// Note that similar to `File::write`, it is not an error to return a
/// short write.
#[unstable(feature = "file_offset", issue = "35918")]
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.inner.write_at(buf, offset)
}
}

impl AsInner<fs_imp::File> for File {
Expand Down Expand Up @@ -1893,6 +1917,53 @@ mod tests {
check!(fs::remove_file(filename));
}

#[test]
fn file_test_io_read_write_at() {
let tmpdir = tmpdir();
let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
let mut buf = [0; 256];
let write1 = "asdf";
let write2 = "qwer-";
let write3 = "-zxcv";
let content = "qwer-asdf-zxcv";
{
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
let mut rw = check!(oo.open(&filename));
assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len());
assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len());
assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
assert_eq!(check!(rw.read(&mut buf)), write1.len());
assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len());
assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
}
{
let mut read = check!(File::open(&filename));
assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0);
assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9);
assert_eq!(check!(read.read(&mut buf)), write3.len());
assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
}
check!(fs::remove_file(&filename));
}

#[test]
fn file_test_stat_is_correct_on_is_file() {
let tmpdir = tmpdir();
Expand Down
24 changes: 22 additions & 2 deletions src/libstd/sys/unix/fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use prelude::v1::*;

use io::{self, Read};
use libc::{self, c_int, size_t, c_void};
use libc::{self, c_int, off_t, size_t, c_void};
use mem;
use sync::atomic::{AtomicBool, Ordering};
use sys::cvt;
Expand Down Expand Up @@ -42,7 +42,7 @@ impl FileDesc {
let ret = cvt(unsafe {
libc::read(self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as size_t)
buf.len())
})?;
Ok(ret as usize)
}
Expand All @@ -52,6 +52,16 @@ impl FileDesc {
(&mut me).read_to_end(buf)
}

pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::pread(self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len(),
offset as off_t)
})?;
Ok(ret as usize)
}

pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::write(self.fd,
Expand All @@ -61,6 +71,16 @@ impl FileDesc {
Ok(ret as usize)
}

pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::pwrite(self.fd,
buf.as_ptr() as *const c_void,
buf.len(),
offset as off_t)
})?;
Ok(ret as usize)
}

#[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))]
pub fn set_cloexec(&self) -> io::Result<()> {
unsafe {
Expand Down
8 changes: 8 additions & 0 deletions src/libstd/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,18 @@ impl File {
self.0.read_to_end(buf)
}

pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.0.read_at(buf, offset)
}

pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}

pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.0.write_at(buf, offset)
}

pub fn flush(&self) -> io::Result<()> { Ok(()) }

pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
Expand Down
8 changes: 8 additions & 0 deletions src/libstd/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ impl File {
self.handle.read(buf)
}

pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.handle.read_at(buf, offset)
}

pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.handle.read_to_end(buf)
}
Expand All @@ -320,6 +324,10 @@ impl File {
self.handle.write(buf)
}

pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.handle.write_at(buf, offset)
}

pub fn flush(&self) -> io::Result<()> { Ok(()) }

pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
Expand Down
26 changes: 26 additions & 0 deletions src/libstd/sys/windows/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ impl RawHandle {
}
}

pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let mut read = 0;
let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
unsafe {
let mut overlapped: c::OVERLAPPED = mem::zeroed();
overlapped.Offset = offset as u32;
overlapped.OffsetHigh = (offset >> 32) as u32;
cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID,
len, &mut read, &mut overlapped))?;
}
Ok(read as usize)
}

pub unsafe fn read_overlapped(&self,
buf: &mut [u8],
overlapped: *mut c::OVERLAPPED)
Expand Down Expand Up @@ -176,6 +189,19 @@ impl RawHandle {
Ok(amt as usize)
}

pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let mut written = 0;
let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
unsafe {
let mut overlapped: c::OVERLAPPED = mem::zeroed();
overlapped.Offset = offset as u32;
overlapped.OffsetHigh = (offset >> 32) as u32;
cvt(c::WriteFile(self.0, buf.as_ptr() as c::LPVOID,
len, &mut written, &mut overlapped))?;
}
Ok(written as usize)
}

pub fn duplicate(&self, access: c::DWORD, inherit: bool,
options: c::DWORD) -> io::Result<Handle> {
let mut ret = 0 as c::HANDLE;
Expand Down

0 comments on commit cf9cf3f

Please sign in to comment.