Repository: tokio-rs/mio
Pull request #1094 (2019-09-24 08:22:14)

Remove bitrig support
support no longer exists for this target in Rust.
See https://github.com/rust-lang/rust/issues/60743


Patch:
diff --git a/README.md b/README.md
index 18e06d3..c768aa7 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,6 @@ or higher-level libraries.
 Currently supported platforms:
 
 * Android
-* Bitrig
 * DragonFly BSD
 * FreeBSD
 * Linux
diff --git a/src/lib.rs b/src/lib.rs
index 770d00a..9cd0d8d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,7 +30,6 @@
 //! Currently supported platforms:
 //!
 //! * Android
-//! * Bitrig
 //! * DragonFly BSD
 //! * FreeBSD
 //! * Linux
@@ -126,6 +125,6 @@ pub use waker::Waker;
 #[cfg(unix)]
 pub mod unix {
     //! Unix only extensions.
-
+    pub use crate::sys::SocketAddr;
     pub use crate::sys::SourceFd;
 }
diff --git a/src/net/mod.rs b/src/net/mod.rs
index 945c2e1..385e966 100644
--- a/src/net/mod.rs
+++ b/src/net/mod.rs
@@ -15,3 +15,8 @@ pub use self::tcp_stream::TcpStream;
 
 mod udp;
 pub use self::udp::UdpSocket;
+
+#[cfg(unix)]
+mod uds;
+#[cfg(unix)]
+pub use self::uds::{UnixDatagram, UnixListener, UnixStream};
diff --git a/src/net/udp.rs b/src/net/udp.rs
index e2103aa..bb56c2d 100644
--- a/src/net/udp.rs
+++ b/src/net/udp.rs
@@ -237,8 +237,6 @@ impl UdpSocket {
     /// Receives data from the socket, without removing it from the input queue.
     /// On success, returns the number of bytes read and the address from whence
     /// the data came.
-    /// Receives data from the socket. On success, returns the number of bytes
-    /// read and the address from whence the data came.
     ///
     /// # Examples
     ///
diff --git a/src/net/uds/datagram.rs b/src/net/uds/datagram.rs
new file mode 100644
index 0000000..df330cf
--- /dev/null
+++ b/src/net/uds/datagram.rs
@@ -0,0 +1,163 @@
+use crate::event::Source;
+#[cfg(debug_assertions)]
+use crate::poll::SelectorId;
+use crate::{sys, Interests, Registry, Token};
+
+use std::io;
+use std::net::Shutdown;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::path::Path;
+
+/// A Unix datagram socket.
+#[derive(Debug)]
+pub struct UnixDatagram {
+    sys: sys::UnixDatagram,
+    #[cfg(debug_assertions)]
+    selector_id: SelectorId,
+}
+
+impl UnixDatagram {
+    fn new(sys: sys::UnixDatagram) -> UnixDatagram {
+        UnixDatagram {
+            sys,
+            #[cfg(debug_assertions)]
+            selector_id: SelectorId::new(),
+        }
+    }
+
+    /// Creates a Unix datagram socket bound to the given path.
+    pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
+        let sys = sys::UnixDatagram::bind(path.as_ref())?;
+        Ok(UnixDatagram::new(sys))
+    }
+
+    /// Connects the socket to the specified address.
+    pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
+        self.sys.connect(path)
+    }
+
+    /// Creates a Unix Datagram socket which is not bound to any address.
+    pub fn unbound() -> io::Result<UnixDatagram> {
+        let sys = sys::UnixDatagram::unbound()?;
+        Ok(UnixDatagram::new(sys))
+    }
+
+    /// Create an unnamed pair of connected sockets.
+    pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
+        let (a, b) = sys::UnixDatagram::pair()?;
+        let a = UnixDatagram::new(a);
+        let b = UnixDatagram::new(b);
+        Ok((a, b))
+    }
+
+    /// Creates a new independently owned handle to the underlying socket.
+    ///
+    /// The returned `UnixListener` is a reference to the same socket that this
+    /// object references. Both handles can be used to accept incoming
+    /// connections and options set on one listener will affect the other.
+    pub fn try_clone(&self) -> io::Result<UnixDatagram> {
+        let sys = self.sys.try_clone()?;
+        Ok(UnixDatagram::new(sys))
+    }
+
+    /// Returns the address of this socket.
+    pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
+        self.sys.local_addr()
+    }
+
+    /// Returns the address of this socket's peer.
+    ///
+    /// The `connect` method will connect the socket to a peer.
+    pub fn peer_addr(&self) -> io::Result<sys::SocketAddr> {
+        self.sys.peer_addr()
+    }
+
+    /// Receives data from the socket.
+    ///
+    /// On success, returns the number of bytes read and the address from
+    /// whence the data came.
+    pub fn recv_from(&self, dst: &mut [u8]) -> io::Result<(usize, sys::SocketAddr)> {
+        self.sys.recv_from(dst)
+    }
+
+    /// Receives data from the socket.
+    ///
+    /// On success, returns the number of bytes read.
+    pub fn recv(&self, dst: &mut [u8]) -> io::Result<usize> {
+        self.sys.recv(dst)
+    }
+
+    /// Sends data on the socket to the specified address.
+    ///
+    /// On success, returns the number of bytes written.
+    pub fn send_to<P: AsRef<Path>>(&self, src: &[u8], path: P) -> io::Result<usize> {
+        self.sys.send_to(src, path)
+    }
+
+    /// Sends data on the socket to the socket's peer.
+    ///
+    /// The peer address may be set by the `connect` method, and this method
+    /// will return an error if the socket has not already been connected.
+    ///
+    /// On success, returns the number of bytes written.
+    pub fn send(&self, src: &[u8]) -> io::Result<usize> {
+        self.sys.send(src)
+    }
+
+    /// Returns the value of the `SO_ERROR` option.
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.sys.take_error()
+    }
+
+    /// Shut down the read, write, or both halves of this connection.
+    ///
+    /// This function will cause all pending and future I/O calls on the
+    /// specified portions to immediately return with an appropriate value
+    /// (see the documentation of `Shutdown`).
+    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        self.sys.shutdown(how)
+    }
+}
+
+impl Source for UnixDatagram {
+    fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
+        #[cfg(debug_assertions)]
+        self.selector_id.associate_selector(registry)?;
+        self.sys.register(registry, token, interests)
+    }
+
+    fn reregister(
+        &self,
+        registry: &Registry,
+        token: Token,
+        interests: Interests,
+    ) -> io::Result<()> {
+        self.sys.reregister(registry, token, interests)
+    }
+
+    fn deregister(&self, registry: &Registry) -> io::Result<()> {
+        self.sys.deregister(registry)
+    }
+}
+
+impl AsRawFd for UnixDatagram {
+    fn as_raw_fd(&self) -> RawFd {
+        self.sys.as_raw_fd()
+    }
+}
+
+impl FromRawFd for UnixDatagram {
+    /// Converts a `std` `RawFd` to a `mio` `UnixDatagram`.
+    ///
+    /// The caller is responsible for ensuring that the socket is in
+    /// non-blocking mode.
+    unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
+        UnixDatagram::new(FromRawFd::from_raw_fd(fd))
+    }
+}
+
+impl IntoRawFd for UnixDatagram {
+    fn into_raw_fd(self) -> RawFd {
+        self.sys.into_raw_fd()
+    }
+}
diff --git a/src/net/uds/listener.rs b/src/net/uds/listener.rs
new file mode 100644
index 0000000..5cd20a8
--- /dev/null
+++ b/src/net/uds/listener.rs
@@ -0,0 +1,109 @@
+use crate::event::Source;
+use crate::net::UnixStream;
+#[cfg(debug_assertions)]
+use crate::poll::SelectorId;
+use crate::unix::SocketAddr;
+use crate::{sys, Interests, Registry, Token};
+
+use std::io;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::path::Path;
+
+/// A non-blocking Unix domain socket server.
+#[derive(Debug)]
+pub struct UnixListener {
+    sys: sys::UnixListener,
+    #[cfg(debug_assertions)]
+    selector_id: SelectorId,
+}
+
+impl UnixListener {
+    fn new(sys: sys::UnixListener) -> UnixListener {
+        UnixListener {
+            sys,
+            #[cfg(debug_assertions)]
+            selector_id: SelectorId::new(),
+        }
+    }
+
+    /// Creates a new `UnixListener` bound to the specified socket.
+    pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
+        let sys = sys::UnixListener::bind(path.as_ref())?;
+        Ok(UnixListener::new(sys))
+    }
+
+    /// Accepts a new incoming connection to this listener.
+    ///
+    /// The call is responsible for ensuring that the listening socket is in
+    /// non-blocking mode.
+    pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
+        let (sys, sockaddr) = self.sys.accept()?;
+        Ok((UnixStream::new(sys), sockaddr))
+    }
+
+    /// Creates a new independently owned handle to the underlying socket.
+    ///
+    /// The returned `UnixListener` is a reference to the same socket that this
+    /// object references. Both handles can be used to accept incoming
+    /// connections and options set on one listener will affect the other.
+    pub fn try_clone(&self) -> io::Result<UnixListener> {
+        let sys = self.sys.try_clone()?;
+        Ok(UnixListener::new(sys))
+    }
+
+    /// Returns the local socket address of this listener.
+    pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
+        self.sys.local_addr()
+    }
+
+    /// Returns the value of the `SO_ERROR` option.
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.sys.take_error()
+    }
+}
+
+impl Source for UnixListener {
+    fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
+        #[cfg(debug_assertions)]
+        self.selector_id.associate_selector(registry)?;
+        self.sys.reregister(registry, token, interests)
+    }
+
+    fn reregister(
+        &self,
+        registry: &Registry,
+        token: Token,
+        interests: Interests,
+    ) -> io::Result<()> {
+        self.sys.reregister(registry, token, interests)
+    }
+
+    fn deregister(&self, registry: &Registry) -> io::Result<()> {
+        self.sys.deregister(registry)
+    }
+}
+
+#[cfg(unix)]
+impl AsRawFd for UnixListener {
+    fn as_raw_fd(&self) -> RawFd {
+        self.sys.as_raw_fd()
+    }
+}
+
+#[cfg(unix)]
+impl IntoRawFd for UnixListener {
+    fn into_raw_fd(self) -> RawFd {
+        self.sys.into_raw_fd()
+    }
+}
+
+#[cfg(unix)]
+impl FromRawFd for UnixListener {
+    /// Converts a `std` `RawFd` to a `mio` `UnixListener`.
+    ///
+    /// The caller is responsible for ensuring that the socket is in
+    /// non-blocking mode.
+    unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
+        UnixListener::new(FromRawFd::from_raw_fd(fd))
+    }
+}
diff --git a/src/net/uds/mod.rs b/src/net/uds/mod.rs
new file mode 100644
index 0000000..e02fd80
--- /dev/null
+++ b/src/net/uds/mod.rs
@@ -0,0 +1,8 @@
+mod datagram;
+pub use self::datagram::UnixDatagram;
+
+mod listener;
+pub use self::listener::UnixListener;
+
+mod stream;
+pub use self::stream::UnixStream;
diff --git a/src/net/uds/stream.rs b/src/net/uds/stream.rs
new file mode 100644
index 0000000..79c8e00
--- /dev/null
+++ b/src/net/uds/stream.rs
@@ -0,0 +1,169 @@
+use crate::event::Source;
+#[cfg(debug_assertions)]
+use crate::poll::SelectorId;
+use crate::{sys, Interests, Registry, Token};
+
+use std::io::{self, IoSlice, IoSliceMut};
+use std::net::Shutdown;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::path::Path;
+
+/// A non-blocking Unix stream socket.
+#[derive(Debug)]
+pub struct UnixStream {
+    sys: sys::UnixStream,
+    #[cfg(debug_assertions)]
+    selector_id: SelectorId,
+}
+
+impl UnixStream {
+    pub(crate) fn new(sys: sys::UnixStream) -> UnixStream {
+        UnixStream {
+            sys,
+            #[cfg(debug_assertions)]
+            selector_id: SelectorId::new(),
+        }
+    }
+
+    /// Connects to the socket named by `path`.
+    pub fn connect<P: AsRef<Path>>(p: P) -> io::Result<UnixStream> {
+        let sys = sys::UnixStream::connect(p.as_ref())?;
+        Ok(UnixStream::new(sys))
+    }
+
+    /// Creates an unnamed pair of connected sockets.
+    ///
+    /// Returns two `UnixStream`s which are connected to each other.
+    pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
+        let (a, b) = sys::UnixStream::pair()?;
+        let a = UnixStream::new(a);
+        let b = UnixStream::new(b);
+        Ok((a, b))
+    }
+
+    /// Creates a new independently owned handle to the underlying socket.
+    ///
+    /// The returned `UnixStream` is a reference to the same stream that this
+    /// object references. Both handles will read and write the same stream of
+    /// data, and options set on one stream will be propogated to the other
+    /// stream.
+    pub fn try_clone(&self) -> io::Result<UnixStream> {
+        let sys = self.sys.try_clone()?;
+        Ok(UnixStream::new(sys))
+    }
+
+    /// Returns the socket address of the local half of this connection.
+    pub fn local_addr(&self) -> io::Result<sys::SocketAddr> {
+        self.sys.local_addr()
+    }
+
+    /// Returns the socket address of the remote half of this connection.
+    pub fn peer_addr(&self) -> io::Result<sys::SocketAddr> {
+        self.sys.peer_addr()
+    }
+
+    /// Returns the value of the `SO_ERROR` option.
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.sys.take_error()
+    }
+
+    /// Shuts down the read, write, or both halves of this connection.
+    ///
+    /// This function will cause all pending and future I/O calls on the
+    /// specified portions to immediately return with an appropriate value
+    /// (see the documentation of `Shutdown`).
+    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        self.sys.shutdown(how)
+    }
+}
+
+impl Source for UnixStream {
+    fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
+        #[cfg(debug_assertions)]
+        self.selector_id.associate_selector(registry)?;
+        self.sys.register(registry, token, interests)
+    }
+
+    fn reregister(
+        &self,
+        registry: &Registry,
+        token: Token,
+        interests: Interests,
+    ) -> io::Result<()> {
+        self.sys.reregister(registry, token, interests)
+    }
+
+    fn deregister(&self, registry: &Registry) -> io::Result<()> {
+        self.sys.deregister(registry)
+    }
+}
+
+impl io::Read for UnixStream {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        (&self.sys).read(buf)
+    }
+
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        (&self.sys).read_vectored(bufs)
+    }
+}
+
+impl<'a> io::Read for &'a UnixStream {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        (&self.sys).read(buf)
+    }
+
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        (&self.sys).read_vectored(bufs)
+    }
+}
+
+impl io::Write for UnixStream {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        (&self.sys).write(buf)
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        (&self.sys).write_vectored(bufs)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        (&self.sys).flush()
+    }
+}
+
+impl<'a> io::Write for &'a UnixStream {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        (&self.sys).write(buf)
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        (&self.sys).write_vectored(bufs)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        (&self.sys).flush()
+    }
+}
+
+impl AsRawFd for UnixStream {
+    fn as_raw_fd(&self) -> RawFd {
+        self.sys.as_raw_fd()
+    }
+}
+
+impl FromRawFd for UnixStream {
+    /// Converts a `std` `RawFd` to a `mio` `UnixStream`.
+    ///
+    /// The caller is responsible for ensuring that the socket is in
+    /// non-blocking mode.
+    unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
+        UnixStream::new(FromRawFd::from_raw_fd(fd))
+    }
+}
+
+impl IntoRawFd for UnixStream {
+    fn into_raw_fd(self) -> RawFd {
+        self.sys.into_raw_fd()
+    }
+}
diff --git a/src/poll.rs b/src/poll.rs
index 71e8b60..52e20e6 100644
--- a/src/poll.rs
+++ b/src/poll.rs
@@ -159,7 +159,6 @@ use std::{fmt, io};
 /// |      OS       |  Selector |
 /// |---------------|-----------|
 /// | Android       | [epoll]   |
-/// | Bitrig        | [kqueue]  |
 /// | DragonFly BSD | [kqueue]  |
 /// | FreeBSD       | [kqueue]  |
 /// | Linux         | [epoll]   |
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index dc81d51..0270a2c 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -7,7 +7,8 @@
 
 #[cfg(unix)]
 pub use self::unix::{
-    event, Event, Events, Selector, SourceFd, TcpListener, TcpStream, UdpSocket, Waker,
+    event, Event, Events, Selector, SocketAddr, SourceFd, TcpListener, TcpStream, UdpSocket,
+    UnixDatagram, UnixListener, UnixStream, Waker,
 };
 
 #[cfg(unix)]
diff --git a/src/sys/unix/kqueue.rs b/src/sys/unix/kqueue.rs
index f148111..41753a6 100644
--- a/src/sys/unix/kqueue.rs
+++ b/src/sys/unix/kqueue.rs
@@ -35,7 +35,7 @@ type Filter = u32;
     target_os = "macos"
 ))]
 type Data = libc::intptr_t;
-#[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd",))]
+#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
 type Data = i64;
 
 // Type of the `udata` field in the `kevent` structure.
diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs
index 0fb5439..244eeb2 100644
--- a/src/sys/unix/mod.rs
+++ b/src/sys/unix/mod.rs
@@ -19,7 +19,6 @@ mod epoll;
 pub use self::epoll::{event, Event, Selector};
 
 #[cfg(any(
-    target_os = "bitrig",
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
@@ -30,7 +29,6 @@ pub use self::epoll::{event, Event, Selector};
 mod kqueue;
 
 #[cfg(any(
-    target_os = "bitrig",
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "ios",
@@ -45,12 +43,14 @@ mod sourcefd;
 mod tcp_listener;
 mod tcp_stream;
 mod udp;
+mod uds;
 mod waker;
 
 pub use self::sourcefd::SourceFd;
 pub use self::tcp_listener::TcpListener;
 pub use self::tcp_stream::TcpStream;
 pub use self::udp::UdpSocket;
+pub use self::uds::{SocketAddr, UnixDatagram, UnixListener, UnixStream};
 pub use self::waker::Waker;
 
 pub type Events = Vec<Event>;
diff --git a/src/sys/unix/net.rs b/src/sys/unix/net.rs
index e999c17..bc6fe40 100644
--- a/src/sys/unix/net.rs
+++ b/src/sys/unix/net.rs
@@ -2,16 +2,19 @@ use std::io;
 use std::mem::size_of_val;
 use std::net::SocketAddr;
 
-/// Create a new non-blocking socket.
-pub fn new_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> {
+pub fn new_ip_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> {
     let domain = match addr {
         SocketAddr::V4(..) => libc::AF_INET,
         SocketAddr::V6(..) => libc::AF_INET6,
     };
 
+    new_socket(domain, socket_type)
+}
+
+/// Create a new non-blocking socket.
+pub fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::Result<libc::c_int> {
     #[cfg(any(
         target_os = "android",
-        target_os = "bitrig",
         target_os = "dragonfly",
         target_os = "freebsd",
         target_os = "linux",
@@ -30,12 +33,14 @@ pub fn new_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc
     let socket = socket.and_then(|socket| {
         // For platforms that don't support flags in socket, we need to
         // set the flags ourselves.
-        syscall!(fcntl(
-            socket,
-            libc::F_SETFL,
-            libc::O_NONBLOCK | libc::O_CLOEXEC
-        ))
-        .map(|_| socket)
+        syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK,))
+            .and_then(|_| syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC,)).map(|_| socket))
+            .map_err(|e| {
+                // If either of the `fcntl` calls failed, ensure the socket is
+                // closed and return the error.
+                let _ = syscall!(close(socket));
+                e
+            })
     });
 
     socket
diff --git a/src/sys/unix/tcp_listener.rs b/src/sys/unix/tcp_listener.rs
index 9fdf375..b7a6e97 100644
--- a/src/sys/unix/tcp_listener.rs
+++ b/src/sys/unix/tcp_listener.rs
@@ -1,4 +1,4 @@
-use crate::sys::unix::net::{new_socket, socket_addr};
+use crate::sys::unix::net::{new_ip_socket, socket_addr};
 use crate::sys::unix::{SourceFd, TcpStream};
 use crate::{event, Interests, Registry, Token};
 
@@ -14,7 +14,7 @@ pub struct TcpListener {
 
 impl TcpListener {
     pub fn bind(addr: SocketAddr) -> io::Result<TcpListener> {
-        new_socket(addr, libc::SOCK_STREAM).and_then(|socket| {
+        new_ip_socket(addr, libc::SOCK_STREAM).and_then(|socket| {
             // Set SO_REUSEADDR (mirrors what libstd does).
             syscall!(setsockopt(
                 socket,
diff --git a/src/sys/unix/tcp_stream.rs b/src/sys/unix/tcp_stream.rs
index 76ae873..b446266 100644
--- a/src/sys/unix/tcp_stream.rs
+++ b/src/sys/unix/tcp_stream.rs
@@ -1,4 +1,4 @@
-use crate::sys::unix::net::{new_socket, socket_addr};
+use crate::sys::unix::net::{new_ip_socket, socket_addr};
 use crate::sys::unix::SourceFd;
 use crate::{event, Interests, Registry, Token};
 
@@ -17,7 +17,7 @@ impl TcpStream {
     }
 
     pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
-        new_socket(addr, libc::SOCK_STREAM)
+        new_ip_socket(addr, libc::SOCK_STREAM)
             .and_then(|socket| {
                 let (raw_addr, raw_addr_length) = socket_addr(&addr);
                 syscall!(connect(socket, raw_addr, raw_addr_length))
diff --git a/src/sys/unix/udp.rs b/src/sys/unix/udp.rs
index 16f9369..99d4610 100644
--- a/src/sys/unix/udp.rs
+++ b/src/sys/unix/udp.rs
@@ -1,4 +1,4 @@
-use crate::sys::unix::net::{new_socket, socket_addr};
+use crate::sys::unix::net::{new_ip_socket, socket_addr};
 use crate::unix::SourceFd;
 use crate::{event, Interests, Registry, Token};
 
@@ -14,7 +14,7 @@ impl UdpSocket {
     pub fn bind(addr: SocketAddr) -> io::Result<UdpSocket> {
         // Gives a warning for non Apple platforms.
         #[allow(clippy::let_and_return)]
-        let socket = new_socket(addr, libc::SOCK_DGRAM);
+        let socket = new_ip_socket(addr, libc::SOCK_DGRAM);
 
         // Set SO_NOSIGPIPE on iOS and macOS (mirrors what libstd does).
         #[cfg(any(target_os = "ios", target_os = "macos"))]
diff --git a/src/sys/unix/uds/datagram.rs b/src/sys/unix/uds/datagram.rs
new file mode 100644
index 0000000..4cde222
--- /dev/null
+++ b/src/sys/unix/uds/datagram.rs
@@ -0,0 +1,169 @@
+use super::{pair_descriptors, socket_addr, SocketAddr};
+use crate::event::Source;
+use crate::sys::unix::net::new_socket;
+use crate::unix::SourceFd;
+use crate::{Interests, Registry, Token};
+
+use std::io;
+use std::net::Shutdown;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::net;
+use std::path::Path;
+
+#[derive(Debug)]
+pub struct UnixDatagram {
+    inner: net::UnixDatagram,
+}
+
+impl UnixDatagram {
+    fn new(inner: net::UnixDatagram) -> UnixDatagram {
+        UnixDatagram { inner }
+    }
+
+    pub(crate) fn bind(path: &Path) -> io::Result<UnixDatagram> {
+        let socket = new_socket(libc::AF_UNIX, libc::SOCK_DGRAM)?;
+        let (sockaddr, socklen) = socket_addr(path)?;
+        let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr;
+
+        syscall!(bind(socket, sockaddr, socklen))?;
+        Ok(unsafe { UnixDatagram::from_raw_fd(socket) })
+    }
+
+    pub(crate) fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
+        self.inner.connect(path)
+    }
+
+    pub(crate) fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
+        let fds = [-1; 2];
+        let flags = libc::SOCK_DGRAM;
+
+        #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "solaris")))]
+        let pair = {
+            pair_descriptors(fds, flags)?;
+            unsafe {
+                (
+                    UnixDatagram::from_raw_fd(fds[0]),
+                    UnixDatagram::from_raw_fd(fds[1]),
+                )
+            }
+        };
+
+        // Darwin and Solaris do not have SOCK_NONBLOCK or SOCK_CLOEXEC.
+        //
+        // In order to set those flags, additional `fcntl` sys calls must be
+        // made in `pair_descriptors` that are fallible. If a `fnctl` fails
+        // after the sockets have been created, the file descriptors will
+        // leak. Creating `s1` and `s2` below ensure that if there is an
+        // error, the file descriptors are closed.
+        #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))]
+        let pair = {
+            let s1 = unsafe { UnixDatagram::from_raw_fd(fds[0]) };
+            let s2 = unsafe { UnixDatagram::from_raw_fd(fds[1]) };
+            pair_descriptors(fds, flags)?;
+            (s1, s2)
+        };
+
+        Ok(pair)
+    }
+
+    pub(crate) fn unbound() -> io::Result<UnixDatagram> {
+        let socket = new_socket(libc::AF_UNIX, libc::SOCK_DGRAM)?;
+        Ok(unsafe { UnixDatagram::from_raw_fd(socket) })
+    }
+
+    pub(crate) fn try_clone(&self) -> io::Result<UnixDatagram> {
+        let inner = self.inner.try_clone()?;
+        Ok(UnixDatagram::new(inner))
+    }
+
+    pub(crate) fn local_addr(&self) -> io::Result<SocketAddr> {
+        SocketAddr::new(|sockaddr, socklen| {
+            syscall!(getsockname(self.inner.as_raw_fd(), sockaddr, socklen))
+        })
+    }
+
+    pub(crate) fn peer_addr(&self) -> io::Result<SocketAddr> {
+        SocketAddr::new(|sockaddr, socklen| {
+            syscall!(getpeername(self.inner.as_raw_fd(), sockaddr, socklen))
+        })
+    }
+
+    pub(crate) fn recv_from(&self, dst: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        let mut count = 0;
+        let socketaddr = SocketAddr::new(|sockaddr, socklen| unsafe {
+            count = libc::recvfrom(
+                self.inner.as_raw_fd(),
+                dst.as_mut_ptr() as *mut _,
+                dst.len(),
+                0,
+                sockaddr,
+                socklen,
+            );
+            if count > 0 {
+                Ok(1)
+            } else if count == 0 {
+                Ok(0)
+            } else {
+                Err(io::Error::last_os_error())
+            }
+        })?;
+        Ok((count as usize, socketaddr))
+    }
+
+    pub(crate) fn recv(&self, dst: &mut [u8]) -> io::Result<usize> {
+        self.inner.recv(dst)
+    }
+
+    pub(crate) fn send_to<P: AsRef<Path>>(&self, src: &[u8], path: P) -> io::Result<usize> {
+        self.inner.send_to(src, path)
+    }
+
+    pub(crate) fn send(&self, src: &[u8]) -> io::Result<usize> {
+        self.inner.send(src)
+    }
+
+    pub(crate) fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.inner.take_error()
+    }
+
+    pub(crate) fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        self.inner.shutdown(how)
+    }
+}
+
+impl Source for UnixDatagram {
+    fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).register(registry, token, interests)
+    }
+
+    fn reregister(
+        &self,
+        registry: &Registry,
+        token: Token,
+        interests: Interests,
+    ) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
+    }
+
+    fn deregister(&self, registry: &Registry) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).deregister(registry)
+    }
+}
+
+impl AsRawFd for UnixDatagram {
+    fn as_raw_fd(&self) -> RawFd {
+        self.inner.as_raw_fd()
+    }
+}
+
+impl FromRawFd for UnixDatagram {
+    unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
+        UnixDatagram::new(net::UnixDatagram::from_raw_fd(fd))
+    }
+}
+
+impl IntoRawFd for UnixDatagram {
+    fn into_raw_fd(self) -> RawFd {
+        self.inner.into_raw_fd()
+    }
+}
diff --git a/src/sys/unix/uds/listener.rs b/src/sys/unix/uds/listener.rs
new file mode 100644
index 0000000..d5f6554
--- /dev/null
+++ b/src/sys/unix/uds/listener.rs
@@ -0,0 +1,254 @@
+use super::{path_offset, socket_addr};
+use crate::event::Source;
+use crate::sys::unix::net::new_socket;
+use crate::sys::unix::UnixStream;
+use crate::unix::SourceFd;
+use crate::{Interests, Registry, Token};
+
+use std::ffi::OsStr;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::net;
+use std::path::Path;
+use std::{ascii, fmt, io, mem};
+
+#[derive(Debug)]
+pub struct UnixListener {
+    inner: net::UnixListener,
+}
+
+/// An address associated with a `mio` specific Unix socket.
+///
+/// This is implemented instead of imported from [`net::SocketAddr`] because
+/// there is no way to create a [`net::SocketAddr`]. One must be returned by
+/// [`accept`], so this is returned instead.
+///
+/// [`net::SocketAddr`]: std::os::unix::net::SocketAddr
+/// [`accept`]: #method.accept
+pub struct SocketAddr {
+    sockaddr: libc::sockaddr_un,
+    socklen: libc::socklen_t,
+}
+
+enum AddressKind<'a> {
+    Unnamed,
+    Pathname(&'a Path),
+    Abstract(&'a [u8]),
+}
+
+impl UnixListener {
+    fn new(inner: net::UnixListener) -> UnixListener {
+        UnixListener { inner }
+    }
+
+    pub(crate) fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
+        let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
+
+        // This is safe to assume because a `libc::sockaddr_un` filled with `0`
+        // bytes is properly initialized.
+        //
+        // `0` is a valid value for `sockaddr_un::sun_family`; it is
+        // `libc::AF_UNSPEC`.
+        //
+        // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
+        // abstract path.
+        let mut sockaddr = unsafe { sockaddr.assume_init() };
+
+        sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
+        let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
+
+        #[cfg(not(any(
+            target_os = "ios",
+            target_os = "macos",
+            target_os = "netbsd",
+            target_os = "solaris"
+        )))]
+        let socket = {
+            let flags = libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
+            syscall!(accept4(
+                self.inner.as_raw_fd(),
+                &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr,
+                &mut socklen,
+                flags
+            ))
+            .map(|socket| unsafe { UnixStream::from_raw_fd(socket) })
+        };
+
+        #[cfg(any(
+            target_os = "ios",
+            target_os = "macos",
+            target_os = "netbsd",
+            target_os = "solaris"
+        ))]
+        let socket = syscall!(accept(
+            self.inner.as_raw_fd(),
+            &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr,
+            &mut socklen,
+        ))
+        .and_then(|socket| {
+            // Ensure the socket is closed if either of the `fcntl` calls
+            // error below.
+            let s = unsafe { UnixStream::from_raw_fd(socket) };
+            syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))
+                .and_then(|_| syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| s))
+        });
+
+        socket.and_then(|s| Ok((s, SocketAddr::from_parts(sockaddr, socklen))))
+    }
+
+    pub(crate) fn bind(path: &Path) -> io::Result<UnixListener> {
+        let socket = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
+        let (sockaddr, socklen) = socket_addr(path)?;
+        let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr;
+
+        syscall!(bind(socket, sockaddr, socklen))
+            .and_then(|_| syscall!(listen(socket, 1024)))
+            .map_err(|err| {
+                // Close the socket if we hit an error, ignoring the error from
+                // closing since we can't pass back two errors.
+                let _ = unsafe { libc::close(socket) };
+                err
+            })
+            .map(|_| unsafe { UnixListener::from_raw_fd(socket) })
+    }
+
+    pub(crate) fn try_clone(&self) -> io::Result<UnixListener> {
+        let inner = self.inner.try_clone()?;
+        Ok(UnixListener::new(inner))
+    }
+
+    pub(crate) fn local_addr(&self) -> io::Result<SocketAddr> {
+        SocketAddr::new(|sockaddr, socklen| {
+            syscall!(getsockname(self.inner.as_raw_fd(), sockaddr, socklen))
+        })
+    }
+
+    /// Returns the value of the `SO_ERROR` option.
+    pub(crate) fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.inner.take_error()
+    }
+}
+
+impl Source for UnixListener {
+    fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).register(registry, token, interests)
+    }
+
+    fn reregister(
+        &self,
+        registry: &Registry,
+        token: Token,
+        interests: Interests,
+    ) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
+    }
+
+    fn deregister(&self, registry: &Registry) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).deregister(registry)
+    }
+}
+
+impl AsRawFd for UnixListener {
+    fn as_raw_fd(&self) -> RawFd {
+        self.inner.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for UnixListener {
+    fn into_raw_fd(self) -> RawFd {
+        self.inner.into_raw_fd()
+    }
+}
+
+impl FromRawFd for UnixListener {
+    unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
+        UnixListener::new(net::UnixListener::from_raw_fd(fd))
+    }
+}
+
+impl SocketAddr {
+    pub(crate) fn new<F>(f: F) -> io::Result<SocketAddr>
+    where
+        F: FnOnce(*mut libc::sockaddr, &mut libc::socklen_t) -> io::Result<libc::c_int>,
+    {
+        let mut sockaddr = {
+            let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
+            unsafe { sockaddr.assume_init() }
+        };
+
+        let raw_sockaddr = &mut sockaddr as *mut libc::sockaddr_un as *mut libc::sockaddr;
+        let mut socklen = mem::size_of_val(&sockaddr) as libc::socklen_t;
+
+        f(raw_sockaddr, &mut socklen)?;
+        Ok(SocketAddr::from_parts(sockaddr, socklen))
+    }
+
+    pub(crate) fn from_parts(sockaddr: libc::sockaddr_un, socklen: libc::socklen_t) -> SocketAddr {
+        SocketAddr { sockaddr, socklen }
+    }
+
+    /// Returns `true` if the address is unnamed.
+    ///
+    /// Documentation reflected in [`SocketAddr`]
+    ///
+    /// [`SocketAddr`]: std::os::unix::net::SocketAddr
+    pub fn is_unnamed(&self) -> bool {
+        if let AddressKind::Unnamed = self.address() {
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Returns the contents of this address if it is a `pathname` address.
+    ///
+    /// Documentation reflected in [`SocketAddr`]
+    ///
+    /// [`SocketAddr`]: std::os::unix::net::SocketAddr
+    pub fn as_pathname(&self) -> Option<&Path> {
+        if let AddressKind::Pathname(path) = self.address() {
+            Some(path)
+        } else {
+            None
+        }
+    }
+
+    fn address(&self) -> AddressKind<'_> {
+        let offset = path_offset(&self.sockaddr);
+        let len = self.socklen as usize - offset;
+        let path = unsafe { &*(&self.sockaddr.sun_path as *const [libc::c_char] as *const [u8]) };
+
+        // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
+        if len == 0
+            || (cfg!(not(any(target_os = "linux", target_os = "android")))
+                && self.sockaddr.sun_path[0] == 0)
+        {
+            AddressKind::Unnamed
+        } else if self.sockaddr.sun_path[0] == 0 {
+            AddressKind::Abstract(&path[1..len])
+        } else {
+            AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
+        }
+    }
+}
+
+impl fmt::Debug for SocketAddr {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.address() {
+            AddressKind::Unnamed => write!(fmt, "(unnamed)"),
+            AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
+            AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
+        }
+    }
+}
+struct AsciiEscaped<'a>(&'a [u8]);
+
+impl<'a> fmt::Display for AsciiEscaped<'a> {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(fmt, "\"")?;
+        for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
+            write!(fmt, "{}", byte as char)?;
+        }
+        write!(fmt, "\"")
+    }
+}
diff --git a/src/sys/unix/uds/mod.rs b/src/sys/unix/uds/mod.rs
new file mode 100644
index 0000000..9ba14c7
--- /dev/null
+++ b/src/sys/unix/uds/mod.rs
@@ -0,0 +1,133 @@
+use std::cmp::Ordering;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::RawFd;
+use std::path::Path;
+use std::{io, mem};
+
+mod datagram;
+pub use self::datagram::UnixDatagram;
+
+mod listener;
+pub use self::listener::{SocketAddr, UnixListener};
+
+mod stream;
+pub use self::stream::UnixStream;
+
+pub fn socket_addr(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
+    let sockaddr = mem::MaybeUninit::<libc::sockaddr_un>::zeroed();
+
+    // This is safe to assume because a `libc::sockaddr_un` filled with `0`
+    // bytes is properly initialized.
+    //
+    // `0` is a valid value for `sockaddr_un::sun_family`; it is
+    // `libc::AF_UNSPEC`.
+    //
+    // `[0; 108]` is a valid value for `sockaddr_un::sun_path`; it begins an
+    // abstract path.
+    let mut sockaddr = unsafe { sockaddr.assume_init() };
+
+    sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
+
+    let bytes = path.as_os_str().as_bytes();
+    match (bytes.get(0), bytes.len().cmp(&sockaddr.sun_path.len())) {
+        // Abstract paths don't need a null terminator
+        (Some(&0), Ordering::Greater) => {
+            return Err(io::Error::new(
+                io::ErrorKind::InvalidInput,
+                "path must be no longer than libc::sockaddr_un.sun_path",
+            ));
+        }
+        (_, Ordering::Greater) | (_, Ordering::Equal) => {
+            return Err(io::Error::new(
+                io::ErrorKind::InvalidInput,
+                "path must be shorter than libc::sockaddr_un.sun_path",
+            ));
+        }
+        _ => {}
+    }
+
+    for (dst, src) in sockaddr.sun_path.iter_mut().zip(bytes.iter()) {
+        *dst = *src as libc::c_char;
+    }
+
+    let offset = path_offset(&sockaddr);
+    let mut socklen = offset + bytes.len();
+
+    match bytes.get(0) {
+        // The struct has already been zeroes so the null byte for pathname
+        // addresses is already there.
+        Some(&0) | None => {}
+        Some(_) => socklen += 1,
+    }
+
+    Ok((sockaddr, socklen as libc::socklen_t))
+}
+
+/// Get the `sun_path` field offset of `sockaddr_un` for the target OS.
+///
+/// On Linux, this funtion equates to the same value as
+/// `size_of::<sa_family_t>()`, but some other implementations include
+/// other fields before `sun_path`, so the expression more portably
+/// describes the size of the address structure.
+pub fn path_offset(sockaddr: &libc::sockaddr_un) -> usize {
+    let base = sockaddr as *const _ as usize;
+    let path = &sockaddr.sun_path as *const _ as usize;
+    path - base
+}
+
+fn pair_descriptors(mut fds: [RawFd; 2], flags: i32) -> io::Result<()> {
+    #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "solaris")))]
+    let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
+
+    syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?;
+
+    // Darwin and Solaris don't have SOCK_NONBLOCK or SOCK_CLOEXEC.
+    //
+    // For platforms that don't support flags in `socket`, the flags must be
+    // set through `fcntl`. The `F_SETFL` command sets the `O_NONBLOCK` bit.
+    // The `F_SETFD` command sets the `FD_CLOEXEC` bit.
+    #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))]
+    {
+        syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?;
+        syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
+        syscall!(fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK))?;
+        syscall!(fcntl(fds[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
+    }
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{path_offset, socket_addr};
+    use std::path::Path;
+    use std::str;
+
+    #[test]
+    fn pathname_address() {
+        const PATH: &str = "./foo/bar.txt";
+        const PATH_LEN: usize = 13;
+
+        // Pathname addresses do have a null terminator, so `socklen` is
+        // expected to be `PATH_LEN` + `offset` + 1.
+        let path = Path::new(PATH);
+        let (sockaddr, actual) = socket_addr(path).unwrap();
+        let offset = path_offset(&sockaddr);
+        let expected = PATH_LEN + offset + 1;
+        assert_eq!(expected as libc::socklen_t, actual)
+    }
+
+    #[test]
+    fn abstract_address() {
+        const PATH: &[u8] = &[0, 116, 111, 107, 105, 111];
+        const PATH_LEN: usize = 6;
+
+        // Abstract addresses do not have a null terminator, so `socklen` is
+        // expected to be `PATH_LEN` + `offset`.
+        let abstract_path = str::from_utf8(PATH).unwrap();
+        let path = Path::new(abstract_path);
+        let (sockaddr, actual) = socket_addr(path).unwrap();
+        let offset = path_offset(&sockaddr);
+        let expected = PATH_LEN + offset;
+        assert_eq!(expected as libc::socklen_t, actual)
+    }
+}
diff --git a/src/sys/unix/uds/stream.rs b/src/sys/unix/uds/stream.rs
new file mode 100644
index 0000000..41492a4
--- /dev/null
+++ b/src/sys/unix/uds/stream.rs
@@ -0,0 +1,161 @@
+use super::{pair_descriptors, socket_addr, SocketAddr};
+use crate::event::Source;
+use crate::sys::unix::net::new_socket;
+use crate::sys::unix::SourceFd;
+use crate::{Interests, Registry, Token};
+
+use std::io::{self, IoSlice, IoSliceMut, Read, Write};
+use std::net::Shutdown;
+use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::unix::net;
+use std::path::Path;
+
+#[derive(Debug)]
+pub struct UnixStream {
+    inner: net::UnixStream,
+}
+
+impl UnixStream {
+    pub fn new(inner: net::UnixStream) -> UnixStream {
+        UnixStream { inner }
+    }
+
+    pub(crate) fn connect(path: &Path) -> io::Result<UnixStream> {
+        let socket = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?;
+        let (sockaddr, socklen) = socket_addr(path)?;
+        let sockaddr = &sockaddr as *const libc::sockaddr_un as *const libc::sockaddr;
+
+        match syscall!(connect(socket, sockaddr, socklen)) {
+            Ok(_) => {}
+            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
+            Err(e) => {
+                // Close the socket if we hit an error, ignoring the error
+                // from closing since we can't pass back two errors.
+                let _ = unsafe { libc::close(socket) };
+
+                return Err(e);
+            }
+        }
+
+        Ok(unsafe { UnixStream::from_raw_fd(socket) })
+    }
+
+    pub(crate) fn pair() -> io::Result<(UnixStream, UnixStream)> {
+        let fds = [-1; 2];
+        let flags = libc::SOCK_STREAM;
+
+        #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "solaris")))]
+        let pair = {
+            pair_descriptors(fds, flags)?;
+            unsafe {
+                (
+                    UnixStream::from_raw_fd(fds[0]),
+                    UnixStream::from_raw_fd(fds[1]),
+                )
+            }
+        };
+
+        // Darwin and Solaris do not have SOCK_NONBLOCK or SOCK_CLOEXEC.
+        //
+        // In order to set those flags, additional `fcntl` sys calls must be
+        // made in `pair_descriptors` that are fallible. If a `fnctl` fails
+        // after the sockets have been created, the file descriptors will
+        // leak. Creating `s1` and `s2` below ensure that if there is an
+        // error, the file descriptors are closed.
+        #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))]
+        let pair = {
+            let s1 = unsafe { UnixStream::from_raw_fd(fds[0]) };
+            let s2 = unsafe { UnixStream::from_raw_fd(fds[1]) };
+            pair_descriptors(fds, flags)?;
+            (s1, s2)
+        };
+
+        Ok(pair)
+    }
+
+    pub(crate) fn try_clone(&self) -> io::Result<UnixStream> {
+        let inner = self.inner.try_clone()?;
+        Ok(UnixStream::new(inner))
+    }
+
+    pub(crate) fn local_addr(&self) -> io::Result<SocketAddr> {
+        SocketAddr::new(|sockaddr, socklen| {
+            syscall!(getsockname(self.inner.as_raw_fd(), sockaddr, socklen))
+        })
+    }
+
+    pub(crate) fn peer_addr(&self) -> io::Result<SocketAddr> {
+        SocketAddr::new(|sockaddr, socklen| {
+            syscall!(getpeername(self.inner.as_raw_fd(), sockaddr, socklen))
+        })
+    }
+
+    pub(crate) fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.inner.take_error()
+    }
+
+    pub(crate) fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        self.inner.shutdown(how)
+    }
+}
+
+impl Source for UnixStream {
+    fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).register(registry, token, interests)
+    }
+
+    fn reregister(
+        &self,
+        registry: &Registry,
+        token: Token,
+        interests: Interests,
+    ) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
+    }
+
+    fn deregister(&self, registry: &Registry) -> io::Result<()> {
+        SourceFd(&self.as_raw_fd()).deregister(registry)
+    }
+}
+
+impl<'a> Read for &'a UnixStream {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        (&self.inner).read(buf)
+    }
+
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        (&self.inner).read_vectored(bufs)
+    }
+}
+
+impl<'a> Write for &'a UnixStream {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        (&self.inner).write(buf)
+    }
+
+    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        (&self.inner).write_vectored(bufs)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        (&self.inner).flush()
+    }
+}
+
+impl AsRawFd for UnixStream {
+    fn as_raw_fd(&self) -> RawFd {
+        self.inner.as_raw_fd()
+    }
+}
+
+impl FromRawFd for UnixStream {
+    unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
+        UnixStream::new(net::UnixStream::from_raw_fd(fd))
+    }
+}
+
+impl IntoRawFd for UnixStream {
+    fn into_raw_fd(self) -> RawFd {
+        self.inner.into_raw_fd()
+    }
+}
diff --git a/src/sys/unix/waker.rs b/src/sys/unix/waker.rs
index 4b13fa2..3555ca2 100644
--- a/src/sys/unix/waker.rs
+++ b/src/sys/unix/waker.rs
@@ -99,7 +99,6 @@ mod kqueue {
 pub use self::kqueue::Waker;
 
 #[cfg(any(
-    target_os = "bitrig",
     target_os = "dragonfly",
     target_os = "netbsd",
     target_os = "openbsd",
@@ -165,7 +164,6 @@ mod pipe {
 }
 
 #[cfg(any(
-    target_os = "bitrig",
     target_os = "dragonfly",
     target_os = "netbsd",
     target_os = "openbsd",


Test patch:
diff --git a/tests/tcp.rs b/tests/tcp.rs
index eb11996..faa3b4a 100644
--- a/tests/tcp.rs
+++ b/tests/tcp.rs
@@ -641,7 +641,6 @@ macro_rules! wait {
             for event in &events {
                 // Hup is only generated on kqueue platforms.
                 #[cfg(any(
-                    target_os = "bitrig",
                     target_os = "dragonfly",
                     target_os = "freebsd",
                     target_os = "ios",
diff --git a/tests/tcp_stream.rs b/tests/tcp_stream.rs
index f2c64e0..f583fd2 100644
--- a/tests/tcp_stream.rs
+++ b/tests/tcp_stream.rs
@@ -183,15 +183,24 @@ fn ttl() {
 }
 
 #[test]
-#[cfg_attr(windows, ignore = "fails on Windows, see #1079")]
 fn nodelay() {
-    init();
+    let (mut poll, mut events) = init_with_poll();
 
     let barrier = Arc::new(Barrier::new(2));
     let (thread_handle, address) = start_listener(1, Some(barrier.clone()));
 
     let stream = TcpStream::connect(address).unwrap();
 
+    poll.registry()
+        .register(&stream, ID1, Interests::WRITABLE)
+        .expect("unable to register TCP stream");
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(ID1, Interests::WRITABLE)],
+    );
+
     const NO_DELAY: bool = true;
     stream.set_nodelay(NO_DELAY).unwrap();
     assert_eq!(stream.nodelay().unwrap(), NO_DELAY);
diff --git a/tests/uds.rs b/tests/uds.rs
new file mode 100644
index 0000000..db9dbba
--- /dev/null
+++ b/tests/uds.rs
@@ -0,0 +1,481 @@
+#![cfg(unix)]
+#[macro_use]
+mod util;
+
+use mio::net::{UnixDatagram, UnixListener, UnixStream};
+use mio::unix::SocketAddr;
+use mio::{Interests, Token};
+use std::io::{self, IoSlice, IoSliceMut, Read, Write};
+use std::net::Shutdown;
+use std::sync::mpsc::{channel, Receiver};
+use std::thread;
+use tempdir::TempDir;
+use util::{
+    assert_send, assert_sync, assert_would_block, expect_events, expect_no_events, init_with_poll,
+    ExpectEvent, TryRead, TryWrite,
+};
+
+const DATA1: &[u8] = b"Hello same host!";
+const DATA2: &[u8] = b"Why hello mio!";
+
+const DEFAULT_BUF_SIZE: usize = 64;
+const DATA1_LEN: usize = 16;
+const DATA2_LEN: usize = 14;
+
+const LOCAL: Token = Token(0);
+const LOCAL_CLONE: Token = Token(1);
+
+#[test]
+fn smoke_test() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let mut local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(
+        &local,
+        LOCAL,
+        Interests::WRITABLE.add(Interests::READABLE)
+    ));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::WRITABLE)],
+    );
+
+    let mut buf = [0; DEFAULT_BUF_SIZE];
+    assert_would_block(local.read(&mut buf));
+
+    let wrote = assert_ok!(local.write(&DATA1));
+    assert_eq!(wrote, DATA1_LEN);
+    assert_ok!(local.flush());
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    let read = assert_ok!(local.read(&mut buf));
+    assert_eq!(read, DATA1_LEN);
+    assert_eq!(&buf[..read], DATA1);
+    assert_eq!(read, wrote, "unequal reads and writes");
+
+    assert!(assert_ok!(local.take_error()).is_none());
+
+    let bufs = [IoSlice::new(&DATA1), IoSlice::new(&DATA2)];
+    let wrote = assert_ok!(local.write_vectored(&bufs));
+    assert_eq!(wrote, DATA1_LEN + DATA2_LEN);
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    let mut buf1 = [1; DATA1_LEN];
+    let mut buf2 = [2; DATA2_LEN + 1];
+    let mut bufs = [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)];
+    let read = assert_ok!(local.read_vectored(&mut bufs));
+    assert_eq!(read, DATA1_LEN + DATA2_LEN);
+    assert_eq!(&buf1, DATA1);
+    assert_eq!(&buf2[..DATA2.len()], DATA2);
+
+    // Last byte should be unchanged
+    assert_eq!(buf2[DATA2.len()], 2);
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn is_send_and_sync() {
+    assert_send::<UnixListener>();
+    assert_sync::<UnixListener>();
+
+    assert_send::<UnixStream>();
+    assert_sync::<UnixStream>();
+
+    assert_send::<UnixDatagram>();
+    assert_sync::<UnixDatagram>();
+}
+
+#[test]
+fn register() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(&local, LOCAL, Interests::READABLE));
+
+    expect_no_events(&mut poll, &mut events);
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn reregister() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(&local, LOCAL, Interests::READABLE));
+    assert_ok!(poll
+        .registry()
+        .reregister(&local, LOCAL_CLONE, Interests::WRITABLE));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL_CLONE, Interests::WRITABLE)],
+    );
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn deregister() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(&local, LOCAL, Interests::WRITABLE));
+    assert_ok!(poll.registry().deregister(&local));
+
+    expect_no_events(&mut poll, &mut events);
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn connect() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let dir = assert_ok!(TempDir::new("uds"));
+    let path = dir.path().join("foo");
+
+    let remote = assert_ok!(UnixListener::bind(path.clone()));
+    let local = assert_ok!(UnixStream::connect(path));
+
+    let (sender_1, receiver_1) = channel();
+    let (sender_2, receiver_2) = channel();
+
+    let handle = thread::spawn(move || {
+        let (stream, _) = assert_ok!(remote.accept());
+        assert_ok!(receiver_1.recv());
+        drop(stream);
+        assert_ok!(sender_2.send(()));
+    });
+
+    assert_ok!(poll
+        .registry()
+        .register(&local, LOCAL, Interests::READABLE | Interests::WRITABLE));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::WRITABLE)],
+    );
+
+    assert_ok!(sender_1.send(()));
+    assert_ok!(receiver_2.recv());
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn try_clone() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let mut local_1 = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll
+        .registry()
+        .register(&local_1, LOCAL, Interests::WRITABLE));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::WRITABLE)],
+    );
+
+    let mut buf = [0; DEFAULT_BUF_SIZE];
+    let wrote = assert_ok!(local_1.write(&DATA1));
+    assert_eq!(wrote, DATA1_LEN);
+
+    let mut local_2 = assert_ok!(local_1.try_clone());
+
+    // When using `try_clone` the `TcpStream` needs to be deregistered!
+    assert_ok!(poll.registry().deregister(&local_1));
+    drop(local_1);
+
+    assert_ok!(poll
+        .registry()
+        .register(&local_2, LOCAL_CLONE, Interests::READABLE));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL_CLONE, Interests::READABLE)],
+    );
+
+    let read = assert_ok!(local_2.read(&mut buf));
+    assert_eq!(read, DATA1_LEN);
+    assert_eq!(&buf[..read], DATA1);
+
+    // Close the connection to allow the remote to shutdown
+    drop(local_2);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn shutdown_read() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let mut local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(
+        &local,
+        LOCAL,
+        Interests::WRITABLE.add(Interests::READABLE)
+    ));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::WRITABLE)],
+    );
+
+    let wrote = assert_ok!(local.write(DATA1));
+    assert_eq!(wrote, DATA1_LEN);
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    assert_ok!(local.shutdown(Shutdown::Read));
+
+    // Shutting down the reading side is different on each platform. For example
+    // on Linux based systems we can still read.
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    {
+        let mut buf = [0; DEFAULT_BUF_SIZE];
+        let read = assert_ok!(local.read(&mut buf));
+        assert_eq!(read, 0);
+    }
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn shutdown_write() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let mut local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(
+        &local,
+        LOCAL,
+        Interests::WRITABLE.add(Interests::READABLE)
+    ));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::WRITABLE)],
+    );
+
+    let wrote = assert_ok!(local.write(DATA1));
+    assert_eq!(wrote, DATA1_LEN);
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    assert_ok!(local.shutdown(Shutdown::Write));
+
+    let err = assert_err!(local.write(DATA2));
+    assert_eq!(err.kind(), io::ErrorKind::BrokenPipe);
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    // Read should be ok
+    let mut buf = [0; DEFAULT_BUF_SIZE];
+    let read = assert_ok!(local.read(&mut buf));
+    assert_eq!(read, DATA1_LEN);
+    assert_eq!(&buf[..read], DATA1);
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+#[test]
+fn shutdown_both() {
+    let (mut poll, mut events) = init_with_poll();
+
+    let (sync_sender, sync_receiver) = channel();
+    let (handle, remote_addr) = echo_remote(1, sync_receiver);
+
+    let path = remote_addr.as_pathname().expect("not a pathname");
+    let mut local = assert_ok!(UnixStream::connect(path));
+    assert_ok!(sync_sender.send(()));
+
+    assert_ok!(poll.registry().register(
+        &local,
+        LOCAL,
+        Interests::WRITABLE.add(Interests::READABLE)
+    ));
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::WRITABLE)],
+    );
+
+    let wrote = assert_ok!(local.write(DATA1));
+    assert_eq!(wrote, DATA1_LEN);
+
+    expect_events(
+        &mut poll,
+        &mut events,
+        vec![ExpectEvent::new(LOCAL, Interests::READABLE)],
+    );
+
+    assert_ok!(local.shutdown(Shutdown::Both));
+
+    // Shutting down the reading side is different on each platform. For example
+    // on Linux based systems we can still read.
+    #[cfg(any(
+        target_os = "dragonfly",
+        target_os = "freebsd",
+        target_os = "ios",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd"
+    ))]
+    {
+        let mut buf = [0; DEFAULT_BUF_SIZE];
+        let read = assert_ok!(local.read(&mut buf));
+        assert_eq!(read, 0);
+    }
+
+    let err = assert_err!(local.write(DATA2));
+    #[cfg(unix)]
+    assert_eq!(err.kind(), io::ErrorKind::BrokenPipe);
+    #[cfg(window)]
+    assert_eq!(err.kind(), io::ErrorKind::ConnectionAbroted);
+
+    // Close the connection to allow the remote to shutdown
+    drop(local);
+    assert_ok!(handle.join());
+}
+
+fn echo_remote(
+    connections: usize,
+    sync_receiver: Receiver<()>,
+) -> (thread::JoinHandle<()>, SocketAddr) {
+    let (addr_sender, addr_receiver) = channel();
+    let handle = thread::spawn(move || {
+        let dir = assert_ok!(TempDir::new("uds"));
+        let path = dir.path().join("foo");
+        let remote = assert_ok!(UnixListener::bind(path.clone()));
+        let local_address = assert_ok!(remote.local_addr());
+        assert_ok!(addr_sender.send(local_address));
+
+        for _ in 0..connections {
+            assert_ok!(sync_receiver.recv());
+            let (mut local, _) = assert_ok!(remote.accept());
+
+            // On Linux based system it will cause a connection reset
+            // error when the reading side of the peer connection is
+            // shutdown, we don't consider it an actual here.
+            let (mut read, mut written) = (0, 0);
+            let mut buf = [0; DEFAULT_BUF_SIZE];
+            loop {
+                let n = match local.try_read(&mut buf) {
+                    Ok(Some(amount)) => {
+                        read += amount;
+                        amount
+                    }
+                    Ok(None) => continue,
+                    Err(ref err) if err.kind() == io::ErrorKind::ConnectionReset => break,
+                    Err(err) => panic!("{}", err),
+                };
+                match local.try_write(&buf[..n]) {
+                    Ok(Some(amount)) => written += amount,
+                    Ok(None) => continue,
+                    Err(ref err) if err.kind() == io::ErrorKind::BrokenPipe => break,
+                    Err(err) => panic!("{:?}", err),
+                };
+            }
+            assert_eq!(read, written, "unequal reads and writes");
+        }
+    });
+    (handle, assert_ok!(addr_receiver.recv()))
+}
diff --git a/tests/util/mod.rs b/tests/util/mod.rs
index 8fe8978..b3a1f5d 100644
--- a/tests/util/mod.rs
+++ b/tests/util/mod.rs
@@ -12,6 +12,50 @@ use log::{error, warn};
 use mio::event::Event;
 use mio::{Events, Interests, Poll, Token};
 
+// TODO: replace w/ assertive
+// https://github.com/carllerche/assertive
+#[macro_export]
+macro_rules! assert_ok {
+    ($e:expr) => {
+        assert_ok!($e,)
+    };
+    ($e:expr,) => {{
+        use std::result::Result::*;
+        match $e {
+            Ok(v) => v,
+            Err(e) => panic!("assertion failed: error = {:?}", e),
+        }
+    }};
+    ($e:expr, $($arg:tt)+) => {{
+        use std::result::Result::*;
+        match $e {
+            Ok(v) => v,
+            Err(e) => panic!("assertion failed: error = {:?}: {}", e, format_args!($($arg)+)),
+        }
+    }};
+}
+
+#[macro_export]
+macro_rules! assert_err {
+    ($e:expr) => {
+        assert_err!($e,);
+    };
+    ($e:expr,) => {{
+        use std::result::Result::*;
+        match $e {
+            Ok(v) => panic!("assertion failed: Ok({:?})", v),
+            Err(e) => e,
+        }
+    }};
+    ($e:expr, $($arg:tt)+) => {{
+        use std::result::Result::*;
+        match $e {
+            Ok(v) => panic!("assertion failed: Ok({:?}): {}", v, format_args!($($arg)+)),
+            Err(e) => e,
+        }
+    }};
+}
+
 pub fn init() {
     static INIT: Once = Once::new();