about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2016-01-20 21:24:23 -0800
committerSteven Fackler <sfackler@gmail.com>2016-02-04 09:43:21 +0000
commita414b61f9279b2806ab92ec17cdd19cf57d59e2f (patch)
treeb92f8590ad093e70df66c45afce1f79b8daeb70f /src
parent51108b64ca3c84d9973736e6b9e094e79c12dc60 (diff)
downloadrust-a414b61f9279b2806ab92ec17cdd19cf57d59e2f.tar.gz
rust-a414b61f9279b2806ab92ec17cdd19cf57d59e2f.zip
Add File::try_clone
Diffstat (limited to 'src')
-rw-r--r--src/libstd/fs.rs34
-rw-r--r--src/libstd/sys/unix/fd.rs42
-rw-r--r--src/libstd/sys/unix/fs.rs4
-rw-r--r--src/libstd/sys/unix/net.rs40
-rw-r--r--src/libstd/sys/windows/fs.rs6
5 files changed, 87 insertions, 39 deletions
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 414a0ebd11f..6c57fd6a4c9 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -309,6 +309,18 @@ impl File {
     pub fn metadata(&self) -> io::Result<Metadata> {
         self.inner.file_attr().map(Metadata)
     }
+
+    /// Creates a new independently owned handle to the underlying file.
+    ///
+    /// The returned `File` is a reference to the same state that this object
+    /// references. Both handles will read and write with the same cursor
+    /// position.
+    #[unstable(feature = "file_try_clone", reason = "newly added", issue = "31405")]
+    pub fn try_clone(&self) -> io::Result<File> {
+        Ok(File {
+            inner: try!(self.inner.duplicate())
+        })
+    }
 }
 
 impl AsInner<fs_imp::File> for File {
@@ -2284,6 +2296,28 @@ mod tests {
     }
 
     #[test]
+    fn file_try_clone() {
+        let tmpdir = tmpdir();
+
+        let mut f1 = check!(OpenOptions::new()
+                                       .read(true)
+                                       .write(true)
+                                       .create(true)
+                                       .open(&tmpdir.join("test")));
+        let mut f2 = check!(f1.try_clone());
+
+        check!(f1.write_all(b"hello world"));
+        check!(f1.seek(SeekFrom::Start(2)));
+
+        let mut buf = vec![];
+        check!(f2.read_to_end(&mut buf));
+        assert_eq!(buf, b"llo world");
+        drop(f2);
+
+        check!(f1.write_all(b"!"));
+    }
+
+    #[test]
     #[cfg(not(windows))]
     fn unlink_readonly() {
         let tmpdir = tmpdir();
diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs
index e09f4ca27bc..1492613c53c 100644
--- a/src/libstd/sys/unix/fd.rs
+++ b/src/libstd/sys/unix/fd.rs
@@ -13,6 +13,7 @@ use libc::{self, c_int, size_t, c_void};
 use mem;
 use sys::cvt;
 use sys_common::AsInner;
+use sync::atomic::{AtomicBool, Ordering};
 
 pub struct FileDesc {
     fd: c_int,
@@ -65,6 +66,47 @@ impl FileDesc {
             debug_assert_eq!(ret, 0);
         }
     }
+
+    pub fn duplicate(&self) -> io::Result<FileDesc> {
+        // We want to atomically duplicate this file descriptor and set the
+        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
+        // flag, however, isn't supported on older Linux kernels (earlier than
+        // 2.6.24).
+        //
+        // To detect this and ensure that CLOEXEC is still set, we
+        // follow a strategy similar to musl [1] where if passing
+        // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
+        // supported (the third parameter, 0, is always valid), so we stop
+        // trying that. We also *still* call the `set_cloexec` method as
+        // apparently some kernel at some point stopped setting CLOEXEC even
+        // though it reported doing so on F_DUPFD_CLOEXEC.
+        //
+        // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
+        // resolve so we at least compile this.
+        //
+        // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
+        #[cfg(target_os = "android")]
+        use libc::F_DUPFD as F_DUPFD_CLOEXEC;
+        #[cfg(not(target_os = "android"))]
+        use libc::F_DUPFD_CLOEXEC;
+
+        let make_filedesc = |fd| {
+            let fd = FileDesc::new(fd);
+            fd.set_cloexec();
+            fd
+        };
+        static TRY_CLOEXEC: AtomicBool = AtomicBool::new(true);
+        let fd = self.raw();
+        if !cfg!(target_os = "android") && TRY_CLOEXEC.load(Ordering::Relaxed) {
+            match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
+                Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
+                    TRY_CLOEXEC.store(false, Ordering::Relaxed);
+                }
+                res => return res.map(make_filedesc),
+            }
+        }
+        cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc)
+    }
 }
 
 impl AsInner<c_int> for FileDesc {
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index d2d2ce35d84..e0212ca5335 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -374,6 +374,10 @@ impl File {
         Ok(n as u64)
     }
 
+    pub fn duplicate(&self) -> io::Result<File> {
+        self.0.duplicate().map(File)
+    }
+
     pub fn fd(&self) -> &FileDesc { &self.0 }
 
     pub fn into_fd(self) -> FileDesc { self.0 }
diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs
index bcda1d651d6..0aa43803048 100644
--- a/src/libstd/sys/unix/net.rs
+++ b/src/libstd/sys/unix/net.rs
@@ -15,7 +15,6 @@ use io;
 use libc::{self, c_int, size_t};
 use net::{SocketAddr, Shutdown};
 use str;
-use sync::atomic::{AtomicBool, Ordering};
 use sys::fd::FileDesc;
 use sys_common::{AsInner, FromInner, IntoInner};
 use sys_common::net::{getsockopt, setsockopt};
@@ -67,44 +66,7 @@ impl Socket {
     }
 
     pub fn duplicate(&self) -> io::Result<Socket> {
-        // We want to atomically duplicate this file descriptor and set the
-        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
-        // flag, however, isn't supported on older Linux kernels (earlier than
-        // 2.6.24).
-        //
-        // To detect this and ensure that CLOEXEC is still set, we
-        // follow a strategy similar to musl [1] where if passing
-        // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
-        // supported (the third parameter, 0, is always valid), so we stop
-        // trying that. We also *still* call the `set_cloexec` method as
-        // apparently some kernel at some point stopped setting CLOEXEC even
-        // though it reported doing so on F_DUPFD_CLOEXEC.
-        //
-        // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
-        // resolve so we at least compile this.
-        //
-        // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
-        #[cfg(target_os = "android")]
-        use libc::F_DUPFD as F_DUPFD_CLOEXEC;
-        #[cfg(not(target_os = "android"))]
-        use libc::F_DUPFD_CLOEXEC;
-
-        let make_socket = |fd| {
-            let fd = FileDesc::new(fd);
-            fd.set_cloexec();
-            Socket(fd)
-        };
-        static TRY_CLOEXEC: AtomicBool = AtomicBool::new(true);
-        let fd = self.0.raw();
-        if !cfg!(target_os = "android") && TRY_CLOEXEC.load(Ordering::Relaxed) {
-            match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
-                Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
-                    TRY_CLOEXEC.store(false, Ordering::Relaxed);
-                }
-                res => return res.map(make_socket),
-            }
-        }
-        cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_socket)
+        self.0.duplicate().map(Socket)
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index 60e3f7c22bd..f5981baf141 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -338,6 +338,12 @@ impl File {
         Ok(newpos as u64)
     }
 
+    pub fn duplicate(&self) -> io::Result<File> {
+        Ok(File {
+            handle: try!(self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)),
+        })
+    }
+
     pub fn handle(&self) -> &Handle { &self.handle }
 
     pub fn into_handle(self) -> Handle { self.handle }