<commit_before>//! Support for sending and receiving data link layer packets using libpcap.
//! Also has support for reading pcap files.
extern crate pcap;

use std::marker::{Send, Sync};
use std::io;
use std::iter::repeat;
use self::pcap::{Active, Activated};
use datalink::{self, NetworkInterface};
use datalink::{DataLinkChannelIterator, DataLinkReceiver, DataLinkSender};
use datalink::Channel::Ethernet;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::path::Path;

/// Configuration for the pcap datalink backend
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when reading packets. Defaults to 4096
    pub read_buffer_size: usize,

    /// The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,
}

impl<'a> From<&'a datalink::Config> for Config {
    fn from(config: &datalink::Config) -> Config {
        Config{
            read_buffer_size: config.read_buffer_size,
            read_timeout: config.read_timeout,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config{
            read_buffer_size: 4096,
            read_timeout: None,
        }
    }
}

/// Create a datalink channel from the provided pcap device
#[inline]
pub fn channel(network_interface: &NetworkInterface,
               config: Config) -> io::Result<datalink::Channel> {
    let cap = match pcap::Capture::from_device(&*network_interface.name) {
        Ok(cap) => cap,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
    }.buffer_size(config.read_buffer_size as i32);
    let cap = match config.read_timeout {
        Some(to) => cap.timeout(
            (to.as_secs() * 1000 + (to.subsec_nanos() / 1000) as u64) as i32
        ),
        None => cap
    };
    let cap = match cap.open() {
        Ok(cap) => cap,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
    };
    let cap = Arc::new(Mutex::new(cap));
    Ok(Ethernet(
        Box::new(DataLinkSenderImpl {
            capture: cap.clone(),
        }),
        Box::new(DataLinkReceiverImpl {
            capture: cap.clone(),
            read_buffer: repeat(0u8).take(config.read_buffer_size).collect(),
        })
    ))
}

/// Create a datalink channel from a pcap file
#[inline]
pub fn from_file<P: AsRef<Path>>(path: P, config: Config) -> io::Result<datalink::Channel> {
    let cap = match pcap::Capture::from_file(path) {
        Ok(cap) => cap,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
    };
    let cap = Arc::new(Mutex::new(cap));
    Ok(Ethernet(
        Box::new(InvalidDataLinkSenderImpl {}),
        Box::new(DataLinkReceiverImpl {
            capture: cap.clone(),
            read_buffer: repeat(0u8).take(config.read_buffer_size).collect(),
        })
    ))
}

struct DataLinkSenderImpl {
    capture: Arc<Mutex<pcap::Capture<Active>>>,
}

impl DataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      num_packets: usize,
                      packet_size: usize,
                      func: &mut FnMut(&mut [u8]))
                      -> Option<io::Result<()>> {
        for _ in 0..num_packets {
            let mut data = vec![0; packet_size];
            func(&mut data);
            let mut cap = self.capture.lock().unwrap();
            if let Err(e) = cap.sendpacket(data) {
                return Some(Err(io::Error::new(io::ErrorKind::Other, e)))
            }
        }
        Some(Ok(()))
    }

    #[inline]
    fn send_to(&mut self,
               packet: &[u8],
               _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        let mut cap = self.capture.lock().unwrap();
        Some(match cap.sendpacket(packet) {
            Ok(()) => Ok(()),
            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
        })
    }
}

struct InvalidDataLinkSenderImpl {}

impl DataLinkSender for InvalidDataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      _num_packets: usize,
                      _packet_size: usize,
                      _func: &mut FnMut(&mut [u8]))
                      -> Option<io::Result<()>> {
        None
    }

    #[inline]
    fn send_to(&mut self,
               _packet: &[u8],
               _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        None
    }
}

struct DataLinkReceiverImpl<T: Activated + Send + Sync> {
    capture: Arc<Mutex<pcap::Capture<T>>>,
    read_buffer: Vec<u8>,
}

impl <T: Activated + Send + Sync> DataLinkReceiver for DataLinkReceiverImpl<T> {
    fn iter<'a>(&'a mut self) -> Box<DataLinkChannelIterator + 'a> {
        Box::new(DataLinkChannelIteratorImpl { pc: self })
    }
}

struct DataLinkChannelIteratorImpl<'a, T: Activated + Send + Sync + 'a> {
    pc: &'a mut DataLinkReceiverImpl<T>,
}

impl<'a, T: Activated + Send + Sync> DataLinkChannelIterator<'a> for DataLinkChannelIteratorImpl<'a, T> {
    fn next(&mut self) -> io::Result<&[u8]> {
        let mut cap = self.pc.capture.lock().unwrap();
        match cap.next() {
            Ok(pkt) => {
                self.pc.read_buffer.truncate(0);
                self.pc.read_buffer.extend(pkt.data);
            },
            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
        };
        Ok(&self.pc.read_buffer)
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    if let Ok(devices) = pcap::Device::list() {
        devices.iter().enumerate().map(|(i, dev)| {
            NetworkInterface {
                name: dev.name.clone(),
                index: i as u32,
                mac: None,
                ips: Vec::new(),
                flags: 0,
            }
        }).collect()
    } else {
        vec![]
    }
}
<commit_msg>Get rid of DataLinkChannelIterator in pcap<commit_after>//! Support for sending and receiving data link layer packets using libpcap.
//! Also has support for reading pcap files.
extern crate pcap;

use std::marker::{Send, Sync};
use std::io;
use std::iter::repeat;
use self::pcap::{Active, Activated};
use datalink::{self, NetworkInterface};
use datalink::{DataLinkReceiver, DataLinkSender};
use datalink::Channel::Ethernet;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::path::Path;

/// Configuration for the pcap datalink backend
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when reading packets. Defaults to 4096
    pub read_buffer_size: usize,

    /// The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,
}

impl<'a> From<&'a datalink::Config> for Config {
    fn from(config: &datalink::Config) -> Config {
        Config{
            read_buffer_size: config.read_buffer_size,
            read_timeout: config.read_timeout,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config{
            read_buffer_size: 4096,
            read_timeout: None,
        }
    }
}

/// Create a datalink channel from the provided pcap device
#[inline]
pub fn channel(network_interface: &NetworkInterface,
               config: Config) -> io::Result<datalink::Channel> {
    let cap = match pcap::Capture::from_device(&*network_interface.name) {
        Ok(cap) => cap,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
    }.buffer_size(config.read_buffer_size as i32);
    let cap = match config.read_timeout {
        Some(to) => cap.timeout(
            (to.as_secs() * 1000 + (to.subsec_nanos() / 1000) as u64) as i32
        ),
        None => cap
    };
    let cap = match cap.open() {
        Ok(cap) => cap,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
    };
    let cap = Arc::new(Mutex::new(cap));
    Ok(Ethernet(
        Box::new(DataLinkSenderImpl {
            capture: cap.clone(),
        }),
        Box::new(DataLinkReceiverImpl {
            capture: cap.clone(),
            read_buffer: repeat(0u8).take(config.read_buffer_size).collect(),
        })
    ))
}

/// Create a datalink channel from a pcap file
#[inline]
pub fn from_file<P: AsRef<Path>>(path: P, config: Config) -> io::Result<datalink::Channel> {
    let cap = match pcap::Capture::from_file(path) {
        Ok(cap) => cap,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
    };
    let cap = Arc::new(Mutex::new(cap));
    Ok(Ethernet(
        Box::new(InvalidDataLinkSenderImpl {}),
        Box::new(DataLinkReceiverImpl {
            capture: cap.clone(),
            read_buffer: repeat(0u8).take(config.read_buffer_size).collect(),
        })
    ))
}

struct DataLinkSenderImpl {
    capture: Arc<Mutex<pcap::Capture<Active>>>,
}

impl DataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      num_packets: usize,
                      packet_size: usize,
                      func: &mut FnMut(&mut [u8]))
                      -> Option<io::Result<()>> {
        for _ in 0..num_packets {
            let mut data = vec![0; packet_size];
            func(&mut data);
            let mut cap = self.capture.lock().unwrap();
            if let Err(e) = cap.sendpacket(data) {
                return Some(Err(io::Error::new(io::ErrorKind::Other, e)))
            }
        }
        Some(Ok(()))
    }

    #[inline]
    fn send_to(&mut self,
               packet: &[u8],
               _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        let mut cap = self.capture.lock().unwrap();
        Some(match cap.sendpacket(packet) {
            Ok(()) => Ok(()),
            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
        })
    }
}

struct InvalidDataLinkSenderImpl {}

impl DataLinkSender for InvalidDataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      _num_packets: usize,
                      _packet_size: usize,
                      _func: &mut FnMut(&mut [u8]))
                      -> Option<io::Result<()>> {
        None
    }

    #[inline]
    fn send_to(&mut self,
               _packet: &[u8],
               _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        None
    }
}

struct DataLinkReceiverImpl<T: Activated + Send + Sync> {
    capture: Arc<Mutex<pcap::Capture<T>>>,
    read_buffer: Vec<u8>,
}

impl <T: Activated + Send + Sync> DataLinkReceiver for DataLinkReceiverImpl<T> {
    fn next(&mut self) -> io::Result<&[u8]> {
        let mut cap = self.capture.lock().unwrap();
        match cap.next() {
            Ok(pkt) => {
                self.read_buffer.truncate(0);
                self.read_buffer.extend(pkt.data);
            },
            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
        };
        Ok(&self.read_buffer)
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    if let Ok(devices) = pcap::Device::list() {
        devices.iter().enumerate().map(|(i, dev)| {
            NetworkInterface {
                name: dev.name.clone(),
                index: i as u32,
                mac: None,
                ips: Vec::new(),
                flags: 0,
            }
        }).collect()
    } else {
        vec![]
    }
}
<|endoftext|>


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

use std::collections::HashMap;
use std::path::Path;
use std::vec::Vec;

use dbus;
use dbus::Message;
use dbus::MessageItem;
use dbus::arg::Array;
use dbus::arg::IterAppend;
use dbus::tree::Access;
use dbus::tree::EmitsChangedSignal;
use dbus::tree::Factory;
use dbus::tree::MethodErr;
use dbus::tree::MTFn;
use dbus::tree::MethodResult;
use dbus::tree::MethodInfo;
use dbus::tree::PropInfo;

use uuid::Uuid;

use engine::{Pool, RenameAction};

use super::filesystem::create_dbus_filesystem;
use super::types::{DbusContext, DbusErrorEnum, OPContext, TData};

use super::util::STRATIS_BASE_PATH;
use super::util::STRATIS_BASE_SERVICE;
use super::util::code_to_message_items;
use super::util::engine_to_dbus_err;
use super::util::get_next_arg;
use super::util::get_uuid;
use super::util::ok_message_items;

fn create_filesystems(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let filesystems: Array<&str, _> = try!(get_next_arg(&mut iter, 0));
    let dbus_context = m.tree.get_data();

    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let return_sig = "(os)";
    let default_return = MessageItem::Array(vec![], return_sig.into());

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = get_pool!(engine; pool_uuid; default_return; return_message);

    let result = pool.create_filesystems(&filesystems.collect::<Vec<&str>>());

    let msg = match result {
        Ok(ref infos) => {
            let mut return_value = Vec::new();
            for &(name, uuid) in infos {
                let fs_object_path: dbus::Path =
                    create_dbus_filesystem(dbus_context, object_path.clone(), uuid);
                return_value.push((fs_object_path, name));
            }

            let return_value = return_value
                .iter()
                .map(|x| {
                         MessageItem::Struct(vec![MessageItem::ObjectPath(x.0.clone()),
                                                  MessageItem::Str((*x.1).into())])
                     })
                .collect();
            let return_value = MessageItem::Array(return_value, return_sig.into());
            let (rc, rs) = ok_message_items();
            return_message.append3(return_value, rc, rs)
        }
        Err(err) => {
            let (rc, rs) = engine_to_dbus_err(&err);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])

}

fn destroy_filesystems(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let filesystems: Array<dbus::Path<'static>, _> = try!(get_next_arg(&mut iter, 0));

    let dbus_context = m.tree.get_data();
    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let return_sig = "s";
    let default_return = MessageItem::Array(vec![], return_sig.into());

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = get_pool!(engine; pool_uuid; default_return; return_message);

    let mut filesystem_map: HashMap<Uuid, dbus::Path<'static>> = HashMap::new();
    for op in filesystems {
        if let Some(filesystem_path) = m.tree.get(&op) {
            let filesystem_uuid = get_data!(filesystem_path; default_return; return_message).uuid;
            filesystem_map.insert(filesystem_uuid.clone(), op);
        }
    }

    let result = pool.destroy_filesystems(&filesystem_map.keys().collect::<Vec<&Uuid>>());
    let msg = match result {
        Ok(ref uuids) => {
            for uuid in uuids {
                let op = filesystem_map
                    .get(uuid)
                    .expect("'uuids' is a subset of filesystem_map.keys()")
                    .clone();
                dbus_context.actions.borrow_mut().push_remove(op);
            }

            let return_value = uuids
                .iter()
                .map(|n| MessageItem::Str(format!("{}", n.simple())))
                .collect();
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Array(return_value, return_sig.into()), rc, rs)
        }
        Err(err) => {
            let (rc, rs) = engine_to_dbus_err(&err);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])
}

fn add_devs(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let force: bool = try!(get_next_arg(&mut iter, 0));
    let devs: Array<&str, _> = try!(get_next_arg(&mut iter, 1));

    let dbus_context = m.tree.get_data();
    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let return_sig = "s";
    let default_return = MessageItem::Array(vec![], return_sig.into());

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = get_pool!(engine; pool_uuid; default_return; return_message);

    let blockdevs = devs.map(|x| Path::new(x)).collect::<Vec<&Path>>();

    let msg = match pool.add_blockdevs(&blockdevs, force) {
        Ok(devnodes) => {
            let paths = devnodes
                .iter()
                .map(|d| {
                         d.to_str()
                             .expect("'d' originated in the 'devs' D-Bus argument.")
                             .into()
                     });
            let paths = paths.map(MessageItem::Str).collect();
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Array(paths, return_sig.into()), rc, rs)
        }
        Err(x) => {
            let (rc, rs) = engine_to_dbus_err(&x);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])
}

fn rename_pool(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let new_name: &str = try!(get_next_arg(&mut iter, 0));

    let dbus_context = m.tree.get_data();
    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let default_return = MessageItem::Bool(false);

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let msg = match dbus_context
              .engine
              .borrow_mut()
              .rename_pool(&pool_uuid, new_name) {
        Ok(RenameAction::NoSource) => {
            let error_message = format!("engine doesn't know about pool {}", pool_uuid);
            let (rc, rs) = code_to_message_items(DbusErrorEnum::INTERNAL_ERROR, error_message);
            return_message.append3(default_return, rc, rs)
        }
        Ok(RenameAction::Identity) => {
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Bool(false), rc, rs)
        }
        Ok(RenameAction::Renamed) => {
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Bool(true), rc, rs)
        }
        Err(err) => {
            let (rc, rs) = engine_to_dbus_err(&err);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])
}

/// Get a pool property and place it on the D-Bus. The property is
/// found by means of the getter method which takes a reference to a
/// Pool and obtains the property from the pool.
fn get_pool_property<F>(i: &mut IterAppend,
                        p: &PropInfo<MTFn<TData>, TData>,
                        getter: F)
                        -> Result<(), MethodErr>
    where F: Fn(&Pool) -> Result<MessageItem, MethodErr>
{
    let dbus_context = p.tree.get_data();
    let object_path = p.path.get_name();
    let pool_path = p.tree
        .get(object_path)
        .expect("implicit argument must be in tree");

    let pool_uuid = try!(pool_path
                        .get_data()
                        .as_ref()
                        .ok_or_else(|| {
                                        MethodErr::failed(&format!("no data for object path {}",
                                                                   object_path))
                                    }))
            .uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = try!(engine
                        .get_pool(&pool_uuid)
                        .ok_or(MethodErr::failed(&format!("no pool corresponding to uuid {}",
                                                          &pool_uuid))));

    i.append(try!(getter(pool)));
    Ok(())
}

fn get_pool_name(i: &mut IterAppend, p: &PropInfo<MTFn<TData>, TData>) -> Result<(), MethodErr> {
    get_pool_property(i, p, |p| Ok(MessageItem::Str(p.name().to_owned())))
}

pub fn create_dbus_pool<'a>(dbus_context: &DbusContext,
                            parent: dbus::Path<'static>,
                            uuid: Uuid)
                            -> dbus::Path<'a> {

    let f = Factory::new_fn();

    let create_filesystems_method = f.method("CreateFilesystems", (), create_filesystems)
        .in_arg(("specs", "as"))
        .out_arg(("filesystems", "a(os)"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let destroy_filesystems_method = f.method("DestroyFilesystems", (), destroy_filesystems)
        .in_arg(("filesystems", "ao"))
        .out_arg(("results", "as"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let add_devs_method = f.method("AddDevs", (), add_devs)
        .in_arg(("force", "b"))
        .in_arg(("devices", "as"))
        .out_arg(("results", "as"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let rename_method = f.method("SetName", (), rename_pool)
        .in_arg(("name", "s"))
        .out_arg(("action", "b"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let name_property = f.property::<&str, _>("Name", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::False)
        .on_get(get_pool_name);

    let uuid_property = f.property::<&str, _>("Uuid", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_uuid);

    let object_name = format!("{}/{}",
                              STRATIS_BASE_PATH,
                              dbus_context.get_next_id().to_string());

    let interface_name = format!("{}.{}", STRATIS_BASE_SERVICE, "pool");

    let object_path = f.object_path(object_name, Some(OPContext::new(parent, uuid)))
        .introspectable()
        .add(f.interface(interface_name, ())
                 .add_m(create_filesystems_method)
                 .add_m(destroy_filesystems_method)
                 .add_m(add_devs_method)
                 .add_m(rename_method)
                 .add_p(name_property)
                 .add_p(uuid_property));

    let path = object_path.get_name().to_owned();
    dbus_context.actions.borrow_mut().push_add(object_path);
    path
}
<commit_msg>Add D-Bus methods to export newly available Pool properties<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::path::Path;
use std::vec::Vec;

use dbus;
use dbus::Message;
use dbus::MessageItem;
use dbus::arg::Array;
use dbus::arg::IterAppend;
use dbus::tree::Access;
use dbus::tree::EmitsChangedSignal;
use dbus::tree::Factory;
use dbus::tree::MethodErr;
use dbus::tree::MTFn;
use dbus::tree::MethodResult;
use dbus::tree::MethodInfo;
use dbus::tree::PropInfo;

use uuid::Uuid;

use engine::{Pool, RenameAction};

use super::filesystem::create_dbus_filesystem;
use super::types::{DbusContext, DbusErrorEnum, OPContext, TData};

use super::util::STRATIS_BASE_PATH;
use super::util::STRATIS_BASE_SERVICE;
use super::util::code_to_message_items;
use super::util::engine_to_dbus_err;
use super::util::get_next_arg;
use super::util::get_uuid;
use super::util::ok_message_items;

fn create_filesystems(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let filesystems: Array<&str, _> = try!(get_next_arg(&mut iter, 0));
    let dbus_context = m.tree.get_data();

    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let return_sig = "(os)";
    let default_return = MessageItem::Array(vec![], return_sig.into());

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = get_pool!(engine; pool_uuid; default_return; return_message);

    let result = pool.create_filesystems(&filesystems.collect::<Vec<&str>>());

    let msg = match result {
        Ok(ref infos) => {
            let mut return_value = Vec::new();
            for &(name, uuid) in infos {
                let fs_object_path: dbus::Path =
                    create_dbus_filesystem(dbus_context, object_path.clone(), uuid);
                return_value.push((fs_object_path, name));
            }

            let return_value = return_value
                .iter()
                .map(|x| {
                         MessageItem::Struct(vec![MessageItem::ObjectPath(x.0.clone()),
                                                  MessageItem::Str((*x.1).into())])
                     })
                .collect();
            let return_value = MessageItem::Array(return_value, return_sig.into());
            let (rc, rs) = ok_message_items();
            return_message.append3(return_value, rc, rs)
        }
        Err(err) => {
            let (rc, rs) = engine_to_dbus_err(&err);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])

}

fn destroy_filesystems(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let filesystems: Array<dbus::Path<'static>, _> = try!(get_next_arg(&mut iter, 0));

    let dbus_context = m.tree.get_data();
    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let return_sig = "s";
    let default_return = MessageItem::Array(vec![], return_sig.into());

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = get_pool!(engine; pool_uuid; default_return; return_message);

    let mut filesystem_map: HashMap<Uuid, dbus::Path<'static>> = HashMap::new();
    for op in filesystems {
        if let Some(filesystem_path) = m.tree.get(&op) {
            let filesystem_uuid = get_data!(filesystem_path; default_return; return_message).uuid;
            filesystem_map.insert(filesystem_uuid.clone(), op);
        }
    }

    let result = pool.destroy_filesystems(&filesystem_map.keys().collect::<Vec<&Uuid>>());
    let msg = match result {
        Ok(ref uuids) => {
            for uuid in uuids {
                let op = filesystem_map
                    .get(uuid)
                    .expect("'uuids' is a subset of filesystem_map.keys()")
                    .clone();
                dbus_context.actions.borrow_mut().push_remove(op);
            }

            let return_value = uuids
                .iter()
                .map(|n| MessageItem::Str(format!("{}", n.simple())))
                .collect();
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Array(return_value, return_sig.into()), rc, rs)
        }
        Err(err) => {
            let (rc, rs) = engine_to_dbus_err(&err);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])
}

fn add_devs(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let force: bool = try!(get_next_arg(&mut iter, 0));
    let devs: Array<&str, _> = try!(get_next_arg(&mut iter, 1));

    let dbus_context = m.tree.get_data();
    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let return_sig = "s";
    let default_return = MessageItem::Array(vec![], return_sig.into());

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = get_pool!(engine; pool_uuid; default_return; return_message);

    let blockdevs = devs.map(|x| Path::new(x)).collect::<Vec<&Path>>();

    let msg = match pool.add_blockdevs(&blockdevs, force) {
        Ok(devnodes) => {
            let paths = devnodes
                .iter()
                .map(|d| {
                         d.to_str()
                             .expect("'d' originated in the 'devs' D-Bus argument.")
                             .into()
                     });
            let paths = paths.map(MessageItem::Str).collect();
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Array(paths, return_sig.into()), rc, rs)
        }
        Err(x) => {
            let (rc, rs) = engine_to_dbus_err(&x);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])
}

fn rename_pool(m: &MethodInfo<MTFn<TData>, TData>) -> MethodResult {
    let message: &Message = m.msg;
    let mut iter = message.iter_init();

    let new_name: &str = try!(get_next_arg(&mut iter, 0));

    let dbus_context = m.tree.get_data();
    let object_path = m.path.get_name();
    let return_message = message.method_return();
    let default_return = MessageItem::Bool(false);

    let pool_path = m.tree
        .get(object_path)
        .expect("implicit argument must be in tree");
    let pool_uuid = &get_data!(pool_path; default_return; return_message).uuid;

    let msg = match dbus_context
              .engine
              .borrow_mut()
              .rename_pool(&pool_uuid, new_name) {
        Ok(RenameAction::NoSource) => {
            let error_message = format!("engine doesn't know about pool {}", pool_uuid);
            let (rc, rs) = code_to_message_items(DbusErrorEnum::INTERNAL_ERROR, error_message);
            return_message.append3(default_return, rc, rs)
        }
        Ok(RenameAction::Identity) => {
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Bool(false), rc, rs)
        }
        Ok(RenameAction::Renamed) => {
            let (rc, rs) = ok_message_items();
            return_message.append3(MessageItem::Bool(true), rc, rs)
        }
        Err(err) => {
            let (rc, rs) = engine_to_dbus_err(&err);
            let (rc, rs) = code_to_message_items(rc, rs);
            return_message.append3(default_return, rc, rs)
        }
    };
    Ok(vec![msg])
}

/// Get a pool property and place it on the D-Bus. The property is
/// found by means of the getter method which takes a reference to a
/// Pool and obtains the property from the pool.
fn get_pool_property<F>(i: &mut IterAppend,
                        p: &PropInfo<MTFn<TData>, TData>,
                        getter: F)
                        -> Result<(), MethodErr>
    where F: Fn(&Pool) -> Result<MessageItem, MethodErr>
{
    let dbus_context = p.tree.get_data();
    let object_path = p.path.get_name();
    let pool_path = p.tree
        .get(object_path)
        .expect("implicit argument must be in tree");

    let pool_uuid = try!(pool_path
                        .get_data()
                        .as_ref()
                        .ok_or_else(|| {
                                        MethodErr::failed(&format!("no data for object path {}",
                                                                   object_path))
                                    }))
            .uuid;

    let mut engine = dbus_context.engine.borrow_mut();
    let pool = try!(engine
                        .get_pool(&pool_uuid)
                        .ok_or(MethodErr::failed(&format!("no pool corresponding to uuid {}",
                                                          &pool_uuid))));

    i.append(try!(getter(pool)));
    Ok(())
}

fn get_pool_name(i: &mut IterAppend, p: &PropInfo<MTFn<TData>, TData>) -> Result<(), MethodErr> {
    get_pool_property(i, p, |p| Ok(MessageItem::Str(p.name().to_owned())))
}

fn get_pool_total_physical_used(i: &mut IterAppend,
                                p: &PropInfo<MTFn<TData>, TData>)
                                -> Result<(), MethodErr> {
    fn get_used(pool: &Pool) -> Result<MessageItem, MethodErr> {
        let err_func = |_| {
            MethodErr::failed(&format!("no total physical size computed for pool with uuid {}",
                                       pool.uuid()))
        };

        try!(pool.total_physical_used()
                 .map(|u| Ok(MessageItem::Str(format!("{}", *u))))
                 .map_err(err_func))
    }

    get_pool_property(i, p, get_used)
}

fn get_pool_total_physical_size(i: &mut IterAppend,
                                p: &PropInfo<MTFn<TData>, TData>)
                                -> Result<(), MethodErr> {
    get_pool_property(i,
                      p,
                      |p| Ok(MessageItem::Str(format!("{}", *p.total_physical_size()))))
}

pub fn create_dbus_pool<'a>(dbus_context: &DbusContext,
                            parent: dbus::Path<'static>,
                            uuid: Uuid)
                            -> dbus::Path<'a> {

    let f = Factory::new_fn();

    let create_filesystems_method = f.method("CreateFilesystems", (), create_filesystems)
        .in_arg(("specs", "as"))
        .out_arg(("filesystems", "a(os)"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let destroy_filesystems_method = f.method("DestroyFilesystems", (), destroy_filesystems)
        .in_arg(("filesystems", "ao"))
        .out_arg(("results", "as"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let add_devs_method = f.method("AddDevs", (), add_devs)
        .in_arg(("force", "b"))
        .in_arg(("devices", "as"))
        .out_arg(("results", "as"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let rename_method = f.method("SetName", (), rename_pool)
        .in_arg(("name", "s"))
        .out_arg(("action", "b"))
        .out_arg(("return_code", "q"))
        .out_arg(("return_string", "s"));

    let name_property = f.property::<&str, _>("Name", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::False)
        .on_get(get_pool_name);

    let total_physical_size_property = f.property::<&str, _>("TotalPhysicalSize", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::False)
        .on_get(get_pool_total_physical_size);

    let total_physical_used_property = f.property::<&str, _>("TotalPhysicalUsed", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::False)
        .on_get(get_pool_total_physical_used);

    let uuid_property = f.property::<&str, _>("Uuid", ())
        .access(Access::Read)
        .emits_changed(EmitsChangedSignal::Const)
        .on_get(get_uuid);

    let object_name = format!("{}/{}",
                              STRATIS_BASE_PATH,
                              dbus_context.get_next_id().to_string());

    let interface_name = format!("{}.{}", STRATIS_BASE_SERVICE, "pool");

    let object_path = f.object_path(object_name, Some(OPContext::new(parent, uuid)))
        .introspectable()
        .add(f.interface(interface_name, ())
                 .add_m(create_filesystems_method)
                 .add_m(destroy_filesystems_method)
                 .add_m(add_devs_method)
                 .add_m(rename_method)
                 .add_p(name_property)
                 .add_p(total_physical_size_property)
                 .add_p(total_physical_used_property)
                 .add_p(uuid_property));

    let path = object_path.get_name().to_owned();
    dbus_context.actions.borrow_mut().push_add(object_path);
    path
}
<|endoftext|>


<commit_before>use super::MembershipState;
use crate::{MxcUri, UserId};

/// The details of a member event required to calculate a [`MembershipChange`].
pub struct MembershipDetails<'a> {
    pub(crate) avatar_url: Option<&'a MxcUri>,
    pub(crate) displayname: Option<&'a str>,
    pub(crate) membership: &'a MembershipState,
}

/// Translation of the membership change in `m.room.member` event.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum MembershipChange {
    /// No change.
    None,

    /// Must never happen.
    Error,

    /// User joined the room.
    Joined,

    /// User left the room.
    Left,

    /// User was banned.
    Banned,

    /// User was unbanned.
    Unbanned,

    /// User was kicked.
    Kicked,

    /// User was invited.
    Invited,

    /// User was kicked and banned.
    KickedAndBanned,

    /// User rejected the invite.
    InvitationRejected,

    /// User had their invite revoked.
    InvitationRevoked,

    /// `displayname` or `avatar_url` changed.
    ProfileChanged {
        /// Whether the `displayname` changed.
        displayname_changed: bool,

        /// Whether the `avatar_url` changed.
        avatar_url_changed: bool,
    },

    /// Not implemented.
    NotImplemented,
}

/// Internal function so all `RoomMemberEventContent` state event kinds can share the same
/// implementation.
pub(super) fn membership_change(
    details: MembershipDetails<'_>,
    prev_details: Option<MembershipDetails<'_>>,
    sender: &UserId,
    state_key: &UserId,
) -> MembershipChange {
    use MembershipChange as Ch;
    use MembershipState as St;

    let prev_details = match prev_details {
        Some(prev) => prev,
        None => MembershipDetails { avatar_url: None, displayname: None, membership: &St::Leave },
    };

    match (&prev_details.membership, &details.membership) {
        (St::Invite, St::Invite) | (St::Leave, St::Leave) | (St::Ban, St::Ban) => Ch::None,
        (St::Invite, St::Join) | (St::Leave, St::Join) => Ch::Joined,
        (St::Invite, St::Leave) if sender == state_key => Ch::InvitationRevoked,
        (St::Invite, St::Leave) => Ch::InvitationRejected,
        (St::Invite, St::Ban) | (St::Leave, St::Ban) => Ch::Banned,
        (St::Join, St::Invite) | (St::Ban, St::Invite) | (St::Ban, St::Join) => Ch::Error,
        (St::Join, St::Join) => Ch::ProfileChanged {
            displayname_changed: prev_details.displayname != details.displayname,
            avatar_url_changed: prev_details.avatar_url != details.avatar_url,
        },
        (St::Join, St::Leave) if sender == state_key => Ch::Left,
        (St::Join, St::Leave) => Ch::Kicked,
        (St::Join, St::Ban) => Ch::KickedAndBanned,
        (St::Leave, St::Invite) => Ch::Invited,
        (St::Ban, St::Leave) => Ch::Unbanned,
        _ => Ch::NotImplemented,
    }
}
<commit_msg>events: Derive Clone and Debug for MembershipDetails<commit_after>use super::MembershipState;
use crate::{MxcUri, UserId};

/// The details of a member event required to calculate a [`MembershipChange`].
#[derive(Clone, Debug)]
pub struct MembershipDetails<'a> {
    pub(crate) avatar_url: Option<&'a MxcUri>,
    pub(crate) displayname: Option<&'a str>,
    pub(crate) membership: &'a MembershipState,
}

/// Translation of the membership change in `m.room.member` event.
#[derive(Clone, Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum MembershipChange {
    /// No change.
    None,

    /// Must never happen.
    Error,

    /// User joined the room.
    Joined,

    /// User left the room.
    Left,

    /// User was banned.
    Banned,

    /// User was unbanned.
    Unbanned,

    /// User was kicked.
    Kicked,

    /// User was invited.
    Invited,

    /// User was kicked and banned.
    KickedAndBanned,

    /// User rejected the invite.
    InvitationRejected,

    /// User had their invite revoked.
    InvitationRevoked,

    /// `displayname` or `avatar_url` changed.
    ProfileChanged {
        /// Whether the `displayname` changed.
        displayname_changed: bool,

        /// Whether the `avatar_url` changed.
        avatar_url_changed: bool,
    },

    /// Not implemented.
    NotImplemented,
}

/// Internal function so all `RoomMemberEventContent` state event kinds can share the same
/// implementation.
pub(super) fn membership_change(
    details: MembershipDetails<'_>,
    prev_details: Option<MembershipDetails<'_>>,
    sender: &UserId,
    state_key: &UserId,
) -> MembershipChange {
    use MembershipChange as Ch;
    use MembershipState as St;

    let prev_details = match prev_details {
        Some(prev) => prev,
        None => MembershipDetails { avatar_url: None, displayname: None, membership: &St::Leave },
    };

    match (&prev_details.membership, &details.membership) {
        (St::Invite, St::Invite) | (St::Leave, St::Leave) | (St::Ban, St::Ban) => Ch::None,
        (St::Invite, St::Join) | (St::Leave, St::Join) => Ch::Joined,
        (St::Invite, St::Leave) if sender == state_key => Ch::InvitationRevoked,
        (St::Invite, St::Leave) => Ch::InvitationRejected,
        (St::Invite, St::Ban) | (St::Leave, St::Ban) => Ch::Banned,
        (St::Join, St::Invite) | (St::Ban, St::Invite) | (St::Ban, St::Join) => Ch::Error,
        (St::Join, St::Join) => Ch::ProfileChanged {
            displayname_changed: prev_details.displayname != details.displayname,
            avatar_url_changed: prev_details.avatar_url != details.avatar_url,
        },
        (St::Join, St::Leave) if sender == state_key => Ch::Left,
        (St::Join, St::Leave) => Ch::Kicked,
        (St::Join, St::Ban) => Ch::KickedAndBanned,
        (St::Leave, St::Invite) => Ch::Invited,
        (St::Ban, St::Leave) => Ch::Unbanned,
        _ => Ch::NotImplemented,
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
* Internal class to handle OpenAl context and device.
*
* Work as a Singleton, check_al_context must be called before each OpenAl object
* to be sure that the context is created.
*/

use std::{local_data, ptr};
use openal::ffi;

static al_context : local_data::Key<@OpenAlData> = &local_data::Key;

pub struct OpenAlData {
    priv al_context : *ffi::ALCcontext,
    priv al_device : *ffi::ALCdevice
}

impl OpenAlData {
    /**
    * Create a new OpenAlData struct
    *
    * Private method.
    */
    #[fixed_stack_segment] #[inline(never)]
    fn new() -> Result<OpenAlData, ~str> {
        let device = unsafe { ffi::alcOpenDevice(ptr::null()) };
        if device.is_null() { return Err(~"Internal error : cannot open the default device."); }
        let context = unsafe { ffi::alcCreateContext(device, ptr::null()) };
        if context.is_null() { return Err(~"Internal error : cannot create the OpenAL context."); }
        if unsafe { ffi::alcMakeContextCurrent(context) } == ffi::ALC_FALSE {
            return Err(~"Internal error : cannot make the OpenAL context current.");
        }

        Ok(
            OpenAlData {
                al_context : context,
                al_device : device
            }
        )
    }

    /**
    * Check if the context is created.
    *
    * This function check is the OpenAl context is already created. If context doesn't exist, create it,
    * and store it in a local_data, else get it from the local data and return it.
    *
    * # Return
    * A result containing nothing if the OpenAlData struct exist, otherwise an error message.
    */
    pub fn check_al_context() -> Result<(), ~str> {
        local_data::get(al_context, |openal_data| {
            match openal_data {
                Some(_)    => Ok(()),
                None        => {
                    match OpenAlData::new() {
                        Ok(al_data) => { local_data::set(al_context, @al_data); Ok(()) },
                        Err(err)    => Err(err)
                    }
                }
            }
        })
    }
}

impl Drop for OpenAlData {
    #[fixed_stack_segment] #[inline(never)]
    fn drop(&mut self) {
        unsafe {
            ffi::alcDestroyContext(self.al_context);
            ffi::alcCloseDevice(self.al_device);
        }
    }
}<commit_msg>check of the openal modified<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
* Internal class to handle OpenAl context and device.
*
* Work as a Singleton, check_al_context must be called before each OpenAl object
* to be sure that the context is created.
*/

use std::{local_data, ptr};
use openal::ffi;

static al_context : local_data::Key<@OpenAlData> = &local_data::Key;

#[deriving(Clone)]
pub struct OpenAlData {
    priv al_context : *ffi::ALCcontext,
    priv al_device : *ffi::ALCdevice
}

impl OpenAlData {
    /**
    * Create a new OpenAlData struct
    *
    * Private method.
    */
    #[fixed_stack_segment] #[inline(never)]
    fn new() -> Result<OpenAlData, ~str> {
        let device = unsafe { ffi::alcOpenDevice(ptr::null()) };
        if device.is_null() { return Err(~"Internal error : cannot open the default device."); }
        let context = unsafe { ffi::alcCreateContext(device, ptr::null()) };
        if context.is_null() { return Err(~"Internal error : cannot create the OpenAL context."); }
        if unsafe { ffi::alcMakeContextCurrent(context) } == ffi::ALC_FALSE {
            return Err(~"Internal error : cannot make the OpenAL context current.");
        }

        Ok(
            OpenAlData {
                al_context : context,
                al_device : device
            }
        )
    }

    /**
    * Check if the context is created.
    *
    * This function check is the OpenAl context is already created. If context doesn't exist, create it,
    * and store it in a local_data, else get it from the local data and return it.
    *
    * # Return
    * A result containing nothing if the OpenAlData struct exist, otherwise an error message.
    */
    #[fixed_stack_segment] #[inline(never)]
    pub fn check_al_context() -> Result<(), ~str> {
        if unsafe { ffi::alcGetCurrentContext() } != ptr::null() {
            return Ok(())
        }
        local_data::get(al_context, |openal_data| {
            match openal_data {
                Some(_)    => Ok(()),
                None        => {
                    match OpenAlData::new() {
                        Ok(al_data) => { local_data::set(al_context, @al_data); Ok(()) },
                        Err(err)    => Err(err)
                    }
                }
            }
        })
    }
}

impl Drop for OpenAlData {
    #[fixed_stack_segment] #[inline(never)]
    fn drop(&mut self) {
        unsafe {
            ffi::alcDestroyContext(self.al_context);
            ffi::alcCloseDevice(self.al_device);
        }
    }
}<|endoftext|>


<commit_before>use std::env;
use std::ffi::{CString, OsStr};
use std::fs::{self, File, OpenOptions};
use std::io;
cfg_if! {
    if #[cfg(not(target_os = "wasi"))] {
        use std::os::unix::ffi::OsStrExt;
        use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
    } else {
        use std::os::wasi::ffi::OsStrExt;
        #[cfg(feature = "nightly")]
        use std::os::wasi::fs::MetadataExt;
    }
}
use crate::util;
use std::path::Path;

#[cfg(not(target_os = "redox"))]
use libc::{c_char, c_int, link, rename, unlink};

#[cfg(not(target_os = "redox"))]
#[inline(always)]
pub fn cvt_err(result: c_int) -> io::Result<c_int> {
    if result == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(result)
    }
}

#[cfg(target_os = "redox")]
#[inline(always)]
pub fn cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize> {
    result.map_err(|err| io::Error::from_raw_os_error(err.errno))
}

// Stolen from std.
pub fn cstr(path: &Path) -> io::Result<CString> {
    CString::new(path.as_os_str().as_bytes())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
    open_options.read(true).write(true).create_new(true);

    #[cfg(not(target_os = "wasi"))]
    {
        open_options.mode(0o600);
    }

    open_options.open(path)
}

fn create_unlinked(path: &Path) -> io::Result<File> {
    let tmp;
    // shadow this to decrease the lifetime. It can't live longer than `tmp`.
    let mut path = path;
    if !path.is_absolute() {
        let cur_dir = env::current_dir()?;
        tmp = cur_dir.join(path);
        path = &tmp;
    }

    let f = create_named(path, &mut OpenOptions::new())?;
    // don't care whether the path has already been unlinked,
    // but perhaps there are some IO error conditions we should send up?
    let _ = fs::remove_file(path);
    Ok(f)
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    use libc::{EISDIR, ENOENT, EOPNOTSUPP, O_EXCL, O_TMPFILE};
    OpenOptions::new()
        .read(true)
        .write(true)
        .custom_flags(O_TMPFILE | O_EXCL) // do not mix with `create_new(true)`
        .open(dir)
        .or_else(|e| {
            match e.raw_os_error() {
                // These are the three "not supported" error codes for O_TMPFILE.
                Some(EOPNOTSUPP) | Some(EISDIR) | Some(ENOENT) => create_unix(dir),
                _ => Err(e),
            }
        })
}

#[cfg(not(target_os = "linux"))]
pub fn create(dir: &Path) -> io::Result<File> {
    create_unix(dir)
}

fn create_unix(dir: &Path) -> io::Result<File> {
    util::create_helper(
        dir,
        OsStr::new(".tmp"),
        OsStr::new(""),
        crate::NUM_RAND_CHARS,
        |path| create_unlinked(&path),
    )
}

#[cfg(any(not(target_os = "wasi"), feature = "nightly"))]
pub fn reopen(file: &File, path: &Path) -> io::Result<File> {
    let new_file = OpenOptions::new().read(true).write(true).open(path)?;
    let old_meta = file.metadata()?;
    let new_meta = new_file.metadata()?;
    if old_meta.dev() != new_meta.dev() || old_meta.ino() != new_meta.ino() {
        return Err(io::Error::new(
            io::ErrorKind::NotFound,
            "original tempfile has been replaced",
        ));
    }
    Ok(new_file)
}

#[cfg(all(target_os = "wasi", not(feature = "nightly")))]
pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> {
    return Err(io::Error::new(
        io::ErrorKind::Other,
        "this operation is supported on WASI only on nightly Rust (with `nightly` feature enabled)",
    ));
}

#[cfg(not(target_os = "redox"))]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    unsafe {
        let old_path = cstr(old_path)?;
        let new_path = cstr(new_path)?;
        if overwrite {
            cvt_err(rename(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
        } else {
            cvt_err(link(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
            // Ignore unlink errors. Can we do better?
            // On recent linux, we can use renameat2 to do this atomically.
            let _ = unlink(old_path.as_ptr() as *const c_char);
        }
        Ok(())
    }
}

#[cfg(target_os = "redox")]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    // XXX implement when possible
    Err(io::Error::from_raw_os_error(syscall::ENOSYS))
}

pub fn keep(_: &Path) -> io::Result<()> {
    Ok(())
}
<commit_msg>feat: Removed O_EXCL to allow for tmpfile persistence on Linux<commit_after>use std::env;
use std::ffi::{CString, OsStr};
use std::fs::{self, File, OpenOptions};
use std::io;
cfg_if! {
    if #[cfg(not(target_os = "wasi"))] {
        use std::os::unix::ffi::OsStrExt;
        use std::os::unix::fs::{MetadataExt, OpenOptionsExt};
    } else {
        use std::os::wasi::ffi::OsStrExt;
        #[cfg(feature = "nightly")]
        use std::os::wasi::fs::MetadataExt;
    }
}
use crate::util;
use std::path::Path;

#[cfg(not(target_os = "redox"))]
use libc::{c_char, c_int, link, rename, unlink};

#[cfg(not(target_os = "redox"))]
#[inline(always)]
pub fn cvt_err(result: c_int) -> io::Result<c_int> {
    if result == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(result)
    }
}

#[cfg(target_os = "redox")]
#[inline(always)]
pub fn cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize> {
    result.map_err(|err| io::Error::from_raw_os_error(err.errno))
}

// Stolen from std.
pub fn cstr(path: &Path) -> io::Result<CString> {
    CString::new(path.as_os_str().as_bytes())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contained a null"))
}

pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
    open_options.read(true).write(true).create_new(true);

    #[cfg(not(target_os = "wasi"))]
    {
        open_options.mode(0o600);
    }

    open_options.open(path)
}

fn create_unlinked(path: &Path) -> io::Result<File> {
    let tmp;
    // shadow this to decrease the lifetime. It can't live longer than `tmp`.
    let mut path = path;
    if !path.is_absolute() {
        let cur_dir = env::current_dir()?;
        tmp = cur_dir.join(path);
        path = &tmp;
    }

    let f = create_named(path, &mut OpenOptions::new())?;
    // don't care whether the path has already been unlinked,
    // but perhaps there are some IO error conditions we should send up?
    let _ = fs::remove_file(path);
    Ok(f)
}

#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
    use libc::{EISDIR, ENOENT, EOPNOTSUPP, O_TMPFILE};
    OpenOptions::new()
        .read(true)
        .write(true)
        .custom_flags(O_TMPFILE) // do not mix with `create_new(true)`
        .open(dir)
        .or_else(|e| {
            match e.raw_os_error() {
                // These are the three "not supported" error codes for O_TMPFILE.
                Some(EOPNOTSUPP) | Some(EISDIR) | Some(ENOENT) => create_unix(dir),
                _ => Err(e),
            }
        })
}

#[cfg(not(target_os = "linux"))]
pub fn create(dir: &Path) -> io::Result<File> {
    create_unix(dir)
}

fn create_unix(dir: &Path) -> io::Result<File> {
    util::create_helper(
        dir,
        OsStr::new(".tmp"),
        OsStr::new(""),
        crate::NUM_RAND_CHARS,
        |path| create_unlinked(&path),
    )
}

#[cfg(any(not(target_os = "wasi"), feature = "nightly"))]
pub fn reopen(file: &File, path: &Path) -> io::Result<File> {
    let new_file = OpenOptions::new().read(true).write(true).open(path)?;
    let old_meta = file.metadata()?;
    let new_meta = new_file.metadata()?;
    if old_meta.dev() != new_meta.dev() || old_meta.ino() != new_meta.ino() {
        return Err(io::Error::new(
            io::ErrorKind::NotFound,
            "original tempfile has been replaced",
        ));
    }
    Ok(new_file)
}

#[cfg(all(target_os = "wasi", not(feature = "nightly")))]
pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> {
    return Err(io::Error::new(
        io::ErrorKind::Other,
        "this operation is supported on WASI only on nightly Rust (with `nightly` feature enabled)",
    ));
}

#[cfg(not(target_os = "redox"))]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    unsafe {
        let old_path = cstr(old_path)?;
        let new_path = cstr(new_path)?;
        if overwrite {
            cvt_err(rename(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
        } else {
            cvt_err(link(
                old_path.as_ptr() as *const c_char,
                new_path.as_ptr() as *const c_char,
            ))?;
            // Ignore unlink errors. Can we do better?
            // On recent linux, we can use renameat2 to do this atomically.
            let _ = unlink(old_path.as_ptr() as *const c_char);
        }
        Ok(())
    }
}

#[cfg(target_os = "redox")]
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
    // XXX implement when possible
    Err(io::Error::from_raw_os_error(syscall::ENOSYS))
}

pub fn keep(_: &Path) -> io::Result<()> {
    Ok(())
}
<|endoftext|>


<commit_before>#![feature(std_misc)]
#![feature(test)]

extern crate test;

use std::time::duration::Duration;
use std::old_io::timer;

use test::Bencher;

pub fn sleep_ns (ns: i64) {
    let interval = Duration::nanoseconds(ns);
    timer::sleep(interval);
}

#[test]
fn it_works() {
    sleep_ns();
}

#[bench]
fn bench_foo (b: &mut test::Bencher) {
    b.iter(|| {
        foo();
    });

    //pritnln!("nanoseconds {}", b.ns_per_iter());
}
<commit_msg>fix name of sleep_ns()<commit_after>#![feature(std_misc)]
#![feature(test)]

extern crate test;

use std::time::duration::Duration;
use std::old_io::timer;

use test::Bencher;

pub fn sleep_ns (ns: i64) {
    let interval = Duration::nanoseconds(ns);
    timer::sleep(interval);
}

#[test]
fn it_works() {
    sleep_ns(1);
}

#[bench]
fn bench_foo (b: &mut test::Bencher) {
    b.iter(|| {
        sleep_ns(1);
    });
}
<|endoftext|>


<commit_before>//! Definition of the Shared combinator, a future that is cloneable,
//! and can be polled in multiple threads.
//!
//! # Examples
//!
//! ```
//! use futures::future::*;
//!
//! let future = ok::<_, bool>(6);
//! let shared1 = future.shared();
//! let shared2 = shared1.clone();
//! assert_eq!(6, *shared1.wait().unwrap());
//! assert_eq!(6, *shared2.wait().unwrap());
//! ```

use std::fmt;
use std::mem;
use std::sync::{Arc, Mutex};
use std::ops::Deref;
use std::collections::HashMap;
use std::vec::Vec;

use {Future, Poll, Async};
use task;

/// A future that is cloneable and can be polled in multiple threads.
/// Use Future::shared() method to convert any future into a `Shared` future.
#[must_use = "futures do nothing unless polled"]
pub struct Shared<F: Future> {
    id: u64,
    inner: Arc<Inner<F>>,
}

impl<F> fmt::Debug for Shared<F>
    where F: Future + fmt::Debug,
          F::Item: fmt::Debug,
          F::Error: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Shared")
            .field("id", &self.id)
            .field("inner", &self.inner)
            .finish()
    }
}

struct Inner<F: Future> {
    next_clone_id: Mutex<u64>,
    state: Mutex<State<F>>,
}

impl<F> fmt::Debug for Inner<F>
    where F: Future + fmt::Debug,
          F::Item: fmt::Debug,
          F::Error: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Inner")
            .field("next_clone_id", &self.next_clone_id)
            .field("state", &self.state)
            .finish()
    }
}

#[derive(Debug)]
enum State<F: Future> {
    Waiting(Arc<Unparker>, F),
    Polling(Arc<Unparker>, Vec<task::Task>),
    Done(Result<Arc<F::Item>, Arc<F::Error>>),
}

impl<F> Shared<F>
    where F: Future
{
    /// Creates a new `Shared` from another future.
    pub fn new(future: F) -> Self {
        Shared {
            id: 0,
            inner: Arc::new(
                Inner {
                    next_clone_id: Mutex::new(1),
                    state: Mutex::new(State::Waiting(Arc::new(Unparker::new()), future)),
                 }),
        }
    }

    /// If any clone of this `Shared` has completed execution, returns its result immediately
    /// without blocking. Otherwise, returns None without triggering the work represented by
    /// this `Shared`.
    pub fn peek(&self) -> Option<Result<SharedItem<F::Item>, SharedError<F::Error>>> {
        match *self.inner.state.lock().unwrap() {
            State::Done(Ok(ref v)) =>
              Some(Ok(SharedItem { item: v.clone() }.into())),
            State::Done(Err(ref e)) =>
              Some(Err(SharedError { error: e.clone() }.into())),
            _ => None,
        }
    }
}

impl<F> Future for Shared<F>
    where F: Future
{
    type Item = SharedItem<F::Item>;
    type Error = SharedError<F::Error>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut state = self.inner.state.lock().unwrap();
        let unparker = match *state {
            State::Waiting(ref unparker, ref _original_future) => {
                let mut unparker_inner = unparker.inner.lock().unwrap();
                if unparker_inner.original_future_needs_poll {
                    unparker_inner.original_future_needs_poll = false;
                    unparker.clone()
                } else {
                    unparker_inner.insert(self.id, task::park());
                    return Ok(Async::NotReady)
                }
            }
            State::Polling(ref unparker, ref mut waiters) => {
                // A clone of this `Shared` is currently calling `original_future.poll()`.
                let mut unparker_inner = unparker.inner.lock().unwrap();
                if unparker_inner.original_future_needs_poll {
                    // We need to poll the original future, but it's not here right now.
                    // So we store the current task to be unconditionally unparked once
                    // `state` is no longer `Polling`.
                    waiters.push(task::park());
                } else {
                    unparker_inner.insert(self.id, task::park());
                }
                return Ok(Async::NotReady)
            }
            State::Done(Ok(ref v)) => return Ok(SharedItem { item: v.clone() }.into()),
            State::Done(Err(ref e)) => return Err(SharedError { error: e.clone() }.into()),
        };

        let new_state = State::Polling(unparker, Vec::new());
        let (unparker, mut original_future) = match mem::replace(&mut *state, new_state) {
            State::Waiting(unparker, original_future) => (unparker, original_future),
            _ => unreachable!(),
        };
        drop(state);

        let event = task::UnparkEvent::new(unparker.clone(), 0);
        let new_state = match task::with_unpark_event(event, || original_future.poll()) {
            Ok(Async::NotReady) => State::Waiting(unparker, original_future),
            Ok(Async::Ready(v)) => State::Done(Ok(Arc::new(v))),
            Err(e) => State::Done(Err(Arc::new(e))),
        };

        let (call_unpark, result) = match new_state {
            State::Waiting(..) => (false, Ok(Async::NotReady)),
            State::Polling(..) => unreachable!(),
            State::Done(Ok(ref v)) => (true, Ok(SharedItem { item: v.clone() }.into())),
            State::Done(Err(ref e)) => (true, Err(SharedError { error: e.clone() }.into())),
        };

        let mut state = self.inner.state.lock().unwrap();
        match mem::replace(&mut *state, new_state) {
            State::Polling(unparker, waiters) => {
                if call_unpark { unparker.unpark() }
                for waiter in waiters {
                    waiter.unpark();
                }
            }
            _ => unreachable!(),
        }

        result
    }
}

impl<F> Clone for Shared<F>
    where F: Future
{
    fn clone(&self) -> Self {
        let mut next_clone_id = self.inner.next_clone_id.lock().unwrap();
        let clone_id = *next_clone_id;
        *next_clone_id += 1;
        Shared {
            id: clone_id,
            inner: self.inner.clone(),
        }
    }
}

impl<F: Future> Drop for Shared<F> {
    fn drop(&mut self) {
        if let Ok(state) = self.inner.state.lock() {
            match *state {
                State::Waiting(ref unparker, _) => {
                    unparker.remove(self.id);
                }
                State::Polling(ref unparker, _) => {
                    unparker.remove(self.id);
                }
                State::Done(_) => (),
            }
        }
    }
}

/// A wrapped item of the original future that is clonable and implements Deref
/// for ease of use.
#[derive(Debug)]
pub struct SharedItem<T> {
    item: Arc<T>,
}

impl<T> Deref for SharedItem<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.item.as_ref()
    }
}

/// A wrapped error of the original future that is clonable and implements Deref
/// for ease of use.
#[derive(Debug)]
pub struct SharedError<E> {
    error: Arc<E>,
}

impl<E> Deref for SharedError<E> {
    type Target = E;

    fn deref(&self) -> &E {
        &self.error.as_ref()
    }
}

/// An `EventSet` implementation for passing to `with_unpark_event()` when a `Shared`
/// polls its underlying future. Usually, the purpose of an `EventSet` implementation
/// is to gather precise information about what triggered an unpark, but that is *not*
/// what this implementation does. Instead, it uses `EventSet::insert()` as a hook
/// to unpark a set of waiting tasks.
#[derive(Debug)]
struct Unparker {
    inner: Mutex<UnparkerInner>,
}

#[derive(Debug)]
struct UnparkerInner {
    original_future_needs_poll: bool,

    /// Tasks that need to be unparked once the original future resolves.
    tasks: HashMap<u64, task::Task>,
}

impl UnparkerInner {
    fn insert(&mut self, idx: u64, task: task::Task) {
        self.tasks.insert(idx, task);
    }
}

impl task::EventSet for Unparker {
    fn insert(&self, _id: usize) {
        // The original future is ready to get polled again.
        self.unpark();
    }
}

impl Unparker {
    fn new() -> Unparker {
        Unparker {
            inner: Mutex::new(UnparkerInner{
                original_future_needs_poll: true,
                tasks: HashMap::new(),
            }),
        }
    }

    fn remove(&self, idx: u64) {
        if let Ok(mut inner) = self.inner.lock() {
            inner.tasks.remove(&idx);
        }
    }

    fn unpark(&self) {
        let UnparkerInner { tasks, .. } = mem::replace(
            &mut *self.inner.lock().unwrap(),
            UnparkerInner {
                original_future_needs_poll: true,
                tasks: HashMap::new(),
            });

        for (_, task) in tasks {
            task.unpark();
        }
    }
}
<commit_msg>Do not use unpark events as part Shared<commit_after>//! Definition of the Shared combinator, a future that is cloneable,
//! and can be polled in multiple threads.
//!
//! # Examples
//!
//! ```
//! use futures::future::*;
//!
//! let future = ok::<_, bool>(6);
//! let shared1 = future.shared();
//! let shared2 = shared1.clone();
//! assert_eq!(6, *shared1.wait().unwrap());
//! assert_eq!(6, *shared2.wait().unwrap());
//! ```

use {Future, Poll, Async};
use executor::{self, Spawn, Unpark};
use task::{self, Task};

use std::{fmt, mem, ops};
use std::cell::UnsafeCell;
use std::sync::{Arc, Mutex};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::collections::HashMap;

/// A future that is cloneable and can be polled in multiple threads.
/// Use Future::shared() method to convert any future into a `Shared` future.
#[must_use = "futures do nothing unless polled"]
pub struct Shared<F: Future> {
    inner: Arc<Inner<F>>,
    waiter: usize,
}

impl<F> fmt::Debug for Shared<F>
    where F: Future + fmt::Debug,
          F::Item: fmt::Debug,
          F::Error: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Shared")
            .field("inner", &self.inner)
            .finish()
    }
}

struct Inner<F: Future> {
    next_clone_id: AtomicUsize,
    future: UnsafeCell<Spawn<F>>,
    result: UnsafeCell<Option<Result<SharedItem<F::Item>, SharedError<F::Error>>>>,
    unparker: Arc<Unparker>,
}

struct Unparker {
    state: AtomicUsize,
    waiters: Mutex<HashMap<usize, Task>>,
}

const IDLE: usize = 0;
const POLLING: usize = 1;
const REPOLL: usize = 2;
const COMPLETE: usize = 3;
const POISONED: usize = 4;

impl<F> Shared<F> where F: Future {
    /// Creates a new `Shared` from another future.
    pub fn new(future: F) -> Self {
        Shared {
            inner: Arc::new(Inner {
                next_clone_id: AtomicUsize::new(1),
                unparker: Arc::new(Unparker {
                    state: AtomicUsize::new(IDLE),
                    waiters: Mutex::new(HashMap::new()),
                }),
                future: UnsafeCell::new(executor::spawn(future)),
                result: UnsafeCell::new(None),
            }),
            waiter: 0,
        }
    }

    /// If any clone of this `Shared` has completed execution, returns its result immediately
    /// without blocking. Otherwise, returns None without triggering the work represented by
    /// this `Shared`.
    pub fn peek(&self) -> Option<Result<SharedItem<F::Item>, SharedError<F::Error>>> {
        match self.inner.unparker.state.load(SeqCst) {
            COMPLETE => {
                Some(unsafe { self.clone_result() })
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => None,
        }
    }

    fn set_waiter(&mut self) {
        let mut waiters = self.inner.unparker.waiters.lock().unwrap();
        waiters.insert(self.waiter, task::park());
    }

    unsafe fn clone_result(&self) -> Result<SharedItem<F::Item>, SharedError<F::Error>> {
        match *self.inner.result.get() {
            Some(Ok(ref item)) => Ok(item.clone()),
            Some(Err(ref e)) => Err(e.clone()),
            _ => unreachable!(),
        }
    }

    fn complete(&self) {
        self.inner.unparker.state.store(COMPLETE, SeqCst);
        self.inner.unparker.unpark();
    }
}

impl<F> Future for Shared<F>
    where F: Future
{
    type Item = SharedItem<F::Item>;
    type Error = SharedError<F::Error>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.set_waiter();

        match self.inner.unparker.state.compare_and_swap(IDLE, POLLING, SeqCst) {
            IDLE => {
                // Lock acquired, fall through
            }
            POLLING | REPOLL => {
                // Another task is currently polling, at this point we just want
                // to ensure that our task handle is currently registered

                return Ok(Async::NotReady);
            }
            COMPLETE => {
                return unsafe { self.clone_result().map(Async::Ready) };
            }
            POISONED => panic!("inner future panicked during poll"),
            _ => unreachable!(),
        }

        loop {
            struct Reset<'a>(&'a AtomicUsize);

            impl<'a> Drop for Reset<'a> {
                fn drop(&mut self) {
                    use std::thread;

                    if thread::panicking() {
                        self.0.store(POISONED, SeqCst);
                    }
                }
            }

            let _reset = Reset(&self.inner.unparker.state);

            // Get a handle to the unparker
            let unpark: Arc<Unpark> = self.inner.unparker.clone();

            // Poll the future
            match unsafe { (*self.inner.future.get()).poll_future(unpark) } {
                Ok(Async::NotReady) => {
                    // Not ready, try to release the handle
                    match self.inner.unparker.state.compare_and_swap(POLLING, IDLE, SeqCst) {
                        POLLING => {
                            // Success
                            return Ok(Async::NotReady);
                        }
                        REPOLL => {
                            // Gotta poll again!
                            let prev = self.inner.unparker.state.swap(POLLING, SeqCst);
                            assert_eq!(prev, REPOLL);
                        }
                        _ => unreachable!(),
                    }

                }
                Ok(Async::Ready(i)) => {
                    unsafe {
                        (*self.inner.result.get()) = Some(Ok(SharedItem { item: Arc::new(i) }));
                    }

                    self.complete();
                    return unsafe { self.clone_result().map(Async::Ready) };
                }
                Err(e) => {
                    unsafe {
                        (*self.inner.result.get()) = Some(Err(SharedError { error: Arc::new(e) }));
                    }

                    self.complete();
                    return unsafe { self.clone_result().map(Async::Ready) };
                }
            }
        }
    }
}

impl<F> Clone for Shared<F> where F: Future {
    fn clone(&self) -> Self {
        let next_clone_id = self.inner.next_clone_id.fetch_add(1, SeqCst);

        Shared {
            inner: self.inner.clone(),
            waiter: next_clone_id,
        }
    }
}

impl Unpark for Unparker {
    fn unpark(&self) {
        self.state.compare_and_swap(POLLING, REPOLL, SeqCst);

        let waiters = mem::replace(&mut *self.waiters.lock().unwrap(), HashMap::new());

        for (_, waiter) in waiters {
            waiter.unpark();
        }
    }
}

unsafe impl<F: Future> Sync for Inner<F> {}
unsafe impl<F: Future> Send for Inner<F> {}

impl<F> fmt::Debug for Inner<F>
    where F: Future + fmt::Debug,
          F::Item: fmt::Debug,
          F::Error: fmt::Debug,
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Inner")
            /*
            .field("next_clone_id", &self.next_clone_id)
            .field("state", &self.state)
            */
            .finish()
    }
}

/// A wrapped item of the original future that is clonable and implements Deref
/// for ease of use.
#[derive(Debug)]
pub struct SharedItem<T> {
    item: Arc<T>,
}

impl<T> ops::Deref for SharedItem<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.item.as_ref()
    }
}

impl<T> Clone for SharedItem<T> {
    fn clone(&self) -> Self {
        SharedItem { item: self.item.clone() }
    }
}

/// A wrapped error of the original future that is clonable and implements Deref
/// for ease of use.
#[derive(Debug)]
pub struct SharedError<E> {
    error: Arc<E>,
}

impl<E> ops::Deref for SharedError<E> {
    type Target = E;

    fn deref(&self) -> &E {
        &self.error.as_ref()
    }
}

impl<T> Clone for SharedError<T> {
    fn clone(&self) -> Self {
        SharedError { error: self.error.clone() }
    }
}
<|endoftext|>


<commit_before>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::ioivec;
import std::ioivec::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

/*
// Imports for "the rest of driver::compile_input"
import driver = rustc::driver::rustc; // see https://github.com/graydon/rust/issues/624
import rustc::back::link;
import rustc::driver::rustc::time;
import rustc::driver::session;

import rustc::metadata::creader;
import rustc::metadata::cstore;
import rustc::syntax::parse::parser;
import rustc::syntax::parse::token;
import rustc::front;
import rustc::front::attr;
import rustc::middle;
import rustc::middle::trans;
import rustc::middle::resolve;
import rustc::middle::ty;
import rustc::middle::typeck;
import rustc::middle::tstate::ck;
import rustc::syntax::print::pp;
import rustc::util::ppaux;
import rustc::lib::llvm;
*/

fn read_whole_file(filename: &str) -> str {
    str::unsafe_from_bytes_ivec(ioivec::file_reader(filename).read_whole_stream())
}

fn write_file(filename: &str, content: &str) {
    ioivec::file_writer(filename,
                        ~[ioivec::create,
                          ioivec::truncate]).write_str(content);
}

fn file_contains(filename: &str, needle: &str) -> bool {
    let contents = read_whole_file(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(haystack: &str, needle: &str) -> bool {
    str::find(haystack, needle) != -1
}

fn find_rust_files(files: &mutable str[], path: str) {
    if str::ends_with(path, ".rs") {
        if file_contains(path, "xfail-stage1") {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else { files += ~[path]; }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1)
     {
        for p: str  in fs::list_dir(path) { find_rust_files(files, p); }
    }
}

fn safe_to_steal(e: ast::expr_) -> bool {
    alt e {

      // pretty-printer precedence issues -- https://github.com/graydon/rust/issues/670
      ast::expr_unary(_, _) {
        false
      }
      ast::expr_lit(lit) {
        alt lit.node {
          ast::lit_str(_, _) { true }
          ast::lit_char(_) { true }
          ast::lit_int(_) { false }
          ast::lit_uint(_) { false }
          ast::lit_mach_int(_, _) { false }
          ast::lit_float(_) { false }
          ast::lit_mach_float(_, _) { false }
          ast::lit_nil. { true }
          ast::lit_bool(_) { true }
        }
      }
      ast::expr_cast(_, _) { false }
      ast::expr_send(_, _) { false }
      ast::expr_recv(_, _) { false }
      ast::expr_assert(_) { false }
      ast::expr_binary(_, _, _) { false }
      ast::expr_assign(_, _) { false }
      ast::expr_assign_op(_, _, _) { false }

      // https://github.com/graydon/rust/issues/676
      ast::expr_ret(option::none.) {
        false
      }
      ast::expr_put(option::none.) { false }

      _ {
        true
      }
    }
}

fn steal_exprs(crate: &ast::crate) -> ast::expr[] {
    let exprs: @mutable ast::expr[] = @mutable ~[];
    // "Stash" is not type-parameterized because of the need for safe_to_steal
    fn stash_expr(es: @mutable ast::expr[], e: &@ast::expr) {
        if safe_to_steal(e.node) {
            *es += ~[*e];
        } else {/* now my indices are wrong :( */ }
    }
    let v =
        {visit_expr_pre: bind stash_expr(exprs, _)
            with walk::default_visitor()};
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(e: ast::expr_) -> bool {
    alt e {
      ast::expr_if(_, _, _) { false }
      ast::expr_block(_) { false }
      _ { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(crate: &ast::crate, i: uint, newexpr: ast::expr_) ->
   ast::crate {
    let j: @mutable uint = @mutable 0u;
    fn fold_expr_rep(j_: @mutable uint, i_: uint, newexpr_: &ast::expr_,
                     original: &ast::expr_, fld: fold::ast_fold) ->
       ast::expr_ {
        *j_ += 1u;
        if i_ + 1u == *j_ && safe_to_replace(original) {
            newexpr_
        } else { fold::noop_fold_expr(original, fld) }
    }
    let afp =
        {fold_expr: bind fold_expr_rep(j, i, newexpr, _, _)
            with *fold::default_ast_fold()};
    let af = fold::make_fold(afp);
    let crate2: @ast::crate = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(n: uint) -> uint {
    let i: uint = 0u;
    while i < n { put i; i += 1u; }
}

fn devnull() -> ioivec::writer { std::ioivec::string_writer().get_writer() }

fn as_str(f: fn(ioivec::writer) ) -> str {
    let w = std::ioivec::string_writer();
    f(w.get_writer());
    ret w.get_str();
}

/*
fn pp_variants(&ast::crate crate, &codemap::codemap cmap, &str filename) {
    auto exprs = steal_exprs(crate);
    auto exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each (uint i in under(uint::min(exprsL, 20u))) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each (uint j in under(uint::min(exprsL, 5u))) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                auto crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                check_roundtrip(crate2, cmap, filename + ".4.rs");
            }
        }
    }
}
*/

fn parse_and_print(code: &str) -> str {
    let filename = "";
    let codemap = codemap::new_codemap();
    let crate =
        parser::parse_crate_from_source_str(filename, code, ~[], codemap);
    ret as_str(bind pprust::print_crate(codemap, crate, filename,
                                        ioivec::string_reader(code), _,
                                        pprust::no_ann()));
}

fn content_is_confusing(code: &str) -> bool {
    let  // https://github.com/graydon/rust/issues/671
         // https://github.com/graydon/rust/issues/669
         // https://github.com/graydon/rust/issues/669
         // https://github.com/graydon/rust/issues/669
         // crazy rules enforced by parser rather than typechecker?
         // more precedence issues
         // more precedence issues?
        confusing_patterns =
        ["#macro", "][]", "][mutable]", "][mutable ]", "self", "spawn",
         "bind"];

    for p: str  in confusing_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn file_is_confusing(filename: &str) -> bool {
    let

         // https://github.com/graydon/rust/issues/674

         // something to do with () as a lone pattern

         // an issue where -2147483648 gains an
         // extra negative sign each time through,
         // which i can't reproduce using "rustc
         // --pretty normal"???
         confusing_files =
        ["block-expr-precedence.rs", "nil-pattern.rs",
         "syntax-extension-fmt.rs"];

    for f: str  in confusing_files { if contains(filename, f) { ret true; } }

    ret false;
}

fn check_roundtrip_convergence(code: &str) {

    let i = 0;
    let new = code;
    let old = code;

    while i < 10 {
        old = new;
        new = parse_and_print(old);
        if content_is_confusing(new) { ret; }
        i += 1;
        log #fmt("cycle %d", i);
    }

    if old != new {
        write_file("round-trip-a.rs", old);
        write_file("round-trip-b.rs", new);
        std::run::run_program("kdiff3",
                              ["round-trip-a.rs", "round-trip-b.rs"]);
        fail "Mismatch";
    }
}
fn check_convergence(files: &str[]) {
    log_err #fmt("pp convergence tests: %u files", ivec::len(files));
    for file: str  in files {

        log_err #fmt("pp converge: %s", file);
        if !file_is_confusing(file) {
            let s = read_whole_file(file);
            if !content_is_confusing(s) { check_roundtrip_convergence(s); }
        }

        //pprust::print_crate(cm, crate, file, devnull(), pprust::no_ann());
        // Currently hits https://github.com/graydon/rust/issues/675
        //pp_variants(*crate, cm, file);
    }
}

fn main(args: vec[str]) {
    if vec::len(args) != 2u {
        log_err #fmt("usage: %s <testdir>", args.(0));
        ret;
    }
    let files = ~[];
    let root = args.(1);

    find_rust_files(files, root);
    check_convergence(files);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Get expr-moving fuzzer working again<commit_after>use std;
use rustc;

import std::fs;
import std::getopts;
import std::getopts::optopt;
import std::getopts::opt_present;
import std::getopts::opt_str;
import std::ioivec;
import std::ioivec::stdout;
import std::vec;
import std::ivec;
import std::str;
import std::uint;
import std::option;

import rustc::syntax::ast;
import rustc::syntax::fold;
import rustc::syntax::walk;
import rustc::syntax::codemap;
import rustc::syntax::parse::parser;
import rustc::syntax::print::pprust;

fn read_whole_file(filename: &str) -> str {
    str::unsafe_from_bytes_ivec(ioivec::file_reader(filename).read_whole_stream())
}

fn write_file(filename: &str, content: &str) {
    ioivec::file_writer(filename,
                        ~[ioivec::create,
                          ioivec::truncate]).write_str(content);
    // Work around https://github.com/graydon/rust/issues/726
    std::run::run_program("chmod", ["644", filename]);
}

fn file_contains(filename: &str, needle: &str) -> bool {
    let contents = read_whole_file(filename);
    ret str::find(contents, needle) != -1;
}

fn contains(haystack: &str, needle: &str) -> bool {
    str::find(haystack, needle) != -1
}

fn find_rust_files(files: &mutable str[], path: str) {
    if str::ends_with(path, ".rs") {
        if file_contains(path, "xfail-stage1") {
            //log_err "Skipping " + path + " because it is marked as xfail-stage1";
        } else { files += ~[path]; }
    } else if (fs::file_is_dir(path) && str::find(path, "compile-fail") == -1)
     {
        for p in fs::list_dir(path) { find_rust_files(files, p); }
    }
}

fn safe_to_steal(e: ast::expr_) -> bool {
    alt e {

      // pretty-printer precedence issues -- https://github.com/graydon/rust/issues/670
      ast::expr_unary(_, _) {
        false
      }
      ast::expr_lit(lit) {
        alt lit.node {
          ast::lit_str(_, _) { true }
          ast::lit_char(_) { true }
          ast::lit_int(_) { false }
          ast::lit_uint(_) { false }
          ast::lit_mach_int(_, _) { false }
          ast::lit_float(_) { false }
          ast::lit_mach_float(_, _) { false }
          ast::lit_nil. { true }
          ast::lit_bool(_) { true }
        }
      }
      ast::expr_cast(_, _) { false }
      ast::expr_send(_, _) { false }
      ast::expr_recv(_, _) { false }
      ast::expr_assert(_) { false }
      ast::expr_binary(_, _, _) { false }
      ast::expr_assign(_, _) { false }
      ast::expr_assign_op(_, _, _) { false }
      ast::expr_fail(option::none.) { false /* https://github.com/graydon/rust/issues/764 */ }
      ast::expr_ret(option::none.) { false }
      ast::expr_put(option::none.) { false }

      _ {
        true
      }
    }
}

fn steal_exprs(crate: &ast::crate) -> ast::expr[] {
    let exprs: @mutable ast::expr[] = @mutable ~[];
    // "Stash" is not type-parameterized because of the need for safe_to_steal
    fn stash_expr(es: @mutable ast::expr[], e: &@ast::expr) {
        if safe_to_steal(e.node) {
            *es += ~[*e];
        } else {/* now my indices are wrong :( */ }
    }
    let v =
        {visit_expr_pre: bind stash_expr(exprs, _)
            with walk::default_visitor()};
    walk::walk_crate(v, crate);
    *exprs
}

// https://github.com/graydon/rust/issues/652
fn safe_to_replace(e: ast::expr_) -> bool {
    alt e {
      ast::expr_if(_, _, _) { false }
      ast::expr_block(_) { false }
      _ { true }
    }
}

// Replace the |i|th expr (in fold order) of |crate| with |newexpr|.
fn replace_expr_in_crate(crate: &ast::crate, i: uint, newexpr: ast::expr_) ->
   ast::crate {
    let j: @mutable uint = @mutable 0u;
    fn fold_expr_rep(j_: @mutable uint, i_: uint, newexpr_: &ast::expr_,
                     original: &ast::expr_, fld: fold::ast_fold) ->
       ast::expr_ {
        *j_ += 1u;
        if i_ + 1u == *j_ && safe_to_replace(original) {
            newexpr_
        } else { fold::noop_fold_expr(original, fld) }
    }
    let afp =
        {fold_expr: bind fold_expr_rep(j, i, newexpr, _, _)
            with *fold::default_ast_fold()};
    let af = fold::make_fold(afp);
    let crate2: @ast::crate = @af.fold_crate(crate);
    fold::dummy_out(af); // work around a leak (https://github.com/graydon/rust/issues/651)
    *crate2
}

iter under(n: uint) -> uint {
    let i: uint = 0u;
    while i < n { put i; i += 1u; }
}

fn devnull() -> ioivec::writer { std::ioivec::string_writer().get_writer() }

fn as_str(f: fn(ioivec::writer) ) -> str {
    let w = std::ioivec::string_writer();
    f(w.get_writer());
    ret w.get_str();
}

fn pp_variants(crate: &ast::crate, codemap: &codemap::codemap, filename: &str) {
    let exprs = steal_exprs(crate);
    let exprsL = ivec::len(exprs);
    if (exprsL < 100u) {
        for each i: uint in under(uint::min(exprsL, 20u)) {
            log_err "Replacing... " + pprust::expr_to_str(@exprs.(i));
            for each j: uint in under(uint::min(exprsL, 5u)) {
                log_err "With... " + pprust::expr_to_str(@exprs.(j));
                let crate2 = @replace_expr_in_crate(crate, i, exprs.(j).node);
                // It would be best to test the *crate* for stability, but testing the
                // string for stability is easier and ok for now.
                let str3 = as_str(bind pprust::print_crate(codemap, crate2, filename,
                                  ioivec::string_reader(""), _,
                                  pprust::no_ann()));
                // 1u would be sane here, but the pretty-printer currently has lots of whitespace and paren issues,
                // and https://github.com/graydon/rust/issues/766 is hilarious.
                check_roundtrip_convergence(str3, 7u);
            }
        }
    }
}

fn parse_and_print(code: &str) -> str {
    let filename = "tmp.rs";
    let codemap = codemap::new_codemap();
    //write_file(filename, code);
    let crate =
        parser::parse_crate_from_source_str(filename, code, ~[], codemap);
    ret as_str(bind pprust::print_crate(codemap, crate, filename,
                                        ioivec::string_reader(code), _,
                                        pprust::no_ann()));
}

fn content_is_dangerous_to_modify(code: &str) -> bool {
    let dangerous_patterns = [
         "obj", // not safe to steal; https://github.com/graydon/rust/issues/761
         "#macro", // not safe to steal things inside of it, because they have a special syntax
         " be " // don't want to replace its child with a non-call: "Non-call expression in tail call"
    ];

    for p: str in dangerous_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn content_is_confusing(code: &str) -> bool {
    let  // https://github.com/graydon/rust/issues/671
         // https://github.com/graydon/rust/issues/669
         // https://github.com/graydon/rust/issues/669
         // https://github.com/graydon/rust/issues/669
         // crazy rules enforced by parser rather than typechecker?
         // more precedence issues
         // more precedence issues?
        confusing_patterns =
        ["#macro", "][]", "][mutable]", "][mutable ]", "self", "spawn",
         "bind",
         "\n\n\n\n\n", // https://github.com/graydon/rust/issues/759
         " : ", // https://github.com/graydon/rust/issues/760
         "if ret",
         "alt ret",
         "if fail",
         "alt fail"
         ];

    for p: str in confusing_patterns { if contains(code, p) { ret true; } }
    ret false;
}

fn file_is_confusing(filename: &str) -> bool {
    let

         // https://github.com/graydon/rust/issues/674

         // something to do with () as a lone pattern

         // an issue where -2147483648 gains an
         // extra negative sign each time through,
         // which i can't reproduce using "rustc
         // --pretty normal"???
         confusing_files =
        ["block-expr-precedence.rs", "nil-pattern.rs",
         "syntax-extension-fmt.rs",
         "newtype.rs" // modifying it hits something like https://github.com/graydon/rust/issues/670
         ];

    for f in confusing_files { if contains(filename, f) { ret true; } }

    ret false;
}

fn check_roundtrip_convergence(code: &str, maxIters: uint) {

    let i = 0u;
    let new = code;
    let old = code;

    while i < maxIters {
        old = new;
        if content_is_confusing(old) { ret; }
        new = parse_and_print(old);
        if old == new { break; }
        i += 1u;
    }

    if old == new {
        log_err #fmt("Converged after %u iterations", i);
    } else {
        log_err #fmt("Did not converge after %u iterations!", i);
        write_file("round-trip-a.rs", old);
        write_file("round-trip-b.rs", new);
        std::run::run_program("diff", ["-w", "-u", "round-trip-a.rs", "round-trip-b.rs"]);
        fail "Mismatch";
    }
}

fn check_convergence(files: &str[]) {
    log_err #fmt("pp convergence tests: %u files", ivec::len(files));
    for file in files {
        if !file_is_confusing(file) {
            let s = read_whole_file(file);
            if !content_is_confusing(s) {
                log_err #fmt("pp converge: %s", file);
                // Change from 7u to 2u when https://github.com/graydon/rust/issues/759 is fixed
                check_roundtrip_convergence(s, 7u);
            }
        }
    }
}

fn check_convergence_of_variants(files: &str[]) {
    for file in files {
        if !file_is_confusing(file) {
            let s = read_whole_file(file);
            if content_is_dangerous_to_modify(s) || content_is_confusing(s) { cont; }
            log_err "check_convergence_of_variants: " + file;
            let codemap = codemap::new_codemap();
            let crate = parser::parse_crate_from_source_str(file, s, ~[], codemap);
            log_err as_str(bind pprust::print_crate(codemap, crate, file,
                                        ioivec::string_reader(s), _,
                                        pprust::no_ann()));
            pp_variants(*crate, codemap, file);
        }
    }
}

fn main(args: vec[str]) {
    if vec::len(args) != 2u {
        log_err #fmt("usage: %s <testdir>", args.(0));
        ret;
    }
    let files = ~[];
    let root = args.(1);

    find_rust_files(files, root);
    check_convergence(files);
    check_convergence_of_variants(files);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>