<commit_before>//! Device event types

use super::EventTrait;
use {AsRaw, Context, FromRaw};
use ffi;

/// Common functions all Device-Events implement.
pub trait DeviceEventTrait: AsRaw<ffi::libinput_event_device_notify> + Context {
    /// Convert into a general `DeviceEvent` again
    fn into_device_event(self) -> DeviceEvent
    where
        Self: Sized,
    {
        unsafe { DeviceEvent::from_raw(self.as_raw_mut(), self.context()) }
    }
}

impl<T: AsRaw<ffi::libinput_event_device_notify> + Context> DeviceEventTrait for T {}

/// A device related `Event`
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum DeviceEvent {
    /// Signals that a device has been added to the context.
    Added(DeviceAddedEvent),
    /// Signals that a device has been removed.
    Removed(DeviceRemovedEvent),
}

impl EventTrait for DeviceEvent {
    #[doc(hidden)]
    fn as_raw_event(&self) -> *mut ffi::libinput_event {
        match *self {
            DeviceEvent::Added(ref event) => event.as_raw_event(),
            DeviceEvent::Removed(ref event) => event.as_raw_event(),
        }
    }
}

impl FromRaw<ffi::libinput_event_device_notify> for DeviceEvent {
    unsafe fn from_raw(event: *mut ffi::libinput_event_device_notify, context: &::context::Libinput) -> Self {
        let base = ffi::libinput_event_device_notify_get_base_event(event);
        match ffi::libinput_event_get_type(base) {
            ffi::libinput_event_type_LIBINPUT_EVENT_DEVICE_ADDED => {
                DeviceEvent::Added(DeviceAddedEvent::from_raw(event, context))
            }
            ffi::libinput_event_type_LIBINPUT_EVENT_DEVICE_REMOVED => {
                DeviceEvent::Removed(DeviceRemovedEvent::from_raw(event, context))
            }
            _ => unreachable!(),
        }
    }
}

impl AsRaw<ffi::libinput_event_device_notify> for DeviceEvent {
    fn as_raw(&self) -> *const ffi::libinput_event_device_notify {
        match *self {
            DeviceEvent::Added(ref event) => event.as_raw(),
            DeviceEvent::Removed(ref event) => event.as_raw(),
        }
    }
}

impl Context for DeviceEvent {
    fn context(&self) -> &::Libinput {
        match *self {
            DeviceEvent::Added(ref event) => event.context(),
            DeviceEvent::Removed(ref event) => event.context(),
        }
    }
}

ffi_event_struct!(
/// Signals that a device has been added to the context.
///
/// The device will not be read until the next time the user calls
/// `Libinput::dispatch` and data is available.
///
/// This allows setting up initial device configuration before any events are created.
struct DeviceAddedEvent, ffi::libinput_event_device_notify, ffi::libinput_event_device_notify_get_base_event);
ffi_event_struct!(
/// Signals that a device has been added to the context.
///
/// The device will not be read until the next time the user calls
/// `Libinput::dispatch` and data is available.
///
/// This allows setting up initial device configuration before any events are
/// created.
struct DeviceRemovedEvent, ffi::libinput_event_device_notify, ffi::libinput_event_device_notify_get_base_event);
<commit_msg>Fixup DeviceRemoveEvent documentation<commit_after>//! Device event types

use super::EventTrait;
use {AsRaw, Context, FromRaw};
use ffi;

/// Common functions all Device-Events implement.
pub trait DeviceEventTrait: AsRaw<ffi::libinput_event_device_notify> + Context {
    /// Convert into a general `DeviceEvent` again
    fn into_device_event(self) -> DeviceEvent
    where
        Self: Sized,
    {
        unsafe { DeviceEvent::from_raw(self.as_raw_mut(), self.context()) }
    }
}

impl<T: AsRaw<ffi::libinput_event_device_notify> + Context> DeviceEventTrait for T {}

/// A device related `Event`
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum DeviceEvent {
    /// Signals that a device has been added to the context.
    Added(DeviceAddedEvent),
    /// Signals that a device has been removed.
    Removed(DeviceRemovedEvent),
}

impl EventTrait for DeviceEvent {
    #[doc(hidden)]
    fn as_raw_event(&self) -> *mut ffi::libinput_event {
        match *self {
            DeviceEvent::Added(ref event) => event.as_raw_event(),
            DeviceEvent::Removed(ref event) => event.as_raw_event(),
        }
    }
}

impl FromRaw<ffi::libinput_event_device_notify> for DeviceEvent {
    unsafe fn from_raw(event: *mut ffi::libinput_event_device_notify, context: &::context::Libinput) -> Self {
        let base = ffi::libinput_event_device_notify_get_base_event(event);
        match ffi::libinput_event_get_type(base) {
            ffi::libinput_event_type_LIBINPUT_EVENT_DEVICE_ADDED => {
                DeviceEvent::Added(DeviceAddedEvent::from_raw(event, context))
            }
            ffi::libinput_event_type_LIBINPUT_EVENT_DEVICE_REMOVED => {
                DeviceEvent::Removed(DeviceRemovedEvent::from_raw(event, context))
            }
            _ => unreachable!(),
        }
    }
}

impl AsRaw<ffi::libinput_event_device_notify> for DeviceEvent {
    fn as_raw(&self) -> *const ffi::libinput_event_device_notify {
        match *self {
            DeviceEvent::Added(ref event) => event.as_raw(),
            DeviceEvent::Removed(ref event) => event.as_raw(),
        }
    }
}

impl Context for DeviceEvent {
    fn context(&self) -> &::Libinput {
        match *self {
            DeviceEvent::Added(ref event) => event.context(),
            DeviceEvent::Removed(ref event) => event.context(),
        }
    }
}

ffi_event_struct!(
/// Signals that a device has been added to the context.
///
/// The device will not be read until the next time the user calls
/// `Libinput::dispatch` and data is available.
///
/// This allows setting up initial device configuration before any events are created.
struct DeviceAddedEvent, ffi::libinput_event_device_notify, ffi::libinput_event_device_notify_get_base_event);
ffi_event_struct!(
/// Signals that a device has been removed.
///
/// No more events from the associated device will be in the queue or be queued after this event.
struct DeviceRemovedEvent, ffi::libinput_event_device_notify, ffi::libinput_event_device_notify_get_base_event);
<|endoftext|>


<commit_before>//! Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
//! Assumes Von Nuemann's neighborhood.

#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self {
        Stage::Collision
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

impl From<usize> for Direction {
    fn from(int: usize) -> Self {
        match int {
            0 => Direction::Up,
            1 => Direction::Left,
            2 => Direction::Right,
            3 => Direction::Down,
            i => panic!("No associated variant to number {}.", i),
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {
    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Transport, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(_) => {
                    let opposite = direction.opposite();

                    let head_on = self.particle(&direction) && self.particle(&opposite) &&
                                  !self.particle(&direction.perpendicular()) &&
                                  !self.particle(&opposite.perpendicular());
                    if head_on {
                        new.set_particle(&direction.perpendicular(), true);
                        new.set_particle(&opposite.perpendicular(), true);
                    }
                }
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    if self.particle(&opposite) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Collision, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    if neighbor.particle(&direction) {
                        new.set_particle(&direction, neighbor.particle(&direction));
                    }
                }
                None => {
                    new.set_particle(&direction, self.particle(&direction));
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {
        let index = *direction as usize;
        self.particles[index]
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {
        let index = *direction as usize;
        self.particles[index] = exists;
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {
    pub fn new() -> Self {
        HPPRulesTestConsumer {}
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {

        let left_particle_count = self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down = self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right),
                   true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..3 {
            print!("|");

            for x in 0..3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count = grid.cells()
        .iter()
        .map(|c| to_cell(c))
        .filter(|c: &HPP| c.particle(&Direction::Right) == true)
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn find_cell<C: Cell>(&self, cells: &Vec<C>, x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
            .find(|c| c.coord().x() == x && c.coord().y() == y)
            .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0..5 {
            print!("|");

            for x in 0..5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle = directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.coord().y() == self.cur_y)
            .all(|c| c.particle(&self.cur_direction) == true));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0..5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<commit_msg>Fixes #1. Correct HPP example.<commit_after>//! Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
//! Assumes Von Nuemann's neighborhood.

#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::Consumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport,
}

impl Default for Stage {
    fn default() -> Self {
        Stage::Collision
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Down,
    Right,
    Left,
    Up,
}

impl Direction {
    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up,
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right,
        }
    }
}

impl From<usize> for Direction {
    fn from(int: usize) -> Self {
        match int {
            0 => Direction::Up,
            1 => Direction::Left,
            2 => Direction::Right,
            3 => Direction::Down,
            i => panic!("No associated variant to number {}.", i),
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32),
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {
    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Transport, ..Default::default() };

        let has_head_on = |d: &Direction, op_d: &Direction| {
            self.particle(&d) && self.particle(&op_d) && !self.particle(&d.perpendicular()) &&
            !self.particle(&op_d.perpendicular())
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(_) => {
                    let opposite = direction.opposite();

                    let head_on = has_head_on(&direction, &opposite);
                    if head_on {
                        new.set_particle(&direction.perpendicular(), true);
                        new.set_particle(&opposite.perpendicular(), true);
                    } else {
                        let exists = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, exists);
                    }
                }
                // Rebound
                None => {
                    let opposite = direction.opposite();
                    let head_on = has_head_on(&direction, &opposite);
                    if head_on {
                        new.set_particle(&direction.perpendicular(), true);
                        new.set_particle(&opposite.perpendicular(), true);
                    } else {
                        if self.particle(&direction) {
                            new.set_particle(&opposite, true);
                        }
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item = Option<&'a Self>>
    {

        let mut new = HPP { stage: Stage::Collision, ..Default::default() };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {
            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();
                    if neighbor.particle(&opposite) {
                        new.set_particle(&opposite, true);
                    }
                }
                None => {
                    if self.particle(&direction) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {
        let index = *direction as usize;
        self.particles[index]
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {
        let index = *direction as usize;
        self.particles[index] = exists;
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

fn find_cell<C: Cell>(cells: &Vec<C>, x: i32, y: i32) -> HPP {

    assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

    let found = cells.iter()
        .find(|c| c.coord().x() == x && c.coord().y() == y)
        .unwrap();

    to_cell(found)
}

fn pretty_print<G: Grid>(grid: &G) {
    let dim = grid.dimensions();
    let iter_order =
        [vec![Direction::Down], vec![Direction::Right, Direction::Left], vec![Direction::Up]];

    println!("");

    for y in 0..dim.y() {

        for dirs in iter_order.iter() {
            print!("|");
            for x in 0..dim.x() {
                let cell = find_cell(grid.cells(), x, y);
                for d in dirs.iter() {
                    let to_print = match *d {
                        Direction::Down => {
                            if cell.particle(d) {
                                " v |"
                            } else {
                                "   |"
                            }
                        }
                        Direction::Right => {
                            if cell.particle(d) {
                                "> "
                            } else {
                                "  "
                            }
                        }
                        Direction::Left => {
                            if cell.particle(d) {
                                "<|"
                            } else {
                                " |"
                            }
                        }
                        Direction::Up => {
                            if cell.particle(d) {
                                " ^ |"
                            } else {
                                "   |"
                            }
                        }
                    };
                    print!("{}", to_print);
                }
            }
            println!("");
        }
        for _ in 0..dim.x() {
            print!(" ---");
        }
        println!("");
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {
    pub fn new() -> Self {
        HPPRulesTestConsumer {}
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn test_collision<G: Grid>(&self, grid: &G) {
        println!("Collision");

        let left_particle_count = self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count = self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count = self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count = self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right = find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up = find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down = find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {
        println!("Transport");

        let simple_move_to_right = find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right),
                   true);

        let move_to_down_after_head_on = find_cell(&grid.cells(), 1, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }
}

impl Consumer for HPPRulesTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 9);

        pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial          collision        transport
    // | * |> <| * |    | * |^ v| * |    | * |  v| * |
    // |  <| * | * | => |>  | * | * | => | * |> v| * |
    // |>  | * | * |    |>  | * | * |    | * |>  | * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, false, true, false];
    let right_particle = [false, true, false, false];
    let about_to_collide = [false, true, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: about_to_collide, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    pretty_print(&grid);

    let right_particles_count = grid.cells()
        .iter()
        .map(|c| to_cell(c))
        .filter(|c: &HPP| c.particle(&Direction::Right) == true)
        .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction,
}

impl HPPSpreadTestConsumer {
    pub fn new() -> Self {
        HPPSpreadTestConsumer {
            cur_y: 0,
            cur_direction: Direction::Down,
        }
    }

    fn particles_count<C: Cell>(&self, cells: &Vec<C>, direction: &Direction) -> i32 {

        cells.iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(direction) == true)
            .count() as i32
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {
        println!("Collision");

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        } else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {
        println!("Transport");

        let particles_count = self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.coord().y() == self.cur_y)
            .all(|c| c.particle(&self.cur_direction) == true));

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl Consumer for HPPSpreadTestConsumer {
    type Cell = HPP;

    fn consume<G: Grid>(&mut self, grid: &mut G) {
        assert_eq!(grid.cells().len(), 25);

        pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0..5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0),
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate scraper;
extern crate chrono;
extern crate dotenv;

use hyper::Client;

use scraper::{Html, Selector};

use chrono::{UTC, TimeZone};

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::env;

const NEWS_URL: &'static str = "http://na.finalfantasyxiv.com/lodestone/news/";

struct NewsScraper {
  client: Client,
  database: NewsDatabase
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct NewsDatabase {
  items: HashMap<String, NewsItem>
}

#[derive(Debug, Serialize, Deserialize)]
struct NewsItem {
  title: String,
  url: String,
  kind: NewsKind,
  time: i64,
  tag: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
enum NewsKind {
  SpecialNotice,
  News,
  Topic
}

impl ToString for NewsKind {
  fn to_string(&self) -> String {
    match *self {
      NewsKind::News => "News",
      NewsKind::Topic => "Topic",
      NewsKind::SpecialNotice => "Special notice"
    }.to_string()
  }
}

fn main() {
  dotenv::dotenv().ok();

  let webhook_url = match env::var("LB_NEWS_WEBHOOK") {
    Ok(u) => u,
    Err(_) => {
      println!("No webhook specified");
      return;
    }
  };

  let database = match NewsDatabase::load() {
    Some(db) => db,
    None => NewsDatabase::default()
  };
  let mut scraper = NewsScraper::new(database);

  let news = match scraper.download_news() {
    Some(n) => n,
    None => {
      println!("could not download news");
      return;
    }
  };

  let downloaded_news = scraper.parse_news(&news);
  for (id, item) in downloaded_news {
    if !scraper.database.items.contains_key(&id) {
      let mut embed = json!({
        "type": "rich",
        "timestamp": UTC.timestamp(item.time, 0).to_rfc3339(),
        "fields": [
          {
            "name": "Title",
            "value": item.title,
            "inline": false
          },
          {
            "name": "Link",
            "value": item.url,
            "inline": false
          },
          {
            "name": "Kind",
            "value": item.kind.to_string(),
            "inline": true
          }
        ]
      });
      if let Some(ref tag) = item.tag {
        embed["fields"].as_array_mut().unwrap().push(json!({
          "name": "Tag",
          "value": tag,
          "inline": true
        }));
      }
      let data = json!({
        "embeds": [embed]
      });
      let res = scraper.client.post(&webhook_url)
        .header(hyper::header::ContentType::json())
        .body(&data.to_string())
        .send();
      let mut data = match res {
        Ok(r) => r,
        Err(e) => {
          println!("error sending webhook: {}", e);
          continue;
        }
      };
      let mut content = String::new();
      if let Err(e) = data.read_to_string(&mut content) {
        println!("could not read webhook response: {}", e);
        continue;
      }
      if data.status.class() != hyper::status::StatusClass::Success {
        println!("discord says no to {}", item.title);
        println!("{}", content);
      } else {
        println!("webhook sent: {}", item.title);
        scraper.database.items.insert(id, item);
      }
    }
  }
  if scraper.database.save().is_none() {
    println!("could not save database");
  }
}

impl NewsDatabase {
  fn load() -> Option<Self> {
    let news_file = match File::open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::from_reader(news_file).ok()
  }

  fn save(&self) -> Option<()> {
    let news_file = match OpenOptions::new().create(true).write(true).open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::to_writer(news_file, self).ok()
  }
}

impl NewsScraper {
  fn new(database: NewsDatabase) -> Self {
    NewsScraper {
      client: Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
      database: database
    }
  }

  fn download_news(&self) -> Option<String> {
    let mut response = match self.client.get(NEWS_URL).send() {
      Ok(r) => r,
      Err(_) => return None
    };

    let mut content = String::new();
    if response.read_to_string(&mut content).is_err() {
      return None;
    }
    Some(content)
  }

  fn parse_news(&self, news: &str) -> HashMap<String, NewsItem> {
    let html = Html::parse_document(news);
    let news_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(2) > li").unwrap();
    let topics_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(3) > li").unwrap();
    let title_selector = Selector::parse("p.news__list--title").unwrap();
    let time_script_selector = Selector::parse("time.news__list--time > script").unwrap();
    let mut lis: Vec<_> = html.select(&news_selector).map(|x| (NewsKind::News, x)).collect();
    lis.append(&mut html.select(&topics_selector).map(|x| (NewsKind::Topic, x)).collect());
    let mut items = HashMap::with_capacity(lis.len());
    for (kind, li) in lis {
      let child = match kind {
        NewsKind::News => li.first_child().and_then(|v| v.value().as_element()),
        NewsKind::Topic => li.select(&title_selector).next().and_then(|v| v.first_child().and_then(|x| x.value().as_element())),
        _ => {
          println!("unsupported news kind");
          continue;
        }
      };

      let child = match child {
        Some(c) => c,
        None => {
          println!("could not get news item child");
          continue
        }
      };

      let href = match child.attr("href") {
        Some(h) => h,
        None => {
          println!("invalid link in news item");
          continue;
        }
      };

      let (title, tag) = match kind {
        NewsKind::News => {
          let title = match li.select(&title_selector).next() {
            Some(t) => t,
            None => {
              println!("missing title in news item");
              continue;
            }
          };

          let tag: Option<String> = title.first_child()
            .and_then(scraper::ElementRef::wrap)
            .map(|c| c.text().collect::<String>())
            .map(|tag| tag[1..tag.len() - 1].to_string());

          let text_iter = title.text();
          let title: String = if tag.is_some() {
            text_iter.skip(1).collect()
          } else {
            text_iter.collect()
          };

          (title, tag)
        },
        NewsKind::Topic => {
          let text = li.select(&title_selector).next()
            .and_then(|v| v.first_child())
            .and_then(scraper::ElementRef::wrap)
            .map(|v| v.text().collect());
          match text {
            Some(t) => (t, None),
            None => {
              println!("invalid topic: no title");
              continue;
            }
          }
        },
        _ => unreachable!()
      };

      let time_script = match li.select(&time_script_selector).next() {
        Some(ts) => ts,
        None => {
          println!("news item missing time script");
          continue
        }
      };

      let time_script: String = time_script.text().collect();
      let time_string = match time_script.split("strftime(").nth(1).and_then(|v| v.split(',').next()) {
        Some(time) => time,
        None => {
          println!("invalid script in news item");
          continue
        }
      };
      let time: i64 = match time_string.parse() {
        Ok(t) => t,
        Err(_) => {
          println!("invalid time in time script");
          continue
        }
      };

      let id = match href.split('/').last() {
        Some(i) => i,
        None => {
          println!("invalid href in news item");
          continue;
        }
      };

      let news_item = NewsItem {
        title: title,
        url: format!("http://na.finalfantasyxiv.com{}", href),
        kind: kind,
        time: time,
        tag: tag
      };
      items.insert(id.to_string(), news_item);
    }

    items
  }
}
<commit_msg>fix: trim titles and tags<commit_after>extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate scraper;
extern crate chrono;
extern crate dotenv;

use hyper::Client;

use scraper::{Html, Selector};

use chrono::{UTC, TimeZone};

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::env;

const NEWS_URL: &'static str = "http://na.finalfantasyxiv.com/lodestone/news/";

struct NewsScraper {
  client: Client,
  database: NewsDatabase
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct NewsDatabase {
  items: HashMap<String, NewsItem>
}

#[derive(Debug, Serialize, Deserialize)]
struct NewsItem {
  title: String,
  url: String,
  kind: NewsKind,
  time: i64,
  tag: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
enum NewsKind {
  SpecialNotice,
  News,
  Topic
}

impl ToString for NewsKind {
  fn to_string(&self) -> String {
    match *self {
      NewsKind::News => "News",
      NewsKind::Topic => "Topic",
      NewsKind::SpecialNotice => "Special notice"
    }.to_string()
  }
}

fn main() {
  dotenv::dotenv().ok();

  let webhook_url = match env::var("LB_NEWS_WEBHOOK") {
    Ok(u) => u,
    Err(_) => {
      println!("No webhook specified");
      return;
    }
  };

  let database = match NewsDatabase::load() {
    Some(db) => db,
    None => NewsDatabase::default()
  };
  let mut scraper = NewsScraper::new(database);

  let news = match scraper.download_news() {
    Some(n) => n,
    None => {
      println!("could not download news");
      return;
    }
  };

  let downloaded_news = scraper.parse_news(&news);
  for (id, item) in downloaded_news {
    if !scraper.database.items.contains_key(&id) {
      let mut embed = json!({
        "type": "rich",
        "timestamp": UTC.timestamp(item.time, 0).to_rfc3339(),
        "fields": [
          {
            "name": "Title",
            "value": item.title,
            "inline": false
          },
          {
            "name": "Link",
            "value": item.url,
            "inline": false
          },
          {
            "name": "Kind",
            "value": item.kind.to_string(),
            "inline": true
          }
        ]
      });
      if let Some(ref tag) = item.tag {
        embed["fields"].as_array_mut().unwrap().push(json!({
          "name": "Tag",
          "value": tag,
          "inline": true
        }));
      }
      let data = json!({
        "embeds": [embed]
      });
      let res = scraper.client.post(&webhook_url)
        .header(hyper::header::ContentType::json())
        .body(&data.to_string())
        .send();
      let mut data = match res {
        Ok(r) => r,
        Err(e) => {
          println!("error sending webhook: {}", e);
          continue;
        }
      };
      let mut content = String::new();
      if let Err(e) = data.read_to_string(&mut content) {
        println!("could not read webhook response: {}", e);
        continue;
      }
      if data.status.class() != hyper::status::StatusClass::Success {
        println!("discord says no to {}", item.title);
        println!("{}", content);
      } else {
        println!("webhook sent: {}", item.title);
        scraper.database.items.insert(id, item);
      }
    }
  }
  if scraper.database.save().is_none() {
    println!("could not save database");
  }
}

impl NewsDatabase {
  fn load() -> Option<Self> {
    let news_file = match File::open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::from_reader(news_file).ok()
  }

  fn save(&self) -> Option<()> {
    let news_file = match OpenOptions::new().create(true).write(true).open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::to_writer(news_file, self).ok()
  }
}

impl NewsScraper {
  fn new(database: NewsDatabase) -> Self {
    NewsScraper {
      client: Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
      database: database
    }
  }

  fn download_news(&self) -> Option<String> {
    let mut response = match self.client.get(NEWS_URL).send() {
      Ok(r) => r,
      Err(_) => return None
    };

    let mut content = String::new();
    if response.read_to_string(&mut content).is_err() {
      return None;
    }
    Some(content)
  }

  fn parse_news(&self, news: &str) -> HashMap<String, NewsItem> {
    let html = Html::parse_document(news);
    let news_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(2) > li").unwrap();
    let topics_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(3) > li").unwrap();
    let title_selector = Selector::parse("p.news__list--title").unwrap();
    let time_script_selector = Selector::parse("time.news__list--time > script").unwrap();
    let mut lis: Vec<_> = html.select(&news_selector).map(|x| (NewsKind::News, x)).collect();
    lis.append(&mut html.select(&topics_selector).map(|x| (NewsKind::Topic, x)).collect());
    let mut items = HashMap::with_capacity(lis.len());
    for (kind, li) in lis {
      let child = match kind {
        NewsKind::News => li.first_child().and_then(|v| v.value().as_element()),
        NewsKind::Topic => li.select(&title_selector).next().and_then(|v| v.first_child().and_then(|x| x.value().as_element())),
        _ => {
          println!("unsupported news kind");
          continue;
        }
      };

      let child = match child {
        Some(c) => c,
        None => {
          println!("could not get news item child");
          continue
        }
      };

      let href = match child.attr("href") {
        Some(h) => h,
        None => {
          println!("invalid link in news item");
          continue;
        }
      };

      let (title, tag) = match kind {
        NewsKind::News => {
          let title = match li.select(&title_selector).next() {
            Some(t) => t,
            None => {
              println!("missing title in news item");
              continue;
            }
          };

          let tag: Option<String> = title.first_child()
            .and_then(scraper::ElementRef::wrap)
            .map(|c| c.text().collect::<String>())
            .map(|tag| tag[1..tag.len() - 1].to_string());

          let text_iter = title.text();
          let title: String = if tag.is_some() {
            text_iter.skip(1).collect()
          } else {
            text_iter.collect()
          };

          (title, tag)
        },
        NewsKind::Topic => {
          let text = li.select(&title_selector).next()
            .and_then(|v| v.first_child())
            .and_then(scraper::ElementRef::wrap)
            .map(|v| v.text().collect());
          match text {
            Some(t) => (t, None),
            None => {
              println!("invalid topic: no title");
              continue;
            }
          }
        },
        _ => unreachable!()
      };

      let time_script = match li.select(&time_script_selector).next() {
        Some(ts) => ts,
        None => {
          println!("news item missing time script");
          continue
        }
      };

      let time_script: String = time_script.text().collect();
      let time_string = match time_script.split("strftime(").nth(1).and_then(|v| v.split(',').next()) {
        Some(time) => time,
        None => {
          println!("invalid script in news item");
          continue
        }
      };
      let time: i64 = match time_string.parse() {
        Ok(t) => t,
        Err(_) => {
          println!("invalid time in time script");
          continue
        }
      };

      let id = match href.split('/').last() {
        Some(i) => i,
        None => {
          println!("invalid href in news item");
          continue;
        }
      };

      let news_item = NewsItem {
        title: title.trim().to_string(),
        url: format!("http://na.finalfantasyxiv.com{}", href),
        kind: kind,
        time: time,
        tag: tag.map(|x| x.trim().to_string())
      };
      items.insert(id.to_string(), news_item);
    }

    items
  }
}
<|endoftext|>


<commit_before>/* diosix hypervisor's physical CPU core management
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

/* Physical CPUs get their own private heaps to manage. Crucially, allocated memory
blocks can be shared by other CPUs. Any CPU can free any block, returning
it to its owner's heap pool. When allocating, a CPU can only draw from
its own heap, reusing any blocks freed by itself or other cores.

The hypervisor layer is unlikely to do much active allocation
so it's OK to keep it really simple for now. */

use super::lock::Mutex;
use hashbrown::hash_map::HashMap;
use platform::physmem::PhysMemSize;
use platform::cpu::{SupervisorState, CPUFeatures};
use platform::timer;
use super::vcore::{VirtualCore, VirtualCoreCanonicalID};
use super::scheduler::ScheduleQueues;
use super::capsule::{self, CapsuleID, CapsuleState};
use super::message;
use super::heap;

/* physical CPU core IDs and count */
pub type PhysicalCoreID = usize;
pub type PhysicalCoreCount = PhysicalCoreID;

pub const BOOT_PCORE_ID: PhysicalCoreID = 0;
const PCORE_MAGIC: usize = 0xc001c0de;

/* require some help from the underlying platform */
extern "C"
{
    fn platform_cpu_private_variables() -> &'static mut PhysicalCore;
    fn platform_cpu_heap_base() -> *mut heap::HeapBlock;
    fn platform_cpu_heap_size() -> PhysMemSize;
    fn platform_save_supervisor_state(state: &SupervisorState);
    fn platform_load_supervisor_state(state: &SupervisorState);
}

lazy_static!
{
    /* map running virtual CPU cores to physical CPU cores, and vice-versa
    we can't store these in Core structs because it upsets Rust's borrow checker.
    note: PCORES keeps track of the last physical CPU core to run a given virtual
    core. this is more of a hint than a concrete guarantee: the virtual core
    may have been scheduled away, though it should be in the last physical
    CPU core's scheduling queue. */
    static ref VCORES: Mutex<HashMap<PhysicalCoreID, VirtualCore>> = Mutex::new(HashMap::new());
    static ref PCORES: Mutex<HashMap<VirtualCoreCanonicalID, PhysicalCoreID>> = Mutex::new(HashMap::new());
}

/* describe a physical CPU core - this structure is stored in the per-CPU private variable space.
   this is below the per-CPU machine-level stack */
#[repr(C)]
pub struct PhysicalCore
{
    /* this magic word is used to make sure the CPU's stack hasn't overflowed
    and corrupted this adjacent structure */
    magic: usize,

    /* every physical CPU core has a hardware-assigned ID number that may be non-linear,
    while the startup code assigns each core a linear ID number from zero. we keep a copy of that
    linear runtime-assigned ID here. the hardware-assigned ID is not used in the portable code */
    id: PhysicalCoreID,

    /* platform-defined bitmask of ISA features this core provides. if a virtual core has a features bit set that
    is unset in a physical core's feature bitmask, the virtual core will not be allowed to run on that physical core */
    features: CPUFeatures,

    /* each physical CPU core gets its own heap that it can share, but it must manage its own */
    pub heap: heap::Heap,

    /* each physical CPU gets its own set of queues of virtual CPU cores to schedule */
    queues: ScheduleQueues,

    /* can this run guest operating systems? or is it a system management core? true if it can run
    supervisor-mode code, false if not */
    smode: bool,

    /* set when this physical core CPU core last ran a scheduling decision */
    timer_sched_last: Option<timer::TimerValue>
}

impl PhysicalCore
{
    /* intiialize a physical CPU core. Prepare it for running supervisor code.
    => id = diosix-assigned CPU core ID at boot time. this is separate from the hardware-assigned
            ID number, which may be non-linear. the runtime-generated core ID will
            run from zero to N-1 where N is the number of available cores */
    pub fn init(id: PhysicalCoreID)
    {
        /* the pre-hvmain startup code has allocated space for per-CPU core variables.
        this function returns a pointer to that structure */
        let mut cpu = PhysicalCore::this();

        cpu.magic = PCORE_MAGIC;
        cpu.id = id;
        cpu.features = platform::cpu::features();
        cpu.smode = platform::cpu::features_priv_check(platform::cpu::PrivilegeMode::Supervisor);
        cpu.timer_sched_last = None;

        let (heap_ptr, heap_size) = PhysicalCore::get_heap_config();
        cpu.heap.init(heap_ptr, heap_size);

        cpu.queues = ScheduleQueues::new();

        /* create a mailbox for messages from other cores */
        message::create_mailbox(id);
    }

    /* return pointer to the calling CPU core's fixed private data structure */
    pub fn this() -> &'static mut PhysicalCore
    {
        unsafe { platform_cpu_private_variables() }
    }

    /* return Ok if magic hasn't been overwritten, or the overwrite value as an error code */
    pub fn integrity_check() -> Result<(), usize>
    {
        match PhysicalCore::this().magic
        {
            PCORE_MAGIC => Ok(()),
            other => Err(other)
        }
    }

    /* return CPU heap base and size set aside by the pre-hvmain boot code */
    fn get_heap_config() -> (*mut heap::HeapBlock, PhysMemSize)
    {
        unsafe { (platform_cpu_heap_base(), platform_cpu_heap_size()) }
    }

    /* return boot-assigned ID number */
    pub fn get_id() -> PhysicalCoreID { PhysicalCore::this().id }

    /* return features bitmask */
    pub fn get_features() -> CPUFeatures { PhysicalCore::this().features }

    /* return a structure describing this core */
    pub fn describe() -> platform::cpu::CPUDescription { platform::cpu::CPUDescription }

    /* return a virtual CPU core awaiting to run on this physical CPU core */
    pub fn dequeue() -> Option<VirtualCore>
    {
        PhysicalCore::this().queues.dequeue()
    }

    /* move a virtual CPU core onto this physical CPU's queue of virtual cores to run */
    pub fn queue(to_queue: VirtualCore)
    {
        PhysicalCore::this().queues.queue(to_queue)
    }

    /* return true if able to run supervisor code. a system management core
    that cannot or is not expected to run guest workloads should return false */
    pub fn smode_supported() -> bool
    {
        PhysicalCore::this().smode
    }

    /* return ID of capsule of the virtual CPU core this physical CPU core is running, or None for none */
    pub fn get_capsule_id() -> Option<CapsuleID>
    {
        if let Some(vcore) = VCORES.lock().get(&PhysicalCore::get_id())
        {
            Some(vcore.get_capsule_id())
        }
        else
        {
            None
        }
    }

    /* update the running virtual core's timer IRQ target. we have to do this here because
    the virtual core is held in a locked data structure. leaving this function relocks
    the structure. it's unsafe to access the vcore struct */
    pub fn set_virtualcore_timer_target(target: Option<timer::TimerValue>)
    {
        if let Some(vcore) = VCORES.lock().get_mut(&(PhysicalCore::get_id()))
        {
            vcore.set_timer_irq_at(target);
        }
    }

    /* get the virtual core's timer IRQ target */
    pub fn get_virtualcore_timer_target() -> Option<timer::TimerValue>
    {
        if let Some(vcore) = VCORES.lock().get_mut(&(PhysicalCore::get_id()))
        {
            return vcore.get_timer_irq_at();
        }
        None
    }

    /* return canonical ID for the virtual core running in the capsule on this CPU, if any */
    pub fn get_virtualcore_id(&self) -> Option<VirtualCoreCanonicalID>
    {
        let cid = match PhysicalCore::get_capsule_id()
        {
            Some(id) => id,
            None => return None
        };

        let vid = match VCORES.lock().get(&PhysicalCore::get_id())
        {
            Some(vcore) => vcore.get_id(),
            None => return None
        };

        Some(VirtualCoreCanonicalID
        {
            capsuleid: cid,
            vcoreid: vid
        })
    }

    /* set the exact per-CPU timer value of the last time this physical core make a scheduling decision */
    pub fn set_timer_sched_last(&mut self, value: Option<timer::TimerValue>)
    {
        self.timer_sched_last = value;
    }

    /* get the exact per-CPU timer value of the last time this physical core make a scheduling decision */
    pub fn get_timer_sched_last(&mut self) -> Option<timer::TimerValue>
    {
        self.timer_sched_last
    }
}

/* save current virtual CPU core's context, if we're running one, and load next virtual core's context.
this should be called from an IRQ context as it preserves the interrupted code's context
and overwrites the context with the next virtual core's context, so returning to supervisor
mode will land us in the new context */
pub fn context_switch(next: VirtualCore)
{
    let next_capsule = next.get_capsule_id();
    let pcore_id = PhysicalCore::get_id();

    /* find what this physical core was running, if anything */
    match VCORES.lock().remove(&pcore_id)
    {
        Some(current_vcore) =>
        {
            let current_capsule = current_vcore.get_capsule_id();

            /* if we're switching to a virtual CPU core in another capsule then replace the
            current hardware access permissions so that we're only allowing access to the RAM assigned
            to the next capsule to run */
            if current_capsule != next_capsule
            {
                capsule::enforce(next_capsule);
            }

            /* queue the current virtual core on the waiting list.
               however, if the vcore's capsule is dying or restarting then
               don't queue the core for later use, and drop it */
            if capsule::get_state(current_capsule) == Some(CapsuleState::Valid)
            {
                platform::cpu::save_supervisor_state(current_vcore.state_as_ref());
                PhysicalCore::queue(current_vcore);
            }
        },
        None =>
        {
            /* if we were not running a virtual CPU core then ensure we return to supervisor mode
            rather than hypervisor mode */
            platform::cpu::prep_supervisor_return();
            /* and enforce its hardware access permissions */
            capsule::enforce(next_capsule);
        }
    }

    /* prepare next virtual core to run when we leave this IRQ context */
    platform::cpu::load_supervisor_state(next.state_as_ref());

    /* link next virtual core and capsule to this physical CPU */
    PCORES.lock().insert(VirtualCoreCanonicalID
        {
            vcoreid: next.get_id(),
            capsuleid: next_capsule
        },
        pcore_id);

    /* and add the virtual core to the running virtual cores list */
    VCORES.lock().insert(pcore_id, next);
}
<commit_msg>Use named mutexes * Also allow a physical CPU core to mark its virtual CPU core as invalid so that it won't be preserved after a context switch, causing the virtual core to be dropped and preventing the scheduler from running the doomed vcore.<commit_after>/* diosix hypervisor's physical CPU core management
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

/* Physical CPUs get their own private heaps to manage. Crucially, allocated memory
blocks can be shared by other CPUs. Any CPU can free any block, returning
it to its owner's heap pool. When allocating, a CPU can only draw from
its own heap, reusing any blocks freed by itself or other cores.

The hypervisor layer is unlikely to do much active allocation
so it's OK to keep it really simple for now. */

use super::lock::Mutex;
use hashbrown::hash_map::HashMap;
use platform::physmem::PhysMemSize;
use platform::cpu::{SupervisorState, CPUFeatures};
use platform::timer;
use super::vcore::{VirtualCore, VirtualCoreCanonicalID};
use super::scheduler::ScheduleQueues;
use super::capsule::{self, CapsuleID};
use super::message;
use super::heap;

/* physical CPU core IDs and count */
pub type PhysicalCoreID = usize;
pub type PhysicalCoreCount = PhysicalCoreID;

pub const BOOT_PCORE_ID: PhysicalCoreID = 0;
const PCORE_MAGIC: usize = 0xc001c0de;

/* require some help from the underlying platform */
extern "C"
{
    fn platform_cpu_private_variables() -> &'static mut PhysicalCore;
    fn platform_cpu_heap_base() -> *mut heap::HeapBlock;
    fn platform_cpu_heap_size() -> PhysMemSize;
    fn platform_save_supervisor_state(state: &SupervisorState);
    fn platform_load_supervisor_state(state: &SupervisorState);
}

lazy_static!
{
    /* map running virtual CPU cores to physical CPU cores, and vice-versa
    we can't store these in Core structs because it upsets Rust's borrow checker.
    note: PCORES keeps track of the last physical CPU core to run a given virtual
    core. this is more of a hint than a concrete guarantee: the virtual core
    may have been scheduled away, though it should be in the last physical
    CPU core's scheduling queue. */
    static ref VCORES: Mutex<HashMap<PhysicalCoreID, VirtualCore>> = Mutex::new("physical-virtual core table", HashMap::new());
    static ref PCORES: Mutex<HashMap<VirtualCoreCanonicalID, PhysicalCoreID>> = Mutex::new("physical-virtual core ID table", HashMap::new());
}

/* describe a physical CPU core - this structure is stored in the per-CPU private variable space.
   this is below the per-CPU machine-level stack */
#[repr(C)]
pub struct PhysicalCore
{
    /* this magic word is used to make sure the CPU's stack hasn't overflowed
    and corrupted this adjacent structure */
    magic: usize,

    /* every physical CPU core has a hardware-assigned ID number that may be non-linear,
    while the startup code assigns each core a linear ID number from zero. we keep a copy of that
    linear runtime-assigned ID here. the hardware-assigned ID is not used in the portable code */
    id: PhysicalCoreID,

    /* platform-defined bitmask of ISA features this core provides. if a virtual core has a features bit set that
    is unset in a physical core's feature bitmask, the virtual core will not be allowed to run on that physical core */
    features: CPUFeatures,

    /* each physical CPU core gets its own heap that it can share, but it must manage its own */
    pub heap: heap::Heap,

    /* each physical CPU gets its own set of queues of virtual CPU cores to schedule */
    queues: ScheduleQueues,

    /* can this run guest operating systems? or is it a system management core? true if it can run
    supervisor-mode code, false if not */
    smode: bool,

    /* set when this physical core CPU core last ran a scheduling decision */
    timer_sched_last: Option<timer::TimerValue>,

    /* set to true when the vcore running on this physical core is doomed.
       that means it's in a capsule that was restarted or killed and
       must not be saved after a context switch */
    vcore_doomed: bool
}

impl PhysicalCore
{
    /* intiialize a physical CPU core. Prepare it for running supervisor code.
    => id = diosix-assigned CPU core ID at boot time. this is separate from the hardware-assigned
            ID number, which may be non-linear. the runtime-generated core ID will
            run from zero to N-1 where N is the number of available cores */
    pub fn init(id: PhysicalCoreID)
    {
        /* the pre-hvmain startup code has allocated space for per-CPU core variables.
        this function returns a pointer to that structure */
        let mut cpu = PhysicalCore::this();

        cpu.magic = PCORE_MAGIC;
        cpu.id = id;
        cpu.features = platform::cpu::features();
        cpu.smode = platform::cpu::features_priv_check(platform::cpu::PrivilegeMode::Supervisor);
        cpu.timer_sched_last = None;
        cpu.vcore_doomed = false;

        let (heap_ptr, heap_size) = PhysicalCore::get_heap_config();
        cpu.heap.init(heap_ptr, heap_size);

        cpu.queues = ScheduleQueues::new();

        /* create a mailbox for messages from other cores */
        message::create_mailbox(id);
    }

    /* return pointer to the calling CPU core's fixed private data structure */
    pub fn this() -> &'static mut PhysicalCore
    {
        unsafe { platform_cpu_private_variables() }
    }

    /* return Ok if magic hasn't been overwritten, or the overwrite value as an error code */
    pub fn integrity_check() -> Result<(), usize>
    {
        match PhysicalCore::this().magic
        {
            PCORE_MAGIC => Ok(()),
            other => Err(other)
        }
    }

    /* return CPU heap base and size set aside by the pre-hvmain boot code */
    fn get_heap_config() -> (*mut heap::HeapBlock, PhysMemSize)
    {
        unsafe { (platform_cpu_heap_base(), platform_cpu_heap_size()) }
    }

    /* return boot-assigned ID number */
    pub fn get_id() -> PhysicalCoreID { PhysicalCore::this().id }

    /* return features bitmask */
    pub fn get_features() -> CPUFeatures { PhysicalCore::this().features }

    /* return a structure describing this core */
    pub fn describe() -> platform::cpu::CPUDescription { platform::cpu::CPUDescription }

    /* return a virtual CPU core awaiting to run on this physical CPU core */
    pub fn dequeue() -> Option<VirtualCore>
    {
        PhysicalCore::this().queues.dequeue()
    }

    /* move a virtual CPU core onto this physical CPU's queue of virtual cores to run */
    pub fn queue(to_queue: VirtualCore)
    {
        PhysicalCore::this().queues.queue(to_queue)
    }

    /* return true if able to run supervisor code. a system management core
    that cannot or is not expected to run guest workloads should return false */
    pub fn smode_supported() -> bool
    {
        PhysicalCore::this().smode
    }

    /* return ID of capsule of the virtual CPU core this physical CPU core is running, or None for none */
    pub fn get_capsule_id() -> Option<CapsuleID>
    {
        if let Some(vcore) = VCORES.lock().get(&PhysicalCore::get_id())
        {
            Some(vcore.get_capsule_id())
        }
        else
        {
            None
        }
    }

    /* mark the running vcore as doomed, meaning after it's context switched out,
    drop it. this is useful when killing or restarting capsules, and
    the current set of vcores needs to be flushed from the scheduling system */
    pub fn doom_vcore(&mut self) { self.vcore_doomed = true; }

    /* ensure the running vcore is not doomed */
    pub fn approve_vcore(&mut self) { self.vcore_doomed = false; }

    /* return true if vcore is doomed, ie: must be discarded */
    pub fn is_vcore_doomed(&self) -> bool { self.vcore_doomed }

    /* update the running virtual core's timer IRQ target. we have to do this here because
    the virtual core is held in a locked data structure. leaving this function relocks
    the structure. it's unsafe to access the vcore struct */
    pub fn set_virtualcore_timer_target(target: Option<timer::TimerValue>)
    {
        if let Some(vcore) = VCORES.lock().get_mut(&(PhysicalCore::get_id()))
        {
            vcore.set_timer_irq_at(target);
        }
    }

    /* get the virtual core's timer IRQ target */
    pub fn get_virtualcore_timer_target() -> Option<timer::TimerValue>
    {
        if let Some(vcore) = VCORES.lock().get_mut(&(PhysicalCore::get_id()))
        {
            return vcore.get_timer_irq_at();
        }
        None
    }

    /* return canonical ID for the virtual core running in the capsule on this CPU, if any */
    pub fn get_virtualcore_id(&self) -> Option<VirtualCoreCanonicalID>
    {
        let cid = match PhysicalCore::get_capsule_id()
        {
            Some(id) => id,
            None => return None
        };

        let vid = match VCORES.lock().get(&PhysicalCore::get_id())
        {
            Some(vcore) => vcore.get_id(),
            None => return None
        };

        Some(VirtualCoreCanonicalID
        {
            capsuleid: cid,
            vcoreid: vid
        })
    }

    /* set the exact per-CPU timer value of the last time this physical core make a scheduling decision */
    pub fn set_timer_sched_last(&mut self, value: Option<timer::TimerValue>)
    {
        self.timer_sched_last = value;
    }

    /* get the exact per-CPU timer value of the last time this physical core make a scheduling decision */
    pub fn get_timer_sched_last(&mut self) -> Option<timer::TimerValue>
    {
        self.timer_sched_last
    }
}

/* save current virtual CPU core's context, if we're running one, and load next virtual core's context.
this should be called from an IRQ context as it preserves the interrupted code's context
and overwrites the context with the next virtual core's context, so returning to supervisor
mode will land us in the new context */
pub fn context_switch(next: VirtualCore)
{
    let next_capsule = next.get_capsule_id();
    let pcore_id = PhysicalCore::get_id();

    /* find what this physical core was running, if anything */
    match VCORES.lock().remove(&pcore_id)
    {
        Some(current_vcore) =>
        {
            let current_capsule = current_vcore.get_capsule_id();

            /* if we're switching to a virtual CPU core in another capsule then replace the
            current hardware access permissions so that we're only allowing access to the RAM assigned
            to the next capsule to run */
            if current_capsule != next_capsule
            {
                capsule::enforce(next_capsule);
            }

            /* if the current virtual core isn't doomed, queue the vcore
               on the waiting list. if it is doomed, drop it */
            if PhysicalCore::this().is_vcore_doomed() == false
            {
                platform::cpu::save_supervisor_state(current_vcore.state_as_ref());
                PhysicalCore::queue(current_vcore);
            }
        },
        None =>
        {
            /* if we were not running a virtual CPU core then ensure we return to supervisor mode
            rather than hypervisor mode */
            platform::cpu::prep_supervisor_return();
            /* and enforce its hardware access permissions */
            capsule::enforce(next_capsule);
        }
    }

    /* prepare next virtual core to run when we leave this IRQ context */
    platform::cpu::load_supervisor_state(next.state_as_ref());

    /* link next virtual core and capsule to this physical CPU */
    PCORES.lock().insert(VirtualCoreCanonicalID
        {
            vcoreid: next.get_id(),
            capsuleid: next_capsule
        },
        pcore_id);

    /* and add the virtual core to the running virtual cores set.
       the previous vcore entry will be dropped */
    VCORES.lock().insert(pcore_id, next);

    /* and ensure this switched-in vcore is not doomed */
    PhysicalCore::this().approve_vcore();
}
<|endoftext|>


<commit_before>// A variation on cares-event-loop.rs.
//
// Here we:
//
// - pass around an Arc<Mutex<c_ares::Channel>>, allowing us to make queries
// after setting the event loop running.
//
// - show one way of transforming the asynchronous c-ares interface into
// a synchronous, blocking interface by using a std::sync::mpsc::channel.
extern crate c_ares;
extern crate mio;

use std::collections::HashSet;
use std::error::Error;
use std::mem;
use std::os::unix::io;
use std::sync::{
    Arc,
    Mutex,
    mpsc,
};
use std::thread;

// Messages for the event loop.
enum CAresHandlerMessage {
    // 'Notify me when this file descriptor becomes readable, or writable'
    // The first bool is for 'readable' and the second is for 'writable'.  It's
    // allowed to set both of these - or neither, meaning 'I am no longer
    // interested in this file descriptor'.
    RegisterInterest(io::RawFd, bool, bool),

    // 'Shut down'.
    ShutDown,
}

struct CAresEventHandler {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    tracked_fds: HashSet<io::RawFd>,
}

impl CAresEventHandler {
    fn new(ares_channel: Arc<Mutex<c_ares::Channel>>) -> CAresEventHandler {
        CAresEventHandler {
            ares_channel: ares_channel,
            tracked_fds: HashSet::new(),
        }
    }
}

impl mio::Handler for CAresEventHandler {
    type Timeout = ();
    type Message = CAresHandlerMessage;

    // mio notifies us that a file descriptor is readable or writable, so we
    // tell the Channel the same.
    fn ready(
        &mut self,
        _event_loop: &mut mio::EventLoop<CAresEventHandler>,
        token: mio::Token,
        events: mio::EventSet) {
        let fd = token.as_usize() as io::RawFd;
        let read_fd = if events.is_readable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let write_fd = if events.is_writable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let mut ares_channel = self.ares_channel.lock().unwrap();
        ares_channel.process_fd(read_fd, write_fd);
    }

    // Process received messages.  Either:
    // - we're asked to register interest (or non-interest) in a file
    // descriptor
    // - we're asked to shut down the event loop.
    fn notify(
        &mut self,
        event_loop:&mut mio::EventLoop<CAresEventHandler>,
        msg: Self::Message) {
        match msg {
            CAresHandlerMessage::RegisterInterest(fd, readable, writable) => {
                let io = mio::Io::from(fd);
                if !readable && !writable {
                    self.tracked_fds.remove(&fd);
                    event_loop
                        .deregister(&io)
                        .ok()
                        .expect("failed to deregister interest");
                } else {
                    let mut interest = mio::EventSet::none();
                    if readable {
                        interest = interest | mio::EventSet::readable();
                    }
                    if writable {
                        interest = interest | mio::EventSet::writable();
                    }
                    let token = mio::Token(fd as usize);
                    let register_result = if !self.tracked_fds.insert(fd) {
                        event_loop
                            .reregister(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                   } else {
                        event_loop
                            .register_opt(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                    };
                    register_result.ok().expect("failed to register interest");
                }

                // Don't accidentally close the file descriptor by dropping io!
                mem::forget(io);
            },

            CAresHandlerMessage::ShutDown => event_loop.shutdown(),
        }
    }

    // We run a recurring timer so that we can spot non-responsive servers.
    //
    // In that case we won't get a callback saying that anything is happening
    // on any file descriptor, but nevertheless need to give the Channel an
    // opportunity to notice that it has timed-out requests pending.
    fn timeout(
        &mut self,
        event_loop: &mut mio::EventLoop<CAresEventHandler>,
        _timeout: Self::Timeout) {
        event_loop.timeout_ms((), 500).unwrap();
        let mut ares_channel = self.ares_channel.lock().unwrap();
        ares_channel.process_fd(c_ares::INVALID_FD, c_ares::INVALID_FD);
    }
}

struct Resolver {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
}

impl Resolver {
    // Create a new Resolver.
    pub fn new(ares_channel: Arc<Mutex<c_ares::Channel>>) -> Resolver {
        Resolver {
            ares_channel: ares_channel,
        }
    }

    // A blocking CNAME query.  Achieve this by having the callback send the
    // result to a std::sync::mpsc::channel, and waiting on that channel.
    pub fn query_cname(&self, name: &str)
        -> Result<c_ares::CNameResult, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_cname(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }

    // A blocking MX query.
    pub fn query_mx(&self, name: &str)
        -> Result<c_ares::MXResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_mx(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }

    // A blocking NAPTR query.
    pub fn query_naptr(&self, name: &str)
        -> Result<c_ares::NAPTRResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_naptr(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }
}

fn print_cname_result(result: Result<c_ares::CNameResult, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("CNAME lookup failed with error '{}'", e.description());
        }
        Ok(cname_result) => {
            println!("Successful CNAME lookup...");
            println!("{}", cname_result.cname());
        }
    }
}

fn print_mx_results(result: Result<c_ares::MXResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("MX lookup failed with error '{}'", e.description());
        }
        Ok(mx_results) => {
            println!("Successful MX lookup...");
            for mx_result in &mx_results {
                println!(
                    "host {}, priority {}",
                    mx_result.host(),
                    mx_result.priority());
            }
        }
    }
}

fn print_naptr_results(
    result: Result<c_ares::NAPTRResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("NAPTR lookup failed with error '{}'", e.description());
        }
        Ok(naptr_results) => {
            println!("Successful NAPTR lookup...");
            for naptr_result in &naptr_results {
                println!("flags: {}", naptr_result.flags());
                println!("service name: {}", naptr_result.service_name());
                println!("regular expression: {}", naptr_result.reg_exp());
                println!(
                    "replacement pattern: {}",
                    naptr_result.replacement_pattern());
                println!("order: {}", naptr_result.order());
                println!("preference: {}", naptr_result.preference());
            }
        }
    }
}

fn main() {
    // Create an event loop.
    let mut event_loop = mio::EventLoop::new()
        .ok()
        .expect("failed to create event loop");
    let event_loop_channel = event_loop.channel();

    // Socket state callback for the c_ares::Channel will be to kick the event
    // loop.
    let event_loop_channel_clone = event_loop_channel.clone();
    let sock_callback = move |fd: io::RawFd, readable: bool, writable: bool| {
        let _ = event_loop_channel_clone
            .send(
                CAresHandlerMessage::RegisterInterest(fd, readable, writable));
    };

    // Create a c_ares::Channel.
    let mut options = c_ares::Options::new();
    options
        .set_socket_state_callback(sock_callback)
        .set_flags(c_ares::flags::STAYOPEN | c_ares::flags::EDNS)
        .set_timeout(500)
        .set_tries(3);
    let ares_channel = c_ares::Channel::new(options)
        .ok()
        .expect("Failed to create channel");
    let locked_channel = Arc::new(Mutex::new(ares_channel));

    // Set the first instance of the recurring timer on the event loop.
    event_loop.timeout_ms((), 500).unwrap();

    // Kick off the event loop.
    let mut event_handler = CAresEventHandler::new(locked_channel.clone());
    let handle = thread::spawn(move || {
        event_loop
            .run(&mut event_handler)
            .ok()
            .expect("failed to run event loop")
    });

    // Wrap the channel in a Resolver, which transforms our queries into
    // synchronous requests.  Then make some requests.
    let resolver = Resolver::new(locked_channel);
    let result = resolver.query_cname("dimbleby.github.io");
    println!("");
    print_cname_result(result);

    let result = resolver.query_mx("gmail.com");
    println!("");
    print_mx_results(result);

    let result = resolver.query_naptr("4.3.2.1.5.5.5.0.0.8.1.e164.arpa");
    println!("");
    print_naptr_results(result);

    // Shut down the event loop and wait for it to finish.
    event_loop_channel
        .send(CAresHandlerMessage::ShutDown)
        .ok()
        .expect("failed to shut down event loop");
    handle.join().unwrap();
}
<commit_msg>Show how to hide the event loop behind a 'Resolver'<commit_after>// A variation on cares-event-loop.rs.
//
// Here we:
//
// - Have the event loop take an `Arc<Mutex<Channel>>`, so that even after it
// is running we still have the ability to submit new queries
//
// - Have the event loop be run by a `Resolver`, hiding away the event loop
// details from the writer of `main()`.
//
// - Show one way of transforming the asynchronous c-ares interface into
// a synchronous, blocking interface by using a std::sync::mpsc::channel.
extern crate c_ares;
extern crate mio;

use std::collections::HashSet;
use std::error::Error;
use std::mem;
use std::os::unix::io;
use std::sync::{
    Arc,
    Mutex,
    mpsc,
};
use std::thread;

// Messages for the event loop.
enum CAresHandlerMessage {
    // 'Notify me when this file descriptor becomes readable, or writable'
    // The first bool is for 'readable' and the second is for 'writable'.  It's
    // allowed to set both of these - or neither, meaning 'I am no longer
    // interested in this file descriptor'.
    RegisterInterest(io::RawFd, bool, bool),

    // 'Shut down'.
    ShutDown,
}

struct CAresEventHandler {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    tracked_fds: HashSet<io::RawFd>,
}

impl CAresEventHandler {
    fn new(ares_channel: Arc<Mutex<c_ares::Channel>>) -> CAresEventHandler {
        CAresEventHandler {
            ares_channel: ares_channel,
            tracked_fds: HashSet::new(),
        }
    }
}

impl mio::Handler for CAresEventHandler {
    type Timeout = ();
    type Message = CAresHandlerMessage;

    // mio notifies us that a file descriptor is readable or writable, so we
    // tell the Channel the same.
    fn ready(
        &mut self,
        _event_loop: &mut mio::EventLoop<CAresEventHandler>,
        token: mio::Token,
        events: mio::EventSet) {
        let fd = token.as_usize() as io::RawFd;
        let read_fd = if events.is_readable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let write_fd = if events.is_writable() {
            fd
        } else {
            c_ares::INVALID_FD
        };
        let mut ares_channel = self.ares_channel.lock().unwrap();
        ares_channel.process_fd(read_fd, write_fd);
    }

    // Process received messages.  Either:
    // - we're asked to register interest (or non-interest) in a file
    // descriptor
    // - we're asked to shut down the event loop.
    fn notify(
        &mut self,
        event_loop:&mut mio::EventLoop<CAresEventHandler>,
        msg: Self::Message) {
        match msg {
            CAresHandlerMessage::RegisterInterest(fd, readable, writable) => {
                let io = mio::Io::from(fd);
                if !readable && !writable {
                    self.tracked_fds.remove(&fd);
                    event_loop
                        .deregister(&io)
                        .ok()
                        .expect("failed to deregister interest");
                } else {
                    let mut interest = mio::EventSet::none();
                    if readable {
                        interest = interest | mio::EventSet::readable();
                    }
                    if writable {
                        interest = interest | mio::EventSet::writable();
                    }
                    let token = mio::Token(fd as usize);
                    let register_result = if !self.tracked_fds.insert(fd) {
                        event_loop
                            .reregister(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                   } else {
                        event_loop
                            .register_opt(
                                &io,
                                token,
                                interest,
                                mio::PollOpt::edge())
                    };
                    register_result.ok().expect("failed to register interest");
                }

                // Don't accidentally close the file descriptor by dropping io!
                mem::forget(io);
            },

            CAresHandlerMessage::ShutDown => event_loop.shutdown(),
        }
    }

    // We run a recurring timer so that we can spot non-responsive servers.
    //
    // In that case we won't get a callback saying that anything is happening
    // on any file descriptor, but nevertheless need to give the Channel an
    // opportunity to notice that it has timed-out requests pending.
    fn timeout(
        &mut self,
        event_loop: &mut mio::EventLoop<CAresEventHandler>,
        _timeout: Self::Timeout) {
        event_loop.timeout_ms((), 500).unwrap();
        let mut ares_channel = self.ares_channel.lock().unwrap();
        ares_channel.process_fd(c_ares::INVALID_FD, c_ares::INVALID_FD);
    }
}

struct Resolver {
    ares_channel: Arc<Mutex<c_ares::Channel>>,
    event_loop_channel: mio::Sender<CAresHandlerMessage>,
    event_loop_handle: Option<thread::JoinHandle<()>>,
}

impl Resolver {
    // Create a new Resolver.
    pub fn new() -> Resolver {
        // Create an event loop.
        let mut event_loop = mio::EventLoop::new()
            .ok()
            .expect("failed to create event loop");
        let event_loop_channel = event_loop.channel();

        // Socket state callback for the c_ares::Channel will be to kick the
        // event loop.
        let event_loop_channel_clone = event_loop_channel.clone();
        let sock_callback =
            move |fd: io::RawFd, readable: bool, writable: bool| {
                let _ = event_loop_channel_clone
                    .send(
                        CAresHandlerMessage::RegisterInterest(
                            fd,
                            readable,
                            writable));
            };

        // Create a c_ares::Channel.
        let mut options = c_ares::Options::new();
        options
            .set_socket_state_callback(sock_callback)
            .set_flags(c_ares::flags::STAYOPEN | c_ares::flags::EDNS)
            .set_timeout(500)
            .set_tries(3);
        let ares_channel = c_ares::Channel::new(options)
            .ok()
            .expect("Failed to create channel");
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Set the first instance of the recurring timer on the event loop.
        event_loop.timeout_ms((), 500).unwrap();

        // Kick off the event loop.
        let mut event_handler = CAresEventHandler::new(locked_channel.clone());
        let event_loop_handle = thread::spawn(move || {
            event_loop
                .run(&mut event_handler)
                .ok()
                .expect("failed to run event loop")
        });

        Resolver {
            ares_channel: locked_channel,
            event_loop_channel: event_loop_channel,
            event_loop_handle: Some(event_loop_handle),
        }
    }

    // A blocking CNAME query.  Achieve this by having the callback send the
    // result to a std::sync::mpsc::channel, and waiting on that channel.
    pub fn query_cname(&self, name: &str)
        -> Result<c_ares::CNameResult, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_cname(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }

    // A blocking MX query.
    pub fn query_mx(&self, name: &str)
        -> Result<c_ares::MXResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_mx(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }

    // A blocking NAPTR query.
    pub fn query_naptr(&self, name: &str)
        -> Result<c_ares::NAPTRResults, c_ares::AresError> {
        let (tx, rx) = mpsc::channel();
        {
            let mut ares_channel = self.ares_channel.lock().unwrap();
            ares_channel.query_naptr(name, move |result| {
                tx.send(result).unwrap();
            });
        }
        rx.recv().unwrap()
    }
}

impl Drop for Resolver {
    fn drop(&mut self) {
        // Shut down the event loop and wait for it to finish.
        self.event_loop_channel
            .send(CAresHandlerMessage::ShutDown)
            .ok()
            .expect("failed to shut down event loop");
       for handle in self.event_loop_handle.take() {
           handle.join().unwrap();
       }
    }
}

fn print_cname_result(result: Result<c_ares::CNameResult, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("CNAME lookup failed with error '{}'", e.description());
        }
        Ok(cname_result) => {
            println!("Successful CNAME lookup...");
            println!("{}", cname_result.cname());
        }
    }
}

fn print_mx_results(result: Result<c_ares::MXResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("MX lookup failed with error '{}'", e.description());
        }
        Ok(mx_results) => {
            println!("Successful MX lookup...");
            for mx_result in &mx_results {
                println!(
                    "host {}, priority {}",
                    mx_result.host(),
                    mx_result.priority());
            }
        }
    }
}

fn print_naptr_results(
    result: Result<c_ares::NAPTRResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("NAPTR lookup failed with error '{}'", e.description());
        }
        Ok(naptr_results) => {
            println!("Successful NAPTR lookup...");
            for naptr_result in &naptr_results {
                println!("flags: {}", naptr_result.flags());
                println!("service name: {}", naptr_result.service_name());
                println!("regular expression: {}", naptr_result.reg_exp());
                println!(
                    "replacement pattern: {}",
                    naptr_result.replacement_pattern());
                println!("order: {}", naptr_result.order());
                println!("preference: {}", naptr_result.preference());
            }
        }
    }
}

fn main() {
    // Create a Resolver.  Then make some requests.
    let resolver = Resolver::new();
    let result = resolver.query_cname("dimbleby.github.io");
    println!("");
    print_cname_result(result);

    let result = resolver.query_mx("gmail.com");
    println!("");
    print_mx_results(result);

    let result = resolver.query_naptr("4.3.2.1.5.5.5.0.0.8.1.e164.arpa");
    println!("");
    print_naptr_results(result);
}
<|endoftext|>