| use {io, poll, Evented, Ready, Poll, PollOpt, Token}; |
| use libc; |
| use magenta; |
| use magenta::HandleBase; |
| use sys::fuchsia::{DontDrop, poll_opts_to_wait_async, sys}; |
| use std::mem; |
| use std::os::unix::io::RawFd; |
| use std::sync::{Arc, Mutex}; |
| |
| /// Properties of an `EventedFd`'s current registration |
| #[derive(Debug)] |
| pub struct EventedFdRegistration { |
| token: Token, |
| handle: DontDrop<magenta::Handle>, |
| rereg_signals: Option<(magenta::Signals, magenta::WaitAsyncOpts)>, |
| } |
| |
| impl EventedFdRegistration { |
| unsafe fn new(token: Token, |
| raw_handle: sys::mx_handle_t, |
| rereg_signals: Option<(magenta::Signals, magenta::WaitAsyncOpts)>, |
| ) -> Self |
| { |
| EventedFdRegistration { |
| token: token, |
| handle: DontDrop::new(magenta::Handle::from_raw(raw_handle)), |
| rereg_signals: rereg_signals |
| } |
| } |
| |
| pub fn rereg_signals(&self) -> Option<(magenta::Signals, magenta::WaitAsyncOpts)> { |
| self.rereg_signals |
| } |
| } |
| |
| /// An event-ed file descriptor. The file descriptor is owned by this structure. |
| #[derive(Debug)] |
| pub struct EventedFdInner { |
| /// Properties of the current registration. |
| registration: Mutex<Option<EventedFdRegistration>>, |
| |
| /// Owned file descriptor. |
| /// |
| /// `fd` is closed on `Drop`, so modifying `fd` is a memory-unsafe operation. |
| fd: RawFd, |
| |
| /// Owned `mxio_t` ponter. |
| mxio: *const sys::mxio_t, |
| } |
| |
| impl EventedFdInner { |
| pub fn rereg_for_level(&self, port: &magenta::Port) { |
| let registration_opt = self.registration.lock().unwrap(); |
| if let Some(ref registration) = *registration_opt { |
| if let Some((rereg_signals, rereg_opts)) = registration.rereg_signals { |
| let _res = |
| registration |
| .handle.inner_ref() |
| .wait_async(port, |
| registration.token.0 as u64, |
| rereg_signals, |
| rereg_opts); |
| } |
| } |
| } |
| |
| pub fn registration(&self) -> &Mutex<Option<EventedFdRegistration>> { |
| &self.registration |
| } |
| |
| pub fn mxio(&self) -> &sys::mxio_t { |
| unsafe { &*self.mxio } |
| } |
| } |
| |
| impl Drop for EventedFdInner { |
| fn drop(&mut self) { |
| unsafe { |
| sys::__mxio_release(self.mxio); |
| let _ = libc::close(self.fd); |
| } |
| } |
| } |
| |
| // `EventedInner` must be manually declared `Send + Sync` because it contains a `RawFd` and a |
| // `*const sys::mxio_t`. These are only used to make thread-safe system calls, so accessing |
| // them is entirely thread-safe. |
| // |
| // Note: one minor exception to this are the calls to `libc::close` and `__mxio_release`, which |
| // happen on `Drop`. These accesses are safe because `drop` can only be called at most once from |
| // a single thread, and after it is called no other functions can be called on the `EventedFdInner`. |
| unsafe impl Sync for EventedFdInner {} |
| unsafe impl Send for EventedFdInner {} |
| |
| #[derive(Clone, Debug)] |
| pub struct EventedFd { |
| pub inner: Arc<EventedFdInner> |
| } |
| |
| impl EventedFd { |
| pub unsafe fn new(fd: RawFd) -> Self { |
| let mxio = sys::__mxio_fd_to_io(fd); |
| assert!(mxio != ::std::ptr::null(), "FileDescriptor given to EventedFd must be valid."); |
| |
| EventedFd { |
| inner: Arc::new(EventedFdInner { |
| registration: Mutex::new(None), |
| fd: fd, |
| mxio: mxio, |
| }) |
| } |
| } |
| |
| fn handle_and_signals_for_events(&self, interest: Ready, opts: PollOpt) |
| -> (sys::mx_handle_t, magenta::Signals) |
| { |
| let epoll_events = ioevent_to_epoll(interest, opts); |
| |
| unsafe { |
| let mut raw_handle: sys::mx_handle_t = mem::uninitialized(); |
| let mut signals: sys::mx_signals_t = mem::uninitialized(); |
| sys::__mxio_wait_begin(self.inner.mxio, epoll_events, &mut raw_handle, &mut signals); |
| |
| (raw_handle, signals) |
| } |
| } |
| |
| fn register_with_lock( |
| &self, |
| registration: &mut Option<EventedFdRegistration>, |
| poll: &Poll, |
| token: Token, |
| interest: Ready, |
| opts: PollOpt) -> io::Result<()> |
| { |
| if registration.is_some() { |
| return Err(io::Error::new( |
| io::ErrorKind::AlreadyExists, |
| "Called register on an already registered file descriptor.")); |
| } |
| |
| let (raw_handle, signals) = self.handle_and_signals_for_events(interest, opts); |
| |
| let needs_rereg = opts.is_level() && !opts.is_oneshot(); |
| |
| // If we need to reregister, then each registration should be `oneshot` |
| let opts = opts | if needs_rereg { PollOpt::oneshot() } else { PollOpt::empty() }; |
| |
| let rereg_signals = if needs_rereg { |
| Some((signals, poll_opts_to_wait_async(opts))) |
| } else { |
| None |
| }; |
| |
| *registration = Some( |
| unsafe { EventedFdRegistration::new(token, raw_handle, rereg_signals) } |
| ); |
| |
| // We don't have ownership of the handle, so we can't drop it |
| let handle = DontDrop::new(unsafe { magenta::Handle::from_raw(raw_handle) }); |
| |
| let registered = poll::selector(poll) |
| .register_fd(handle.inner_ref(), self, token, signals, opts); |
| |
| if registered.is_err() { |
| *registration = None; |
| } |
| |
| registered |
| } |
| |
| fn deregister_with_lock( |
| &self, |
| registration: &mut Option<EventedFdRegistration>, |
| poll: &Poll) -> io::Result<()> |
| { |
| let old_registration = if let Some(old_reg) = registration.take() { |
| old_reg |
| } else { |
| return Err(io::Error::new( |
| io::ErrorKind::NotFound, |
| "Called rereregister on an unregistered file descriptor.")) |
| }; |
| |
| poll::selector(poll) |
| .deregister_fd(old_registration.handle.inner_ref(), old_registration.token) |
| } |
| } |
| |
| impl Evented for EventedFd { |
| fn register(&self, |
| poll: &Poll, |
| token: Token, |
| interest: Ready, |
| opts: PollOpt) -> io::Result<()> |
| { |
| self.register_with_lock( |
| &mut *self.inner.registration.lock().unwrap(), |
| poll, |
| token, |
| interest, |
| opts) |
| } |
| |
| fn reregister(&self, |
| poll: &Poll, |
| token: Token, |
| interest: Ready, |
| opts: PollOpt) -> io::Result<()> |
| { |
| // Take out the registration lock |
| let mut registration_lock = self.inner.registration.lock().unwrap(); |
| |
| // Deregister |
| self.deregister_with_lock(&mut *registration_lock, poll)?; |
| |
| self.register_with_lock( |
| &mut *registration_lock, |
| poll, |
| token, |
| interest, |
| opts) |
| } |
| |
| fn deregister(&self, poll: &Poll) -> io::Result<()> { |
| let mut registration_lock = self.inner.registration.lock().unwrap(); |
| self.deregister_with_lock(&mut *registration_lock, poll) |
| } |
| } |
| |
| fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> u32 { |
| use event_imp::ready_from_usize; |
| const HUP: usize = 0b01000; |
| |
| let mut kind = 0; |
| |
| if interest.is_readable() { |
| kind |= libc::EPOLLIN; |
| } |
| |
| if interest.is_writable() { |
| kind |= libc::EPOLLOUT; |
| } |
| |
| if interest.contains(ready_from_usize(HUP)) { |
| kind |= libc::EPOLLRDHUP; |
| } |
| |
| if opts.is_edge() { |
| kind |= libc::EPOLLET; |
| } |
| |
| if opts.is_oneshot() { |
| kind |= libc::EPOLLONESHOT; |
| } |
| |
| if opts.is_level() { |
| kind &= !libc::EPOLLET; |
| } |
| |
| kind as u32 |
| } |