<commit_before>use std::default::Default;
use std::collections::BTreeMap;

use quire::validate as V;
use quire::ast::Ast as A;
use quire::ast::Ast;
use quire::ast::Tag::{NonSpecific};
use quire::ast::ScalarKind::{Plain};

type PortNumValidator = V::Numeric;

#[derive(RustcDecodable, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum Pid1Mode {
    exec = 0,
    wait = 1,
    wait_all_children = 2,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum SuperviseMode {
    wait_all,
    stop_on_failure,
    restart,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum WriteMode {
    read_only,
    //transient_reflink_copy, // TODO(tailhook)
    transient_hard_link_copy,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct Network {
    pub ip: String,
    pub hostname: Option<String>,
    pub ports: BTreeMap<u16, u16>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct CommandInfo {
    // Common for toplevel commands
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,

    // Command
    pub network: Option<Network>, // Only for top-levels
    pub pid1mode: Pid1Mode,
    pub work_dir: Option<String>,
    pub container: String,
    pub accepts_arguments: Option<bool>,
    pub environ: BTreeMap<String, String>,
    pub write_mode: WriteMode,
    pub run: Vec<String>,
    pub user_id: u32,
    pub external_user_id: Option<u32>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct SuperviseInfo {
    // Common
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,

    // Supervise
    pub mode: SuperviseMode,
    pub kill_unresponsive_after: u32,
    pub children: BTreeMap<String, ChildCommand>,
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum MainCommand {
    Command(CommandInfo),
    Supervise(SuperviseInfo),
}

impl MainCommand {
    pub fn description<'x>(&'x self) -> Option<&'x String> {
        match *self {
            MainCommand::Command(ref cmd) => cmd.description.as_ref(),
            MainCommand::Supervise(ref cmd) => cmd.description.as_ref(),
        }
    }
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum ChildCommand {
    Command(CommandInfo),
    BridgeCommand(CommandInfo),
}

impl ChildCommand {
    pub fn get_container<'x>(&'x self) -> &String {
        match *self {
            ChildCommand::Command(ref info) => &info.container,
            ChildCommand::BridgeCommand(ref info) => &info.container,
        }
    }
}

fn shell_command(ast: Ast) -> Vec<Ast> {
    match ast {
        A::Scalar(pos, _, style, value) => {
            return vec!(
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "/bin/sh".to_string()),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "-c".to_string()),
                A::Scalar(pos.clone(), NonSpecific, style,
                          value),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "--".to_string()),
                );
        }
        _ => unreachable!(),
    }
}

fn run_fields<'a>(cmd: V::Structure, network: bool) -> V::Structure {
    let mut cmd = cmd
        .member("pid1mode", V::Scalar::new().default("wait"))
        .member("work_dir", V::Scalar::new().optional())
        .member("container", V::Scalar::new())
        .member("accepts_arguments", V::Scalar::new().optional())
        .member("environ", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("write_mode", V::Scalar::new().default("read-only"))
        .member("run", V::Sequence::new(V::Scalar::new())
            .parser(shell_command as fn(Ast) -> Vec<Ast>))
        .member("user_id", V::Numeric::new().min(0).max(1 << 30).default(0))
        .member("external_user_id",
            V::Numeric::new().min(0).max(1 << 30).optional());
    if network {
        cmd = cmd
            .member("network", V::Structure::new().optional()
                .member("ip", V::Scalar::new().optional())
                .member("hostname", V::Scalar::new().optional())
                .member("ports", V::Mapping::new(
                    V::Numeric::new(),
                    V::Numeric::new())))
    }
    return cmd;
}

fn command_fields<'a>(cmd: V::Structure) -> V::Structure {
    cmd
    .member("description", V::Scalar::new().optional())
    .member("banner", V::Scalar::new().optional())
    .member("banner_delay", V::Numeric::new().optional().min(0))
    .member("epilog", V::Scalar::new().optional())
}

fn subcommand_validator<'a>() -> V::Enum<'a> {
    V::Enum::new()
    .option("Command",
        run_fields(command_fields(V::Structure::new()), true))
    .option("BridgeCommand",
        run_fields(command_fields(V::Structure::new()), false))
}

pub fn command_validator<'a>() -> V::Enum<'a> {
    let cmd = run_fields(command_fields(V::Structure::new()), false);
    let sup = command_fields(V::Structure::new());

    let sup = sup
        .member("mode", V::Scalar::new().default("stop-on-failure"))
        .member("children", V::Mapping::new(
            V::Scalar::new(),
            subcommand_validator()))
        .member("kill_unresponsive_after",
            V::Numeric::new().default(2).min(1).max(86400));

    return V::Enum::new()
        .option("Command", cmd)
        .option("Supervise", sup);
}

pub trait Networking {
    fn network<'x>(&'x self) -> Option<&'x Network>;
}

impl Networking for ChildCommand {
    fn network<'x>(&'x self) -> Option<&'x Network> {
        match self {
            &ChildCommand::Command(ref cmd) => cmd.network.as_ref(),
            &ChildCommand::BridgeCommand(_) => None,
        }
    }
}
<commit_msg>add 'tags' field to Command<commit_after>use std::default::Default;
use std::collections::BTreeMap;

use quire::validate as V;
use quire::ast::Ast as A;
use quire::ast::Ast;
use quire::ast::Tag::{NonSpecific};
use quire::ast::ScalarKind::{Plain};

type PortNumValidator = V::Numeric;

#[derive(RustcDecodable, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum Pid1Mode {
    exec = 0,
    wait = 1,
    wait_all_children = 2,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum SuperviseMode {
    wait_all,
    stop_on_failure,
    restart,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum WriteMode {
    read_only,
    //transient_reflink_copy, // TODO(tailhook)
    transient_hard_link_copy,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct Network {
    pub ip: String,
    pub hostname: Option<String>,
    pub ports: BTreeMap<u16, u16>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct CommandInfo {
    // Common for toplevel commands
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub tags: Vec<String>,

    // Command
    pub network: Option<Network>, // Only for top-levels
    pub pid1mode: Pid1Mode,
    pub work_dir: Option<String>,
    pub container: String,
    pub accepts_arguments: Option<bool>,
    pub environ: BTreeMap<String, String>,
    pub write_mode: WriteMode,
    pub run: Vec<String>,
    pub user_id: u32,
    pub external_user_id: Option<u32>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct SuperviseInfo {
    // Common
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,

    // Supervise
    pub mode: SuperviseMode,
    pub kill_unresponsive_after: u32,
    pub children: BTreeMap<String, ChildCommand>,
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum MainCommand {
    Command(CommandInfo),
    Supervise(SuperviseInfo),
}

impl MainCommand {
    pub fn description<'x>(&'x self) -> Option<&'x String> {
        match *self {
            MainCommand::Command(ref cmd) => cmd.description.as_ref(),
            MainCommand::Supervise(ref cmd) => cmd.description.as_ref(),
        }
    }
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum ChildCommand {
    Command(CommandInfo),
    BridgeCommand(CommandInfo),
}

impl ChildCommand {
    pub fn get_container<'x>(&'x self) -> &String {
        match *self {
            ChildCommand::Command(ref info) => &info.container,
            ChildCommand::BridgeCommand(ref info) => &info.container,
        }
    }
    pub fn get_tags<'x>(&'x self) -> &Vec<String> {
        match *self {
            ChildCommand::Command(ref info) => &info.tags,
            ChildCommand::BridgeCommand(ref info) => &info.tags,
        }
    }
}

fn shell_command(ast: Ast) -> Vec<Ast> {
    match ast {
        A::Scalar(pos, _, style, value) => {
            return vec!(
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "/bin/sh".to_string()),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "-c".to_string()),
                A::Scalar(pos.clone(), NonSpecific, style,
                          value),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "--".to_string()),
                );
        }
        _ => unreachable!(),
    }
}

fn run_fields<'a>(cmd: V::Structure, network: bool) -> V::Structure {
    let mut cmd = cmd
        .member("pid1mode", V::Scalar::new().default("wait"))
        .member("work_dir", V::Scalar::new().optional())
        .member("container", V::Scalar::new())
        .member("accepts_arguments", V::Scalar::new().optional())
        .member("environ", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("write_mode", V::Scalar::new().default("read-only"))
        .member("run", V::Sequence::new(V::Scalar::new())
            .parser(shell_command as fn(Ast) -> Vec<Ast>))
        .member("user_id", V::Numeric::new().min(0).max(1 << 30).default(0))
        .member("external_user_id",
            V::Numeric::new().min(0).max(1 << 30).optional());
    if network {
        cmd = cmd
            .member("network", V::Structure::new().optional()
                .member("ip", V::Scalar::new().optional())
                .member("hostname", V::Scalar::new().optional())
                .member("ports", V::Mapping::new(
                    V::Numeric::new(),
                    V::Numeric::new())))
    }
    return cmd;
}

fn command_fields<'a>(cmd: V::Structure) -> V::Structure {
    cmd
    .member("description", V::Scalar::new().optional())
    .member("banner", V::Scalar::new().optional())
    .member("banner_delay", V::Numeric::new().optional().min(0))
    .member("epilog", V::Scalar::new().optional())
    .member("tags", V::Sequence::new(V::Scalar::new()))
}

fn subcommand_validator<'a>() -> V::Enum<'a> {
    V::Enum::new()
    .option("Command",
        run_fields(command_fields(V::Structure::new()), true))
    .option("BridgeCommand",
        run_fields(command_fields(V::Structure::new()), false))
}

pub fn command_validator<'a>() -> V::Enum<'a> {
    let cmd = run_fields(command_fields(V::Structure::new()), false);
    let sup = command_fields(V::Structure::new());

    let sup = sup
        .member("mode", V::Scalar::new().default("stop-on-failure"))
        .member("children", V::Mapping::new(
            V::Scalar::new(),
            subcommand_validator()))
        .member("kill_unresponsive_after",
            V::Numeric::new().default(2).min(1).max(86400));

    return V::Enum::new()
        .option("Command", cmd)
        .option("Supervise", sup);
}

pub trait Networking {
    fn network<'x>(&'x self) -> Option<&'x Network>;
}

impl Networking for ChildCommand {
    fn network<'x>(&'x self) -> Option<&'x Network> {
        match self {
            &ChildCommand::Command(ref cmd) => cmd.network.as_ref(),
            &ChildCommand::BridgeCommand(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2015 Rustcc Developers

// 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.

use std::collections::LinkedList;

use sync::spinlock::SpinLock;
use coroutine::{self, Coroutine, Handle};

pub struct Mutex<T> {
    lock: SpinLock,
    inner: T,
    wait_list: LinkedList<Handle>,
}

impl<T> Mutex<T> {
    pub fn new(inner: T) -> Mutex<T> {
        Mutex {
            lock: SpinLock::new(),
            inner: inner,
            wait_list: LinkedList::new(),
        }
    }

    pub fn into_inner(self) -> T {
        self.inner
    }

    pub fn lock<'a>(&'a mut self) -> LockGuard<'a, T> {
        match self.try_lock() {
            Some(lg) => lg,
            None => {

                LockGuard::new(self)
            }
        }
    }

    pub fn try_lock<'a>(&'a self) -> Option<LockGuard<'a, T>> {
        if self.lock.try_lock() {
            Some(LockGuard::new(self))
        } else {
            None
        }
    }

    fn unlock(&self) {

    }
}

pub struct LockGuard<'a, T: 'a> {
    mutex: &'a Mutex<T>,
}

impl<'a, T: 'a> LockGuard<'a, T> {
    fn new(mutex: &'a Mutex<T>) -> LockGuard<'a, T> {
        LockGuard {
            mutex: mutex,
        }
    }
}

impl<'a, T: 'a> Drop for LockGuard<'a, T> {
    fn drop(&mut self) {
        self.mutex.unlock()
    }
}
<commit_msg>basic implementation of mutex<commit_after>// The MIT License (MIT)

// Copyright (c) 2015 Rustcc Developers

// 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.

use std::collections::VecDeque;
use std::ops::{Deref, DerefMut};
use std::cell::UnsafeCell;

use sync::spinlock::SpinLock;
use coroutine::{self, Coroutine, Handle};

pub struct Mutex<T> {
    lock: SpinLock,
    inner: UnsafeCell<T>,
    wait_list: UnsafeCell<VecDeque<Handle>>,
}

impl<T> Mutex<T> {
    pub fn new(inner: T) -> Mutex<T> {
        Mutex {
            lock: SpinLock::new(),
            inner: UnsafeCell::new(inner),
            wait_list: UnsafeCell::new(VecDeque::new()),
        }
    }

    pub fn into_inner(self) -> T {
        unsafe {
            self.inner.into_inner()
        }
    }

    pub fn lock<'a>(&'a self) -> LockGuard<'a, T> {
        if !self.lock.try_lock() {
            let current = coroutine::current();
            unsafe {
                let wait_list: &mut VecDeque<Handle> = &mut *self.wait_list.get();
                wait_list.push_back(current);
            }
            coroutine::sched();
        }

        LockGuard::new(self, &self.inner)
    }

    pub fn try_lock<'a>(&'a self) -> Option<LockGuard<'a, T>> {
        if self.lock.try_lock() {
            Some(LockGuard::new(self, &self.inner))
        } else {
            None
        }
    }

    fn unlock(&self) {
        self.lock.unlock();
        let front = unsafe {
            let wait_list: &mut VecDeque<Handle> = &mut *self.wait_list.get();
            wait_list.pop_front()
        };

        match front {
            Some(hdl) => {
                coroutine::resume(&hdl).unwrap();
            },
            None => {}
        }
    }
}

unsafe impl<T: Send> Send for Mutex<T> {}

unsafe impl<T: Send> Sync for Mutex<T> {}

pub struct LockGuard<'a, T: 'a> {
    mutex: &'a Mutex<T>,
    data: &'a UnsafeCell<T>,
}

impl<'a, T: 'a> LockGuard<'a, T> {
    fn new(mutex: &'a Mutex<T>, data: &'a UnsafeCell<T>) -> LockGuard<'a, T> {
        LockGuard {
            mutex: mutex,
            data: data,
        }
    }
}

impl<'a, T: 'a> Drop for LockGuard<'a, T> {
    fn drop(&mut self) {
        self.mutex.unlock()
    }
}

impl<'a, T: 'a> Deref for LockGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe {
            &*self.data.get()
        }
    }
}

impl<'a, T: 'a> DerefMut for LockGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe {
            &mut *self.data.get()
        }
    }
}

#[cfg(test)]
mod test {
    use std::sync::Arc;

    use coroutine::{spawn, sched};

    use super::Mutex;

    #[test]
    fn test_mutex_basic() {
        let lock = Arc::new(Mutex::new(1));

        for _ in 0..10 {
            let lock = lock.clone();
            spawn(move|| {
                let mut guard = lock.lock();

                *guard += 1;
            }).resume();
        }

        assert_eq!(*lock.lock(), 11);
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::os::unix::io::RawFd;
use std::ptr::{null, null_mut};
use libc::{self, c_int};
use Result;
use errno::Errno;
use sys::signal::SigSet;
use sys::time::{TimeSpec, TimeVal};

pub use libc::FD_SETSIZE;

// FIXME: Change to repr(transparent) once it's stable
#[repr(C)]
#[derive(Clone, Copy)]
#[allow(missing_debug_implementations)]
pub struct FdSet(libc::fd_set);

impl FdSet {
    pub fn new() -> FdSet {
        let mut fdset = unsafe { mem::uninitialized() };
        unsafe { libc::FD_ZERO(&mut fdset) };
        FdSet(fdset)
    }

    pub fn insert(&mut self, fd: RawFd) {
        unsafe { libc::FD_SET(fd, &mut self.0) };
    }

    pub fn remove(&mut self, fd: RawFd) {
        unsafe { libc::FD_CLR(fd, &mut self.0) };
    }

    pub fn contains(&mut self, fd: RawFd) -> bool {
        unsafe { libc::FD_ISSET(fd, &mut self.0) }
    }

    pub fn clear(&mut self) {
        unsafe { libc::FD_ZERO(&mut self.0) };
    }

    /// Finds the highest file descriptor in the set.
    ///
    /// Returns `None` if the set is empty.
    ///
    /// This can be used to calculate the `nfds` parameter of the [`select`] function.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate nix;
    /// # use nix::sys::select::FdSet;
    /// # fn main() {
    /// let mut set = FdSet::new();
    /// set.insert(4);
    /// set.insert(9);
    /// assert_eq!(set.highest(), Some(9));
    /// # }
    /// ```
    ///
    /// [`select`]: fn.select.html
    pub fn highest(&mut self) -> Option<RawFd> {
        for i in (0..FD_SETSIZE).rev() {
            let i = i as RawFd;
            if unsafe { libc::FD_ISSET(i, self as *mut _ as *mut libc::fd_set) } {
                return Some(i)
            }
        }

        None
    }
}

/// Monitors file descriptors for readiness
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, `timeout` has an implementation-defined value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
///   to the maximum of that.
/// * `readfds`: File descriptors to check for being ready to read.
/// * `writefds`: File descriptors to check for being ready to write.
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
///   indefinitely).
///
/// # References
///
/// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, N, R, W, E, T>(nfds: N,
                                 readfds: R,
                                 writefds: W,
                                 errorfds: E,
                                 timeout: T) -> Result<c_int>
where
    N: Into<Option<c_int>>,
    R: Into<Option<&'a mut FdSet>>,
    W: Into<Option<&'a mut FdSet>>,
    E: Into<Option<&'a mut FdSet>>,
    T: Into<Option<&'a mut TimeVal>>,
{
    let mut readfds = readfds.into();
    let mut writefds = writefds.into();
    let mut errorfds = errorfds.into();
    let timeout = timeout.into();

    let nfds = nfds.into().unwrap_or_else(|| {
        readfds.iter_mut()
            .chain(writefds.iter_mut())
            .chain(errorfds.iter_mut())
            .map(|set| set.highest().unwrap_or(-1))
            .max()
            .unwrap_or(-1) + 1
    });

    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
                         .unwrap_or(null_mut());

    let res = unsafe {
        libc::select(nfds, readfds, writefds, errorfds, timeout)
    };

    Errno::result(res)
}

/// Monitors file descriptors for readiness with an altered signal mask.
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, the original signal mask is restored.
///
/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
///   to the maximum of that.
/// * `readfds`: File descriptors to check for read readiness
/// * `writefds`: File descriptors to check for write readiness
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
///   indefinitely).
/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
///    ready (`None` to set no alternative signal mask).
///
/// # References
///
/// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
///
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
                                     readfds: R,
                                     writefds: W,
                                     errorfds: E,
                                     timeout: T,
                                     sigmask: S) -> Result<c_int>
where
    N: Into<Option<c_int>>,
    R: Into<Option<&'a mut FdSet>>,
    W: Into<Option<&'a mut FdSet>>,
    E: Into<Option<&'a mut FdSet>>,
    T: Into<Option<&'a TimeSpec>>,
    S: Into<Option<&'a SigSet>>,
{
    let mut readfds = readfds.into();
    let mut writefds = writefds.into();
    let mut errorfds = errorfds.into();
    let sigmask = sigmask.into();
    let timeout = timeout.into();

    let nfds = nfds.into().unwrap_or_else(|| {
        readfds.iter_mut()
            .chain(writefds.iter_mut())
            .chain(errorfds.iter_mut())
            .map(|set| set.highest().unwrap_or(-1))
            .max()
            .unwrap_or(-1) + 1
    });

    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
    let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());

    let res = unsafe {
        libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
    };

    Errno::result(res)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::os::unix::io::RawFd;
    use sys::time::{TimeVal, TimeValLike};
    use unistd::{write, pipe};

    #[test]
    fn fdset_insert() {
        let mut fd_set = FdSet::new();

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }

        fd_set.insert(7);

        assert!(fd_set.contains(7));
    }

    #[test]
    fn fdset_remove() {
        let mut fd_set = FdSet::new();

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }

        fd_set.insert(7);
        fd_set.remove(7);

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }
    }

    #[test]
    fn fdset_clear() {
        let mut fd_set = FdSet::new();
        fd_set.insert(1);
        fd_set.insert((FD_SETSIZE / 2) as RawFd);
        fd_set.insert((FD_SETSIZE - 1) as RawFd);

        fd_set.clear();

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }
    }

    #[test]
    fn fdset_highest() {
        let mut set = FdSet::new();
        assert_eq!(set.highest(), None);
        set.insert(0);
        assert_eq!(set.highest(), Some(0));
        set.insert(90);
        assert_eq!(set.highest(), Some(90));
        set.remove(0);
        assert_eq!(set.highest(), Some(90));
        set.remove(90);
        assert_eq!(set.highest(), None);

        set.insert(4);
        set.insert(5);
        set.insert(7);
        assert_eq!(set.highest(), Some(7));
    }

    // powerpc-unknown-linux-gnu currently fails on the first `assert_eq` because
    // `select()` returns a 0 instead of a 1. Since this test has only been run on
    // qemu, it's unclear if this is a OS or qemu bug. Just disable it on that arch
    // for now.
    // FIXME: Fix tests for powerpc and mips
    // FIXME: Add a link to an upstream qemu bug if there is one
    #[test]
    #[cfg_attr(any(target_arch = "powerpc", target_arch = "mips"), ignore)]
    fn test_select() {
        let (r1, w1) = pipe().unwrap();
        write(w1, b"hi!").unwrap();
        let (r2, _w2) = pipe().unwrap();

        let mut fd_set = FdSet::new();
        fd_set.insert(r1);
        fd_set.insert(r2);

        let mut timeout = TimeVal::seconds(10);
        assert_eq!(1, select(None,
                             &mut fd_set,
                             None,
                             None,
                             &mut timeout).unwrap());
        assert!(fd_set.contains(r1));
        assert!(!fd_set.contains(r2));
    }

    #[test]
    #[cfg_attr(any(target_arch = "powerpc", target_arch = "mips"), ignore)]
    fn test_select_nfds() {
        let (r1, w1) = pipe().unwrap();
        write(w1, b"hi!").unwrap();
        let (r2, _w2) = pipe().unwrap();

        let mut fd_set = FdSet::new();
        fd_set.insert(r1);
        fd_set.insert(r2);

        let mut timeout = TimeVal::seconds(10);
        assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
                             &mut fd_set,
                             None,
                             None,
                             &mut timeout).unwrap());
        assert!(fd_set.contains(r1));
        assert!(!fd_set.contains(r2));
    }

    #[test]
    #[cfg_attr(any(target_arch = "powerpc", target_arch = "mips"), ignore)]
    fn test_select_nfds2() {
        let (r1, w1) = pipe().unwrap();
        write(w1, b"hi!").unwrap();
        let (r2, _w2) = pipe().unwrap();

        let mut fd_set = FdSet::new();
        fd_set.insert(r1);
        fd_set.insert(r2);

        let mut timeout = TimeVal::seconds(10);
        assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
                             &mut fd_set,
                             None,
                             None,
                             &mut timeout).unwrap());
        assert!(fd_set.contains(r1));
        assert!(!fd_set.contains(r2));
    }
}
<commit_msg>Enable the select tests on powerpc and mips<commit_after>use std::mem;
use std::os::unix::io::RawFd;
use std::ptr::{null, null_mut};
use libc::{self, c_int};
use Result;
use errno::Errno;
use sys::signal::SigSet;
use sys::time::{TimeSpec, TimeVal};

pub use libc::FD_SETSIZE;

// FIXME: Change to repr(transparent) once it's stable
#[repr(C)]
#[derive(Clone, Copy)]
#[allow(missing_debug_implementations)]
pub struct FdSet(libc::fd_set);

impl FdSet {
    pub fn new() -> FdSet {
        let mut fdset = unsafe { mem::uninitialized() };
        unsafe { libc::FD_ZERO(&mut fdset) };
        FdSet(fdset)
    }

    pub fn insert(&mut self, fd: RawFd) {
        unsafe { libc::FD_SET(fd, &mut self.0) };
    }

    pub fn remove(&mut self, fd: RawFd) {
        unsafe { libc::FD_CLR(fd, &mut self.0) };
    }

    pub fn contains(&mut self, fd: RawFd) -> bool {
        unsafe { libc::FD_ISSET(fd, &mut self.0) }
    }

    pub fn clear(&mut self) {
        unsafe { libc::FD_ZERO(&mut self.0) };
    }

    /// Finds the highest file descriptor in the set.
    ///
    /// Returns `None` if the set is empty.
    ///
    /// This can be used to calculate the `nfds` parameter of the [`select`] function.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate nix;
    /// # use nix::sys::select::FdSet;
    /// # fn main() {
    /// let mut set = FdSet::new();
    /// set.insert(4);
    /// set.insert(9);
    /// assert_eq!(set.highest(), Some(9));
    /// # }
    /// ```
    ///
    /// [`select`]: fn.select.html
    pub fn highest(&mut self) -> Option<RawFd> {
        for i in (0..FD_SETSIZE).rev() {
            let i = i as RawFd;
            if unsafe { libc::FD_ISSET(i, self as *mut _ as *mut libc::fd_set) } {
                return Some(i)
            }
        }

        None
    }
}

/// Monitors file descriptors for readiness
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, `timeout` has an implementation-defined value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
///   to the maximum of that.
/// * `readfds`: File descriptors to check for being ready to read.
/// * `writefds`: File descriptors to check for being ready to write.
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
///   indefinitely).
///
/// # References
///
/// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn select<'a, N, R, W, E, T>(nfds: N,
                                 readfds: R,
                                 writefds: W,
                                 errorfds: E,
                                 timeout: T) -> Result<c_int>
where
    N: Into<Option<c_int>>,
    R: Into<Option<&'a mut FdSet>>,
    W: Into<Option<&'a mut FdSet>>,
    E: Into<Option<&'a mut FdSet>>,
    T: Into<Option<&'a mut TimeVal>>,
{
    let mut readfds = readfds.into();
    let mut writefds = writefds.into();
    let mut errorfds = errorfds.into();
    let timeout = timeout.into();

    let nfds = nfds.into().unwrap_or_else(|| {
        readfds.iter_mut()
            .chain(writefds.iter_mut())
            .chain(errorfds.iter_mut())
            .map(|set| set.highest().unwrap_or(-1))
            .max()
            .unwrap_or(-1) + 1
    });

    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
                         .unwrap_or(null_mut());

    let res = unsafe {
        libc::select(nfds, readfds, writefds, errorfds, timeout)
    };

    Errno::result(res)
}

/// Monitors file descriptors for readiness with an altered signal mask.
///
/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
/// file descriptors that are ready for the given operation are set.
///
/// When this function returns, the original signal mask is restored.
///
/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
///
/// # Parameters
///
/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
///   to the maximum of that.
/// * `readfds`: File descriptors to check for read readiness
/// * `writefds`: File descriptors to check for write readiness
/// * `errorfds`: File descriptors to check for pending error conditions.
/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
///   indefinitely).
/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
///    ready (`None` to set no alternative signal mask).
///
/// # References
///
/// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
///
/// [The new pselect() system call](https://lwn.net/Articles/176911/)
///
/// [`FdSet::highest`]: struct.FdSet.html#method.highest
pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
                                     readfds: R,
                                     writefds: W,
                                     errorfds: E,
                                     timeout: T,
                                     sigmask: S) -> Result<c_int>
where
    N: Into<Option<c_int>>,
    R: Into<Option<&'a mut FdSet>>,
    W: Into<Option<&'a mut FdSet>>,
    E: Into<Option<&'a mut FdSet>>,
    T: Into<Option<&'a TimeSpec>>,
    S: Into<Option<&'a SigSet>>,
{
    let mut readfds = readfds.into();
    let mut writefds = writefds.into();
    let mut errorfds = errorfds.into();
    let sigmask = sigmask.into();
    let timeout = timeout.into();

    let nfds = nfds.into().unwrap_or_else(|| {
        readfds.iter_mut()
            .chain(writefds.iter_mut())
            .chain(errorfds.iter_mut())
            .map(|set| set.highest().unwrap_or(-1))
            .max()
            .unwrap_or(-1) + 1
    });

    let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
    let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
    let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());

    let res = unsafe {
        libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
    };

    Errno::result(res)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::os::unix::io::RawFd;
    use sys::time::{TimeVal, TimeValLike};
    use unistd::{write, pipe};

    #[test]
    fn fdset_insert() {
        let mut fd_set = FdSet::new();

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }

        fd_set.insert(7);

        assert!(fd_set.contains(7));
    }

    #[test]
    fn fdset_remove() {
        let mut fd_set = FdSet::new();

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }

        fd_set.insert(7);
        fd_set.remove(7);

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }
    }

    #[test]
    fn fdset_clear() {
        let mut fd_set = FdSet::new();
        fd_set.insert(1);
        fd_set.insert((FD_SETSIZE / 2) as RawFd);
        fd_set.insert((FD_SETSIZE - 1) as RawFd);

        fd_set.clear();

        for i in 0..FD_SETSIZE {
            assert!(!fd_set.contains(i as RawFd));
        }
    }

    #[test]
    fn fdset_highest() {
        let mut set = FdSet::new();
        assert_eq!(set.highest(), None);
        set.insert(0);
        assert_eq!(set.highest(), Some(0));
        set.insert(90);
        assert_eq!(set.highest(), Some(90));
        set.remove(0);
        assert_eq!(set.highest(), Some(90));
        set.remove(90);
        assert_eq!(set.highest(), None);

        set.insert(4);
        set.insert(5);
        set.insert(7);
        assert_eq!(set.highest(), Some(7));
    }

    #[test]
    fn test_select() {
        let (r1, w1) = pipe().unwrap();
        write(w1, b"hi!").unwrap();
        let (r2, _w2) = pipe().unwrap();

        let mut fd_set = FdSet::new();
        fd_set.insert(r1);
        fd_set.insert(r2);

        let mut timeout = TimeVal::seconds(10);
        assert_eq!(1, select(None,
                             &mut fd_set,
                             None,
                             None,
                             &mut timeout).unwrap());
        assert!(fd_set.contains(r1));
        assert!(!fd_set.contains(r2));
    }

    #[test]
    fn test_select_nfds() {
        let (r1, w1) = pipe().unwrap();
        write(w1, b"hi!").unwrap();
        let (r2, _w2) = pipe().unwrap();

        let mut fd_set = FdSet::new();
        fd_set.insert(r1);
        fd_set.insert(r2);

        let mut timeout = TimeVal::seconds(10);
        assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
                             &mut fd_set,
                             None,
                             None,
                             &mut timeout).unwrap());
        assert!(fd_set.contains(r1));
        assert!(!fd_set.contains(r2));
    }

    #[test]
    fn test_select_nfds2() {
        let (r1, w1) = pipe().unwrap();
        write(w1, b"hi!").unwrap();
        let (r2, _w2) = pipe().unwrap();

        let mut fd_set = FdSet::new();
        fd_set.insert(r1);
        fd_set.insert(r2);

        let mut timeout = TimeVal::seconds(10);
        assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
                             &mut fd_set,
                             None,
                             None,
                             &mut timeout).unwrap());
        assert!(fd_set.contains(r1));
        assert!(!fd_set.contains(r2));
    }
}
<|endoftext|>


<commit_before>use std::{mem, ptr};
use libc::{c_int, socklen_t};
use fcntl::{Fd, fcntl, F_SETFL, F_SETFD, FD_CLOEXEC, O_NONBLOCK};
use errno::{SysResult, SysError, from_ffi};
use features;

pub use libc::{in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, sa_family_t};

pub use self::consts::*;

mod ffi {
    use libc::{c_int, c_void, socklen_t};
    pub use libc::{socket, listen, bind, accept, connect, setsockopt};

    extern {
        pub fn getsockopt(
            sockfd: c_int,
            level: c_int,
            optname: c_int,
            optval: *mut c_void,
            optlen: *mut socklen_t) -> c_int;
    }
}

// Extra flags - Supported by Linux 2.6.27, normalized on other platforms
bitflags!(
    flags SockFlag: c_int {
        const SOCK_NONBLOCK = 0o0004000,
        const SOCK_CLOEXEC  = 0o2000000
    }
)

pub enum SockAddr {
    SockIpV4(sockaddr_in),
    SockIpV6(sockaddr_in6),
    SockUnix(sockaddr_un)
}

#[cfg(target_os = "linux")]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 10;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_IP: SockLevel     = 0;
    pub const SOL_SOCKET: SockLevel = 1;
    pub const SOL_TCP: SockLevel    = 6;
    pub const SOL_UDP: SockLevel    = 17;
    pub const SOL_IPV6: SockLevel   = 41;

    pub type SockOpt = c_int;

<<<<<<< HEAD
    pub const SO_ACCEPTCONN: SockOpt = 30;
    pub const SO_BINDTODEVICE: SockOpt = 25;
    pub const SO_BROADCAST: SockOpt = 6;
    pub const SO_BSDCOMPAT: SockOpt = 14;
    pub const SO_DEBUG: SockOpt = 1;
    pub const SO_DOMAIN: SockOpt = 39;
    pub const SO_ERROR: SockOpt = 4;
    pub const SO_DONTROUTE: SockOpt = 5;
    pub const SO_KEEPALIVE: SockOpt = 9;
    pub const SO_LINGER: SockOpt = 13;
    pub const SO_MARK: SockOpt = 36;
    pub const SO_OOBINLINE: SockOpt = 10;
    pub const SO_PASSCRED: SockOpt = 16;
    pub const SO_PEEK_OFF: SockOpt = 42;
    pub const SO_PEERCRED: SockOpt = 17;
    pub const SO_PRIORITY: SockOpt = 12;
    pub const SO_PROTOCOL: SockOpt = 38;
    pub const SO_RCVBUF: SockOpt = 8;
    pub const SO_RCVBUFFORCE: SockOpt = 33;
    pub const SO_RCVLOWAT: SockOpt = 18;
    pub const SO_SNDLOWAT: SockOpt = 19;
    pub const SO_RCVTIMEO: SockOpt = 20;
    pub const SO_SNDTIMEO: SockOpt = 21;
    pub const SO_REUSEADDR: SockOpt = 2;
    pub const SO_REUSEPORT: SockOpt = 15;
    pub const SO_RXQ_OVFL: SockOpt = 40;
    pub const SO_SNDBUF: SockOpt = 7;
    pub const SO_SNDBUFFORCE: SockOpt = 32;
    pub const SO_TIMESTAMP: SockOpt = 29;
    pub const SO_TYPE: SockOpt = 3;
    pub const SO_BUSY_POLL: SockOpt = 46;
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 30;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_SOCKET: SockLevel = 0xffff;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt          = 0x0002;
    pub const SO_BROADCAST: SockOpt           = 0x0020;
    pub const SO_DEBUG: SockOpt               = 0x0001;
    pub const SO_DONTTRUNC: SockOpt           = 0x2000;
    pub const SO_ERROR: SockOpt               = 0x1007;
    pub const SO_DONTROUTE: SockOpt           = 0x0010;
    pub const SO_KEEPALIVE: SockOpt           = 0x0008;
    pub const SO_LABEL: SockOpt               = 0x1010;
    pub const SO_LINGER: SockOpt              = 0x0080;
    pub const SO_NREAD: SockOpt               = 0x1020;
    pub const SO_NKE: SockOpt                 = 0x1021;
    pub const SO_NOSIGPIPE: SockOpt           = 0x1022;
    pub const SO_NOADDRERR: SockOpt           = 0x1023;
    pub const SO_NOTIFYCONFLICT: SockOpt      = 0x1026;
    pub const SO_NP_EXTENSIONS: SockOpt       = 0x1083;
    pub const SO_NWRITE: SockOpt              = 0x1024;
    pub const SO_OOBINLINE: SockOpt           = 0x0100;
    pub const SO_PEERLABEL: SockOpt           = 0x1011;
    pub const SO_RCVBUF: SockOpt              = 0x1002;
    pub const SO_RCVLOWAT: SockOpt            = 0x1004;
    pub const SO_SNDLOWAT: SockOpt            = 0x1003;
    pub const SO_RCVTIMEO: SockOpt            = 0x1006;
    pub const SO_SNDTIMEO: SockOpt            = 0x1005;
    pub const SO_RANDOMPORT: SockOpt          = 0x1082;
    pub const SO_RESTRICTIONS: SockOpt        = 0x1081;
    pub const SO_RESTRICT_DENYIN: SockOpt     = 0x00000001;
    pub const SO_RESTRICT_DENYOUT: SockOpt    = 0x00000002;
    pub const SO_REUSEADDR: SockOpt           = 0x0004;
    pub const SO_REUSESHAREUID: SockOpt       = 0x1025;
    pub const SO_SNDBUF: SockOpt              = 0x1001;
    pub const SO_TIMESTAMP: SockOpt           = 0x0400;
    pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800;
    pub const SO_TYPE: SockOpt                = 0x1008;
    pub const SO_WANTMORE: SockOpt            = 0x4000;
    pub const SO_WANTOOBFLAG: SockOpt         = 0x8000;
    #[allow(type_overflow)]
    pub const SO_RESTRICT_DENYSET: SockOpt    = 0x80000000;
}

pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> SysResult<Fd> {
    let feat_atomic = features::socket_atomic_cloexec();

    if feat_atomic {
        ty = ty | flags.bits();
    }

    // TODO: Check the kernel version
    let res = unsafe { ffi::socket(domain, ty, 0) };

    if res < 0 {
        return Err(SysError::last());
    }

    if !feat_atomic {
        if flags.contains(SOCK_CLOEXEC) {
            try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
        }

        if flags.contains(SOCK_NONBLOCK) {
            try!(fcntl(res, F_SETFL(O_NONBLOCK)));
        }
    }

    Ok(res)
}

pub fn listen(sockfd: Fd, backlog: uint) -> SysResult<()> {
    let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
    from_ffi(res)
}

pub fn bind(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

pub fn accept(sockfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    use libc::sockaddr;

    type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static accept4: *const ();
    }

    if !accept4.is_null() {
        let res = unsafe {
            mem::transmute::<*const (), F>(accept4)(
                sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits)
        };

        if res < 0 {
            return Err(SysError::last());
        }

        Ok(res)
    } else {
        accept4_polyfill(sockfd, flags)
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    accept4_polyfill(sockfd, flags)
}

#[inline]
fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    let res =  unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    if flags.contains(SOCK_CLOEXEC) {
        try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(SOCK_NONBLOCK) {
        try!(fcntl(res, F_SETFL(O_NONBLOCK)));
    }

    Ok(res)
}

pub fn connect(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

#[repr(C)]
pub struct linger {
    pub l_onoff: c_int,
    pub l_linger: c_int
}

pub fn getsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> SysResult<uint> {
    let mut len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
        ffi::getsockopt(
            fd, level, opt,
            mem::transmute(val),
            &mut len as *mut socklen_t)
    };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(len as uint)
}

pub fn setsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> SysResult<()> {
    let len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
            ffi::setsockopt(
            fd, level, opt,
            mem::transmute(val),
            len)
    };

    from_ffi(res)
}
<commit_msg>Add a handful of useful TCP socket options<commit_after>use std::{mem, ptr};
use libc::{c_int, socklen_t};
use fcntl::{Fd, fcntl, F_SETFL, F_SETFD, FD_CLOEXEC, O_NONBLOCK};
use errno::{SysResult, SysError, from_ffi};
use features;

pub use libc::{in_addr, sockaddr_in, sockaddr_in6, sockaddr_un, sa_family_t};

pub use self::consts::*;

mod ffi {
    use libc::{c_int, c_void, socklen_t};
    pub use libc::{socket, listen, bind, accept, connect, setsockopt};

    extern {
        pub fn getsockopt(
            sockfd: c_int,
            level: c_int,
            optname: c_int,
            optval: *mut c_void,
            optlen: *mut socklen_t) -> c_int;
    }
}

// Extra flags - Supported by Linux 2.6.27, normalized on other platforms
bitflags!(
    flags SockFlag: c_int {
        const SOCK_NONBLOCK = 0o0004000,
        const SOCK_CLOEXEC  = 0o2000000
    }
)

pub enum SockAddr {
    SockIpV4(sockaddr_in),
    SockIpV6(sockaddr_in6),
    SockUnix(sockaddr_un)
}

#[cfg(target_os = "linux")]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 10;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_IP: SockLevel     = 0;
    pub const SOL_SOCKET: SockLevel = 1;
    pub const SOL_TCP: SockLevel    = 6;
    pub static IPPROTO_TCP: SockLevel = SOL_TCP;
    pub const SOL_UDP: SockLevel    = 17;
    pub const SOL_IPV6: SockLevel   = 41;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt = 30;
    pub const SO_BINDTODEVICE: SockOpt = 25;
    pub const SO_BROADCAST: SockOpt = 6;
    pub const SO_BSDCOMPAT: SockOpt = 14;
    pub const SO_DEBUG: SockOpt = 1;
    pub const SO_DOMAIN: SockOpt = 39;
    pub const SO_ERROR: SockOpt = 4;
    pub const SO_DONTROUTE: SockOpt = 5;
    pub const SO_KEEPALIVE: SockOpt = 9;
    pub const SO_LINGER: SockOpt = 13;
    pub const SO_MARK: SockOpt = 36;
    pub const SO_OOBINLINE: SockOpt = 10;
    pub const SO_PASSCRED: SockOpt = 16;
    pub const SO_PEEK_OFF: SockOpt = 42;
    pub const SO_PEERCRED: SockOpt = 17;
    pub const SO_PRIORITY: SockOpt = 12;
    pub const SO_PROTOCOL: SockOpt = 38;
    pub const SO_RCVBUF: SockOpt = 8;
    pub const SO_RCVBUFFORCE: SockOpt = 33;
    pub const SO_RCVLOWAT: SockOpt = 18;
    pub const SO_SNDLOWAT: SockOpt = 19;
    pub const SO_RCVTIMEO: SockOpt = 20;
    pub const SO_SNDTIMEO: SockOpt = 21;
    pub const SO_REUSEADDR: SockOpt = 2;
    pub const SO_REUSEPORT: SockOpt = 15;
    pub const SO_RXQ_OVFL: SockOpt = 40;
    pub const SO_SNDBUF: SockOpt = 7;
    pub const SO_SNDBUFFORCE: SockOpt = 32;
    pub const SO_TIMESTAMP: SockOpt = 29;
    pub const SO_TYPE: SockOpt = 3;
    pub const SO_BUSY_POLL: SockOpt = 46;

    // Socket options for TCP sockets
    pub const TCP_NODELAY: SockOpt = 1;
    pub const TCP_MAXSEG: SockOpt = 2;
    pub const TCP_CORK: SockOpt = 3;
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::{c_int};

    pub type AddressFamily = c_int;

    pub const AF_UNIX: AddressFamily  = 1;
    pub const AF_LOCAL: AddressFamily = AF_UNIX;
    pub const AF_INET: AddressFamily  = 2;
    pub const AF_INET6: AddressFamily = 30;

    pub type SockType = c_int;

    pub const SOCK_STREAM: SockType = 1;
    pub const SOCK_DGRAM: SockType = 2;
    pub const SOCK_SEQPACKET: SockType = 5;
    pub const SOCK_RAW: SockType = 3;
    pub const SOCK_RDM: SockType = 4;

    pub type SockLevel = c_int;

    pub const SOL_SOCKET: SockLevel = 0xffff;

    pub type SockOpt = c_int;

    pub const SO_ACCEPTCONN: SockOpt          = 0x0002;
    pub const SO_BROADCAST: SockOpt           = 0x0020;
    pub const SO_DEBUG: SockOpt               = 0x0001;
    pub const SO_DONTTRUNC: SockOpt           = 0x2000;
    pub const SO_ERROR: SockOpt               = 0x1007;
    pub const SO_DONTROUTE: SockOpt           = 0x0010;
    pub const SO_KEEPALIVE: SockOpt           = 0x0008;
    pub const SO_LABEL: SockOpt               = 0x1010;
    pub const SO_LINGER: SockOpt              = 0x0080;
    pub const SO_NREAD: SockOpt               = 0x1020;
    pub const SO_NKE: SockOpt                 = 0x1021;
    pub const SO_NOSIGPIPE: SockOpt           = 0x1022;
    pub const SO_NOADDRERR: SockOpt           = 0x1023;
    pub const SO_NOTIFYCONFLICT: SockOpt      = 0x1026;
    pub const SO_NP_EXTENSIONS: SockOpt       = 0x1083;
    pub const SO_NWRITE: SockOpt              = 0x1024;
    pub const SO_OOBINLINE: SockOpt           = 0x0100;
    pub const SO_PEERLABEL: SockOpt           = 0x1011;
    pub const SO_RCVBUF: SockOpt              = 0x1002;
    pub const SO_RCVLOWAT: SockOpt            = 0x1004;
    pub const SO_SNDLOWAT: SockOpt            = 0x1003;
    pub const SO_RCVTIMEO: SockOpt            = 0x1006;
    pub const SO_SNDTIMEO: SockOpt            = 0x1005;
    pub const SO_RANDOMPORT: SockOpt          = 0x1082;
    pub const SO_RESTRICTIONS: SockOpt        = 0x1081;
    pub const SO_RESTRICT_DENYIN: SockOpt     = 0x00000001;
    pub const SO_RESTRICT_DENYOUT: SockOpt    = 0x00000002;
    pub const SO_REUSEADDR: SockOpt           = 0x0004;
    pub const SO_REUSESHAREUID: SockOpt       = 0x1025;
    pub const SO_SNDBUF: SockOpt              = 0x1001;
    pub const SO_TIMESTAMP: SockOpt           = 0x0400;
    pub const SO_TIMESTAMP_MONOTONIC: SockOpt = 0x0800;
    pub const SO_TYPE: SockOpt                = 0x1008;
    pub const SO_WANTMORE: SockOpt            = 0x4000;
    pub const SO_WANTOOBFLAG: SockOpt         = 0x8000;
    #[allow(type_overflow)]
    pub const SO_RESTRICT_DENYSET: SockOpt    = 0x80000000;
}

pub fn socket(domain: AddressFamily, mut ty: SockType, flags: SockFlag) -> SysResult<Fd> {
    let feat_atomic = features::socket_atomic_cloexec();

    if feat_atomic {
        ty = ty | flags.bits();
    }

    // TODO: Check the kernel version
    let res = unsafe { ffi::socket(domain, ty, 0) };

    if res < 0 {
        return Err(SysError::last());
    }

    if !feat_atomic {
        if flags.contains(SOCK_CLOEXEC) {
            try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
        }

        if flags.contains(SOCK_NONBLOCK) {
            try!(fcntl(res, F_SETFL(O_NONBLOCK)));
        }
    }

    Ok(res)
}

pub fn listen(sockfd: Fd, backlog: uint) -> SysResult<()> {
    let res = unsafe { ffi::listen(sockfd, backlog as c_int) };
    from_ffi(res)
}

pub fn bind(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::bind(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

pub fn accept(sockfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    use libc::sockaddr;

    type F = unsafe extern "C" fn(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int;

    extern {
        #[linkage = "extern_weak"]
        static accept4: *const ();
    }

    if !accept4.is_null() {
        let res = unsafe {
            mem::transmute::<*const (), F>(accept4)(
                sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits)
        };

        if res < 0 {
            return Err(SysError::last());
        }

        Ok(res)
    } else {
        accept4_polyfill(sockfd, flags)
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn accept4(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    accept4_polyfill(sockfd, flags)
}

#[inline]
fn accept4_polyfill(sockfd: Fd, flags: SockFlag) -> SysResult<Fd> {
    let res =  unsafe { ffi::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };

    if res < 0 {
        return Err(SysError::last());
    }

    if flags.contains(SOCK_CLOEXEC) {
        try!(fcntl(res, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(SOCK_NONBLOCK) {
        try!(fcntl(res, F_SETFL(O_NONBLOCK)));
    }

    Ok(res)
}

pub fn connect(sockfd: Fd, addr: &SockAddr) -> SysResult<()> {
    let res = unsafe {
        match *addr {
            SockIpV4(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in>() as socklen_t),
            SockIpV6(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_in6>() as socklen_t),
            SockUnix(ref addr) => ffi::connect(sockfd, mem::transmute(addr), mem::size_of::<sockaddr_un>() as socklen_t)
        }
    };

    from_ffi(res)
}

#[repr(C)]
pub struct linger {
    pub l_onoff: c_int,
    pub l_linger: c_int
}

pub fn getsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &mut T) -> SysResult<uint> {
    let mut len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
        ffi::getsockopt(
            fd, level, opt,
            mem::transmute(val),
            &mut len as *mut socklen_t)
    };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(len as uint)
}

pub fn setsockopt<T>(fd: Fd, level: SockLevel, opt: SockOpt, val: &T) -> SysResult<()> {
    let len = mem::size_of::<T>() as socklen_t;

    let res = unsafe {
            ffi::setsockopt(
            fd, level, opt,
            mem::transmute(val),
            len)
    };

    from_ffi(res)
}
<|endoftext|>


<commit_before>// #[cfg(all(test, has_display))]

// use crate::*;
// use crate::tests;
// use std::env;
// use std::path;

/* TODO; the font API has changed and I don't want to deal with it now
#[test]
fn test_calculated_text_width() {
    let ctx = &mut make_context();
    let font = graphics::Font::default();

    let text = "Hello There";

    let expected_width = font.width(text);
    let rendered_width = graphics::Text::new((text, font, 24)).unwrap().width();

    println!("Text: {:?}, expected: {}, rendered: {}", text, expected_width, rendered_width);
    assert_eq!(expected_width as usize, rendered_width as usize);
}

#[test]
fn test_monospace_text_is_actually_monospace() {
    let ctx = &mut make_context();
    let font = graphics::Font::new(ctx, "/DejaVuSansMono.ttf");

    let text1 = "Hello 1";
    let text2 = "Hello 2";
    let text3 = "Hello 3";
    let text4 = "Hello 4";

    let width1 = font.width(text1);
    let width2 = font.width(text2);
    let width3 = font.width(text3);
    let width4 = font.width(text4);

    assert_eq!(width1, width2);
    assert_eq!(width2, width3);
    assert_eq!(width3, width4);
}

*/
<commit_msg>Add unit test for text height.<commit_after>// #[cfg(all(test, has_display))]

use crate::*;
use crate::tests;

#[test]
fn test_calculated_text_width() {
    let (ctx, _ev) = &mut tests::make_context();
    let font = graphics::Font::default();

    let text = graphics::Text::new(("Hello There", font, 24.0));

    let expected_width = text.width(ctx);
    // For now we just test against a known value, since rendering it
    // is odd.
    assert_eq!(expected_width, 123);
    // let rendered_width = graphics::Text::new((text, font, 24)).unwrap().width();

    // println!("Text: {:?}, expected: {}, rendered: {}", text, expected_width, rendered_width);
    // assert_eq!(expected_width as usize, rendered_width as usize);
}

/// Make sure that the "height" of text with ascenders/descenders
/// is the same as text without
#[test]
fn test_calculated_text_height() {
    let (ctx, _ev) = &mut tests::make_context();
    let font = graphics::Font::default();

    let text1 = graphics::Text::new(("strength", font, 24.0));
    let text2 = graphics::Text::new(("moves", font, 24.0));

    let h1 = text1.height(ctx);
    let h2 = text2.height(ctx);
    assert_eq!(h1, h2);
}

#[test]
fn test_monospace_text_is_actually_monospace() {
    let (ctx, _ev) = &mut tests::make_context();
    let font = graphics::Font::new(ctx, "/DejaVuSansMono.ttf").unwrap();

    let text1 = graphics::Text::new(("Hello 1", font, 24.0));
    let text2 = graphics::Text::new(("Hello 2", font, 24.0));;
    let text3 = graphics::Text::new(("Hello 3", font, 24.0));;
    let text4 = graphics::Text::new(("Hello 4", font, 24.0));;

    let width1 = text1.width(ctx);
    let width2 = text3.width(ctx);
    let width3 = text2.width(ctx);
    let width4 = text4.width(ctx);

    assert_eq!(width1, width2);
    assert_eq!(width2, width3);
    assert_eq!(width3, width4);
}
<|endoftext|>


<commit_before>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::{Arc, Once},
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{debug_delay, Lazy, OneShot, Result};

static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);
static IO_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);
static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);

type Work = Box<dyn FnOnce() + Send + 'static>;

#[derive(Default)]
struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

#[allow(unsafe_code)]
unsafe impl Send for Queue {}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<Work> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            let res = self.cv.wait_until(&mut queue, cutoff);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front()
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }

    fn perform_work(&self) {
        let wait_limit = Duration::from_millis(100);

        loop {
            let guard = crate::pin();
            guard.flush();
            drop(guard);
            debug_delay();
            let task_res = self.recv_timeout(wait_limit);

            if let Some(task) = task_res {
                (task)();
            }
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &BLOCKING_QUEUE)
}

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    )
))]
fn spawn_to<F, R>(work: F, queue: &'static Queue) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    static START_THREADS: Once = Once::new();

    START_THREADS.call_once(|| {
        std::thread::Builder::new()
            .name("sled-io-thread".into())
            .spawn(|| IO_QUEUE.perform_work())
            .expect("failed to spawn critical IO thread");

        std::thread::Builder::new()
            .name("sled-blocking-thread".into())
            .spawn(|| BLOCKING_QUEUE.perform_work())
            .expect("failed to spawn critical blocking thread");

        std::thread::Builder::new()
            .name("sled-snapshot-thread".into())
            .spawn(|| SNAPSHOT_QUEUE.perform_work())
            .expect("failed to spawn critical snapshot thread");
    });

    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        promise_filler.fill((work)());
    };

    queue.send(Box::new(task));

    Ok(promise)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))
))]
fn spawn_to<F, R>(work: F, _: &'static Queue) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    // Polyfill for platforms other than those we explicitly trust to
    // perform threaded work on. Just execute a task without involving threads.
    let (promise_filler, promise) = OneShot::pair();
    promise_filler.fill((work)());
    Ok(promise)
}

pub fn truncate(
    config: crate::RunningConfig,
    at: u64,
) -> Result<OneShot<Result<()>>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &IO_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(
    pc: crate::pagecache::PageCache,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &IO_QUEUE,
    )
}
<commit_msg>Add some dynamism into the threadpool<commit_after>//! A simple adaptive threadpool that returns a oneshot future.

use std::{
    collections::VecDeque,
    sync::{Arc, Once},
    time::{Duration, Instant},
};

use parking_lot::{Condvar, Mutex};

use crate::{debug_delay, Lazy, OneShot, Result};

static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);
static IO_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);
static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> = Lazy::new(Default::default);

type Work = Box<dyn FnOnce() + Send + 'static>;

#[derive(Default)]
struct Queue {
    cv: Condvar,
    mu: Mutex<VecDeque<Work>>,
}

#[allow(unsafe_code)]
unsafe impl Send for Queue {}

impl Queue {
    fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
        let mut queue = self.mu.lock();

        let cutoff = Instant::now() + duration;

        while queue.is_empty() {
            let res = self.cv.wait_until(&mut queue, cutoff);
            if res.timed_out() {
                break;
            }
        }

        queue.pop_front().map(|w| (w, queue.len()))
    }

    fn send(&self, work: Work) -> usize {
        let mut queue = self.mu.lock();
        queue.push_back(work);

        let len = queue.len();

        // having held the mutex makes this linearized
        // with the notify below.
        drop(queue);

        self.cv.notify_all();

        len
    }

    fn perform_work(&'static self, elastic: bool, temporary: bool) {
        let wait_limit = Duration::from_millis(100);

        let mut last_employed = Instant::now();
        while !temporary || last_employed.elapsed() < Duration::from_secs(5) {
            let guard = crate::pin();
            guard.flush();
            drop(guard);
            debug_delay();
            let task_opt = self.recv_timeout(wait_limit);

            if let Some((task, outstanding_work)) = task_opt {
                // spin up some help if we're falling behind
                if elastic && outstanding_work > 5 {
                    let spawn_res = std::thread::Builder::new()
                        .name("sled-temporary-thread".into())
                        .spawn(move || self.perform_work(false, true));
                    if let Err(e) = spawn_res {
                        log::error!(
                            "failed to spin-up temporary work thread: {:?}",
                            e
                        );
                    }
                }

                // execute the work sent to this thread
                (task)();

                last_employed = Instant::now();
            }
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &BLOCKING_QUEUE)
}

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    )
))]
fn spawn_to<F, R>(work: F, queue: &'static Queue) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    static START_THREADS: Once = Once::new();

    START_THREADS.call_once(|| {
        std::thread::Builder::new()
            .name("sled-io-thread".into())
            .spawn(|| IO_QUEUE.perform_work(true, false))
            .expect("failed to spawn critical IO thread");

        std::thread::Builder::new()
            .name("sled-blocking-thread".into())
            .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
            .expect("failed to spawn critical blocking thread");

        std::thread::Builder::new()
            .name("sled-snapshot-thread".into())
            .spawn(|| SNAPSHOT_QUEUE.perform_work(false, false))
            .expect("failed to spawn critical snapshot thread");
    });

    let (promise_filler, promise) = OneShot::pair();
    let task = move || {
        promise_filler.fill((work)());
    };

    queue.send(Box::new(task));

    Ok(promise)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
    ))
))]
fn spawn_to<F, R>(work: F, _: &'static Queue) -> Result<OneShot<R>>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    // Polyfill for platforms other than those we explicitly trust to
    // perform threaded work on. Just execute a task without involving threads.
    let (promise_filler, promise) = OneShot::pair();
    promise_filler.fill((work)());
    Ok(promise)
}

pub fn truncate(
    config: crate::RunningConfig,
    at: u64,
) -> Result<OneShot<Result<()>>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &IO_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(
    pc: crate::pagecache::PageCache,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> Result<OneShot<()>> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &IO_QUEUE,
    )
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::io;
use std::fmt;

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TlsErrorKind {
    // corresponds to alert messages

    UnexpectedMessage,
    BadRecordMac,
    RecordOverflow,
    IllegalParameter,
    DecodeError,
    DecryptError,
    InternalError,

    // we probably can't even send alert?
    IoFailure,
    AlertReceived,
}

#[derive(Debug)]
pub struct TlsError {
    pub kind: TlsErrorKind,
    pub desc: String,
}

impl TlsError {
    pub fn new<T>(kind: TlsErrorKind, desc: String) -> TlsResult<T> {
        Err(TlsError {
            kind: kind,
            desc: desc,
        })
    }
}

impl Error for TlsError {
    fn description(&self) -> &str {
        match self.kind {
            TlsErrorKind::UnexpectedMessage => "unexpected message",
            TlsErrorKind::BadRecordMac => "record has bad mac and/or encryption",
            TlsErrorKind::RecordOverflow => "record too long",
            TlsErrorKind::IllegalParameter => "illegal parameter during handshake",
            TlsErrorKind::DecodeError => "cannot decode message",
            TlsErrorKind::DecryptError => "failed to verify signature/message",
            TlsErrorKind::InternalError => "internal error",

            // UnsupportedExtension,

            // we probably can't even send alert?
            TlsErrorKind::IoFailure => "i/o error",
            TlsErrorKind::AlertReceived => "received an alert",
        }
    }
}

impl From<io::Error> for TlsError {
    fn from(err: io::Error) -> TlsError {
        TlsError {
            kind: TlsErrorKind::IoFailure,
            desc: format!("io error: {}", err),
        }
    }
}

impl fmt::Display for TlsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

pub type TlsResult<T> = Result<T, TlsError>;
<commit_msg>tls_result: Fix `impl fmt::Display for TlsError`<commit_after>use std::error::Error;
use std::io;
use std::fmt;

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum TlsErrorKind {
    // corresponds to alert messages

    UnexpectedMessage,
    BadRecordMac,
    RecordOverflow,
    IllegalParameter,
    DecodeError,
    DecryptError,
    InternalError,

    // we probably can't even send alert?
    IoFailure,
    AlertReceived,
}

#[derive(Debug)]
pub struct TlsError {
    pub kind: TlsErrorKind,
    pub desc: String,
}

impl TlsError {
    pub fn new<T>(kind: TlsErrorKind, desc: String) -> TlsResult<T> {
        Err(TlsError {
            kind: kind,
            desc: desc,
        })
    }
}

impl Error for TlsError {
    fn description(&self) -> &str {
        match self.kind {
            TlsErrorKind::UnexpectedMessage => "unexpected message",
            TlsErrorKind::BadRecordMac => "record has bad mac and/or encryption",
            TlsErrorKind::RecordOverflow => "record too long",
            TlsErrorKind::IllegalParameter => "illegal parameter during handshake",
            TlsErrorKind::DecodeError => "cannot decode message",
            TlsErrorKind::DecryptError => "failed to verify signature/message",
            TlsErrorKind::InternalError => "internal error",

            // UnsupportedExtension,

            // we probably can't even send alert?
            TlsErrorKind::IoFailure => "i/o error",
            TlsErrorKind::AlertReceived => "received an alert",
        }
    }
}

impl From<io::Error> for TlsError {
    fn from(err: io::Error) -> TlsError {
        TlsError {
            kind: TlsErrorKind::IoFailure,
            desc: format!("io error: {}", err),
        }
    }
}

impl fmt::Display for TlsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <Self as fmt::Debug>::fmt(self, f)
    }
}

pub type TlsResult<T> = Result<T, TlsError>;
<|endoftext|>