<commit_before>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//! Snapshot listing plugin
//!
//! This module is responsible for continually discovering snapshots of filesystems.
//!
//!

use crate::{
    action_plugins::lustre::snapshot,
    agent_error::ImlAgentError,
    daemon_plugins::{DaemonPlugin, Output},
    lustre::lctl,
};
use async_trait::async_trait;
use futures::{
    future::{join_all, AbortHandle, Abortable},
    lock::Mutex,
    Future, FutureExt,
};
use iml_wire_types::snapshot::{List, Snapshot};
use std::collections::BTreeSet;
use std::{pin::Pin, sync::Arc, time::Duration};
use tokio::time::delay_for;

struct State {
    updated: bool,
    output: Output,
}

#[derive(Debug)]
pub(crate) struct SnapshotList {
    reader: Option<AbortHandle>,
    state: Arc<Mutex<State>>,
}

pub(crate) fn create() -> SnapshotList {
    SnapshotList {
        reader: None,
        state: Arc::new(Mutex::new(State {
            updated: false,
            output: None,
        })),
    }
}

async fn list() -> Result<Vec<Snapshot>, ()> {
    let fss: Vec<String> = lctl(vec!["get_param", "-N", "mgs.MGS.live.*"])
        .await
        .map_err(|e| {
            // XXX debug because of false positives
            tracing::debug!("listing filesystems failed: {}", e);
        })
        .map(|o| {
            o.lines()
                .map(|line| line.split('.').nth(3).unwrap().to_string())
                .filter(|n| n != "params")
                .collect()
        })?;

    tracing::debug!("filesystems: {:?}", &fss);

    let futs = fss.into_iter().map(|fs| async move {
        snapshot::list(List {
            fsname: fs.clone(),
            name: None,
        })
        .await
        .map_err(|e| (fs, e))
    });

    let (oks, errs): (Vec<_>, Vec<_>) = join_all(futs).await.into_iter().partition(Result::is_ok);

    let snaps = oks
        .into_iter()
        .map(|x| x.unwrap())
        .flatten()
        .collect::<Vec<Snapshot>>();

    let snapshot_fsnames = snaps
        .iter()
        .map(|s| &s.snapshot_fsname)
        .collect::<BTreeSet<&String>>();

    let really_failed_fss = errs
        .into_iter()
        .map(|x| x.unwrap_err())
        .filter(|x| !snapshot_fsnames.contains(&x.0))
        .collect::<Vec<_>>();

    if !really_failed_fss.is_empty() {
        // XXX debug because of false positives
        tracing::debug!("listing failed: {:?}", really_failed_fss);
    }
    Ok(snaps)
}

#[async_trait]
impl DaemonPlugin for SnapshotList {
    fn start_session(
        &mut self,
    ) -> Pin<Box<dyn Future<Output = Result<Output, ImlAgentError>> + Send>> {
        let state = Arc::clone(&self.state);

        let (reader, reader_reg) = AbortHandle::new_pair();
        self.reader = Some(reader);

        async move {
            tokio::spawn(Abortable::new(
                async move {
                    loop {
                        if let Ok(snapshots) = list().await {
                            tracing::debug!("snapshots ({}): {:?}", snapshots.len(), &snapshots);

                            let output = serde_json::to_value(snapshots).map(Some).unwrap();
                            let mut lock = state.lock().await;
                            if lock.output != output {
                                lock.output = output;
                                lock.updated = true;
                            }
                        }
                        delay_for(Duration::from_secs(60)).await;
                    }
                },
                reader_reg,
            ));
            Ok(None)
        }
        .boxed()
    }

    fn update_session(
        &self,
    ) -> Pin<Box<dyn Future<Output = Result<Output, ImlAgentError>> + Send>> {
        let state = Arc::clone(&self.state);
        async move {
            let mut lock = state.lock().await;

            if lock.updated {
                lock.updated = false;
                Ok(lock.output.clone())
            } else {
                Ok(None)
            }
        }
        .boxed()
    }

    async fn teardown(&mut self) -> Result<(), ImlAgentError> {
        if let Some(reader) = &self.reader {
            reader.abort();
            self.reader = None;
        }

        Ok(())
    }
}
<commit_msg>Lower snapshot listing interval (#2276)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//! Snapshot listing plugin
//!
//! This module is responsible for continually discovering snapshots of filesystems.
//!
//!

use crate::{
    action_plugins::lustre::snapshot,
    agent_error::ImlAgentError,
    daemon_plugins::{DaemonPlugin, Output},
    lustre::lctl,
};
use async_trait::async_trait;
use futures::{
    future::{join_all, AbortHandle, Abortable},
    lock::Mutex,
    Future, FutureExt,
};
use iml_wire_types::snapshot::{List, Snapshot};
use std::collections::BTreeSet;
use std::{pin::Pin, sync::Arc, time::Duration};
use tokio::time::delay_for;

struct State {
    updated: bool,
    output: Output,
}

#[derive(Debug)]
pub(crate) struct SnapshotList {
    reader: Option<AbortHandle>,
    state: Arc<Mutex<State>>,
}

pub(crate) fn create() -> SnapshotList {
    SnapshotList {
        reader: None,
        state: Arc::new(Mutex::new(State {
            updated: false,
            output: None,
        })),
    }
}

async fn list() -> Result<Vec<Snapshot>, ()> {
    let fss: Vec<String> = lctl(vec!["get_param", "-N", "mgs.MGS.live.*"])
        .await
        .map_err(|e| {
            // XXX debug because of false positives
            tracing::debug!("listing filesystems failed: {}", e);
        })
        .map(|o| {
            o.lines()
                .map(|line| line.split('.').nth(3).unwrap().to_string())
                .filter(|n| n != "params")
                .collect()
        })?;

    tracing::debug!("filesystems: {:?}", &fss);

    let futs = fss.into_iter().map(|fs| async move {
        snapshot::list(List {
            fsname: fs.clone(),
            name: None,
        })
        .await
        .map_err(|e| (fs, e))
    });

    let (oks, errs): (Vec<_>, Vec<_>) = join_all(futs).await.into_iter().partition(Result::is_ok);

    let snaps = oks
        .into_iter()
        .map(|x| x.unwrap())
        .flatten()
        .collect::<Vec<Snapshot>>();

    let snapshot_fsnames = snaps
        .iter()
        .map(|s| &s.snapshot_fsname)
        .collect::<BTreeSet<&String>>();

    let really_failed_fss = errs
        .into_iter()
        .map(|x| x.unwrap_err())
        .filter(|x| !snapshot_fsnames.contains(&x.0))
        .collect::<Vec<_>>();

    if !really_failed_fss.is_empty() {
        // XXX debug because of false positives
        tracing::debug!("listing failed: {:?}", really_failed_fss);
    }
    Ok(snaps)
}

#[async_trait]
impl DaemonPlugin for SnapshotList {
    fn start_session(
        &mut self,
    ) -> Pin<Box<dyn Future<Output = Result<Output, ImlAgentError>> + Send>> {
        let state = Arc::clone(&self.state);

        let (reader, reader_reg) = AbortHandle::new_pair();
        self.reader = Some(reader);

        async move {
            tokio::spawn(Abortable::new(
                async move {
                    loop {
                        if let Ok(snapshots) = list().await {
                            tracing::debug!("snapshots ({}): {:?}", snapshots.len(), &snapshots);

                            let output = serde_json::to_value(snapshots).map(Some).unwrap();
                            let mut lock = state.lock().await;
                            if lock.output != output {
                                lock.output = output;
                                lock.updated = true;
                            }
                        }
                        delay_for(Duration::from_secs(10)).await;
                    }
                },
                reader_reg,
            ));
            Ok(None)
        }
        .boxed()
    }

    fn update_session(
        &self,
    ) -> Pin<Box<dyn Future<Output = Result<Output, ImlAgentError>> + Send>> {
        let state = Arc::clone(&self.state);
        async move {
            let mut lock = state.lock().await;

            if lock.updated {
                lock.updated = false;
                Ok(lock.output.clone())
            } else {
                Ok(None)
            }
        }
        .boxed()
    }

    async fn teardown(&mut self) -> Result<(), ImlAgentError> {
        if let Some(reader) = &self.reader {
            reader.abort();
            self.reader = None;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use core::ptr::Unique;

use memory::{allocate_frame, deallocate_frame, Frame};

use super::{Page, PAGE_SIZE, PhysicalAddress, VirtualAddress};
use super::entry::{self, EntryFlags};
use super::table::{self, Table, Level4};

pub struct Mapper {
    p4: Unique<Table<Level4>>,
}

impl Mapper {
    /// Create a new page table
    pub unsafe fn new() -> Mapper {
        Mapper {
            p4: Unique::new(table::P4),
        }
    }

    pub fn p4(&self) -> &Table<Level4> {
        unsafe { self.p4.get() }
    }

    pub fn p4_mut(&mut self) -> &mut Table<Level4> {
        unsafe { self.p4.get_mut() }
    }

    /// Map a page to a frame
    pub fn map_to(&mut self, page: Page, frame: Frame, flags: EntryFlags) {
        let mut p3 = self.p4_mut().next_table_create(page.p4_index());
        let mut p2 = p3.next_table_create(page.p3_index());
        let mut p1 = p2.next_table_create(page.p2_index());

        assert!(p1[page.p1_index()].is_unused(),
            "{:X}: Set to {:X}: {:?}, requesting {:X}: {:?}",
            page.start_address().get(),
            p1[page.p1_index()].address().get(), p1[page.p1_index()].flags(),
            frame.start_address().get(), flags);
        p1[page.p1_index()].set(frame, flags | entry::PRESENT);
    }

    /// Map a page to the next free frame
    pub fn map(&mut self, page: Page, flags: EntryFlags) {
        let frame = allocate_frame().expect("out of frames");
        self.map_to(page, frame, flags)
    }

    /// Update flags for a page
    pub fn remap(&mut self, page: Page, flags: EntryFlags) {
        let mut p3 = self.p4_mut().next_table_mut(page.p4_index()).expect("failed to remap: no p3");
        let mut p2 = p3.next_table_mut(page.p3_index()).expect("failed to remap: no p2");
        let mut p1 = p2.next_table_mut(page.p2_index()).expect("failed to remap: no p1");
        let frame = p1[page.p1_index()].pointed_frame().expect("failed to remap: not mapped");
        p1[page.p1_index()].set(frame, flags | entry::PRESENT);
    }

    /// Identity map a frame
    pub fn identity_map(&mut self, frame: Frame, flags: EntryFlags) {
        let page = Page::containing_address(VirtualAddress::new(frame.start_address().get()));
        self.map_to(page, frame, flags)
    }

    /// Unmap a page
    pub fn unmap(&mut self, page: Page) {
        let p1 = self.p4_mut()
                     .next_table_mut(page.p4_index())
                     .and_then(|p3| p3.next_table_mut(page.p3_index()))
                     .and_then(|p2| p2.next_table_mut(page.p2_index()))
                     .expect("unmap does not support huge pages");
        let frame = p1[page.p1_index()].pointed_frame().unwrap();
        p1[page.p1_index()].set_unused();
        // TODO free p(1,2,3) table if empty
        deallocate_frame(frame);
    }

    /// Unmap a page, return frame without free
    pub fn unmap_return(&mut self, page: Page) -> Frame {
        let p1 = self.p4_mut()
                     .next_table_mut(page.p4_index())
                     .and_then(|p3| p3.next_table_mut(page.p3_index()))
                     .and_then(|p2| p2.next_table_mut(page.p2_index()))
                     .expect("unmap_return code does not support huge pages");
        let frame = p1[page.p1_index()].pointed_frame().unwrap();
        p1[page.p1_index()].set_unused();
        frame
    }

    pub fn translate_page(&self, page: Page) -> Option<Frame> {
        self.p4().next_table(page.p4_index())
            .and_then(|p3| p3.next_table(page.p3_index()))
            .and_then(|p2| p2.next_table(page.p2_index()))
            .and_then(|p1| p1[page.p1_index()].pointed_frame())
    }

    pub fn translate_page_flags(&self, page: Page) -> Option<EntryFlags> {
        self.p4().next_table(page.p4_index())
            .and_then(|p3| p3.next_table(page.p3_index()))
            .and_then(|p2| p2.next_table(page.p2_index()))
            .and_then(|p1| Some(p1[page.p1_index()].flags()))
    }

    /// Translate a virtual address to a physical one
    pub fn translate(&self, virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
        let offset = virtual_address.get() % PAGE_SIZE;
        self.translate_page(Page::containing_address(virtual_address))
            .map(|frame| PhysicalAddress::new(frame.start_address().get() + offset))
    }
}
<commit_msg>Fix unmap_return panic<commit_after>use core::ptr::Unique;

use memory::{allocate_frame, deallocate_frame, Frame};

use super::{Page, PAGE_SIZE, PhysicalAddress, VirtualAddress};
use super::entry::{self, EntryFlags};
use super::table::{self, Table, Level4};

pub struct Mapper {
    p4: Unique<Table<Level4>>,
}

impl Mapper {
    /// Create a new page table
    pub unsafe fn new() -> Mapper {
        Mapper {
            p4: Unique::new(table::P4),
        }
    }

    pub fn p4(&self) -> &Table<Level4> {
        unsafe { self.p4.get() }
    }

    pub fn p4_mut(&mut self) -> &mut Table<Level4> {
        unsafe { self.p4.get_mut() }
    }

    /// Map a page to a frame
    pub fn map_to(&mut self, page: Page, frame: Frame, flags: EntryFlags) {
        let mut p3 = self.p4_mut().next_table_create(page.p4_index());
        let mut p2 = p3.next_table_create(page.p3_index());
        let mut p1 = p2.next_table_create(page.p2_index());

        assert!(p1[page.p1_index()].is_unused(),
            "{:X}: Set to {:X}: {:?}, requesting {:X}: {:?}",
            page.start_address().get(),
            p1[page.p1_index()].address().get(), p1[page.p1_index()].flags(),
            frame.start_address().get(), flags);
        p1[page.p1_index()].set(frame, flags | entry::PRESENT);
    }

    /// Map a page to the next free frame
    pub fn map(&mut self, page: Page, flags: EntryFlags) {
        let frame = allocate_frame().expect("out of frames");
        self.map_to(page, frame, flags)
    }

    /// Update flags for a page
    pub fn remap(&mut self, page: Page, flags: EntryFlags) {
        let mut p3 = self.p4_mut().next_table_mut(page.p4_index()).expect("failed to remap: no p3");
        let mut p2 = p3.next_table_mut(page.p3_index()).expect("failed to remap: no p2");
        let mut p1 = p2.next_table_mut(page.p2_index()).expect("failed to remap: no p1");
        let frame = p1[page.p1_index()].pointed_frame().expect("failed to remap: not mapped");
        p1[page.p1_index()].set(frame, flags | entry::PRESENT);
    }

    /// Identity map a frame
    pub fn identity_map(&mut self, frame: Frame, flags: EntryFlags) {
        let page = Page::containing_address(VirtualAddress::new(frame.start_address().get()));
        self.map_to(page, frame, flags)
    }

    /// Unmap a page
    pub fn unmap(&mut self, page: Page) {
        let p1 = self.p4_mut()
                     .next_table_mut(page.p4_index())
                     .and_then(|p3| p3.next_table_mut(page.p3_index()))
                     .and_then(|p2| p2.next_table_mut(page.p2_index()))
                     .expect("unmap does not support huge pages");
        let frame = p1[page.p1_index()].pointed_frame().unwrap();
        p1[page.p1_index()].set_unused();
        // TODO free p(1,2,3) table if empty
        deallocate_frame(frame);
    }

    /// Unmap a page, return frame without free
    pub fn unmap_return(&mut self, page: Page) -> Frame {
        let p1 = self.p4_mut()
                     .next_table_mut(page.p4_index())
                     .and_then(|p3| p3.next_table_mut(page.p3_index()))
                     .and_then(|p2| p2.next_table_mut(page.p2_index()))
                     .expect("unmap_return does not support huge pages");
        let frame = p1[page.p1_index()].pointed_frame().unwrap();
        p1[page.p1_index()].set_unused();
        frame
    }

    pub fn translate_page(&self, page: Page) -> Option<Frame> {
        self.p4().next_table(page.p4_index())
            .and_then(|p3| p3.next_table(page.p3_index()))
            .and_then(|p2| p2.next_table(page.p2_index()))
            .and_then(|p1| p1[page.p1_index()].pointed_frame())
    }

    pub fn translate_page_flags(&self, page: Page) -> Option<EntryFlags> {
        self.p4().next_table(page.p4_index())
            .and_then(|p3| p3.next_table(page.p3_index()))
            .and_then(|p2| p2.next_table(page.p2_index()))
            .and_then(|p1| Some(p1[page.p1_index()].flags()))
    }

    /// Translate a virtual address to a physical one
    pub fn translate(&self, virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
        let offset = virtual_address.get() % PAGE_SIZE;
        self.translate_page(Page::containing_address(virtual_address))
            .map(|frame| PhysicalAddress::new(frame.start_address().get() + offset))
    }
}
<|endoftext|>


<commit_before>use stewards::crossverified as s;
use {TimeSteward, DeterministicRandomId, Column, ColumnId, RowId, PredictorId, EventId, ColumnType, EventType, PredictorType};
// use serde_json;
use bincode;
use stewards::amortized;
use stewards::memoized_flat;
use stewards::simply_synchronized;

type Time = i64;

const HOW_MANY_PHILOSOPHERS: i32 = 7;

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Default)]
struct Basics;
impl ::Basics for Basics {
  type Time = Time;
  type Constants = ();
  type IncludedTypes = TimeStewardTypes;
}

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
struct Philosopher {
  // This is sometimes in the future because
  // they muse philosophically about handshakes
  // for a while, whenever one of them happens.
  time_when_next_initiates_handshake: Time,
}
impl Column for Philosopher {
  type FieldType = Self;
  fn column_id() -> ColumnId {
    ColumnId(0x4084d1501468b6dd)
  }
}

type Steward = s::Steward<Basics,
                          amortized::Steward<Basics>,
                          memoized_flat::Steward<Basics>>;

fn get_philosopher_id(index: i32) -> RowId {
  DeterministicRandomId::new(&(0x2302c38efb47e0d0u64, index))
}

type TimeStewardTypes = (ColumnType<Philosopher>, EventType <Initialize>, EventType <Tweak>, EventType <TweakUnsafe>, EventType <Shake>, PredictorType <Shaker>);

fn display_snapshot<S: ::Snapshot<Basics = Basics>>(snapshot: &S) {
  println!("snapshot for {}", snapshot.now());
  for index in 0..HOW_MANY_PHILOSOPHERS {
    println!("{}",
             snapshot.get::<Philosopher>(get_philosopher_id(index))
                     .expect("missing philosopher")
                     .time_when_next_initiates_handshake);
  }
}

    time_steward_predictor! (struct Shaker, Basics, PredictorId(0x0e7f27c7643f8167), Philosopher::column_id(), | pa, whodunnit | {
      println!("Planning {}", whodunnit);

      let me = pa.get::<Philosopher>(whodunnit).unwrap().clone();
      pa.predict_at_time(me.time_when_next_initiates_handshake, Shake::new (whodunnit));
    });

        time_steward_event! (struct Shake {whodunnit: RowId}, Basics, EventId (0x8987a0b8e7d3d624), | &self, m | {
          let now = *m.now();
          let friend_id = get_philosopher_id(m.gen_range(0, HOW_MANY_PHILOSOPHERS));
          let awaken_time_1 = now + m.gen_range(-1, 4);
          let awaken_time_2 = now + m.gen_range(-1, 7);
          println!("SHAKE!!! @{}. {}={}; {}={}", now, self.whodunnit, awaken_time_2, friend_id, awaken_time_1);
          // IF YOU SHAKE YOUR OWN HAND YOU RECOVER
          // IN THE SECOND TIME APPARENTLY
          m.set::<Philosopher>(friend_id,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time_1,
                                   }));
          m.set::<Philosopher>(self.whodunnit,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time_2,
                                   }));
        });

time_steward_event! (struct Initialize {}, Basics, EventId (0xd5e73d8ba6ec59a2), | &self, m | {
      println!("FIAT!!!!!");
      for i in 0..HOW_MANY_PHILOSOPHERS {
        m.set::<Philosopher>(get_philosopher_id(i),
          Some(Philosopher {
            time_when_next_initiates_handshake: (i + 1) as Time,
          })
        );
      }
    });

time_steward_event! (struct Tweak {}, Basics, EventId (0xfe9ff3047f9a9552), | &self, m | {
      println!(" Tweak !!!!!");
          let now = *m.now();
          let friend_id = get_philosopher_id(m.gen_range(0, HOW_MANY_PHILOSOPHERS));
          let awaken_time = now + m.gen_range(-1, 7);

          m.set::<Philosopher>(friend_id,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time,
                                   }));
    });
time_steward_event! (struct TweakUnsafe {}, Basics, EventId (0xfe9ff3047f9a9552), | &self, m | {
      println!(" Tweak !!!!!");
          let now = *m.now();
          let friend_id = get_philosopher_id(m.gen_range(0, HOW_MANY_PHILOSOPHERS));
          use rand::{self, Rng};
          let awaken_time = now + rand::thread_rng().gen_range(-1, 7);

          m.set::<Philosopher>(friend_id,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time,
                                   }));
    });

pub fn testfunc() {
  let mut stew: Steward = ::TimeSteward::new_empty(());

  stew.insert_fiat_event(0,
                       DeterministicRandomId::new(&0x32e1570766e768a7u64),
                       Initialize::new())
    .unwrap();

  let mut snapshots = Vec::new();
  for increment in 1..21 {
    snapshots.push(stew.snapshot_before(&(increment * 100i64)));
    stew = ::TimeSteward::from_snapshot::<<Steward as ::TimeSteward>::Snapshot> (snapshots.last().unwrap().as_ref().unwrap());
  }
  for snapshot in snapshots.iter_mut()
    .map(|option| option.as_mut().expect("all these snapshots should have been valid")) {
    display_snapshot(snapshot);
    let mut writer: Vec<u8> = Vec::with_capacity(128);
    ::serialize_snapshot:: <Basics, <Steward as TimeSteward>::Snapshot,_> (snapshot, &mut writer, bincode::SizeLimit::Infinite).unwrap();
    // let serialized = String::from_utf8 (serializer.into_inner()).unwrap();
    println!("{:?}", writer);
    use std::io::Cursor;
    let mut reader = Cursor::new (writer);
    let deserialized = ::deserialize_snapshot:: <Basics, _> (&mut reader, bincode::SizeLimit::Infinite/*serialized.as_bytes().iter().map (| bite | Ok (bite.clone()))*/).unwrap();
    println!("{:?}", deserialized);
    display_snapshot(&deserialized);
    use MomentaryAccessor;
    display_snapshot(&<Steward as ::TimeSteward>::from_snapshot::<::FiatSnapshot<Basics>>(&deserialized).snapshot_before(deserialized.now()).unwrap());
  }
  // panic!("anyway")
}

#[test]
fn actuallytest() {
  testfunc();
}

#[test]
fn local_synchronization_test() {
  use std::net::{TcpListener, TcpStream};
  use std::io::{BufReader, BufWriter};
  ::std::thread::spawn (| | {
    let end_0 = TcpListener::bind ("localhost:7324").unwrap().accept().unwrap().0;
    let mut stew_0: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&0u32), 0, 4, (), BufReader::new (end_0.try_clone().unwrap()), BufWriter::new (end_0));
    stew_0.insert_fiat_event(0, DeterministicRandomId::new(&0x32e1570766e768a7u64),
                       Initialize::new()).unwrap();

    for increment in 1..21 {
      let time =increment * 100i64;
      if increment % 3 == 0 {stew_0.insert_fiat_event(time, DeterministicRandomId::new(& increment), Tweak::new()).unwrap();}
      stew_0.snapshot_before(& time);
      stew_0.settle_before (time);
    }
    stew_0.finish ();
  });
  let end_1 = TcpStream::connect ("localhost:7324").unwrap();
  let mut stew_1: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&1u32), 0, 4, (), BufReader::new (end_1.try_clone().unwrap()), BufWriter::new (end_1));

  for increment in 1..21 {
    let time =increment * 100i64;
    if increment % 4 == 0 {stew_1.insert_fiat_event(time, DeterministicRandomId::new(& increment), Tweak::new()).unwrap();}
    stew_1.snapshot_before (& time);
    stew_1.settle_before (time);
  }
  stew_1.finish ();
}

#[test]
#[should_panic]
fn local_synchronization_failure() {
  use std::net::{TcpListener, TcpStream};
  use std::io::{BufReader, BufWriter};
  ::std::thread::spawn (| | {
    let end_0 = TcpListener::bind ("localhost:7324").unwrap().accept().unwrap().0;
    let mut stew_0: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&0u32), 0, 4, (), BufReader::new (end_0.try_clone().unwrap()), BufWriter::new (end_0));
    stew_0.insert_fiat_event(0, DeterministicRandomId::new(&0x32e1570766e768a7u64),
                       Initialize::new()).unwrap();

    for increment in 1..21 {
      let time =increment * 100i64;
      if increment % 3 == 0 {stew_0.insert_fiat_event(time, DeterministicRandomId::new(& increment), TweakUnsafe::new()).unwrap();}
      stew_0.snapshot_before(& time);
      stew_0.settle_before (time);
    }
    stew_0.finish ();
  });
  let end_1 = TcpStream::connect ("localhost:7324").unwrap();
  let mut stew_1: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&1u32), 0, 4, (), BufReader::new (end_1.try_clone().unwrap()), BufWriter::new (end_1));

  for increment in 1..21 {
    let time =increment * 100i64;
    if increment % 4 == 0 {stew_1.insert_fiat_event(time, DeterministicRandomId::new(& increment), TweakUnsafe::new()).unwrap();}
    stew_1.snapshot_before (& time);
    stew_1.settle_before (time);
  }
  stew_1.finish ();
}

<commit_msg>improve TCP self-connect code<commit_after>use stewards::crossverified as s;
use {TimeSteward, DeterministicRandomId, Column, ColumnId, RowId, PredictorId, EventId, ColumnType, EventType, PredictorType};
// use serde_json;
use bincode;
use stewards::amortized;
use stewards::memoized_flat;
use stewards::simply_synchronized;

type Time = i64;

const HOW_MANY_PHILOSOPHERS: i32 = 7;

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Default)]
struct Basics;
impl ::Basics for Basics {
  type Time = Time;
  type Constants = ();
  type IncludedTypes = TimeStewardTypes;
}

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
struct Philosopher {
  // This is sometimes in the future because
  // they muse philosophically about handshakes
  // for a while, whenever one of them happens.
  time_when_next_initiates_handshake: Time,
}
impl Column for Philosopher {
  type FieldType = Self;
  fn column_id() -> ColumnId {
    ColumnId(0x4084d1501468b6dd)
  }
}

type Steward = s::Steward<Basics,
                          amortized::Steward<Basics>,
                          memoized_flat::Steward<Basics>>;

fn get_philosopher_id(index: i32) -> RowId {
  DeterministicRandomId::new(&(0x2302c38efb47e0d0u64, index))
}

type TimeStewardTypes = (ColumnType<Philosopher>, EventType <Initialize>, EventType <Tweak>, EventType <TweakUnsafe>, EventType <Shake>, PredictorType <Shaker>);

fn display_snapshot<S: ::Snapshot<Basics = Basics>>(snapshot: &S) {
  println!("snapshot for {}", snapshot.now());
  for index in 0..HOW_MANY_PHILOSOPHERS {
    println!("{}",
             snapshot.get::<Philosopher>(get_philosopher_id(index))
                     .expect("missing philosopher")
                     .time_when_next_initiates_handshake);
  }
}

    time_steward_predictor! (struct Shaker, Basics, PredictorId(0x0e7f27c7643f8167), Philosopher::column_id(), | pa, whodunnit | {
      println!("Planning {}", whodunnit);

      let me = pa.get::<Philosopher>(whodunnit).unwrap().clone();
      pa.predict_at_time(me.time_when_next_initiates_handshake, Shake::new (whodunnit));
    });

        time_steward_event! (struct Shake {whodunnit: RowId}, Basics, EventId (0x8987a0b8e7d3d624), | &self, m | {
          let now = *m.now();
          let friend_id = get_philosopher_id(m.gen_range(0, HOW_MANY_PHILOSOPHERS));
          let awaken_time_1 = now + m.gen_range(-1, 4);
          let awaken_time_2 = now + m.gen_range(-1, 7);
          println!("SHAKE!!! @{}. {}={}; {}={}", now, self.whodunnit, awaken_time_2, friend_id, awaken_time_1);
          // IF YOU SHAKE YOUR OWN HAND YOU RECOVER
          // IN THE SECOND TIME APPARENTLY
          m.set::<Philosopher>(friend_id,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time_1,
                                   }));
          m.set::<Philosopher>(self.whodunnit,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time_2,
                                   }));
        });

time_steward_event! (struct Initialize {}, Basics, EventId (0xd5e73d8ba6ec59a2), | &self, m | {
      println!("FIAT!!!!!");
      for i in 0..HOW_MANY_PHILOSOPHERS {
        m.set::<Philosopher>(get_philosopher_id(i),
          Some(Philosopher {
            time_when_next_initiates_handshake: (i + 1) as Time,
          })
        );
      }
    });

time_steward_event! (struct Tweak {}, Basics, EventId (0xfe9ff3047f9a9552), | &self, m | {
      println!(" Tweak !!!!!");
          let now = *m.now();
          let friend_id = get_philosopher_id(m.gen_range(0, HOW_MANY_PHILOSOPHERS));
          let awaken_time = now + m.gen_range(-1, 7);

          m.set::<Philosopher>(friend_id,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time,
                                   }));
    });
time_steward_event! (struct TweakUnsafe {}, Basics, EventId (0xfe9ff3047f9a9552), | &self, m | {
      println!(" Tweak !!!!!");
          let now = *m.now();
          let friend_id = get_philosopher_id(m.gen_range(0, HOW_MANY_PHILOSOPHERS));
          use rand::{self, Rng};
          let awaken_time = now + rand::thread_rng().gen_range(-1, 7);

          m.set::<Philosopher>(friend_id,
                                   Some(Philosopher {
                                     time_when_next_initiates_handshake: awaken_time,
                                   }));
    });

pub fn testfunc() {
  let mut stew: Steward = ::TimeSteward::new_empty(());

  stew.insert_fiat_event(0,
                       DeterministicRandomId::new(&0x32e1570766e768a7u64),
                       Initialize::new())
    .unwrap();

  let mut snapshots = Vec::new();
  for increment in 1..21 {
    snapshots.push(stew.snapshot_before(&(increment * 100i64)));
    stew = ::TimeSteward::from_snapshot::<<Steward as ::TimeSteward>::Snapshot> (snapshots.last().unwrap().as_ref().unwrap());
  }
  for snapshot in snapshots.iter_mut()
    .map(|option| option.as_mut().expect("all these snapshots should have been valid")) {
    display_snapshot(snapshot);
    let mut writer: Vec<u8> = Vec::with_capacity(128);
    ::serialize_snapshot:: <Basics, <Steward as TimeSteward>::Snapshot,_> (snapshot, &mut writer, bincode::SizeLimit::Infinite).unwrap();
    // let serialized = String::from_utf8 (serializer.into_inner()).unwrap();
    println!("{:?}", writer);
    use std::io::Cursor;
    let mut reader = Cursor::new (writer);
    let deserialized = ::deserialize_snapshot:: <Basics, _> (&mut reader, bincode::SizeLimit::Infinite/*serialized.as_bytes().iter().map (| bite | Ok (bite.clone()))*/).unwrap();
    println!("{:?}", deserialized);
    display_snapshot(&deserialized);
    use MomentaryAccessor;
    display_snapshot(&<Steward as ::TimeSteward>::from_snapshot::<::FiatSnapshot<Basics>>(&deserialized).snapshot_before(deserialized.now()).unwrap());
  }
  // panic!("anyway")
}

#[test]
fn actuallytest() {
  testfunc();
}

#[test]
fn local_synchronization_test() {
  use std::net::{TcpListener, TcpStream};
  use std::io::{BufReader, BufWriter};
  let listener = TcpListener::bind(("127.0.0.1", 0)).unwrap();
  let port = listener.local_addr().unwrap().port();
  ::std::thread::spawn (move | | {
    let end_0 = listener.accept().unwrap().0;
    let mut stew_0: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&0u32), 0, 4, (), BufReader::new (end_0.try_clone().unwrap()), BufWriter::new (end_0));
    stew_0.insert_fiat_event(0, DeterministicRandomId::new(&0x32e1570766e768a7u64),
                       Initialize::new()).unwrap();

    for increment in 1..21 {
      let time =increment * 100i64;
      if increment % 3 == 0 {stew_0.insert_fiat_event(time, DeterministicRandomId::new(& increment), Tweak::new()).unwrap();}
      stew_0.snapshot_before(& time);
      stew_0.settle_before (time);
    }
    stew_0.finish ();
  });
  let end_1 = TcpStream::connect(("127.0.0.1", port)).unwrap();
  let mut stew_1: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&1u32), 0, 4, (), BufReader::new (end_1.try_clone().unwrap()), BufWriter::new (end_1));

  for increment in 1..21 {
    let time =increment * 100i64;
    if increment % 4 == 0 {stew_1.insert_fiat_event(time, DeterministicRandomId::new(& increment), Tweak::new()).unwrap();}
    stew_1.snapshot_before (& time);
    stew_1.settle_before (time);
  }
  stew_1.finish ();
}

#[test]
#[should_panic]
fn local_synchronization_failure() {
  use std::net::{TcpListener, TcpStream};
  use std::io::{BufReader, BufWriter};
  let listener = TcpListener::bind(("127.0.0.1", 0)).unwrap();
  let port = listener.local_addr().unwrap().port();
  ::std::thread::spawn (move | | {
    let end_0 = listener.accept().unwrap().0;
    let mut stew_0: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&0u32), 0, 4, (), BufReader::new (end_0.try_clone().unwrap()), BufWriter::new (end_0));
    stew_0.insert_fiat_event(0, DeterministicRandomId::new(&0x32e1570766e768a7u64),
                       Initialize::new()).unwrap();

    for increment in 1..21 {
      let time =increment * 100i64;
      if increment % 3 == 0 {stew_0.insert_fiat_event(time, DeterministicRandomId::new(& increment), TweakUnsafe::new()).unwrap();}
      stew_0.snapshot_before(& time);
      stew_0.settle_before (time);
    }
    stew_0.finish ();
  });
  let end_1 = TcpStream::connect(("127.0.0.1", port)).unwrap();
  let mut stew_1: simply_synchronized::Steward <Basics, amortized::Steward <Basics>> = simply_synchronized::Steward::new (DeterministicRandomId::new (&1u32), 0, 4, (), BufReader::new (end_1.try_clone().unwrap()), BufWriter::new (end_1));

  for increment in 1..21 {
    let time =increment * 100i64;
    if increment % 4 == 0 {stew_1.insert_fiat_event(time, DeterministicRandomId::new(& increment), TweakUnsafe::new()).unwrap();}
    stew_1.snapshot_before (& time);
    stew_1.settle_before (time);
  }
  stew_1.finish ();
}

<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! The executor runs all given futures to completion. Futures register wakers associated with file
//! descriptors. The wakers will be called when the FD becomes readable or writable depending on
//! the situation.
//!
//! `FdExecutor` is meant to be used with the `futures-rs` crate that provides combinators and
//! utility functions to combine futures.

use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt::{self, Display};
use std::fs::File;
use std::future::Future;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd;
use std::pin::Pin;
use std::task::Waker;

use slab::Slab;

use sys_util::{PollContext, WatchingEvents};

use crate::executor::{ExecutableFuture, Executor, FutureList};
use crate::WakerToken;

#[derive(Debug, PartialEq)]
pub enum Error {
    /// Attempts to create two Executors on the same thread fail.
    AttemptedDuplicateExecutor,
    /// Failed to copy the FD for the polling context.
    DuplicatingFd(sys_util::Error),
    /// Failed accessing the thread local storage for wakers.
    InvalidContext,
    /// Creating a context to wait on FDs failed.
    CreatingContext(sys_util::Error),
    /// PollContext failure.
    PollContextError(sys_util::Error),
    /// Failed to submit the waker to the polling context.
    SubmittingWaker(sys_util::Error),
    /// A Waker was canceled, but the operation isn't running.
    UnknownWaker,
}
pub type Result<T> = std::result::Result<T, Error>;

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            AttemptedDuplicateExecutor => write!(f, "Cannot have two executors on one thread."),
            DuplicatingFd(e) => write!(f, "Failed to copy the FD for the polling context: {}", e),
            InvalidContext => write!(
                f,
                "Invalid context, was the Fd executor created successfully?"
            ),
            CreatingContext(e) => write!(f, "An error creating the fd waiting context: {}.", e),
            PollContextError(e) => write!(f, "PollContext failure: {}", e),
            SubmittingWaker(e) => write!(f, "An error adding to the Aio context: {}.", e),
            UnknownWaker => write!(f, "Unknown waker"),
        }
    }
}

// Temporary vectors of new additions to the executor.

// Tracks active wakers and the futures they are associated with.
thread_local!(static STATE: RefCell<Option<FdWakerState>> = RefCell::new(None));

fn add_waker(fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<WakerToken> {
    STATE.with(|state| {
        let mut state = state.borrow_mut();
        if let Some(state) = state.as_mut() {
            state.add_waker(fd, waker, events)
        } else {
            Err(Error::InvalidContext)
        }
    })
}

/// A token returned from `add_waker` that can be used to cancel the waker before it completes.
/// Used to manage getting the result from the underlying executor for a completed operation.
/// Dropping a `PendingWaker` will get the result from the executor.
pub struct PendingWaker {
    token: Option<WakerToken>,
}

impl PendingWaker {
    pub(crate) fn new(token: WakerToken) -> PendingWaker {
        PendingWaker { token: Some(token) }
    }
}

impl Drop for PendingWaker {
    fn drop(&mut self) {
        if let Some(token) = self.token.take() {
            let _ = cancel_waker(token);
        }
    }
}

/// Tells the waking system to wake `waker` when `fd` becomes readable.
/// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
/// next time the future is polled. If the fd is closed, there is a race where another FD can be
/// opened on top of it causing the next poll to access the new target file.
/// Returns a `PendingWaker` that can be used to poll for completion or cancel the waker before it
/// completes.
pub(crate) fn add_read_waker(fd: RawFd, waker: Waker) -> Result<PendingWaker> {
    add_waker(fd, waker, WatchingEvents::empty().set_read()).map(PendingWaker::new)
}

/// Tells the waking system to wake `waker` when `fd` becomes writable.
/// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
/// next time the future is polled. If the fd is closed, there is a race where another FD can be
/// opened on top of it causing the next poll to access the new target file.
/// Returns a `PendingWaker` that can be used to poll for completion or cancel the waker before it
/// completes.
pub(crate) fn add_write_waker(fd: RawFd, waker: Waker) -> Result<PendingWaker> {
    add_waker(fd, waker, WatchingEvents::empty().set_write()).map(PendingWaker::new)
}

/// Cancels the waker that returned the given token if the waker hasn't yet fired.
pub(crate) fn cancel_waker(token: WakerToken) -> Result<()> {
    STATE.with(|state| {
        let mut state = state.borrow_mut();
        if let Some(state) = state.as_mut() {
            state.cancel_waker(token)
        } else {
            Err(Error::InvalidContext)
        }
    })
}

/// Adds a new top level future to the Executor.
/// These futures must return `()`, indicating they are intended to create side-effects only.
pub(crate) fn add_future(future: Pin<Box<dyn Future<Output = ()>>>) -> Result<()> {
    STATE.with(|state| {
        let mut state = state.borrow_mut();
        if let Some(state) = state.as_mut() {
            state.new_futures.push_back(ExecutableFuture::new(future));
            Ok(())
        } else {
            Err(Error::InvalidContext)
        }
    })
}

// Tracks active wakers and associates wakers with the futures that registered them.
struct FdWakerState {
    poll_ctx: PollContext<usize>,
    tokens: Slab<(File, Waker)>,
    new_futures: VecDeque<ExecutableFuture<()>>,
}

impl FdWakerState {
    fn new() -> Result<Self> {
        Ok(FdWakerState {
            poll_ctx: PollContext::new().map_err(Error::CreatingContext)?,
            tokens: Slab::with_capacity(64),
            new_futures: VecDeque::new(),
        })
    }

    // Adds an fd that, when signaled, will trigger the given waker.
    fn add_waker(&mut self, fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<WakerToken> {
        let duped_fd = unsafe {
            // Safe because duplicating an FD doesn't affect memory safety, and the dup'd FD
            // will only be added to the poll loop.
            File::from_raw_fd(dup_fd(fd)?)
        };
        let entry = self.tokens.vacant_entry();
        let next_token = entry.key();
        self.poll_ctx
            .add_fd_with_events(&duped_fd, events, next_token)
            .map_err(Error::SubmittingWaker)?;
        entry.insert((duped_fd, waker));
        Ok(WakerToken(next_token))
    }

    // Waits until one of the FDs is readable and wakes the associated waker.
    fn wait_wake_event(&mut self) -> Result<()> {
        let events = self.poll_ctx.wait().map_err(Error::PollContextError)?;
        for e in events.iter() {
            let token = e.token();
            let (fd, waker) = self.tokens.remove(token);
            self.poll_ctx.delete(&fd).map_err(Error::PollContextError)?;
            waker.wake();
        }
        Ok(())
    }

    // Remove the waker for the given token if it hasn't fired yet.
    fn cancel_waker(&mut self, token: WakerToken) -> Result<()> {
        let (fd, _waker) = self.tokens.remove(token.0);
        self.poll_ctx.delete(&fd).map_err(Error::PollContextError)?;
        Ok(())
    }
}

/// Runs futures to completion on a single thread. Futures are allowed to block on file descriptors
/// only. Futures can only block on FDs becoming readable or writable. `FdExecutor` is meant to be
/// used where a poll or select loop would be used otherwise.
pub(crate) struct FdExecutor<T: FutureList> {
    futures: T,
}

impl<T: FutureList> Executor for FdExecutor<T> {
    type Output = Result<T::Output>;

    fn run(&mut self) -> Self::Output {
        self.append_futures();

        loop {
            if let Some(output) = self.futures.poll_results() {
                return Ok(output);
            }

            self.append_futures();

            // If no futures are ready, sleep until a waker is signaled.
            if !self.futures.any_ready() {
                STATE.with(|state| {
                    let mut state = state.borrow_mut();
                    if let Some(state) = state.as_mut() {
                        state.wait_wake_event()?;
                    } else {
                        unreachable!("Can't get here without a context being created");
                    }
                    Ok(())
                })?;
            }
        }
    }
}

impl<T: FutureList> FdExecutor<T> {
    /// Create a new executor.
    pub fn new(futures: T) -> Result<FdExecutor<T>> {
        STATE.with(|state| {
            if state.borrow().is_some() {
                return Err(Error::AttemptedDuplicateExecutor);
            }
            state.replace(Some(FdWakerState::new()?));
            Ok(())
        })?;
        Ok(FdExecutor { futures })
    }

    // Add any new futures and wakers to the lists.
    fn append_futures(&mut self) {
        STATE.with(|state| {
            let mut state = state.borrow_mut();
            if let Some(state) = state.as_mut() {
                self.futures.futures_mut().append(&mut state.new_futures);
            } else {
                unreachable!("Can't get here without a context being created");
            }
        });
    }
}

impl<T: FutureList> Drop for FdExecutor<T> {
    fn drop(&mut self) {
        STATE.with(|state| {
            state.replace(None);
        });
    }
}

// Used to `dup` the FDs passed to the executor so there is a guarantee they aren't closed while
// waiting in TLS to be added to the main polling context.
unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
    let ret = libc::dup(fd);
    if ret < 0 {
        Err(Error::DuplicatingFd(sys_util::Error::last()))
    } else {
        Ok(ret)
    }
}

#[cfg(test)]
mod test {
    use std::cell::RefCell;
    use std::fs::File;
    use std::future::Future;
    use std::os::unix::io::AsRawFd;
    use std::rc::Rc;
    use std::task::{Context, Poll};

    use futures::future::Either;

    use super::*;

    // test function to get the number of pending wakers.
    fn pending_ops() -> usize {
        STATE.with(|state| {
            let state = state.borrow_mut();
            state.as_ref().unwrap().tokens.len()
        })
    }

    struct TestFut {
        f: File,
        pending_waker: Option<PendingWaker>,
    }

    impl TestFut {
        fn new(f: File) -> TestFut {
            TestFut {
                f,
                pending_waker: None,
            }
        }
    }

    impl Future for TestFut {
        type Output = u64;
        fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
            if self.pending_waker.is_none() {
                println!("pend");
                self.pending_waker = Some(
                    crate::fd_executor::add_read_waker(self.f.as_raw_fd(), cx.waker().clone())
                        .unwrap(),
                );
            }
            Poll::Pending
        }
    }

    impl Drop for TestFut {
        fn drop(&mut self) {
            println!("drop test fut");
        }
    }

    #[test]
    fn test_it() {
        async fn do_test() {
            let (r, _w) = sys_util::pipe(true).unwrap();
            let done = Box::pin(async { 5usize });
            let pending = Box::pin(TestFut::new(r));
            match futures::future::select(pending, done).await {
                Either::Right((5, pending)) => std::mem::drop(pending),
                _ => panic!("unexpected select result"),
            }
            // test that dropping the incomplete future removed the waker.
            assert_eq!(0, pending_ops());
        }

        let fut = do_test();

        crate::run_one_poll(Box::pin(fut)).unwrap();

        // Example of starting the framework and running a future:
        async fn my_async(x: Rc<RefCell<u64>>) {
            x.replace(4);
        }

        let x = Rc::new(RefCell::new(0));
        crate::run_one_poll(Box::pin(my_async(x.clone()))).unwrap();
        assert_eq!(*x.borrow(), 4);
    }
}
<commit_msg>cros_async: Fix double cancellation of fd wakers<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! The executor runs all given futures to completion. Futures register wakers associated with file
//! descriptors. The wakers will be called when the FD becomes readable or writable depending on
//! the situation.
//!
//! `FdExecutor` is meant to be used with the `futures-rs` crate that provides combinators and
//! utility functions to combine futures.

use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt::{self, Display};
use std::fs::File;
use std::future::Future;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::RawFd;
use std::pin::Pin;
use std::task::Waker;

use slab::Slab;

use sys_util::{error, PollContext, WatchingEvents};

use crate::executor::{ExecutableFuture, Executor, FutureList};
use crate::WakerToken;

#[derive(Debug, PartialEq)]
pub enum Error {
    /// Attempts to create two Executors on the same thread fail.
    AttemptedDuplicateExecutor,
    /// Failed to copy the FD for the polling context.
    DuplicatingFd(sys_util::Error),
    /// Failed accessing the thread local storage for wakers.
    InvalidContext,
    /// Creating a context to wait on FDs failed.
    CreatingContext(sys_util::Error),
    /// PollContext failure.
    PollContextError(sys_util::Error),
    /// Failed to submit the waker to the polling context.
    SubmittingWaker(sys_util::Error),
    /// A Waker was canceled, but the operation isn't running.
    UnknownWaker,
}
pub type Result<T> = std::result::Result<T, Error>;

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            AttemptedDuplicateExecutor => write!(f, "Cannot have two executors on one thread."),
            DuplicatingFd(e) => write!(f, "Failed to copy the FD for the polling context: {}", e),
            InvalidContext => write!(
                f,
                "Invalid context, was the Fd executor created successfully?"
            ),
            CreatingContext(e) => write!(f, "An error creating the fd waiting context: {}.", e),
            PollContextError(e) => write!(f, "PollContext failure: {}", e),
            SubmittingWaker(e) => write!(f, "An error adding to the Aio context: {}.", e),
            UnknownWaker => write!(f, "Unknown waker"),
        }
    }
}

// Temporary vectors of new additions to the executor.

// Tracks active wakers and the futures they are associated with.
thread_local!(static STATE: RefCell<Option<FdWakerState>> = RefCell::new(None));

fn add_waker(fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<WakerToken> {
    STATE.with(|state| {
        let mut state = state.borrow_mut();
        if let Some(state) = state.as_mut() {
            state.add_waker(fd, waker, events)
        } else {
            Err(Error::InvalidContext)
        }
    })
}

/// A token returned from `add_waker` that can be used to cancel the waker before it completes.
/// Used to manage getting the result from the underlying executor for a completed operation.
/// Dropping a `PendingWaker` will get the result from the executor.
pub struct PendingWaker {
    token: Option<WakerToken>,
}

impl PendingWaker {
    pub(crate) fn new(token: WakerToken) -> PendingWaker {
        PendingWaker { token: Some(token) }
    }
}

impl Drop for PendingWaker {
    fn drop(&mut self) {
        if let Some(token) = self.token.take() {
            let _ = cancel_waker(token);
        }
    }
}

/// Tells the waking system to wake `waker` when `fd` becomes readable.
/// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
/// next time the future is polled. If the fd is closed, there is a race where another FD can be
/// opened on top of it causing the next poll to access the new target file.
/// Returns a `PendingWaker` that can be used to poll for completion or cancel the waker before it
/// completes.
pub(crate) fn add_read_waker(fd: RawFd, waker: Waker) -> Result<PendingWaker> {
    add_waker(fd, waker, WatchingEvents::empty().set_read()).map(PendingWaker::new)
}

/// Tells the waking system to wake `waker` when `fd` becomes writable.
/// The 'fd' must be fully owned by the future adding the waker, and must not be closed until the
/// next time the future is polled. If the fd is closed, there is a race where another FD can be
/// opened on top of it causing the next poll to access the new target file.
/// Returns a `PendingWaker` that can be used to poll for completion or cancel the waker before it
/// completes.
pub(crate) fn add_write_waker(fd: RawFd, waker: Waker) -> Result<PendingWaker> {
    add_waker(fd, waker, WatchingEvents::empty().set_write()).map(PendingWaker::new)
}

/// Cancels the waker that returned the given token if the waker hasn't yet fired.
pub(crate) fn cancel_waker(token: WakerToken) -> Result<()> {
    STATE.with(|state| {
        let mut state = state.borrow_mut();
        if let Some(state) = state.as_mut() {
            state.cancel_waker(token)
        } else {
            Err(Error::InvalidContext)
        }
    })
}

/// Adds a new top level future to the Executor.
/// These futures must return `()`, indicating they are intended to create side-effects only.
pub(crate) fn add_future(future: Pin<Box<dyn Future<Output = ()>>>) -> Result<()> {
    STATE.with(|state| {
        let mut state = state.borrow_mut();
        if let Some(state) = state.as_mut() {
            state.new_futures.push_back(ExecutableFuture::new(future));
            Ok(())
        } else {
            Err(Error::InvalidContext)
        }
    })
}

// Tracks active wakers and associates wakers with the futures that registered them.
struct FdWakerState {
    poll_ctx: PollContext<usize>,
    tokens: Slab<(File, Option<Waker>)>,
    new_futures: VecDeque<ExecutableFuture<()>>,
}

impl FdWakerState {
    fn new() -> Result<Self> {
        Ok(FdWakerState {
            poll_ctx: PollContext::new().map_err(Error::CreatingContext)?,
            tokens: Slab::with_capacity(64),
            new_futures: VecDeque::new(),
        })
    }

    // Adds an fd that, when signaled, will trigger the given waker.
    fn add_waker(&mut self, fd: RawFd, waker: Waker, events: WatchingEvents) -> Result<WakerToken> {
        let duped_fd = unsafe {
            // Safe because duplicating an FD doesn't affect memory safety, and the dup'd FD
            // will only be added to the poll loop.
            File::from_raw_fd(dup_fd(fd)?)
        };
        let entry = self.tokens.vacant_entry();
        let next_token = entry.key();
        self.poll_ctx
            .add_fd_with_events(&duped_fd, events, next_token)
            .map_err(Error::SubmittingWaker)?;
        entry.insert((duped_fd, Some(waker)));
        Ok(WakerToken(next_token))
    }

    // Waits until one of the FDs is readable and wakes the associated waker.
    fn wait_wake_event(&mut self) -> Result<()> {
        let events = self.poll_ctx.wait().map_err(Error::PollContextError)?;
        for e in events.iter() {
            let token = e.token();
            if let Some((fd, waker)) = self.tokens.get_mut(token) {
                self.poll_ctx.delete(fd).map_err(Error::PollContextError)?;
                if let Some(waker) = waker.take() {
                    waker.wake();
                } else {
                    error!("Woken twice");
                }
            } else {
                error!("Unknown waker");
            }
        }
        Ok(())
    }

    // Remove the waker for the given token if it hasn't fired yet.
    fn cancel_waker(&mut self, token: WakerToken) -> Result<()> {
        let (fd, _waker) = self.tokens.remove(token.0);
        self.poll_ctx.delete(&fd).map_err(Error::PollContextError)?;
        Ok(())
    }
}

/// Runs futures to completion on a single thread. Futures are allowed to block on file descriptors
/// only. Futures can only block on FDs becoming readable or writable. `FdExecutor` is meant to be
/// used where a poll or select loop would be used otherwise.
pub(crate) struct FdExecutor<T: FutureList> {
    futures: T,
}

impl<T: FutureList> Executor for FdExecutor<T> {
    type Output = Result<T::Output>;

    fn run(&mut self) -> Self::Output {
        self.append_futures();

        loop {
            if let Some(output) = self.futures.poll_results() {
                return Ok(output);
            }

            self.append_futures();

            // If no futures are ready, sleep until a waker is signaled.
            if !self.futures.any_ready() {
                STATE.with(|state| {
                    let mut state = state.borrow_mut();
                    if let Some(state) = state.as_mut() {
                        state.wait_wake_event()?;
                    } else {
                        unreachable!("Can't get here without a context being created");
                    }
                    Ok(())
                })?;
            }
        }
    }
}

impl<T: FutureList> FdExecutor<T> {
    /// Create a new executor.
    pub fn new(futures: T) -> Result<FdExecutor<T>> {
        STATE.with(|state| {
            if state.borrow().is_some() {
                return Err(Error::AttemptedDuplicateExecutor);
            }
            state.replace(Some(FdWakerState::new()?));
            Ok(())
        })?;
        Ok(FdExecutor { futures })
    }

    // Add any new futures and wakers to the lists.
    fn append_futures(&mut self) {
        STATE.with(|state| {
            let mut state = state.borrow_mut();
            if let Some(state) = state.as_mut() {
                self.futures.futures_mut().append(&mut state.new_futures);
            } else {
                unreachable!("Can't get here without a context being created");
            }
        });
    }
}

impl<T: FutureList> Drop for FdExecutor<T> {
    fn drop(&mut self) {
        STATE.with(|state| {
            state.replace(None);
        });
    }
}

// Used to `dup` the FDs passed to the executor so there is a guarantee they aren't closed while
// waiting in TLS to be added to the main polling context.
unsafe fn dup_fd(fd: RawFd) -> Result<RawFd> {
    let ret = libc::dup(fd);
    if ret < 0 {
        Err(Error::DuplicatingFd(sys_util::Error::last()))
    } else {
        Ok(ret)
    }
}

#[cfg(test)]
mod test {
    use std::cell::RefCell;
    use std::fs::File;
    use std::future::Future;
    use std::os::unix::io::AsRawFd;
    use std::rc::Rc;
    use std::task::{Context, Poll};

    use futures::future::Either;

    use super::*;

    // test function to get the number of pending wakers.
    fn pending_ops() -> usize {
        STATE.with(|state| {
            let state = state.borrow_mut();
            state.as_ref().unwrap().tokens.len()
        })
    }

    struct TestFut {
        f: File,
        pending_waker: Option<PendingWaker>,
    }

    impl TestFut {
        fn new(f: File) -> TestFut {
            TestFut {
                f,
                pending_waker: None,
            }
        }
    }

    impl Future for TestFut {
        type Output = u64;
        fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
            if self.pending_waker.is_none() {
                println!("pend");
                self.pending_waker = Some(
                    crate::fd_executor::add_read_waker(self.f.as_raw_fd(), cx.waker().clone())
                        .unwrap(),
                );
            }
            Poll::Pending
        }
    }

    impl Drop for TestFut {
        fn drop(&mut self) {
            println!("drop test fut");
        }
    }

    #[test]
    fn test_it() {
        async fn do_test() {
            let (r, _w) = sys_util::pipe(true).unwrap();
            let done = Box::pin(async { 5usize });
            let pending = Box::pin(TestFut::new(r));
            match futures::future::select(pending, done).await {
                Either::Right((5, pending)) => std::mem::drop(pending),
                _ => panic!("unexpected select result"),
            }
            // test that dropping the incomplete future removed the waker.
            assert_eq!(0, pending_ops());
        }

        let fut = do_test();

        crate::run_one_poll(Box::pin(fut)).unwrap();

        // Example of starting the framework and running a future:
        async fn my_async(x: Rc<RefCell<u64>>) {
            x.replace(4);
        }

        let x = Rc::new(RefCell::new(0));
        crate::run_one_poll(Box::pin(my_async(x.clone()))).unwrap();
        assert_eq!(*x.borrow(), 4);
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(std_misc)]
#![feature(fs)]
#![feature(io)]

//! #Introduction
//! **This crate focuses on geting system information.**
//!
//! Now it can get kernel and cpu information, memory and disk usage, average load value and so on.
//!
//! If you have some advice or want the crate supports more system information, contact with me.

use std::ffi;
use std::io::Read;
use std::fs::File;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    pub one: f64,
    pub five: f64,
    pub fifteen: f64
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    pub total: u64,
    pub free:  u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached:  u64,

    pub swap_total: u64,
    pub swap_free:  u64
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64
}

extern {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/ostype").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_type();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/osrelease").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_release();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, String> {
    if cfg!(target_os = "linux")
        || cfg!(target_os = "macos") || cfg!(target_os = "windows")
    {
        Ok(unsafe { get_cpu_num() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
        let mut f = File::open("/proc/cpuinfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        Ok({
            for _ in 0..7 {
                lines.next();
            }
            let mut words = lines.next().unwrap().split(':');
            words.next();
            let s = words.next().unwrap().trim().trim_right_matches('\n');
            s.parse::<f64>().unwrap() as u64
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_cpu_speed() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut words = s.as_slice().split(' ');
        let one = words.next().unwrap().parse::<f64>().unwrap();
        let five = words.next().unwrap().parse::<f64>().unwrap();
        let fifteen = words.next().unwrap().parse::<f64>().unwrap();
        Ok(LoadAvg { one: one, five: five, fifteen: fifteen} )
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        Ok({
            let mut words = s.as_slice().splitn(3, ' ');
            for _ in 0..3 {
                words.next();
            }
            let mut words = words.next().unwrap().split('/');
            words.next();
            let mut words = words.next().unwrap().split(' ');
            words.next().unwrap().parse::<u64>().unwrap()
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err("Unsupported system".to_string())
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    let mut line = line.splitn(1, ' ');
    line.next();
    line.next().unwrap().trim_left().split(' ').next().unwrap()
        .parse::<u64>().unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/meminfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        let total = get_mem_num(lines.next().unwrap());
        let free = get_mem_num(lines.next().unwrap());
        let avail = get_mem_num(lines.next().unwrap());
        let buffers = get_mem_num(lines.next().unwrap());
        let cached = get_mem_num(lines.next().unwrap());
        let swap_total = {
            for _ in 0..9 {
                lines.next();
            }
            get_mem_num(lines.next().unwrap())
        };
        let swap_free = get_mem_num(lines.next().unwrap());

        Ok(MemInfo{ total: total, free: free, avail: avail,
                    buffers: buffers, cached: cached,
                    swap_total: swap_total, swap_free: swap_free})
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, String> {
    if cfg!(target_os = "linux") ||
        cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe { get_disk_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

#[test]
fn test() {
    os_type().unwrap();
    os_release().unwrap();
    cpu_num().unwrap();
    cpu_speed().unwrap();
    loadavg().unwrap();
    proc_total().unwrap();
    mem_info().unwrap();
    disk_info().unwrap();
}

<commit_msg>change test<commit_after>#![feature(core)]
#![feature(std_misc)]
#![feature(fs)]
#![feature(io)]

//! #Introduction
//! **This crate focuses on geting system information.**
//!
//! Now it can get kernel and cpu information, memory and disk usage, average load value and so on.
//!
//! If you have some advice or want the crate supports more system information, contact with me.

use std::ffi;
use std::io::Read;
use std::fs::File;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    pub one: f64,
    pub five: f64,
    pub fifteen: f64
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    pub total: u64,
    pub free:  u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached:  u64,

    pub swap_total: u64,
    pub swap_free:  u64
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64
}

extern {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/ostype").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_type();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/osrelease").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_release();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, String> {
    if cfg!(target_os = "linux")
        || cfg!(target_os = "macos") || cfg!(target_os = "windows")
    {
        Ok(unsafe { get_cpu_num() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
        let mut f = File::open("/proc/cpuinfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        Ok({
            for _ in 0..7 {
                lines.next();
            }
            let mut words = lines.next().unwrap().split(':');
            words.next();
            let s = words.next().unwrap().trim().trim_right_matches('\n');
            s.parse::<f64>().unwrap() as u64
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_cpu_speed() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut words = s.as_slice().split(' ');
        let one = words.next().unwrap().parse::<f64>().unwrap();
        let five = words.next().unwrap().parse::<f64>().unwrap();
        let fifteen = words.next().unwrap().parse::<f64>().unwrap();
        Ok(LoadAvg { one: one, five: five, fifteen: fifteen} )
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        Ok({
            let mut words = s.as_slice().splitn(3, ' ');
            for _ in 0..3 {
                words.next();
            }
            let mut words = words.next().unwrap().split('/');
            words.next();
            let mut words = words.next().unwrap().split(' ');
            words.next().unwrap().parse::<u64>().unwrap()
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err("Unsupported system".to_string())
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    let mut line = line.splitn(1, ' ');
    line.next();
    line.next().unwrap().trim_left().split(' ').next().unwrap()
        .parse::<u64>().unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/meminfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        let total = get_mem_num(lines.next().unwrap());
        let free = get_mem_num(lines.next().unwrap());
        let avail = get_mem_num(lines.next().unwrap());
        let buffers = get_mem_num(lines.next().unwrap());
        let cached = get_mem_num(lines.next().unwrap());
        let swap_total = {
            for _ in 0..9 {
                lines.next();
            }
            get_mem_num(lines.next().unwrap())
        };
        let swap_free = get_mem_num(lines.next().unwrap());

        Ok(MemInfo{ total: total, free: free, avail: avail,
                    buffers: buffers, cached: cached,
                    swap_total: swap_total, swap_free: swap_free})
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, String> {
    if cfg!(target_os = "linux") ||
        cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe { get_disk_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

#[test]
fn test_os_type() {
    os_type().unwrap();
}

#[test]
fn test_os_release() {
    os_release().unwrap();
}

#[test]
fn test_cpu_num() {
    cpu_num().unwrap();
}

#[test]
fn test_cpu_speed() {
    cpu_speed().unwrap();
}

#[test]
fn test_loadavg() {
    loadavg().unwrap();
}

#[test]
fn test_proc_total() {
    proc_total().unwrap();
}

#[test]
fn test_mem_info() {
    mem_info().unwrap();
}

#[test]
fn test_disk_info() {
    disk_info().unwrap();
}

<|endoftext|>


<commit_before>#![allow(dead_code)]
/// # Render engine middlware
/// Currently only `HandlebarsEngine` support
///
/// ## Features
/// * Init Tempalte engine
/// * Adding Template paths
/// * useful additional helpers with strong params checking
/// * helpers logger for critical situations
///
/// ## Helpers
/// * `link` - css link helper
/// * `script` - js  link helper
///
/// it's included paths ./views/**/*
use iron::prelude::*;
use iron::status;
use hbs::{HandlebarsEngine, DirectorySource};
use handlebars::{Handlebars, RenderError, RenderContext, Helper, Context};
use std::error::Error;
use std::collections::BTreeMap;
use rustc_serialize::json::{self, Json, ToJson};
use rustc_serialize::json::DecoderError::*;
use rustc_serialize::Decodable;
use hbs::{Template};
use handlebars::Renderable;

/// Alias for Basic Data struct
pub type BaseDataMap = BTreeMap<String, Json>;
/// Alias for basic Iron Response Result
pub type RenderResult = IronResult<Response>;
/// Templфte Render strшct
pub struct Render {
    pub data : BaseDataMap
}

/// BaseDataMap Json decoder trait
pub trait BaseDataMapDecoder {
    fn decode<J: Decodable>(&self) -> J;
}

/// Omplementation for Jscond decoding
/// from BaseDataMap that implemented Decodable trait
/// to specific generic type.
/// It useful for struct init via BaseDataMap data.
/// For Example - models.
impl BaseDataMapDecoder for BaseDataMap {
    /// Json decoder for BaseDataMap
    fn decode<J: Decodable>(&self) -> J {
        let json_obj: Json = Json::Object(self.to_owned());
        match json::decode(&json_obj.to_string()) {
            Ok(decoded) => decoded,
            Err(err) => {
                let msg = match err {
                    ParseError(_) => "JSON parse error",
                    ExpectedError(_, _) => "Validation field expected (not declared)",
                    MissingFieldError(_) => "Validation field missing",
                    _ => "Other error",
                };
                panic!("\
              \n\n |> Validator::new error: {:?}\
                \n |> Validation fields: {:?}\
                \n |> Message: {}\
                \n |> At source code: => ", err, json_obj, msg);
            }
        }
    }
}

/// Basic render with StatusOK tempalte name and data
/// basic usage:
/// `Render::new("my/template/path", ())``
impl Render {
    /// Render Template file with status 200
    pub fn new<T: ToJson>(name: &str, data: T) -> RenderResult {
        let mut resp = Response::new();
        resp.set_mut(Template::new(name, data)).set_mut(status::Ok);
        Ok(resp)
    }
}

/// Init Template renderer and add Tempaltes path.
/// It invoke to after middleware
pub fn template_render(paths: Vec<&str>) -> HandlebarsEngine {
    // First init Handlebars
    let mut hregistry = Handlebars::new();

    // Add helpers to Handlebars
    hregistry.register_helper("link", Box::new(link_helper));
    hregistry.register_helper("script", Box::new(script_helper));
    hregistry.register_helper("active", Box::new(active_page_helper));
    hregistry.register_helper("ifeq", Box::new(ifeq_helper));
    hregistry.register_helper("ifgt", Box::new(ifgt_helper));

    // Our instance HandlebarsEngine depended of Handlebars
    let mut template = HandlebarsEngine::from(hregistry);

    // Add a directory source, all files with .html suffix
    // will be loaded as template
    for path in paths.iter() {
        template.add(Box::new(DirectorySource::new(path, ".html")));
    }

    // load templates from all registered sources
    if let Err(r) = template.reload() {
        // Paniced cause it critical situation
        panic!("{:?}", r.description());
    }
    template
}

/// Css link Helper
/// usege: `{{#link ["some/url1", "some/url2"]}}{{/link}}`
fn link_helper(_: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let css_links = try!(h.param(0)
            .and_then(|v| v.value().as_array())
            .ok_or(RenderError::new("|> link_helper - param 1 with array type is required")));
    let mut css = "".to_owned();
    for link in css_links.iter() {
        let link = try!(link
                    .as_string()
                    .ok_or(RenderError::new("|> link_helper - array param with string type is required")));
        css = format!("{}\t<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">\n", css, link);
    }
    try!(rc.writer.write(css.into_bytes().as_ref()));
    Ok(())
}

/// Js link Helper
/// usege: `{{#script ["some/url1", "some/url2"]}}{{/script}}`
fn script_helper(_: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let js_links = try!(h.param(0)
            .and_then(|v| v.value().as_array())
            .ok_or(RenderError::new("|> script_helper - param 1 with array type is required")));
    let mut js = "".to_owned();
    for link in js_links.iter() {
        let link = try!(link
                    .as_string()
                    .ok_or(RenderError::new("|> script_helper - array param with string type is required")));
            js = format!("{}\t<script type=\"text/javascript\" charset=\"utf-8\" src=\"{}\"></script>\n", js, link);
    }
    try!(rc.writer.write(js.into_bytes().as_ref()));
    Ok(())
}

/// Active helper.
/// It checking is value same with exacted value
/// usege: `{{#active "pages" module }}{{/active}}`
/// It should pre-init value at Handler!
fn active_page_helper(_: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    println!("ACTIVE=> {:?} {:?}\n", h.param(0), h.param(1));
    let exact_page = try!(h.param(0)
            .and_then(|v| v.value().as_string())
            .ok_or(RenderError::new("|> active_page - param 1 with string type is required")));
    let active_page = try!(h.param(1)
            .and_then(|v| v.value().as_string())
            .ok_or(RenderError::new("|> active_page - param 2 with string type is required")));
    let mut active = "".to_owned();
    if exact_page == active_page {
        active = "active".to_owned();
    }
    try!(rc.writer.write(active.into_bytes().as_ref()));
    Ok(())
}

fn ifeq_helper(ctx: &Context, h: &Helper, hbs: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = try!(h.param(0)
            .and_then(|v| Some(v.value()) )
            .ok_or(RenderError::new("|> ifeq_helper - param 1 with is required")));
    let eq_field = try!(h.param(1)
            .and_then(|v| Some(v.value()) )
            .ok_or(RenderError::new("|> ifeq_helper - param 2 with is required")));
    println!("==> {:?}\n\n", h.param(0));

    let is_true = value == eq_field;

    if is_true {
        if let Some(tpl) = h.template() {
            tpl.render(ctx, hbs, rc)?;
        }
    } else {
        if let Some(tpl) = h.inverse() {
            tpl.render(ctx, hbs, rc)?;
        }
    }
    Ok(())
}

fn ifgt_helper(ctx: &Context, h: &Helper, hbs: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = try!(h.param(0)
            .and_then(|v| Some(v.value()) )
             .ok_or(RenderError::new("|> ifgt_helper - param 1 withis required")));
    let eq_field = try!(h.param(1)
            .and_then(|v| Some(v.value()) )
            .ok_or(RenderError::new("|> ifgt_helper - param 2 with is required")));
    println!("IFGT==> {:?}\n\n", value);
    let mut active = "5 23".to_owned();
    try!(rc.writer.write(active.into_bytes().as_ref()));
    println!("IFGT==>>> {:?}\n\n", value);

    Ok(())
}<commit_msg>Added: render debug mode<commit_after>#![allow(dead_code)]
/// # Render engine middlware
/// Currently only `HandlebarsEngine` support
///
/// ## Features
/// * Init Tempalte engine
/// * Adding Template paths
/// * useful additional helpers with strong params checking
/// * helpers logger for critical situations
///
/// ## Helpers
/// * `link` - css link helper
/// * `script` - js  link helper
///
/// it's included paths ./views/**/*
use iron::prelude::*;
use iron::status;
use hbs::{HandlebarsEngine, DirectorySource};
use handlebars::{Handlebars, RenderError, RenderContext, Helper, Context};
use std::error::Error;
use std::collections::BTreeMap;
use rustc_serialize::json::{self, Json, ToJson};
use rustc_serialize::json::DecoderError::*;
use rustc_serialize::Decodable;
use hbs::{Template};
use handlebars::Renderable;

const DEBUG_RENDER: bool = true;

/// Alias for Basic Data struct
pub type BaseDataMap = BTreeMap<String, Json>;
/// Alias for basic Iron Response Result
pub type RenderResult = IronResult<Response>;
/// Templфte Render strшct
pub struct Render {
    pub data : BaseDataMap
}

/// BaseDataMap Json decoder trait
pub trait BaseDataMapDecoder {
    fn decode<J: Decodable>(&self) -> J;
}

/// Omplementation for Jscond decoding
/// from BaseDataMap that implemented Decodable trait
/// to specific generic type.
/// It useful for struct init via BaseDataMap data.
/// For Example - models.
impl BaseDataMapDecoder for BaseDataMap {
    /// Json decoder for BaseDataMap
    fn decode<J: Decodable>(&self) -> J {
        let json_obj: Json = Json::Object(self.to_owned());
        match json::decode(&json_obj.to_string()) {
            Ok(decoded) => decoded,
            Err(err) => {
                let msg = match err {
                    ParseError(_) => "JSON parse error",
                    ExpectedError(_, _) => "Validation field expected (not declared)",
                    MissingFieldError(_) => "Validation field missing",
                    _ => "Other error",
                };
                panic!("\
              \n\n |> Validator::new error: {:?}\
                \n |> Validation fields: {:?}\
                \n |> Message: {}\
                \n |> At source code: => ", err, json_obj, msg);
            }
        }
    }
}

/// Basic render with StatusOK tempalte name and data
/// basic usage:
/// `Render::new("my/template/path", ())``
impl Render {
    /// Render Template file with status 200
    pub fn new<T: ToJson>(name: &str, data: T) -> RenderResult {
        let mut resp = Response::new();
        resp.set_mut(Template::new(name, data)).set_mut(status::Ok);
        Ok(resp)
    }
}

/// Init Template renderer and add Tempaltes path.
/// It invoke to after middleware
pub fn template_render(paths: Vec<&str>) -> HandlebarsEngine {
    // First init Handlebars
    let mut hregistry = Handlebars::new();

    // Add helpers to Handlebars
    hregistry.register_helper("link", Box::new(link_helper));
    hregistry.register_helper("script", Box::new(script_helper));
    hregistry.register_helper("active", Box::new(active_page_helper));
    hregistry.register_helper("ifeq", Box::new(ifeq_helper));
    hregistry.register_helper("ifgt", Box::new(ifgt_helper));

    // Our instance HandlebarsEngine depended of Handlebars
    let mut template = HandlebarsEngine::from(hregistry);

    // Add a directory source, all files with .html suffix
    // will be loaded as template
    for path in paths.iter() {
        template.add(Box::new(DirectorySource::new(path, ".html")));
    }

    // load templates from all registered sources
    if let Err(r) = template.reload() {
        // Paniced cause it critical situation
        panic!("{:?}", r.description());
    }
    template
}

/// Css link Helper
/// usege: `{{#link ["some/url1", "some/url2"]}}{{/link}}`
fn link_helper(_: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let css_links = try!(h.param(0)
            .and_then(|v| v.value().as_array())
            .ok_or(RenderError::new("|> link_helper - param 1 with array type is required")));
    let mut css = "".to_owned();
    for link in css_links.iter() {
        let link = try!(link
                    .as_string()
                    .ok_or(RenderError::new("|> link_helper - array param with string type is required")));
        css = format!("{}\t<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">\n", css, link);
    }
    try!(rc.writer.write(css.into_bytes().as_ref()));
    Ok(())
}

/// Js link Helper
/// usege: `{{#script ["some/url1", "some/url2"]}}{{/script}}`
fn script_helper(_: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let js_links = try!(h.param(0)
            .and_then(|v| v.value().as_array())
            .ok_or(RenderError::new("|> script_helper - param 1 with array type is required")));
    let mut js = "".to_owned();
    for link in js_links.iter() {
        let link = try!(link
                    .as_string()
                    .ok_or(RenderError::new("|> script_helper - array param with string type is required")));
            js = format!("{}\t<script type=\"text/javascript\" charset=\"utf-8\" src=\"{}\"></script>\n", js, link);
    }
    try!(rc.writer.write(js.into_bytes().as_ref()));
    Ok(())
}

/// Active helper.
/// It checking is value same with exacted value
/// usege: `{{#active "pages" module }}{{/active}}`
/// It should pre-init value at Handler!
fn active_page_helper(_: &Context, h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    println!("ACTIVE=> {:?} {:?}\n", h.param(0), h.param(1));
    let exact_page = try!(h.param(0)
            .and_then(|v| v.value().as_string())
            .ok_or(RenderError::new("|> active_page - param 1 with string type is required")));
    let active_page = try!(h.param(1)
            .and_then(|v| v.value().as_string())
            .ok_or(RenderError::new("|> active_page - param 2 with string type is required")));
    let mut active = "".to_owned();
    if exact_page == active_page {
        active = "active".to_owned();
    }
    try!(rc.writer.write(active.into_bytes().as_ref()));
    Ok(())
}

fn ifeq_helper(ctx: &Context, h: &Helper, hbs: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = try!(h.param(0)
            .and_then(|v| Some(v.value()) )
            .ok_or(RenderError::new("|> ifeq_helper - param 1 with is required")));
    let eq_field = try!(h.param(1)
            .and_then(|v| Some(v.value()) )
            .ok_or(RenderError::new("|> ifeq_helper - param 2 with is required")));
    println!("==> {:?}\n\n", h.param(0));

    let is_true = value == eq_field;

    if is_true {
        if let Some(tpl) = h.template() {
            tpl.render(ctx, hbs, rc)?;
        }
    } else {
        if let Some(tpl) = h.inverse() {
            tpl.render(ctx, hbs, rc)?;
        }
    }
    Ok(())
}

fn ifgt_helper(ctx: &Context, h: &Helper, hbs: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let mut active = "".to_owned();;
    let value = try!(h.param(0)
            .and_then(|v| Some(v.value()) )
             .ok_or(RenderError::new("|> ifgt_helper - param 1 withis required")));
    if DEBUG_RENDER {
        let eq_field = try!(h.param(1)
            .and_then(|v| Some(v.value()))
            .ok_or(RenderError::new("|> ifgt_helper - param 2 with is required")));
        println!("IFGT==> {:?}\n\n", value);
        active = "5 23".to_owned();
        println!("IFGT==>>> {:?}\n\n", value);
        let (_, _, _) = (eq_field, hbs, ctx);
    }
    try!(rc.writer.write(active.into_bytes().as_ref()));

    Ok(())
}<|endoftext|>


<commit_before>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use Result;
use message::Message;

/// A mobile originated (MO) message;
pub struct MobileOriginated;

impl Message {

    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        Ok(MobileOriginated)
    }
}

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

    use message::Message;

    #[test]
    fn from_message() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }
}
<commit_msg>Open MO from path<commit_after>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::path::Path;

use Result;
use message::Message;

/// A mobile originated (MO) message;
pub struct MobileOriginated;

impl MobileOriginated {

    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }
}

impl Message {

    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        Ok(MobileOriginated)
    }
}

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

    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }
}
<|endoftext|>