<commit_before>// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::io;
use std::ops::Deref;
use std::os::unix::io::{AsRawFd, RawFd};

use libc::{
    epoll_create1, epoll_ctl, epoll_event, epoll_wait, EPOLLERR, EPOLLET, EPOLLEXCLUSIVE, EPOLLHUP,
    EPOLLIN, EPOLLONESHOT, EPOLLOUT, EPOLLPRI, EPOLLRDHUP, EPOLLWAKEUP, EPOLL_CLOEXEC,
    EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD,
};

use utils::syscall::SyscallReturnCode;

/// Wrapper over EPOLL_CTL_* operations that can be performed on a file descriptor.
#[repr(i32)]
pub enum ControlOperation {
    /// Add a file descriptor to the interest list.
    Add = EPOLL_CTL_ADD,
    /// Change the settings associated with a file descriptor that is
    /// already in the interest list.
    Modify = EPOLL_CTL_MOD,
    /// Remove a file descriptor from the interest list.
    Delete = EPOLL_CTL_DEL,
}

bitflags! {
    /// The type of events we can monitor a file descriptor for.
    pub struct EventSet: u32 {
        /// The associated file descriptor is available for read operations.
        const IN = EPOLLIN as u32;
        /// The associated file descriptor is available for write operations.
        const OUT = EPOLLOUT as u32;
        /// Error condition happened on the associated file descriptor.
        const ERROR = EPOLLERR as u32;
        /// This can be used to detect peer shutdown when using Edge Triggered monitoring.
        const READ_HANG_UP = EPOLLRDHUP as u32;
        /// Sets the Edge Triggered behavior for the associated file descriptor.
        /// The default behavior is Level Triggered.
        const EDGE_TRIGGERED = EPOLLET as u32;
        /// Hang up happened on the associated file descriptor. Note that `epoll_wait`
        /// will always wait for this event and it is not necessary to set it in events.
        const HANG_UP = EPOLLHUP as u32;
        /// There is an exceptional condition on that file descriptor. It is mostly used to
        /// set high priority for some data.
        const PRIORITY = EPOLLPRI as u32;
        /// The event is considered as being "processed" from the time when it is returned
        /// by a call to `epoll_wait` until the next call to `epoll_wait` on the same
        /// epoll file descriptor, the closure of that file descriptor, the removal of the
        /// event file descriptor via EPOLL_CTL_DEL, or the clearing of EPOLLWAKEUP
        /// for the event file descriptor via EPOLL_CTL_MOD.
        const WAKE_UP = EPOLLWAKEUP as u32;
        /// Sets the one-shot behavior for the associated file descriptor.
        const ONE_SHOT = EPOLLONESHOT as u32;
        /// Sets an exclusive wake up mode for the epoll file descriptor that is being
        /// attached to the associated file descriptor.
        /// When a wake up event occurs and multiple epoll file descriptors are attached to
        /// the same target file using this mode, one or more of the epoll file descriptors
        /// will receive an event with `epoll_wait`. The default here is for all those file
        /// descriptors to receive an event.
        const EXCLUSIVE = EPOLLEXCLUSIVE as u32;
    }
}

/// Wrapper over 'libc::epoll_event'.
///
// We are using `transparent` here to be super sure that this struct and its fields
// have the same alignment as those from the `epoll_event` struct from C.
#[repr(transparent)]
#[derive(Clone)]
pub struct EpollEvent(epoll_event);

impl Deref for EpollEvent {
    type Target = epoll_event;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Default for EpollEvent {
    fn default() -> Self {
        EpollEvent(epoll_event {
            events: 0u32,
            u64: 0u64,
        })
    }
}

impl EpollEvent {
    /// Create a new epoll_event instance with the following fields: `events`, which contains
    /// an event mask and `data` which represents a user data variable. `data` field can be
    /// a fd on which we want to monitor the events specified by `events`.
    pub fn new(events: EventSet, data: u64) -> Self {
        EpollEvent(epoll_event {
            events: events.bits(),
            u64: data,
        })
    }

    /// Returns the `events` from `libc::epoll_event`.
    pub fn events(&self) -> u32 {
        self.events
    }

    /// Returns the `EventSet` corresponding to `epoll_event.events`.
    ///
    /// # Panics
    ///
    /// Panics if `libc::epoll_event` contains invalid events.
    pub fn event_set(&self) -> EventSet {
        // This unwrap is safe because `epoll_events` can only be user created or
        // initialized by the kernel.We trust the kernel to only send us valid
        // events. The user can only initialize `epoll_events` using valid events.
        EventSet::from_bits(self.events()).unwrap()
    }

    /// Returns the `data` from the `libc::epoll_event`.
    pub fn data(&self) -> u64 {
        self.u64
    }

    /// Converts the `libc::epoll_event` data to a RawFd.
    ///
    /// This conversion is lossy when the data does not correspond to a RawFd
    /// (data does not fit in a i32).
    pub fn fd(&self) -> RawFd {
        self.u64 as i32
    }
}

/// Wrapper over epoll functionality.
#[derive(Debug)]
pub struct Epoll {
    epoll_fd: RawFd,
}

impl Epoll {
    /// Create a new epoll file descriptor.
    pub fn new() -> io::Result<Self> {
        let epoll_fd = SyscallReturnCode(unsafe { epoll_create1(EPOLL_CLOEXEC) }).into_result()?;
        Ok(Epoll { epoll_fd })
    }

    /// Wrapper for `libc::epoll_ctl`.
    ///
    /// This can be used for adding, modifying or removing a file descriptor in the
    /// interest list of the epoll instance.
    ///
    /// # Arguments
    ///
    /// * `operation` refers to the action to be performed on the file descriptor.
    /// * `fd` is the file descriptor on which we want to perform `operation`.
    /// * `event` refers to the `epoll_event` instance that is linked to `fd`.
    pub fn ctl(
        &self,
        operation: ControlOperation,
        fd: RawFd,
        event: &EpollEvent,
    ) -> io::Result<()> {
        // Safe because we give a valid epoll file descriptor, a valid file descriptor to watch,
        // as well as a valid epoll_event structure. We also check the return value.
        SyscallReturnCode(unsafe {
            epoll_ctl(
                self.epoll_fd,
                operation as i32,
                fd,
                event as *const EpollEvent as *mut epoll_event,
            )
        })
        .into_empty_result()
    }

    /// Wrapper for `libc::epoll_wait`.
    /// Returns the number of file descriptors in the interest list that became ready
    /// for I/O or `errno` if an error occurred.
    ///
    /// # Arguments
    ///
    /// * `max_events` is the maximum number of events that we want to be returned in
    /// `events` buffer.
    /// * `timeout` specifies for how long the `epoll_wait` system call will block
    /// (measured in milliseconds).
    /// * `events` points to a memory area that will be used for storing the events
    /// returned by `epoll_wait()` call.
    pub fn wait(
        &self,
        max_events: usize,
        timeout: i32,
        events: &mut [EpollEvent],
    ) -> io::Result<usize> {
        // Safe because we give a valid epoll file descriptor and an array of epoll_event structures
        // that will be modified by the kernel to indicate information about the subset of file
        // descriptors in the interest list. We also check the return value.
        let events_count = SyscallReturnCode(unsafe {
            epoll_wait(
                self.epoll_fd,
                events.as_mut_ptr() as *mut epoll_event,
                max_events as i32,
                timeout,
            )
        })
        .into_result()? as usize;

        Ok(events_count)
    }
}

impl AsRawFd for Epoll {
    fn as_raw_fd(&self) -> RawFd {
        self.epoll_fd
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use utils::eventfd::EventFd;

    #[test]
    fn test_event_ops() {
        let mut event = EpollEvent::default();
        assert_eq!(event.events(), 0);
        assert_eq!(event.data(), 0);

        event = EpollEvent::new(EventSet::IN, 2);
        assert_eq!(event.events(), 1);
        assert_eq!(event.event_set(), EventSet::IN);

        assert_eq!(event.data(), 2);
        assert_eq!(event.fd(), 2);
    }

    #[test]
    fn test_epoll() {
        const DEFAULT__TIMEOUT: i32 = 250;
        const EVENT_BUFFER_SIZE: usize = 128;
        const MAX_EVENTS: usize = 10;

        let epoll = Epoll::new().unwrap();
        assert_eq!(epoll.epoll_fd, epoll.as_raw_fd());

        // Let's test different scenarios for `epoll_ctl()` and `epoll_wait()` functionality.

        let event_fd_1 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        // For EPOLLOUT to be available it is enough only to be possible to write a value of
        // at least 1 to the eventfd counter without blocking.
        // If we write a value greater than 0 to this counter, the fd will be available for
        // EPOLLIN events too.
        event_fd_1.write(1).unwrap();

        let mut event_1 =
            EpollEvent::new(EventSet::IN | EventSet::OUT, event_fd_1.as_raw_fd() as u64);

        // For EPOLL_CTL_ADD behavior we will try to add some fds with different event masks into
        // the interest list of epoll instance.
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_1.as_raw_fd() as i32,
                &event_1
            )
            .is_ok());

        // We can't add twice the same fd to epoll interest list.
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_1.as_raw_fd() as i32,
                &event_1
            )
            .is_err());

        let event_fd_2 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        event_fd_2.write(1).unwrap();
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_2.as_raw_fd() as i32,
                // For this fd, we want an Event instance that has `data` field set to other
                // value than the value of the fd and `events` without EPOLLIN type set.
                &EpollEvent::new(EventSet::OUT, 10)
            )
            .is_ok());

        // For the following eventfd we won't write anything to its counter, so we expect EPOLLIN
        // event to not be available for this fd, even if we say that we want to monitor this type
        // of event via EPOLL_CTL_ADD operation.
        let event_fd_3 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        let event_3 = EpollEvent::new(EventSet::OUT | EventSet::IN, event_fd_3.as_raw_fd() as u64);
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_3.as_raw_fd() as i32,
                &event_3
            )
            .is_ok());

        // Let's check `epoll_wait()` behavior for our epoll instance.
        let mut ready_events = vec![EpollEvent::default(); EVENT_BUFFER_SIZE];
        let mut ev_count = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();

        // We expect to have 3 fds in the ready list of epoll instance.
        assert_eq!(ev_count, 3);

        // Let's check also the Event values that are now returned in the ready list.
        assert_eq!(ready_events[0].data(), event_fd_1.as_raw_fd() as u64);
        // For this fd, `data` field was populated with random data instead of the
        // corresponding fd value.
        assert_eq!(ready_events[1].data(), 10);
        assert_eq!(ready_events[2].data(), event_fd_3.as_raw_fd() as u64);

        // EPOLLIN and EPOLLOUT should be available for this fd.
        assert_eq!(
            ready_events[0].events(),
            (EventSet::IN | EventSet::OUT).bits()
        );
        // Only EPOLLOUT is expected because we didn't want to monitor EPOLLIN on this fd.
        assert_eq!(ready_events[1].events(), EventSet::OUT.bits());
        // Only EPOLLOUT too because eventfd counter value is 0 (we didn't write a value
        // greater than 0 to it).
        assert_eq!(ready_events[2].events(), EventSet::OUT.bits());

        // Now we're gonna modify the Event instance for a fd to test EPOLL_CTL_MOD
        // behavior.
        // We create here a new Event with some events, other than those previously set,
        // that we want to monitor this time on event_fd_1.
        event_1 = EpollEvent::new(EventSet::OUT, 20);
        assert!(epoll
            .ctl(
                ControlOperation::Modify,
                event_fd_1.as_raw_fd() as i32,
                &event_1
            )
            .is_ok());

        let event_fd_4 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        // Can't modify a fd that wasn't added to epoll interest list.
        assert!(epoll
            .ctl(
                ControlOperation::Modify,
                event_fd_4.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_err());

        let _ = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();

        // Let's check that Event fields were indeed changed for the `event_fd_1` fd.
        assert_eq!(ready_events[0].data(), 20);
        // EPOLLOUT is now available for this fd as we've intended with EPOLL_CTL_MOD operation.
        assert_eq!(ready_events[0].events(), EventSet::OUT.bits());

        // Now let's set for a fd to not have any events monitored.
        assert!(epoll
            .ctl(
                ControlOperation::Modify,
                event_fd_1.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_ok());

        // In this particular case we expect to remain only with 2 fds in the ready list.
        ev_count = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();
        assert_eq!(ev_count, 2);

        // Let's also delete a fd from the interest list.
        assert!(epoll
            .ctl(
                ControlOperation::Delete,
                event_fd_2.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_ok());

        // We expect to have only one fd remained in the ready list (event_fd_3).
        ev_count = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();

        assert_eq!(ev_count, 1);
        assert_eq!(ready_events[0].data(), event_fd_3.as_raw_fd() as u64);
        assert_eq!(ready_events[0].events(), EventSet::OUT.bits());

        // If we try to remove a fd from epoll interest list that wasn't added before it will fail.
        assert!(epoll
            .ctl(
                ControlOperation::Delete,
                event_fd_4.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_err());
    }
}
<commit_msg>implement Drop for Epoll<commit_after>// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::io;
use std::ops::Deref;
use std::os::unix::io::{AsRawFd, RawFd};

use libc::{
    epoll_create1, epoll_ctl, epoll_event, epoll_wait, EPOLLERR, EPOLLET, EPOLLEXCLUSIVE, EPOLLHUP,
    EPOLLIN, EPOLLONESHOT, EPOLLOUT, EPOLLPRI, EPOLLRDHUP, EPOLLWAKEUP, EPOLL_CLOEXEC,
    EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD,
};

use utils::syscall::SyscallReturnCode;

/// Wrapper over EPOLL_CTL_* operations that can be performed on a file descriptor.
#[repr(i32)]
pub enum ControlOperation {
    /// Add a file descriptor to the interest list.
    Add = EPOLL_CTL_ADD,
    /// Change the settings associated with a file descriptor that is
    /// already in the interest list.
    Modify = EPOLL_CTL_MOD,
    /// Remove a file descriptor from the interest list.
    Delete = EPOLL_CTL_DEL,
}

bitflags! {
    /// The type of events we can monitor a file descriptor for.
    pub struct EventSet: u32 {
        /// The associated file descriptor is available for read operations.
        const IN = EPOLLIN as u32;
        /// The associated file descriptor is available for write operations.
        const OUT = EPOLLOUT as u32;
        /// Error condition happened on the associated file descriptor.
        const ERROR = EPOLLERR as u32;
        /// This can be used to detect peer shutdown when using Edge Triggered monitoring.
        const READ_HANG_UP = EPOLLRDHUP as u32;
        /// Sets the Edge Triggered behavior for the associated file descriptor.
        /// The default behavior is Level Triggered.
        const EDGE_TRIGGERED = EPOLLET as u32;
        /// Hang up happened on the associated file descriptor. Note that `epoll_wait`
        /// will always wait for this event and it is not necessary to set it in events.
        const HANG_UP = EPOLLHUP as u32;
        /// There is an exceptional condition on that file descriptor. It is mostly used to
        /// set high priority for some data.
        const PRIORITY = EPOLLPRI as u32;
        /// The event is considered as being "processed" from the time when it is returned
        /// by a call to `epoll_wait` until the next call to `epoll_wait` on the same
        /// epoll file descriptor, the closure of that file descriptor, the removal of the
        /// event file descriptor via EPOLL_CTL_DEL, or the clearing of EPOLLWAKEUP
        /// for the event file descriptor via EPOLL_CTL_MOD.
        const WAKE_UP = EPOLLWAKEUP as u32;
        /// Sets the one-shot behavior for the associated file descriptor.
        const ONE_SHOT = EPOLLONESHOT as u32;
        /// Sets an exclusive wake up mode for the epoll file descriptor that is being
        /// attached to the associated file descriptor.
        /// When a wake up event occurs and multiple epoll file descriptors are attached to
        /// the same target file using this mode, one or more of the epoll file descriptors
        /// will receive an event with `epoll_wait`. The default here is for all those file
        /// descriptors to receive an event.
        const EXCLUSIVE = EPOLLEXCLUSIVE as u32;
    }
}

/// Wrapper over 'libc::epoll_event'.
///
// We are using `transparent` here to be super sure that this struct and its fields
// have the same alignment as those from the `epoll_event` struct from C.
#[repr(transparent)]
#[derive(Clone)]
pub struct EpollEvent(epoll_event);

impl Deref for EpollEvent {
    type Target = epoll_event;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Default for EpollEvent {
    fn default() -> Self {
        EpollEvent(epoll_event {
            events: 0u32,
            u64: 0u64,
        })
    }
}

impl EpollEvent {
    /// Create a new epoll_event instance with the following fields: `events`, which contains
    /// an event mask and `data` which represents a user data variable. `data` field can be
    /// a fd on which we want to monitor the events specified by `events`.
    pub fn new(events: EventSet, data: u64) -> Self {
        EpollEvent(epoll_event {
            events: events.bits(),
            u64: data,
        })
    }

    /// Returns the `events` from `libc::epoll_event`.
    pub fn events(&self) -> u32 {
        self.events
    }

    /// Returns the `EventSet` corresponding to `epoll_event.events`.
    ///
    /// # Panics
    ///
    /// Panics if `libc::epoll_event` contains invalid events.
    pub fn event_set(&self) -> EventSet {
        // This unwrap is safe because `epoll_events` can only be user created or
        // initialized by the kernel.We trust the kernel to only send us valid
        // events. The user can only initialize `epoll_events` using valid events.
        EventSet::from_bits(self.events()).unwrap()
    }

    /// Returns the `data` from the `libc::epoll_event`.
    pub fn data(&self) -> u64 {
        self.u64
    }

    /// Converts the `libc::epoll_event` data to a RawFd.
    ///
    /// This conversion is lossy when the data does not correspond to a RawFd
    /// (data does not fit in a i32).
    pub fn fd(&self) -> RawFd {
        self.u64 as i32
    }
}

/// Wrapper over epoll functionality.
#[derive(Debug)]
pub struct Epoll {
    epoll_fd: RawFd,
}

impl Epoll {
    /// Create a new epoll file descriptor.
    pub fn new() -> io::Result<Self> {
        let epoll_fd = SyscallReturnCode(unsafe { epoll_create1(EPOLL_CLOEXEC) }).into_result()?;
        Ok(Epoll { epoll_fd })
    }

    /// Wrapper for `libc::epoll_ctl`.
    ///
    /// This can be used for adding, modifying or removing a file descriptor in the
    /// interest list of the epoll instance.
    ///
    /// # Arguments
    ///
    /// * `operation` refers to the action to be performed on the file descriptor.
    /// * `fd` is the file descriptor on which we want to perform `operation`.
    /// * `event` refers to the `epoll_event` instance that is linked to `fd`.
    pub fn ctl(
        &self,
        operation: ControlOperation,
        fd: RawFd,
        event: &EpollEvent,
    ) -> io::Result<()> {
        // Safe because we give a valid epoll file descriptor, a valid file descriptor to watch,
        // as well as a valid epoll_event structure. We also check the return value.
        SyscallReturnCode(unsafe {
            epoll_ctl(
                self.epoll_fd,
                operation as i32,
                fd,
                event as *const EpollEvent as *mut epoll_event,
            )
        })
        .into_empty_result()
    }

    /// Wrapper for `libc::epoll_wait`.
    /// Returns the number of file descriptors in the interest list that became ready
    /// for I/O or `errno` if an error occurred.
    ///
    /// # Arguments
    ///
    /// * `max_events` is the maximum number of events that we want to be returned in
    /// `events` buffer.
    /// * `timeout` specifies for how long the `epoll_wait` system call will block
    /// (measured in milliseconds).
    /// * `events` points to a memory area that will be used for storing the events
    /// returned by `epoll_wait()` call.
    pub fn wait(
        &self,
        max_events: usize,
        timeout: i32,
        events: &mut [EpollEvent],
    ) -> io::Result<usize> {
        // Safe because we give a valid epoll file descriptor and an array of epoll_event structures
        // that will be modified by the kernel to indicate information about the subset of file
        // descriptors in the interest list. We also check the return value.
        let events_count = SyscallReturnCode(unsafe {
            epoll_wait(
                self.epoll_fd,
                events.as_mut_ptr() as *mut epoll_event,
                max_events as i32,
                timeout,
            )
        })
        .into_result()? as usize;

        Ok(events_count)
    }
}

impl AsRawFd for Epoll {
    fn as_raw_fd(&self) -> RawFd {
        self.epoll_fd
    }
}

impl std::ops::Drop for Epoll {
    fn drop(&mut self) {
        // Safe because this fd is opened with `epoll_create` and we trust
        // the kernel to give us a valid fd.
        unsafe {
            libc::close(self.epoll_fd);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use utils::eventfd::EventFd;

    #[test]
    fn test_event_ops() {
        let mut event = EpollEvent::default();
        assert_eq!(event.events(), 0);
        assert_eq!(event.data(), 0);

        event = EpollEvent::new(EventSet::IN, 2);
        assert_eq!(event.events(), 1);
        assert_eq!(event.event_set(), EventSet::IN);

        assert_eq!(event.data(), 2);
        assert_eq!(event.fd(), 2);
    }

    #[test]
    fn test_epoll() {
        const DEFAULT__TIMEOUT: i32 = 250;
        const EVENT_BUFFER_SIZE: usize = 128;
        const MAX_EVENTS: usize = 10;

        let epoll = Epoll::new().unwrap();
        assert_eq!(epoll.epoll_fd, epoll.as_raw_fd());

        // Let's test different scenarios for `epoll_ctl()` and `epoll_wait()` functionality.

        let event_fd_1 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        // For EPOLLOUT to be available it is enough only to be possible to write a value of
        // at least 1 to the eventfd counter without blocking.
        // If we write a value greater than 0 to this counter, the fd will be available for
        // EPOLLIN events too.
        event_fd_1.write(1).unwrap();

        let mut event_1 =
            EpollEvent::new(EventSet::IN | EventSet::OUT, event_fd_1.as_raw_fd() as u64);

        // For EPOLL_CTL_ADD behavior we will try to add some fds with different event masks into
        // the interest list of epoll instance.
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_1.as_raw_fd() as i32,
                &event_1
            )
            .is_ok());

        // We can't add twice the same fd to epoll interest list.
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_1.as_raw_fd() as i32,
                &event_1
            )
            .is_err());

        let event_fd_2 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        event_fd_2.write(1).unwrap();
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_2.as_raw_fd() as i32,
                // For this fd, we want an Event instance that has `data` field set to other
                // value than the value of the fd and `events` without EPOLLIN type set.
                &EpollEvent::new(EventSet::OUT, 10)
            )
            .is_ok());

        // For the following eventfd we won't write anything to its counter, so we expect EPOLLIN
        // event to not be available for this fd, even if we say that we want to monitor this type
        // of event via EPOLL_CTL_ADD operation.
        let event_fd_3 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        let event_3 = EpollEvent::new(EventSet::OUT | EventSet::IN, event_fd_3.as_raw_fd() as u64);
        assert!(epoll
            .ctl(
                ControlOperation::Add,
                event_fd_3.as_raw_fd() as i32,
                &event_3
            )
            .is_ok());

        // Let's check `epoll_wait()` behavior for our epoll instance.
        let mut ready_events = vec![EpollEvent::default(); EVENT_BUFFER_SIZE];
        let mut ev_count = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();

        // We expect to have 3 fds in the ready list of epoll instance.
        assert_eq!(ev_count, 3);

        // Let's check also the Event values that are now returned in the ready list.
        assert_eq!(ready_events[0].data(), event_fd_1.as_raw_fd() as u64);
        // For this fd, `data` field was populated with random data instead of the
        // corresponding fd value.
        assert_eq!(ready_events[1].data(), 10);
        assert_eq!(ready_events[2].data(), event_fd_3.as_raw_fd() as u64);

        // EPOLLIN and EPOLLOUT should be available for this fd.
        assert_eq!(
            ready_events[0].events(),
            (EventSet::IN | EventSet::OUT).bits()
        );
        // Only EPOLLOUT is expected because we didn't want to monitor EPOLLIN on this fd.
        assert_eq!(ready_events[1].events(), EventSet::OUT.bits());
        // Only EPOLLOUT too because eventfd counter value is 0 (we didn't write a value
        // greater than 0 to it).
        assert_eq!(ready_events[2].events(), EventSet::OUT.bits());

        // Now we're gonna modify the Event instance for a fd to test EPOLL_CTL_MOD
        // behavior.
        // We create here a new Event with some events, other than those previously set,
        // that we want to monitor this time on event_fd_1.
        event_1 = EpollEvent::new(EventSet::OUT, 20);
        assert!(epoll
            .ctl(
                ControlOperation::Modify,
                event_fd_1.as_raw_fd() as i32,
                &event_1
            )
            .is_ok());

        let event_fd_4 = EventFd::new(libc::EFD_NONBLOCK).unwrap();
        // Can't modify a fd that wasn't added to epoll interest list.
        assert!(epoll
            .ctl(
                ControlOperation::Modify,
                event_fd_4.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_err());

        let _ = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();

        // Let's check that Event fields were indeed changed for the `event_fd_1` fd.
        assert_eq!(ready_events[0].data(), 20);
        // EPOLLOUT is now available for this fd as we've intended with EPOLL_CTL_MOD operation.
        assert_eq!(ready_events[0].events(), EventSet::OUT.bits());

        // Now let's set for a fd to not have any events monitored.
        assert!(epoll
            .ctl(
                ControlOperation::Modify,
                event_fd_1.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_ok());

        // In this particular case we expect to remain only with 2 fds in the ready list.
        ev_count = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();
        assert_eq!(ev_count, 2);

        // Let's also delete a fd from the interest list.
        assert!(epoll
            .ctl(
                ControlOperation::Delete,
                event_fd_2.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_ok());

        // We expect to have only one fd remained in the ready list (event_fd_3).
        ev_count = epoll
            .wait(MAX_EVENTS, DEFAULT__TIMEOUT, &mut ready_events[..])
            .unwrap();

        assert_eq!(ev_count, 1);
        assert_eq!(ready_events[0].data(), event_fd_3.as_raw_fd() as u64);
        assert_eq!(ready_events[0].events(), EventSet::OUT.bits());

        // If we try to remove a fd from epoll interest list that wasn't added before it will fail.
        assert!(epoll
            .ctl(
                ControlOperation::Delete,
                event_fd_4.as_raw_fd() as i32,
                &EpollEvent::default()
            )
            .is_err());
    }
}
<|endoftext|>


<commit_before>//! Main module to handle the layout.
//! This is where the i3-specific code is.

pub mod layout {
    use super::super::container::{Container, Handle, ContainerType};
    use super::super::tree::Node;
    use super::super::super::rustwlc::handle::{WlcView, WlcOutput};

    use std::sync::Mutex;

    use std::ptr;

    struct RootNode {
        root: Node,
        active_container: *const Node,
    }

    impl RootNode {
        fn get_active_container(&self) -> Option<&Node> {
            if self.active_container.is_null() {
                None
            } else {
                unsafe {
                    Some(&*self.active_container)
                }
            }
        }
    }

    unsafe impl Send for RootNode {}

    lazy_static! {
        static ref ROOT: Mutex<RootNode> = {
            Mutex::new(RootNode{
                root: Node::new(Container::new_root()),
                active_container: ptr::null(),
            })
        };
    }

    pub fn add_output(wlc_output: WlcOutput) {
        let mut root = ROOT.lock().unwrap();
        let output = Container::new_output(wlc_output);
        root.root.new_child(output);
        drop(root);
        add_workspace(&"1");
        switch_workspace(&"1");
    }

    pub fn add_workspace(name: &str) {
        trace!("Adding new workspace to root");
        let mut root = ROOT.lock().unwrap();
        let workspace = Container::new_workspace(name.to_string());
        // NOTE handle multiple outputs
        root.root.get_children_mut()[0].new_child(workspace);
    }

    pub fn add_view(wlc_view: WlcView) {
        let mut root = ROOT.lock().unwrap();
        if let Some(current_workspace) = get_current_workspace(&root) {
            trace!("Adding view {:?} to {:?}", wlc_view, current_workspace);
            current_workspace.new_child(Container::new_view(wlc_view));
        }
    }

    pub fn remove_view(wlc_view: &WlcView) {
        let mut root = ROOT.lock().unwrap();
        if let Some(view) = root.root.find_view_by_handle(&wlc_view) {
            let parent = view.get_parent().unwrap();
            parent.remove_child(view);
        }
    }

    pub fn switch_workspace(name: &str) {
        trace!("Switching to workspace {}", name);
        let mut root = ROOT.lock().unwrap();
        if let Some(old_workspace) = get_current_workspace(&root) {
            // Make all the views in the original workspace to be invisible
            for view in old_workspace.get_children_mut() {
                trace!("Setting {:?} invisible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(0),
                    _ => {},
                }
            }
        }
        let current_workspace: *const Node;
        {
            let new_current_workspace: &mut Node;
            if let Some(_) = get_workspace_by_name(&root, name) {
                trace!("Found workspace {}", name);
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            } else {
                drop(root);
                add_workspace(name);
                root = ROOT.lock().unwrap();
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            }
            for view in new_current_workspace.get_children_mut() {
                trace!("Setting {:?} visible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(1),
                    _ => {},
                }
            }
            current_workspace = new_current_workspace as *const Node;
        }
        root.active_container = current_workspace;
    }

    fn get_focused_workspace<'a>(root: &'a RootNode) -> Option<&'a Node> {
        for output in root.root.get_children() {
            if output.get_val().is_focused() {
                for workspace in output.get_children() {
                    if workspace.get_val().is_focused() {
                        return Some(workspace);
                    }
                }
            }
        }
        None

    }

    fn get_current_workspace<'a>(root: &'a RootNode) -> Option<&'a mut Node> {
        if let Some(container) = root.get_active_container() {
            //if let Some(child) = container.get_ancestor_of_type(ContainerType::Workspace) {
                //return child.get_children()[0].get_parent()

            //}
            // NOTE hack here, remove commented code above to make this work properly
            let parent = container.get_parent().unwrap();
            for child in parent.get_children_mut() {
                if child == container {
                    return Some(child);
                }
            }
        }
        return None
    }

    fn get_workspace_by_name<'a, 'b>(root: &'a RootNode, name: &'b str) -> Option<&'a Node> {
        for child in root.root.get_children()[0].get_children() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    fn get_workspace_by_name_mut<'a, 'b>(root: &'a mut RootNode, name: &'b str) -> Option<&'a mut Node> {
        for child in root.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }
 }

<commit_msg>Focus should now change when switching workspaces, also the screen is now properly updated when that occurs<commit_after>//! Main module to handle the layout.
//! This is where the i3-specific code is.

pub mod layout {
    use super::super::container::{Container, Handle, ContainerType};
    use super::super::tree::Node;
    use super::super::super::rustwlc::handle::{WlcView, WlcOutput};

    use std::sync::Mutex;

    use std::ptr;

    struct RootNode {
        root: Node,
        active_container: *const Node,
    }

    impl RootNode {
        fn get_active_container(&self) -> Option<&Node> {
            if self.active_container.is_null() {
                None
            } else {
                unsafe {
                    Some(&*self.active_container)
                }
            }
        }
    }

    unsafe impl Send for RootNode {}

    lazy_static! {
        static ref ROOT: Mutex<RootNode> = {
            Mutex::new(RootNode{
                root: Node::new(Container::new_root()),
                active_container: ptr::null(),
            })
        };
    }

    pub fn add_output(wlc_output: WlcOutput) {
        let mut root = ROOT.lock().unwrap();
        let output = Container::new_output(wlc_output);
        root.root.new_child(output);
        drop(root);
        add_workspace(&"1");
        switch_workspace(&"1");
    }

    pub fn add_workspace(name: &str) {
        trace!("Adding new workspace to root");
        let mut root = ROOT.lock().unwrap();
        let workspace = Container::new_workspace(name.to_string());
        // NOTE handle multiple outputs
        root.root.get_children_mut()[0].new_child(workspace);
    }

    pub fn add_view(wlc_view: WlcView) {
        let mut root = ROOT.lock().unwrap();
        if let Some(current_workspace) = get_current_workspace(&root) {
            trace!("Adding view {:?} to {:?}", wlc_view, current_workspace);
            current_workspace.new_child(Container::new_view(wlc_view));
        }
    }

    pub fn remove_view(wlc_view: &WlcView) {
        let mut root = ROOT.lock().unwrap();
        if let Some(view) = root.root.find_view_by_handle(&wlc_view) {
            let parent = view.get_parent().unwrap();
            parent.remove_child(view);
        }
    }

    pub fn switch_workspace(name: &str) {
        trace!("Switching to workspace {}", name);
        let mut root = ROOT.lock().unwrap();
        if let Some(old_workspace) = get_current_workspace(&root) {
            // Make all the views in the original workspace to be invisible
            for view in old_workspace.get_children_mut() {
                trace!("Setting {:?} invisible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(0),
                    _ => {},
                }
            }
        }
        let current_workspace: *const Node;
        {
            let new_current_workspace: &mut Node;
            if let Some(_) = get_workspace_by_name(&root, name) {
                trace!("Found workspace {}", name);
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            } else {
                drop(root);
                add_workspace(name);
                root = ROOT.lock().unwrap();
                new_current_workspace = get_workspace_by_name_mut(&mut root, name).unwrap();
            }
            for view in new_current_workspace.get_children_mut() {
                trace!("Setting {:?} visible", view);
                match view.get_val().get_handle().unwrap() {
                    Handle::View(view) => view.set_mask(1),
                    _ => {},
                }
            }
            // Set the first view to be focused, so that the view is updated to this new workspace
            if new_current_workspace.get_children().len() > 0 {
                trace!("Focusing view");
                match new_current_workspace.get_children_mut()[0].get_val().get_handle().unwrap() {
                    Handle::View(view) => view.focus(),
                    _ => {},
                }
            } else {
                WlcView::root().focus();
            }
            current_workspace = new_current_workspace as *const Node;
        }
        root.active_container = current_workspace;
    }

    fn get_focused_workspace<'a>(root: &'a RootNode) -> Option<&'a Node> {
        for output in root.root.get_children() {
            if output.get_val().is_focused() {
                for workspace in output.get_children() {
                    if workspace.get_val().is_focused() {
                        return Some(workspace);
                    }
                }
            }
        }
        None

    }

    fn get_current_workspace<'a>(root: &'a RootNode) -> Option<&'a mut Node> {
        if let Some(container) = root.get_active_container() {
            //if let Some(child) = container.get_ancestor_of_type(ContainerType::Workspace) {
                //return child.get_children()[0].get_parent()

            //}
            // NOTE hack here, remove commented code above to make this work properly
            let parent = container.get_parent().unwrap();
            for child in parent.get_children_mut() {
                if child == container {
                    return Some(child);
                }
            }
        }
        return None
    }

    fn get_workspace_by_name<'a, 'b>(root: &'a RootNode, name: &'b str) -> Option<&'a Node> {
        for child in root.root.get_children()[0].get_children() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }

    fn get_workspace_by_name_mut<'a, 'b>(root: &'a mut RootNode, name: &'b str) -> Option<&'a mut Node> {
        for child in root.root.get_children_mut()[0].get_children_mut() {
            if child.get_val().get_name().unwrap() != name {
                continue
            }
            return Some(child);
        }
        return None
    }
 }

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map;
use std::hash::Hash;
use std::borrow::Borrow;

pub struct LazyHashMap<K, V> {
    map: Option<HashMap<K, V>>,
}

impl<K, V> LazyHashMap<K, V>
    where K: ::std::hash::Hash + Eq
{
    pub fn new() -> LazyHashMap<K, V> {
        LazyHashMap { map: None }
    }

    pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
        where K: Borrow<Q>,
              Q: Hash + Eq
    {
        self.map.as_ref().map(|m| m.contains_key(key)).unwrap_or(false)
    }

    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
        where K: Borrow<Q>,
              Q: Hash + Eq
    {
        self.map.as_ref().and_then(|m| m.get(key))
    }

    pub fn insert(&mut self, key: K, val: V) -> Option<V> {
        self.map = match self.map.take() {
            Some(m) => Some(m),
            None => Some(HashMap::new()),
        };

        self.map.as_mut().and_then(|m| {
            m.insert(key, val)
        })
    }

    pub fn iter(&self) -> Iter<K, V> {
        Iter(self.map.as_ref().map(|m| m.iter()))
    }
}

pub struct Iter<'a, K: 'a, V: 'a>(Option<hash_map::Iter<'a, K, V>>);

impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> {
    type Item = (&'a K, &'a V);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.as_mut().and_then(|i| i.next())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0)))
    }
}
<commit_msg>[Clippy] Use `map_or` instead of `map().unwrap_or`<commit_after>use std::collections::HashMap;
use std::collections::hash_map;
use std::hash::Hash;
use std::borrow::Borrow;

pub struct LazyHashMap<K, V> {
    map: Option<HashMap<K, V>>,
}

impl<K, V> LazyHashMap<K, V>
    where K: ::std::hash::Hash + Eq
{
    pub fn new() -> LazyHashMap<K, V> {
        LazyHashMap { map: None }
    }

    pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
        where K: Borrow<Q>,
              Q: Hash + Eq
    {
        self.map.as_ref().map_or(false, |m| m.contains_key(key))
    }

    pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
        where K: Borrow<Q>,
              Q: Hash + Eq
    {
        self.map.as_ref().and_then(|m| m.get(key))
    }

    pub fn insert(&mut self, key: K, val: V) -> Option<V> {
        self.map = match self.map.take() {
            Some(m) => Some(m),
            None => Some(HashMap::new()),
        };

        self.map.as_mut().and_then(|m| {
            m.insert(key, val)
        })
    }

    pub fn iter(&self) -> Iter<K, V> {
        Iter(self.map.as_ref().map(|m| m.iter()))
    }
}

pub struct Iter<'a, K: 'a, V: 'a>(Option<hash_map::Iter<'a, K, V>>);

impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> {
    type Item = (&'a K, &'a V);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.as_mut().and_then(|i| i.next())
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.as_ref().map_or((0, Some(0)), |i| i.size_hint())
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use euclid::TypedSize2D;
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
use std::collections::HashMap;
use std::iter::once;
use std::mem::replace;
use std::time::Instant;
use style_traits::CSSPixel;

/// The constellation's view of a browsing context.
/// Each browsing context has a session history, caused by
/// navigation and traversing the history. Each browsing context has its
/// current entry, plus past and future entries. The past is sorted
/// chronologically, the future is sorted reverse chronologically:
/// in particular prev.pop() is the latest past entry, and
/// next.pop() is the earliest future entry.
pub struct BrowsingContext {
    /// The browsing context id.
    pub id: BrowsingContextId,

    /// The top-level browsing context ancestor
    pub top_level_id: TopLevelBrowsingContextId,

    /// The size of the frame.
    pub size: Option<TypedSize2D<f32, CSSPixel>>,

    /// The timestamp for the current session history entry.
    pub instant: Instant,

    /// The pipeline for the current session history entry.
    pub pipeline_id: PipelineId,

    /// The load data for the current session history entry.
    pub load_data: LoadData,

    /// The past session history, ordered chronologically.
    pub prev: Vec<SessionHistoryEntry>,

    /// The future session history, ordered reverse chronologically.
    pub next: Vec<SessionHistoryEntry>,
}

impl BrowsingContext {
    /// Create a new browsing context.
    /// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts.
    pub fn new(id: BrowsingContextId,
               top_level_id: TopLevelBrowsingContextId,
               pipeline_id: PipelineId,
               load_data: LoadData)
               -> BrowsingContext
    {
        BrowsingContext {
            id: id,
            top_level_id: top_level_id,
            size: None,
            pipeline_id: pipeline_id,
            instant: Instant::now(),
            load_data: load_data,
            prev: vec!(),
            next: vec!(),
        }
    }

    /// Get the current session history entry.
    pub fn current(&self) -> SessionHistoryEntry {
        SessionHistoryEntry {
            instant: self.instant,
            browsing_context_id: self.id,
            pipeline_id: Some(self.pipeline_id),
            load_data: self.load_data.clone(),
        }
    }

    /// Set the current session history entry, and push the current frame entry into the past.
    pub fn load(&mut self, pipeline_id: PipelineId, load_data: LoadData) {
        let current = self.current();
        self.prev.push(current);
        self.instant = Instant::now();
        self.pipeline_id = pipeline_id;
        self.load_data = load_data;
    }

    /// Set the future to be empty.
    pub fn remove_forward_entries(&mut self) -> Vec<SessionHistoryEntry> {
        replace(&mut self.next, vec!())
    }

    /// Update the current entry of the BrowsingContext from an entry that has been traversed to.
    pub fn update_current(&mut self, pipeline_id: PipelineId, entry: SessionHistoryEntry) {
        self.pipeline_id = pipeline_id;
        self.instant = entry.instant;
        self.load_data = entry.load_data;
    }

    /// Is this a top-level browsing context?
    pub fn is_top_level(&self) -> bool {
        self.id == self.top_level_id
    }
}

/// An entry in a browsing context's session history.
/// Each entry stores the pipeline id for a document in the session history.
///
/// When we operate on the joint session history, entries are sorted chronologically,
/// so we timestamp the entries by when the entry was added to the session history.
///
/// <https://html.spec.whatwg.org/multipage/#session-history-entry>
#[derive(Clone)]
pub struct SessionHistoryEntry {
    /// The timestamp for when the session history entry was created
    pub instant: Instant,

    /// The pipeline for the document in the session history,
    /// None if the entry has been discarded
    pub pipeline_id: Option<PipelineId>,

    /// The load data for this entry, used to reload the pipeline if it has been discarded
    pub load_data: LoadData,

    /// The frame that this session history entry is part of
    pub browsing_context_id: BrowsingContextId,
}

/// Represents a pending change in a session history, that will be applied
/// once the new pipeline has loaded and completed initial layout / paint.
pub struct SessionHistoryChange {
    /// The browsing context to change.
    pub browsing_context_id: BrowsingContextId,

    /// The top-level browsing context ancestor.
    pub top_level_browsing_context_id: TopLevelBrowsingContextId,

    /// The pipeline for the document being loaded.
    pub new_pipeline_id: PipelineId,

    /// The data for the document being loaded.
    pub load_data: LoadData,

    /// Is the new document replacing the current document (e.g. a reload)
    /// or pushing it into the session history (e.g. a navigation)?
    /// If it is replacing an existing entry, we store its timestamp.
    pub replace_instant: Option<Instant>,
}

/// An iterator over browsing contexts, returning the descendant
/// contexts whose active documents are fully active, in depth-first
/// order.
pub struct FullyActiveBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the active
    /// children of a frame, which are the iframes in the currently
    /// active document.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for FullyActiveBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        loop {
            let browsing_context_id = match self.stack.pop() {
                Some(browsing_context_id) => browsing_context_id,
                None => return None,
            };
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let pipeline = match self.pipelines.get(&browsing_context.pipeline_id) {
                Some(pipeline) => pipeline,
                None => {
                    warn!("Pipeline {:?} iterated after closure.", browsing_context.pipeline_id);
                    continue;
                },
            };
            self.stack.extend(pipeline.children.iter());
            return Some(browsing_context)
        }
    }
}

/// An iterator over browsing contexts, returning all descendant
/// contexts in depth-first order. Note that this iterator returns all
/// contexts, not just the fully active ones.
pub struct AllBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the
    /// children of a browsing context, which are the iframes in all documents
    /// in the session history.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for AllBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        let pipelines = self.pipelines;
        loop {
            let browsing_context_id = match self.stack.pop() {
                Some(browsing_context_id) => browsing_context_id,
                None => return None,
            };
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let child_browsing_context_ids = browsing_context.prev.iter().chain(browsing_context.next.iter())
                .filter_map(|entry| entry.pipeline_id)
                .chain(once(browsing_context.pipeline_id))
                .filter_map(|pipeline_id| pipelines.get(&pipeline_id))
                .flat_map(|pipeline| pipeline.children.iter());
            self.stack.extend(child_browsing_context_ids);
            return Some(browsing_context)
        }
    }
}
<commit_msg>constellation: Use ? on Option more often.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use euclid::TypedSize2D;
use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
use std::collections::HashMap;
use std::iter::once;
use std::mem::replace;
use std::time::Instant;
use style_traits::CSSPixel;

/// The constellation's view of a browsing context.
/// Each browsing context has a session history, caused by
/// navigation and traversing the history. Each browsing context has its
/// current entry, plus past and future entries. The past is sorted
/// chronologically, the future is sorted reverse chronologically:
/// in particular prev.pop() is the latest past entry, and
/// next.pop() is the earliest future entry.
pub struct BrowsingContext {
    /// The browsing context id.
    pub id: BrowsingContextId,

    /// The top-level browsing context ancestor
    pub top_level_id: TopLevelBrowsingContextId,

    /// The size of the frame.
    pub size: Option<TypedSize2D<f32, CSSPixel>>,

    /// The timestamp for the current session history entry.
    pub instant: Instant,

    /// The pipeline for the current session history entry.
    pub pipeline_id: PipelineId,

    /// The load data for the current session history entry.
    pub load_data: LoadData,

    /// The past session history, ordered chronologically.
    pub prev: Vec<SessionHistoryEntry>,

    /// The future session history, ordered reverse chronologically.
    pub next: Vec<SessionHistoryEntry>,
}

impl BrowsingContext {
    /// Create a new browsing context.
    /// Note this just creates the browsing context, it doesn't add it to the constellation's set of browsing contexts.
    pub fn new(id: BrowsingContextId,
               top_level_id: TopLevelBrowsingContextId,
               pipeline_id: PipelineId,
               load_data: LoadData)
               -> BrowsingContext
    {
        BrowsingContext {
            id: id,
            top_level_id: top_level_id,
            size: None,
            pipeline_id: pipeline_id,
            instant: Instant::now(),
            load_data: load_data,
            prev: vec!(),
            next: vec!(),
        }
    }

    /// Get the current session history entry.
    pub fn current(&self) -> SessionHistoryEntry {
        SessionHistoryEntry {
            instant: self.instant,
            browsing_context_id: self.id,
            pipeline_id: Some(self.pipeline_id),
            load_data: self.load_data.clone(),
        }
    }

    /// Set the current session history entry, and push the current frame entry into the past.
    pub fn load(&mut self, pipeline_id: PipelineId, load_data: LoadData) {
        let current = self.current();
        self.prev.push(current);
        self.instant = Instant::now();
        self.pipeline_id = pipeline_id;
        self.load_data = load_data;
    }

    /// Set the future to be empty.
    pub fn remove_forward_entries(&mut self) -> Vec<SessionHistoryEntry> {
        replace(&mut self.next, vec!())
    }

    /// Update the current entry of the BrowsingContext from an entry that has been traversed to.
    pub fn update_current(&mut self, pipeline_id: PipelineId, entry: SessionHistoryEntry) {
        self.pipeline_id = pipeline_id;
        self.instant = entry.instant;
        self.load_data = entry.load_data;
    }

    /// Is this a top-level browsing context?
    pub fn is_top_level(&self) -> bool {
        self.id == self.top_level_id
    }
}

/// An entry in a browsing context's session history.
/// Each entry stores the pipeline id for a document in the session history.
///
/// When we operate on the joint session history, entries are sorted chronologically,
/// so we timestamp the entries by when the entry was added to the session history.
///
/// <https://html.spec.whatwg.org/multipage/#session-history-entry>
#[derive(Clone)]
pub struct SessionHistoryEntry {
    /// The timestamp for when the session history entry was created
    pub instant: Instant,

    /// The pipeline for the document in the session history,
    /// None if the entry has been discarded
    pub pipeline_id: Option<PipelineId>,

    /// The load data for this entry, used to reload the pipeline if it has been discarded
    pub load_data: LoadData,

    /// The frame that this session history entry is part of
    pub browsing_context_id: BrowsingContextId,
}

/// Represents a pending change in a session history, that will be applied
/// once the new pipeline has loaded and completed initial layout / paint.
pub struct SessionHistoryChange {
    /// The browsing context to change.
    pub browsing_context_id: BrowsingContextId,

    /// The top-level browsing context ancestor.
    pub top_level_browsing_context_id: TopLevelBrowsingContextId,

    /// The pipeline for the document being loaded.
    pub new_pipeline_id: PipelineId,

    /// The data for the document being loaded.
    pub load_data: LoadData,

    /// Is the new document replacing the current document (e.g. a reload)
    /// or pushing it into the session history (e.g. a navigation)?
    /// If it is replacing an existing entry, we store its timestamp.
    pub replace_instant: Option<Instant>,
}

/// An iterator over browsing contexts, returning the descendant
/// contexts whose active documents are fully active, in depth-first
/// order.
pub struct FullyActiveBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the active
    /// children of a frame, which are the iframes in the currently
    /// active document.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for FullyActiveBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        loop {
            let browsing_context_id = self.stack.pop()?;
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let pipeline = match self.pipelines.get(&browsing_context.pipeline_id) {
                Some(pipeline) => pipeline,
                None => {
                    warn!("Pipeline {:?} iterated after closure.", browsing_context.pipeline_id);
                    continue;
                },
            };
            self.stack.extend(pipeline.children.iter());
            return Some(browsing_context)
        }
    }
}

/// An iterator over browsing contexts, returning all descendant
/// contexts in depth-first order. Note that this iterator returns all
/// contexts, not just the fully active ones.
pub struct AllBrowsingContextsIterator<'a> {
    /// The browsing contexts still to iterate over.
    pub stack: Vec<BrowsingContextId>,

    /// The set of all browsing contexts.
    pub browsing_contexts: &'a HashMap<BrowsingContextId, BrowsingContext>,

    /// The set of all pipelines.  We use this to find the
    /// children of a browsing context, which are the iframes in all documents
    /// in the session history.
    pub pipelines: &'a HashMap<PipelineId, Pipeline>,
}

impl<'a> Iterator for AllBrowsingContextsIterator<'a> {
    type Item = &'a BrowsingContext;
    fn next(&mut self) -> Option<&'a BrowsingContext> {
        let pipelines = self.pipelines;
        loop {
            let browsing_context_id = self.stack.pop()?;
            let browsing_context = match self.browsing_contexts.get(&browsing_context_id) {
                Some(browsing_context) => browsing_context,
                None => {
                    warn!("BrowsingContext {:?} iterated after closure.", browsing_context_id);
                    continue;
                },
            };
            let child_browsing_context_ids = browsing_context.prev.iter().chain(browsing_context.next.iter())
                .filter_map(|entry| entry.pipeline_id)
                .chain(once(browsing_context.pipeline_id))
                .filter_map(|pipeline_id| pipelines.get(&pipeline_id))
                .flat_map(|pipeline| pipeline.children.iter());
            self.stack.extend(child_browsing_context_ids);
            return Some(browsing_context)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap::{App, Arg};
use std::result;

use export_docker as docker;

/// A Kubernetes-specific clap:App wrapper
///
/// The API here is provided to make it possible to reuse the CLI code of the Kubernetes exporter.
/// The CLI argument addition is divided between multiple methods to allow you to only pick the
/// parts of the CLI that you need.
#[derive(Clone)]
pub struct Cli<'a, 'b>
where
    'a: 'b,
{
    pub app: App<'a, 'b>,
}

impl<'a, 'b> Cli<'a, 'b> {
    /// Create a `Cli`
    ///
    /// # Arguments
    ///
    /// * `name` - The name of the CLI application to show in `--help' output.
    /// * `about` - The long description of the CLi to show in `--help' output.
    pub fn new(name: &str, about: &'a str) -> Self {
        let app = docker::Cli::new(name, about).app;

        Cli { app: app }
    }

    /// Convenient method to add all known arguments to the CLI.
    pub fn add_all_args(self) -> Self {
        self.add_docker_args()
            .add_output_args()
            .add_runtime_args()
            .add_secret_names_args()
            .add_bind_args()
    }

    pub fn add_docker_args(self) -> Self {
        let cli = docker::Cli { app: self.app };
        let app = cli.add_base_packages_args()
            .add_builder_args()
            .add_tagging_args()
            .add_publishing_args()
            .add_pkg_ident_arg(docker::PkgIdentArgOptions { multiple: false })
            .app
            .arg(
                Arg::with_name("NO_DOCKER_IMAGE")
                    .long("no-docker-image")
                    .short("d")
                    .help(
                        "Disable creation of the Docker image and only create a Kubernetes \
                         manifest",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_output_args(self) -> Self {
        Cli {
            app: self.app.arg(
                Arg::with_name("OUTPUT")
                    .value_name("OUTPUT")
                    .long("output")
                    .short("o")
                    .help("Name of manifest file to create. Pass '-' for stdout (default: -)"),
            ),
        }
    }

    /// Add Habitat (operator) runtime arguments to the CLI.
    pub fn add_runtime_args(self) -> Self {
        Cli {
            app: self.app
                .arg(
                    Arg::with_name("K8S_NAME")
                        .value_name("K8S_NAME")
                        .long("k8s-name")
                        .help(
                            "The Kubernetes resource name \
                             (default: {{pkg_name}}-{{pkg_version}}-{{pkg_release}})",
                        ),
                )
                .arg(
                    Arg::with_name("COUNT")
                        .value_name("COUNT")
                        .long("count")
                        .validator(valid_natural_number)
                        .help("Count is the number of desired instances"),
                )
                .arg(
                    Arg::with_name("TOPOLOGY")
                        .value_name("TOPOLOGY")
                        .long("topology")
                        .short("t")
                        .possible_values(&["standalone", "leader"])
                        .help(
                            "A topology describes the intended relationship between peers \
                             within a Habitat service group. Specify either standalone or leader \
                             topology (default: standalone)",
                        ),
                )
                .arg(
                    Arg::with_name("GROUP")
                        .value_name("GROUP")
                        .long("service-group")
                        .short("g")
                        .help(
                            "group is a logical grouping of services with the same package and \
                             topology type connected together in a ring (default: default)",
                        ),
                )
                .arg(
                    Arg::with_name("CONFIG")
                        .value_name("CONFIG")
                        .long("config")
                        .short("n")
                        .help(
                            "The path to Habitat configuration file in user.toml format. Habitat \
                             will use it for initial configuration of the service running in a \
                             Kubernetes cluster",
                        ),
                )
                .arg(
                    Arg::with_name("ENVIRONMENT")
                        .value_name("ENVIRONMENT")
                        .long("env")
                        .short("e")
                        .multiple(true)
                        .number_of_values(1)
                        .help("Additional environment variables to set for the service"),
                )
                .arg(
                    Arg::with_name("PERSISTENT_STORAGE")
                        .value_name("PERSISTENT_STORAGE")
                        .long("storage")
                        .help(
                            "Storage specification in form of <size>:<path>:<storage class name>. \
                             <size> uses the same format as Kubernetes' size field (e.g. 10Gi). \
                             <path> describes where the storage will be mounted. \
                             <storage class name> is the name of the storage class \
                             that will be used as a backing store; \
                             it is a Kubernetes platform-specific thing \
                             (GCE has its own classes, Azure - its own).",
                        ),
                ),
        }
    }

    pub fn add_secret_names_args(self) -> Self {
        Cli {
            app: self.app.arg(
                Arg::with_name("RING_SECRET_NAME")
                    .value_name("RING_SECRET_NAME")
                    .long("ring-secret-name")
                    .short("r")
                    .help(
                        "name of the Kubernetes Secret that contains the ring key, which \
                         encrypts the communication between Habitat supervisors",
                    ),
            ),
        }
    }

    pub fn add_bind_args(self) -> Self {
        Cli {
            app: self.app.arg(
                Arg::with_name("BIND")
                    .value_name("BIND")
                    .long("bind")
                    .short("b")
                    .multiple(true)
                    .number_of_values(1)
                    .help(
                        "Bind to another service to form a producer/consumer relationship, \
                         specified as name:service.group",
                    ),
            ),
        }
    }
}

fn valid_natural_number(val: String) -> result::Result<(), String> {
    match val.parse::<u32>() {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("{} is not a natural number", val)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_valid_natural_number() {
        valid_natural_number("99".to_owned()).unwrap();

        for &s in ["x", "", "#####", "0x11", "ab"].iter() {
            assert!(valid_natural_number(s.to_owned()).is_err());
        }
    }
}
<commit_msg>pkg-export-k8s: Make some help strings consistent<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use clap::{App, Arg};
use std::result;

use export_docker as docker;

/// A Kubernetes-specific clap:App wrapper
///
/// The API here is provided to make it possible to reuse the CLI code of the Kubernetes exporter.
/// The CLI argument addition is divided between multiple methods to allow you to only pick the
/// parts of the CLI that you need.
#[derive(Clone)]
pub struct Cli<'a, 'b>
where
    'a: 'b,
{
    pub app: App<'a, 'b>,
}

impl<'a, 'b> Cli<'a, 'b> {
    /// Create a `Cli`
    ///
    /// # Arguments
    ///
    /// * `name` - The name of the CLI application to show in `--help' output.
    /// * `about` - The long description of the CLi to show in `--help' output.
    pub fn new(name: &str, about: &'a str) -> Self {
        let app = docker::Cli::new(name, about).app;

        Cli { app: app }
    }

    /// Convenient method to add all known arguments to the CLI.
    pub fn add_all_args(self) -> Self {
        self.add_docker_args()
            .add_output_args()
            .add_runtime_args()
            .add_secret_names_args()
            .add_bind_args()
    }

    pub fn add_docker_args(self) -> Self {
        let cli = docker::Cli { app: self.app };
        let app = cli.add_base_packages_args()
            .add_builder_args()
            .add_tagging_args()
            .add_publishing_args()
            .add_pkg_ident_arg(docker::PkgIdentArgOptions { multiple: false })
            .app
            .arg(
                Arg::with_name("NO_DOCKER_IMAGE")
                    .long("no-docker-image")
                    .short("d")
                    .help(
                        "Disable creation of the Docker image and only create a Kubernetes \
                         manifest",
                    ),
            );

        Cli { app: app }
    }

    pub fn add_output_args(self) -> Self {
        Cli {
            app: self.app.arg(
                Arg::with_name("OUTPUT")
                    .value_name("OUTPUT")
                    .long("output")
                    .short("o")
                    .help("Name of manifest file to create. Pass '-' for stdout (default: -)"),
            ),
        }
    }

    /// Add Habitat (operator) runtime arguments to the CLI.
    pub fn add_runtime_args(self) -> Self {
        Cli {
            app: self.app
                .arg(
                    Arg::with_name("K8S_NAME")
                        .value_name("K8S_NAME")
                        .long("k8s-name")
                        .help(
                            "The Kubernetes resource name \
                             (default: {{pkg_name}}-{{pkg_version}}-{{pkg_release}})",
                        ),
                )
                .arg(
                    Arg::with_name("COUNT")
                        .value_name("COUNT")
                        .long("count")
                        .validator(valid_natural_number)
                        .help("Count is the number of desired instances"),
                )
                .arg(
                    Arg::with_name("TOPOLOGY")
                        .value_name("TOPOLOGY")
                        .long("topology")
                        .short("t")
                        .possible_values(&["standalone", "leader"])
                        .help(
                            "A topology describes the intended relationship between peers \
                             within a Habitat service group. Specify either standalone or leader \
                             topology (default: standalone)",
                        ),
                )
                .arg(
                    Arg::with_name("GROUP")
                        .value_name("GROUP")
                        .long("service-group")
                        .short("g")
                        .help(
                            "Group is a logical grouping of services with the same package and \
                             topology type connected together in a ring (default: default)",
                        ),
                )
                .arg(
                    Arg::with_name("CONFIG")
                        .value_name("CONFIG")
                        .long("config")
                        .short("n")
                        .help(
                            "The path to Habitat configuration file in user.toml format. Habitat \
                             will use it for initial configuration of the service running in a \
                             Kubernetes cluster",
                        ),
                )
                .arg(
                    Arg::with_name("ENVIRONMENT")
                        .value_name("ENVIRONMENT")
                        .long("env")
                        .short("e")
                        .multiple(true)
                        .number_of_values(1)
                        .help("Additional environment variables to set for the service"),
                )
                .arg(
                    Arg::with_name("PERSISTENT_STORAGE")
                        .value_name("PERSISTENT_STORAGE")
                        .long("storage")
                        .help(
                            "Storage specification in form of <size>:<path>:<storage class name>. \
                             <size> uses the same format as Kubernetes' size field (e.g. 10Gi). \
                             <path> describes where the storage will be mounted. \
                             <storage class name> is the name of the storage class \
                             that will be used as a backing store; \
                             it is a Kubernetes platform-specific thing \
                             (GCE has its own classes, Azure - its own).",
                        ),
                ),
        }
    }

    pub fn add_secret_names_args(self) -> Self {
        Cli {
            app: self.app.arg(
                Arg::with_name("RING_SECRET_NAME")
                    .value_name("RING_SECRET_NAME")
                    .long("ring-secret-name")
                    .short("r")
                    .help(
                        "Name of the Kubernetes Secret that contains the ring key, which \
                         encrypts the communication between Habitat supervisors",
                    ),
            ),
        }
    }

    pub fn add_bind_args(self) -> Self {
        Cli {
            app: self.app.arg(
                Arg::with_name("BIND")
                    .value_name("BIND")
                    .long("bind")
                    .short("b")
                    .multiple(true)
                    .number_of_values(1)
                    .help(
                        "Bind to another service to form a producer/consumer relationship, \
                         specified as name:service.group",
                    ),
            ),
        }
    }
}

fn valid_natural_number(val: String) -> result::Result<(), String> {
    match val.parse::<u32>() {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("{} is not a natural number", val)),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_valid_natural_number() {
        valid_natural_number("99".to_owned()).unwrap();

        for &s in ["x", "", "#####", "0x11", "ab"].iter() {
            assert!(valid_natural_number(s.to_owned()).is_err());
        }
    }
}
<|endoftext|>