<commit_before>use futures::{Poll, Future, Async};
use tokio_service::Service;
use store::Store;
use {LogPos, Error};
use std::net::SocketAddr;
use replica::{ReplicaRequest, ReplicaResponse, LogEntry, HostConfig};

pub trait DownstreamService {
    type Future: Future<Item = ReplicaResponse, Error = Error>;
    fn call(&self, req: ReplicaRequest) -> Self::Future;
}

pub trait NewDownstreamService {
    type Item: DownstreamService;
    fn new_downstream(&self, addr: SocketAddr) -> Self::Item;
}

impl<D, F> DownstreamService for D
    where D: Service<Request = ReplicaRequest,
                     Response = ReplicaResponse,
                     Error = Error,
                     Future = F>,
          F: Future<Item = ReplicaResponse, Error = Error> + Send
{
    type Future = D::Future;

    fn call(&self, req: ReplicaRequest) -> Self::Future {
        Service::call(self, req)
    }
}

impl<F, D> NewDownstreamService for F
    where F: Fn(SocketAddr) -> D,
          D: DownstreamService
{
    type Item = D;
    fn new_downstream(&self, addr: SocketAddr) -> D {
        debug!("new downstream: {:?}", addr);
        self(addr)
    }
}

enum ReplicatorState<S: Store, N>
    where N: NewDownstreamService
{
    Idle,
    Fetching(S::FetchFut),
    Forwarding(<N::Item as DownstreamService>::Future),
}

pub struct Replicator<S: Store, N: NewDownstreamService> {
    store: S,
    new_downstream: N,
    downstream: Option<N::Item>,
    last_seen_seq: LogPos,
    state: ReplicatorState<S, N>,
}

impl<S: Store, N: NewDownstreamService> Replicator<S, N> {
    pub fn new(store: S, new_downstream: N) -> Self {
        Replicator {
            store: store,
            new_downstream: new_downstream,
            downstream: None,
            last_seen_seq: LogPos::zero(),
            state: ReplicatorState::Idle,
        }
    }

    fn process_config_message(&mut self, conf: &HostConfig<SocketAddr>) {
        debug!("logged Config message: {:?}", conf);
        // FIXME: Well, this is blatantly wrong.
        debug!("connecting downstream to: {:?}", conf.head);
        self.downstream = Some(self.new_downstream
            .new_downstream(conf.head));
    }

    fn try_take_next(&mut self) -> Poll<(), Error> {
        if let ReplicatorState::Idle = self.state {
            debug!("Idle, fetching after: {:?}", self.last_seen_seq);
            let fetch_f = self.store.fetch_next(self.last_seen_seq);
            self.state = ReplicatorState::Fetching(fetch_f);
        }
        Ok(Async::Ready(()))
    }

    fn try_process_message(&mut self) -> Poll<(), Error> {
        let (off, val) = if let &mut ReplicatorState::Fetching(ref mut fetch_f) = &mut self.state {
            debug!("Fetching");
            try_ready!(fetch_f.poll())
        } else {
            return Ok(Async::Ready(()));
        };

        debug!("Fetched: {:?}", (&off, &val));
        if let &LogEntry::Config(ref conf) = &val {
            self.process_config_message(conf);
        };

        self.state = if let Some(ref downstream) = self.downstream {
            debug!("Forward {:?} -> {:?}", self.last_seen_seq, off);
            let req = ReplicaRequest::AppendLogEntry {
                assumed_offset: self.last_seen_seq,
                // FIXME: NO. WRONG.
                entry_offset: off,
                datum: val,
            };
            ReplicatorState::Forwarding(downstream.call(req))
        } else {
            debug!("No downstream at {:?}", off);
            ReplicatorState::Idle
        };

        self.last_seen_seq = off;
        Ok(Async::Ready(()))
    }

    fn try_process_forward(&mut self) -> Poll<(), Error> {
        let next = if let &mut ReplicatorState::Forwarding(ref mut f) = &mut self.state {
            match try_ready!(f.poll()) {
                ReplicaResponse::Done(pos) => {
                    debug!("woo! {:?}", pos);
                    Some(ReplicatorState::Idle)
                }
                ReplicaResponse::BadSequence(pos) => {
                    debug!("Bad sequence: resetting to {:?}", pos);
                    self.last_seen_seq = pos;
                    Some(ReplicatorState::Idle)
                }
            }
        } else {
            None
        };
        if let Some(next) = next {
            self.state = next;
        }
        Ok(Async::Ready(()))
    }
}

impl<S: Store, N: NewDownstreamService> Drop for Replicator<S, N> {
    fn drop(&mut self) {
        debug!("Drop Replicator");
    }
}

impl<S: Store, N: NewDownstreamService> Future for Replicator<S, N> {
    type Item = ();
    type Error = Error;
    fn poll(&mut self) -> Poll<(), Error> {
        debug!("Replicator#poll");
        loop {
            try_ready!(self.try_take_next());
            try_ready!(self.try_process_message());
            try_ready!(self.try_process_forward());
        }
    }
}

#[cfg(test)]
mod test {
    use futures::{self, Future, BoxFuture, Async};
    use futures::stream::Stream;
    use service::Service;
    use tokio::channel;
    use replica::LogPos;
    use store::{RamStore, Store};
    use replica::{ReplicaRequest, ReplicaResponse, LogEntry, HostConfig};
    use errors::Error;
    use std::net::SocketAddr;
    use super::*;
    use tokio::reactor::Core;
    use std::time::Duration;
    use std::io;
    use tokio_timer;
    use env_logger;

    type MessageType = ((SocketAddr, ReplicaRequest), futures::Complete<ReplicaResponse>);
    struct MyMagicalDownstream {
        addr: SocketAddr,
        send: channel::Sender<MessageType>,
    }

    struct MyDownstreamBuilder {
        send: channel::Sender<MessageType>,
    }

    impl Service for MyMagicalDownstream {
        type Request = ReplicaRequest;
        type Response = ReplicaResponse;
        type Error = Error;
        type Future = BoxFuture<ReplicaResponse, Error>;
        fn poll_ready(&self) -> Async<()> {
            Async::Ready(())
        }
        fn call(&self, req: Self::Request) -> Self::Future {
            let (c, p) = futures::oneshot();
            match self.send.send(((self.addr.clone(), req), c)) {
                Ok(()) => (),
                Err(e) => return futures::failed(e.into()).boxed(),
            };
            p.map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe").into())
                .boxed()
        }
    }

    impl NewDownstreamService for MyDownstreamBuilder {
        type Item = MyMagicalDownstream;
        fn new_downstream(&self, addr: SocketAddr) -> Self::Item {
            MyMagicalDownstream {
                addr: addr,
                send: self.send.clone(),
            }
        }
    }

    #[test]
    fn should_start_replicating_to_downstream_on_committed_config_message() {
        env_logger::init().unwrap_or(());
        let store = RamStore::new();
        let timer = tokio_timer::wheel().tick_duration(Duration::from_millis(1)).build();

        let mut core = Core::new().expect("core::new");
        let (tx, rx) = channel::channel(&core.handle()).expect("channel");

        let downstream = MyDownstreamBuilder { send: tx };

        let replica = Replicator::new(store.clone(), downstream);

        core.handle().spawn(replica.map_err(|e| panic!("Replicator failed!: {:?}", e)));
        debug!("Spawned replica task");

        let head_addr = "1.2.3.4:5".parse().expect("parse");

        let config: HostConfig<::std::net::SocketAddr> = HostConfig {
            head: head_addr,
            tail: "1.2.3.6:7".parse().expect("parse"),
        };
        let off = LogPos::zero();
        let _ = append_entry(&store, &mut core, off, LogEntry::Config(config.clone()));

        let (_rx, _response, _assumed0, _off0, entry0) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        assert_eq!(entry0, LogEntry::Config(config));
    }

    #[test]
    fn should_send_next_log_entry_on_okay_from_downstream() {
        env_logger::init().unwrap_or(());
        let store = RamStore::new();
        let timer = tokio_timer::wheel().tick_duration(Duration::from_millis(1)).build();

        let mut core = Core::new().expect("core::new");
        let (tx, rx) = channel::channel(&core.handle()).expect("channel");

        let downstream = MyDownstreamBuilder { send: tx };

        let replica = Replicator::new(store.clone(), downstream);

        core.handle().spawn(replica.map_err(|e| panic!("Replicator failed!: {:?}", e)));
        debug!("Spawned replica task");

        let head_addr = "1.2.3.4:5".parse().expect("parse");

        let config: HostConfig<::std::net::SocketAddr> = HostConfig {
            head: head_addr,
            tail: "1.2.3.6:7".parse().expect("parse"),
        };

        let off0 = append_entry(&store,
                                &mut core,
                                LogPos::zero(),
                                LogEntry::Config(config.clone()));
        let _off1 = append_entry(&store,
                                 &mut core,
                                 off0,
                                 LogEntry::Data(b"Hello world!".to_vec().into()));

        let (rx, response, _assumed0, off0, _entry0) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        response.complete(ReplicaResponse::Done(off0));

        let (_rx, _response, assumed1, off1, entry1) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        assert_eq!((assumed1, entry1),
                   (off0, LogEntry::Data(b"Hello world!".to_vec().into())));
        assert!(off1 > assumed1);
    }

    #[test]
    fn should_restart_replication_on_badsequence_from_downstream() {
        env_logger::init().unwrap_or(());
        let store = RamStore::new();
        let timer = tokio_timer::wheel().tick_duration(Duration::from_millis(1)).build();

        let mut core = Core::new().expect("core::new");
        let (tx, rx) = channel::channel(&core.handle()).expect("channel");

        let downstream = MyDownstreamBuilder { send: tx };

        let replica = Replicator::new(store.clone(), downstream);

        core.handle().spawn(replica.map_err(|e| panic!("Replicator failed!: {:?}", e)));
        debug!("Spawned replica task");

        let head_addr = "1.2.3.4:5".parse().expect("parse");

        let config: HostConfig<::std::net::SocketAddr> = HostConfig {
            head: head_addr,
            tail: "1.2.3.6:7".parse().expect("parse"),
        };

        let log_off = LogPos::zero();
        let log_off0 = append_entry(&store, &mut core, log_off, LogEntry::Config(config.clone()));
        let log_off1 = append_entry(&store,
                                    &mut core,
                                    log_off0,
                                    LogEntry::Data(b"Hello".to_vec().into()));
        let log_off2 = append_entry(&store,
                                    &mut core,
                                    log_off1,
                                    LogEntry::Data(b"world!".to_vec().into()));

        let (rx, response, _assumed0, _off0, _entry0) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        // So we expect the delivery of "world!"
        let resp = ReplicaResponse::BadSequence(log_off1);
        println!("Respond with: {:?}", resp);
        response.complete(resp);
        let (_rx, _response, assumed1, off1, entry1) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        assert_eq!((assumed1, off1, entry1),
                   (log_off1, log_off2, LogEntry::Data(b"world!".to_vec().into())));
    }

    fn append_entry<S: Store>(store: &S, core: &mut Core, off: LogPos, entry: LogEntry) -> LogPos {
        let next = off.next();
        debug!("append config message {:?} -> {:?}", off, next);
        core.run(store.append_entry(off, next, entry))
            .expect("append");
        next
    }

    fn take_next_entry(rx: channel::Receiver<MessageType>,
                       target: &SocketAddr,
                       core: &mut Core,
                       timer: &tokio_timer::Timer)
                       -> (channel::Receiver<MessageType>,
                           futures::Complete<ReplicaResponse>,
                           LogPos,
                           LogPos,
                           LogEntry) {
        let f = timer.timeout(rx.into_future().map_err(|(e, _)| e),
                              Duration::from_millis(1000));
        debug!("wait for stub message");
        let (data, rx) = core.run(f).expect("receive downstream");
        let ((addr, msg), response) = data.expect("Some message");
        assert_eq!(&addr, target);
        println!("Some: {:?}", (&addr, &msg));
        // Expect that the target at `head_addr` receives a set of replication messages.
        match msg {
            ReplicaRequest::AppendLogEntry { assumed_offset, entry_offset, datum } => {
                (rx, response, assumed_offset, entry_offset, datum)
            }
        }
    }
}
<commit_msg>And a tad more.<commit_after>use futures::{Poll, Future, Async};
use tokio_service::Service;
use store::Store;
use {LogPos, Error};
use std::net::SocketAddr;
use replica::{ReplicaRequest, ReplicaResponse, LogEntry, HostConfig};

pub trait DownstreamService {
    type Future: Future<Item = ReplicaResponse, Error = Error>;
    fn call(&self, req: ReplicaRequest) -> Self::Future;
}

pub trait NewDownstreamService {
    type Item: DownstreamService;
    fn new_downstream(&self, addr: SocketAddr) -> Self::Item;
}

impl<D, F> DownstreamService for D
    where D: Service<Request = ReplicaRequest,
                     Response = ReplicaResponse,
                     Error = Error,
                     Future = F>,
          F: Future<Item = ReplicaResponse, Error = Error> + Send
{
    type Future = D::Future;

    fn call(&self, req: ReplicaRequest) -> Self::Future {
        Service::call(self, req)
    }
}

impl<F, D> NewDownstreamService for F
    where F: Fn(SocketAddr) -> D,
          D: DownstreamService
{
    type Item = D;
    fn new_downstream(&self, addr: SocketAddr) -> D {
        debug!("new downstream: {:?}", addr);
        self(addr)
    }
}

enum ReplicatorState<S: Store, N>
    where N: NewDownstreamService
{
    Idle,
    Fetching(S::FetchFut),
    Forwarding(<N::Item as DownstreamService>::Future),
}

pub struct Replicator<S: Store, N: NewDownstreamService> {
    store: S,
    new_downstream: N,
    downstream: Option<N::Item>,
    last_seen_seq: LogPos,
    state: ReplicatorState<S, N>,
}

impl<S: Store, N: NewDownstreamService> Replicator<S, N> {
    pub fn new(store: S, new_downstream: N) -> Self {
        Replicator {
            store: store,
            new_downstream: new_downstream,
            downstream: None,
            last_seen_seq: LogPos::zero(),
            state: ReplicatorState::Idle,
        }
    }

    fn process_config_message(&mut self, conf: &HostConfig<SocketAddr>) {
        debug!("logged Config message: {:?}", conf);
        // FIXME: Well, this is blatantly wrong.
        debug!("connecting downstream to: {:?}", conf.head);
        self.downstream = Some(self.new_downstream
            .new_downstream(conf.head));
    }

    fn try_take_next(&mut self) -> Poll<(), Error> {
        if let ReplicatorState::Idle = self.state {
            debug!("Idle, fetching after: {:?}", self.last_seen_seq);
            let fetch_f = self.store.fetch_next(self.last_seen_seq);
            self.state = ReplicatorState::Fetching(fetch_f);
        }
        Ok(Async::Ready(()))
    }

    fn try_process_message(&mut self) -> Poll<(), Error> {
        let (off, val) = if let &mut ReplicatorState::Fetching(ref mut fetch_f) = &mut self.state {
            debug!("Fetching");
            try_ready!(fetch_f.poll())
        } else {
            return Ok(Async::Ready(()));
        };

        debug!("Fetched: {:?}", (&off, &val));
        if let &LogEntry::Config(ref conf) = &val {
            self.process_config_message(conf);
        };

        self.state = if let Some(ref downstream) = self.downstream {
            debug!("Forward {:?} -> {:?}", self.last_seen_seq, off);
            let req = ReplicaRequest::AppendLogEntry {
                assumed_offset: self.last_seen_seq,
                // FIXME: NO. WRONG.
                entry_offset: off,
                datum: val,
            };
            ReplicatorState::Forwarding(downstream.call(req))
        } else {
            debug!("No downstream at {:?}", off);
            ReplicatorState::Idle
        };

        self.last_seen_seq = off;
        Ok(Async::Ready(()))
    }

    fn try_process_forward(&mut self) -> Poll<(), Error> {
        let resp = if let &mut ReplicatorState::Forwarding(ref mut f) = &mut self.state {
            try_ready!(f.poll())
        } else {
            return Ok(Async::Ready(()));
        };

        match resp {
            ReplicaResponse::Done(pos) => {
                debug!("woo! {:?}", pos);
            }
            ReplicaResponse::BadSequence(pos) => {
                debug!("Bad sequence: resetting to {:?}", pos);
                self.last_seen_seq = pos;
            }
        };

        self.state = ReplicatorState::Idle;
        Ok(Async::Ready(()))
    }
}

impl<S: Store, N: NewDownstreamService> Drop for Replicator<S, N> {
    fn drop(&mut self) {
        debug!("Drop Replicator");
    }
}

impl<S: Store, N: NewDownstreamService> Future for Replicator<S, N> {
    type Item = ();
    type Error = Error;
    fn poll(&mut self) -> Poll<(), Error> {
        debug!("Replicator#poll");
        loop {
            try_ready!(self.try_take_next());
            try_ready!(self.try_process_message());
            try_ready!(self.try_process_forward());
        }
    }
}

#[cfg(test)]
mod test {
    use futures::{self, Future, BoxFuture, Async};
    use futures::stream::Stream;
    use service::Service;
    use tokio::channel;
    use replica::LogPos;
    use store::{RamStore, Store};
    use replica::{ReplicaRequest, ReplicaResponse, LogEntry, HostConfig};
    use errors::Error;
    use std::net::SocketAddr;
    use super::*;
    use tokio::reactor::Core;
    use std::time::Duration;
    use std::io;
    use tokio_timer;
    use env_logger;

    type MessageType = ((SocketAddr, ReplicaRequest), futures::Complete<ReplicaResponse>);
    struct MyMagicalDownstream {
        addr: SocketAddr,
        send: channel::Sender<MessageType>,
    }

    struct MyDownstreamBuilder {
        send: channel::Sender<MessageType>,
    }

    impl Service for MyMagicalDownstream {
        type Request = ReplicaRequest;
        type Response = ReplicaResponse;
        type Error = Error;
        type Future = BoxFuture<ReplicaResponse, Error>;
        fn poll_ready(&self) -> Async<()> {
            Async::Ready(())
        }
        fn call(&self, req: Self::Request) -> Self::Future {
            let (c, p) = futures::oneshot();
            match self.send.send(((self.addr.clone(), req), c)) {
                Ok(()) => (),
                Err(e) => return futures::failed(e.into()).boxed(),
            };
            p.map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe").into())
                .boxed()
        }
    }

    impl NewDownstreamService for MyDownstreamBuilder {
        type Item = MyMagicalDownstream;
        fn new_downstream(&self, addr: SocketAddr) -> Self::Item {
            MyMagicalDownstream {
                addr: addr,
                send: self.send.clone(),
            }
        }
    }

    #[test]
    fn should_start_replicating_to_downstream_on_committed_config_message() {
        env_logger::init().unwrap_or(());
        let store = RamStore::new();
        let timer = tokio_timer::wheel().tick_duration(Duration::from_millis(1)).build();

        let mut core = Core::new().expect("core::new");
        let (tx, rx) = channel::channel(&core.handle()).expect("channel");

        let downstream = MyDownstreamBuilder { send: tx };

        let replica = Replicator::new(store.clone(), downstream);

        core.handle().spawn(replica.map_err(|e| panic!("Replicator failed!: {:?}", e)));
        debug!("Spawned replica task");

        let head_addr = "1.2.3.4:5".parse().expect("parse");

        let config: HostConfig<::std::net::SocketAddr> = HostConfig {
            head: head_addr,
            tail: "1.2.3.6:7".parse().expect("parse"),
        };
        let off = LogPos::zero();
        let _ = append_entry(&store, &mut core, off, LogEntry::Config(config.clone()));

        let (_rx, _response, _assumed0, _off0, entry0) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        assert_eq!(entry0, LogEntry::Config(config));
    }

    #[test]
    fn should_send_next_log_entry_on_okay_from_downstream() {
        env_logger::init().unwrap_or(());
        let store = RamStore::new();
        let timer = tokio_timer::wheel().tick_duration(Duration::from_millis(1)).build();

        let mut core = Core::new().expect("core::new");
        let (tx, rx) = channel::channel(&core.handle()).expect("channel");

        let downstream = MyDownstreamBuilder { send: tx };

        let replica = Replicator::new(store.clone(), downstream);

        core.handle().spawn(replica.map_err(|e| panic!("Replicator failed!: {:?}", e)));
        debug!("Spawned replica task");

        let head_addr = "1.2.3.4:5".parse().expect("parse");

        let config: HostConfig<::std::net::SocketAddr> = HostConfig {
            head: head_addr,
            tail: "1.2.3.6:7".parse().expect("parse"),
        };

        let off0 = append_entry(&store,
                                &mut core,
                                LogPos::zero(),
                                LogEntry::Config(config.clone()));
        let _off1 = append_entry(&store,
                                 &mut core,
                                 off0,
                                 LogEntry::Data(b"Hello world!".to_vec().into()));

        let (rx, response, _assumed0, off0, _entry0) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        response.complete(ReplicaResponse::Done(off0));

        let (_rx, _response, assumed1, off1, entry1) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        assert_eq!((assumed1, entry1),
                   (off0, LogEntry::Data(b"Hello world!".to_vec().into())));
        assert!(off1 > assumed1);
    }

    #[test]
    fn should_restart_replication_on_badsequence_from_downstream() {
        env_logger::init().unwrap_or(());
        let store = RamStore::new();
        let timer = tokio_timer::wheel().tick_duration(Duration::from_millis(1)).build();

        let mut core = Core::new().expect("core::new");
        let (tx, rx) = channel::channel(&core.handle()).expect("channel");

        let downstream = MyDownstreamBuilder { send: tx };

        let replica = Replicator::new(store.clone(), downstream);

        core.handle().spawn(replica.map_err(|e| panic!("Replicator failed!: {:?}", e)));
        debug!("Spawned replica task");

        let head_addr = "1.2.3.4:5".parse().expect("parse");

        let config: HostConfig<::std::net::SocketAddr> = HostConfig {
            head: head_addr,
            tail: "1.2.3.6:7".parse().expect("parse"),
        };

        let log_off = LogPos::zero();
        let log_off0 = append_entry(&store, &mut core, log_off, LogEntry::Config(config.clone()));
        let log_off1 = append_entry(&store,
                                    &mut core,
                                    log_off0,
                                    LogEntry::Data(b"Hello".to_vec().into()));
        let log_off2 = append_entry(&store,
                                    &mut core,
                                    log_off1,
                                    LogEntry::Data(b"world!".to_vec().into()));

        let (rx, response, _assumed0, _off0, _entry0) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        // So we expect the delivery of "world!"
        let resp = ReplicaResponse::BadSequence(log_off1);
        println!("Respond with: {:?}", resp);
        response.complete(resp);
        let (_rx, _response, assumed1, off1, entry1) =
            take_next_entry(rx, &head_addr, &mut core, &timer);

        assert_eq!((assumed1, off1, entry1),
                   (log_off1, log_off2, LogEntry::Data(b"world!".to_vec().into())));
    }

    fn append_entry<S: Store>(store: &S, core: &mut Core, off: LogPos, entry: LogEntry) -> LogPos {
        let next = off.next();
        debug!("append config message {:?} -> {:?}", off, next);
        core.run(store.append_entry(off, next, entry))
            .expect("append");
        next
    }

    fn take_next_entry(rx: channel::Receiver<MessageType>,
                       target: &SocketAddr,
                       core: &mut Core,
                       timer: &tokio_timer::Timer)
                       -> (channel::Receiver<MessageType>,
                           futures::Complete<ReplicaResponse>,
                           LogPos,
                           LogPos,
                           LogEntry) {
        let f = timer.timeout(rx.into_future().map_err(|(e, _)| e),
                              Duration::from_millis(1000));
        debug!("wait for stub message");
        let (data, rx) = core.run(f).expect("receive downstream");
        let ((addr, msg), response) = data.expect("Some message");
        assert_eq!(&addr, target);
        println!("Some: {:?}", (&addr, &msg));
        // Expect that the target at `head_addr` receives a set of replication messages.
        match msg {
            ReplicaRequest::AppendLogEntry { assumed_offset, entry_offset, datum } => {
                (rx, response, assumed_offset, entry_offset, datum)
            }
        }
    }
}
<|endoftext|>


<commit_before>
use imp_prelude::*;
use libnum;

/// A slice extension trait for concatenating arrays.
pub trait ArrayStackingExt {
    type Output;

    /// Stack the given arrays along the specified axis.
    ///
    /// *Panics* if axis is out of bounds
    /// *Panics* if the slice is empty.
    fn stack(&self, axis: Axis) -> Self::Output;
}

impl<'a, A, D> ArrayStackingExt for [ArrayView<'a, A, D>]
    where A: Clone + libnum::Zero,
          D: Dimension + RemoveAxis
{
    type Output = OwnedArray<A, D>;

    fn stack(&self, axis: Axis) -> <Self as ArrayStackingExt>::Output {
        assert!(self.len() > 0);
        let mut res_dim = self[0].dim().clone();
        let stacked_dim = self.iter()
                              .fold(0, |acc, a| acc + a.dim().index(axis));
        *res_dim.index_mut(axis) = stacked_dim;
        let mut res = OwnedArray::zeros(res_dim);

        {
            let mut assign_view = res.view_mut();
            for array in self {
                let len = *array.dim().index(axis);
                let (mut front, rest) = assign_view.split_at(axis, len);
                front.assign(array);
                assign_view = rest;
            }
        }
        res
    }
}
<commit_msg>stacking: require Copy bound, no zero-initialization<commit_after>
use imp_prelude::*;

/// A slice extension trait for concatenating arrays.
pub trait ArrayStackingExt {
    type Output;

    /// Stack the given arrays along the specified axis.
    ///
    /// *Panics* if axis is out of bounds
    /// *Panics* if the slice is empty.
    fn stack(&self, axis: Axis) -> Self::Output;
}

impl<'a, A, D> ArrayStackingExt for [ArrayView<'a, A, D>]
    where A: Copy,
          D: Dimension + RemoveAxis
{
    type Output = OwnedArray<A, D>;

    fn stack(&self, axis: Axis) -> <Self as ArrayStackingExt>::Output {
        assert!(self.len() > 0);
        let mut res_dim = self[0].dim().clone();
        let stacked_dim = self.iter()
                              .fold(0, |acc, a| acc + a.dim().index(axis));
        *res_dim.index_mut(axis) = stacked_dim;

        // we can safely use uninitialized values here because they are Copy
        // and we will only ever write to them
        let size = res_dim.size();
        let mut v = Vec::with_capacity(size);
        unsafe {
            v.set_len(size);
        }
        let mut res = OwnedArray::from_vec_dim(res_dim, v).unwrap();

        {
            let mut assign_view = res.view_mut();
            for array in self {
                let len = *array.dim().index(axis);
                let (mut front, rest) = assign_view.split_at(axis, len);
                front.assign(array);
                assign_view = rest;
            }
        }
        res
    }
}
<|endoftext|>


<commit_before>pub use libc::{dev_t, mode_t};
pub use libc::stat as FileStat;

use crate::{Result, NixPath, errno::Errno};
#[cfg(not(target_os = "redox"))]
use crate::fcntl::{AtFlags, at_rawfd};
use std::mem;
use std::os::unix::io::RawFd;
use crate::sys::time::{TimeSpec, TimeVal};

libc_bitflags!(
    pub struct SFlag: mode_t {
        S_IFIFO;
        S_IFCHR;
        S_IFDIR;
        S_IFBLK;
        S_IFREG;
        S_IFLNK;
        S_IFSOCK;
        S_IFMT;
    }
);

libc_bitflags! {
    pub struct Mode: mode_t {
        S_IRWXU;
        S_IRUSR;
        S_IWUSR;
        S_IXUSR;
        S_IRWXG;
        S_IRGRP;
        S_IWGRP;
        S_IXGRP;
        S_IRWXO;
        S_IROTH;
        S_IWOTH;
        S_IXOTH;
        S_ISUID as mode_t;
        S_ISGID as mode_t;
        S_ISVTX as mode_t;
    }
}

pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
    let res = path.with_nix_path(|cstr| {
        unsafe {
            libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
        }
    })?;

    Errno::result(res).map(drop)
}

#[cfg(target_os = "linux")]
pub fn major(dev: dev_t) -> u64 {
    ((dev >> 32) & 0xffff_f000) |
    ((dev >>  8) & 0x0000_0fff)
}

#[cfg(target_os = "linux")]
pub fn minor(dev: dev_t) -> u64 {
    ((dev >> 12) & 0xffff_ff00) |
    ((dev      ) & 0x0000_00ff)
}

#[cfg(target_os = "linux")]
pub fn makedev(major: u64, minor: u64) -> dev_t {
    ((major & 0xffff_f000) << 32) |
    ((major & 0x0000_0fff) <<  8) |
    ((minor & 0xffff_ff00) << 12) |
     (minor & 0x0000_00ff)
}

pub fn umask(mode: Mode) -> Mode {
    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}

pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = path.with_nix_path(|cstr| {
        unsafe {
            libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
        }
    })?;

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = path.with_nix_path(|cstr| {
        unsafe {
            libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
        }
    })?;

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

pub fn fstat(fd: RawFd) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

#[cfg(not(target_os = "redox"))]
pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = pathname.with_nix_path(|cstr| {
        unsafe { libc::fstatat(dirfd, cstr.as_ptr(), dst.as_mut_ptr(), f.bits() as libc::c_int) }
    })?;

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

/// Change the file permission bits of the file specified by a file descriptor.
///
/// # References
///
/// [fchmod(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
    let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };

    Errno::result(res).map(drop)
}

/// Flags for `fchmodat` function.
#[derive(Clone, Copy, Debug)]
pub enum FchmodatFlags {
    FollowSymlink,
    NoFollowSymlink,
}

/// Change the file permission bits.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
///
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `fchmod(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
/// in the `nix` crate.
///
/// # References
///
/// [fchmodat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<P: ?Sized + NixPath>(
    dirfd: Option<RawFd>,
    path: &P,
    mode: Mode,
    flag: FchmodatFlags,
) -> Result<()> {
    let atflag =
        match flag {
            FchmodatFlags::FollowSymlink => AtFlags::empty(),
            FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
        };
    let res = path.with_nix_path(|cstr| unsafe {
        libc::fchmodat(
            at_rawfd(dirfd),
            cstr.as_ptr(),
            mode.bits() as mode_t,
            atflag.bits() as libc::c_int,
        )
    })?;

    Errno::result(res).map(drop)
}

/// Change the access and modification times of a file.
///
/// `utimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [utimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = path.with_nix_path(|cstr| unsafe {
        libc::utimes(cstr.as_ptr(), &times[0])
    })?;

    Errno::result(res).map(drop)
}

/// Change the access and modification times of a file without following symlinks.
///
/// `lutimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [lutimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
#[cfg(any(target_os = "linux",
          target_os = "haiku",
          target_os = "ios",
          target_os = "macos",
          target_os = "freebsd",
          target_os = "netbsd"))]
pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = path.with_nix_path(|cstr| unsafe {
        libc::lutimes(cstr.as_ptr(), &times[0])
    })?;

    Errno::result(res).map(drop)
}

/// Change the access and modification times of the file specified by a file descriptor.
///
/// # References
///
/// [futimens(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = unsafe { libc::futimens(fd, &times[0]) };

    Errno::result(res).map(drop)
}

/// Flags for `utimensat` function.
#[derive(Clone, Copy, Debug)]
pub enum UtimensatFlags {
    FollowSymlink,
    NoFollowSymlink,
}

/// Change the access and modification times of a file.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
///
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
/// former if the platforms you care about support it.
///
/// # References
///
/// [utimensat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<P: ?Sized + NixPath>(
    dirfd: Option<RawFd>,
    path: &P,
    atime: &TimeSpec,
    mtime: &TimeSpec,
    flag: UtimensatFlags
) -> Result<()> {
    let atflag =
        match flag {
            UtimensatFlags::FollowSymlink => AtFlags::empty(),
            UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
        };
    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = path.with_nix_path(|cstr| unsafe {
        libc::utimensat(
            at_rawfd(dirfd),
            cstr.as_ptr(),
            &times[0],
            atflag.bits() as libc::c_int,
        )
    })?;

    Errno::result(res).map(drop)
}

#[cfg(not(target_os = "redox"))]
pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
    let res = path.with_nix_path(|cstr| {
        unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
    })?;

    Errno::result(res).map(drop)
}
<commit_msg>doc typo in fchmodat<commit_after>pub use libc::{dev_t, mode_t};
pub use libc::stat as FileStat;

use crate::{Result, NixPath, errno::Errno};
#[cfg(not(target_os = "redox"))]
use crate::fcntl::{AtFlags, at_rawfd};
use std::mem;
use std::os::unix::io::RawFd;
use crate::sys::time::{TimeSpec, TimeVal};

libc_bitflags!(
    pub struct SFlag: mode_t {
        S_IFIFO;
        S_IFCHR;
        S_IFDIR;
        S_IFBLK;
        S_IFREG;
        S_IFLNK;
        S_IFSOCK;
        S_IFMT;
    }
);

libc_bitflags! {
    pub struct Mode: mode_t {
        S_IRWXU;
        S_IRUSR;
        S_IWUSR;
        S_IXUSR;
        S_IRWXG;
        S_IRGRP;
        S_IWGRP;
        S_IXGRP;
        S_IRWXO;
        S_IROTH;
        S_IWOTH;
        S_IXOTH;
        S_ISUID as mode_t;
        S_ISGID as mode_t;
        S_ISVTX as mode_t;
    }
}

pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
    let res = path.with_nix_path(|cstr| {
        unsafe {
            libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
        }
    })?;

    Errno::result(res).map(drop)
}

#[cfg(target_os = "linux")]
pub fn major(dev: dev_t) -> u64 {
    ((dev >> 32) & 0xffff_f000) |
    ((dev >>  8) & 0x0000_0fff)
}

#[cfg(target_os = "linux")]
pub fn minor(dev: dev_t) -> u64 {
    ((dev >> 12) & 0xffff_ff00) |
    ((dev      ) & 0x0000_00ff)
}

#[cfg(target_os = "linux")]
pub fn makedev(major: u64, minor: u64) -> dev_t {
    ((major & 0xffff_f000) << 32) |
    ((major & 0x0000_0fff) <<  8) |
    ((minor & 0xffff_ff00) << 12) |
     (minor & 0x0000_00ff)
}

pub fn umask(mode: Mode) -> Mode {
    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
}

pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = path.with_nix_path(|cstr| {
        unsafe {
            libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
        }
    })?;

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = path.with_nix_path(|cstr| {
        unsafe {
            libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
        }
    })?;

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

pub fn fstat(fd: RawFd) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

#[cfg(not(target_os = "redox"))]
pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
    let mut dst = mem::MaybeUninit::uninit();
    let res = pathname.with_nix_path(|cstr| {
        unsafe { libc::fstatat(dirfd, cstr.as_ptr(), dst.as_mut_ptr(), f.bits() as libc::c_int) }
    })?;

    Errno::result(res)?;

    Ok(unsafe{dst.assume_init()})
}

/// Change the file permission bits of the file specified by a file descriptor.
///
/// # References
///
/// [fchmod(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
    let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };

    Errno::result(res).map(drop)
}

/// Flags for `fchmodat` function.
#[derive(Clone, Copy, Debug)]
pub enum FchmodatFlags {
    FollowSymlink,
    NoFollowSymlink,
}

/// Change the file permission bits.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
///
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
/// in the `nix` crate.
///
/// # References
///
/// [fchmodat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
#[cfg(not(target_os = "redox"))]
pub fn fchmodat<P: ?Sized + NixPath>(
    dirfd: Option<RawFd>,
    path: &P,
    mode: Mode,
    flag: FchmodatFlags,
) -> Result<()> {
    let atflag =
        match flag {
            FchmodatFlags::FollowSymlink => AtFlags::empty(),
            FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
        };
    let res = path.with_nix_path(|cstr| unsafe {
        libc::fchmodat(
            at_rawfd(dirfd),
            cstr.as_ptr(),
            mode.bits() as mode_t,
            atflag.bits() as libc::c_int,
        )
    })?;

    Errno::result(res).map(drop)
}

/// Change the access and modification times of a file.
///
/// `utimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [utimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = path.with_nix_path(|cstr| unsafe {
        libc::utimes(cstr.as_ptr(), &times[0])
    })?;

    Errno::result(res).map(drop)
}

/// Change the access and modification times of a file without following symlinks.
///
/// `lutimes(path, times)` is identical to
/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
/// is a deprecated API so prefer using the latter if the platforms you care
/// about support it.
///
/// # References
///
/// [lutimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
#[cfg(any(target_os = "linux",
          target_os = "haiku",
          target_os = "ios",
          target_os = "macos",
          target_os = "freebsd",
          target_os = "netbsd"))]
pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = path.with_nix_path(|cstr| unsafe {
        libc::lutimes(cstr.as_ptr(), &times[0])
    })?;

    Errno::result(res).map(drop)
}

/// Change the access and modification times of the file specified by a file descriptor.
///
/// # References
///
/// [futimens(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
#[inline]
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = unsafe { libc::futimens(fd, &times[0]) };

    Errno::result(res).map(drop)
}

/// Flags for `utimensat` function.
#[derive(Clone, Copy, Debug)]
pub enum UtimensatFlags {
    FollowSymlink,
    NoFollowSymlink,
}

/// Change the access and modification times of a file.
///
/// The file to be changed is determined relative to the directory associated
/// with the file descriptor `dirfd` or the current working directory
/// if `dirfd` is `None`.
///
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
/// then the mode of the symbolic link is changed.
///
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
/// former if the platforms you care about support it.
///
/// # References
///
/// [utimensat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
#[cfg(not(target_os = "redox"))]
pub fn utimensat<P: ?Sized + NixPath>(
    dirfd: Option<RawFd>,
    path: &P,
    atime: &TimeSpec,
    mtime: &TimeSpec,
    flag: UtimensatFlags
) -> Result<()> {
    let atflag =
        match flag {
            UtimensatFlags::FollowSymlink => AtFlags::empty(),
            UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
        };
    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
    let res = path.with_nix_path(|cstr| unsafe {
        libc::utimensat(
            at_rawfd(dirfd),
            cstr.as_ptr(),
            &times[0],
            atflag.bits() as libc::c_int,
        )
    })?;

    Errno::result(res).map(drop)
}

#[cfg(not(target_os = "redox"))]
pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
    let res = path.with_nix_path(|cstr| {
        unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
    })?;

    Errno::result(res).map(drop)
}
<|endoftext|>


<commit_before>use libc::{self, c_int};
use {Errno, Result};
use unistd::Pid;

use sys::signal::Signal;

libc_bitflags!(
    pub struct WaitPidFlag: c_int {
        WNOHANG;
        WUNTRACED;
        WEXITED;
        WCONTINUED;
        WSTOPPED;
        /// Don't reap, just poll status.
        WNOWAIT;
        /// Don't wait on children of other threads in this group
        #[cfg(any(target_os = "android", target_os = "linux"))]
        __WNOTHREAD;
        /// Wait on all children, regardless of type
        #[cfg(any(target_os = "android", target_os = "linux"))]
        __WALL;
        #[cfg(any(target_os = "android", target_os = "linux"))]
        __WCLONE;
    }
);

/// Possible return values from `wait()` or `waitpid()`.
///
/// Each status (other than `StillAlive`) describes a state transition
/// in a child process `Pid`, such as the process exiting or stopping,
/// plus additional data about the transition if any.
///
/// Note that there are two Linux-specific enum variants, `PtraceEvent`
/// and `PtraceSyscall`. Portable code should avoid exhaustively
/// matching on `WaitStatus`.
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub enum WaitStatus {
    /// The process exited normally (as with `exit()` or returning from
    /// `main`) with the given exit code. This case matches the C macro
    /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
    Exited(Pid, i32),
    /// The process was killed by the given signal. The third field
    /// indicates whether the signal generated a core dump. This case
    /// matches the C macro `WIFSIGNALED(status)`; the last two fields
    /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
    Signaled(Pid, Signal, bool),
    /// The process is alive, but was stopped by the given signal. This
    /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
    /// case matches the C macro `WIFSTOPPED(status)`; the second field
    /// is `WSTOPSIG(status)`.
    Stopped(Pid, Signal),
    /// The traced process was stopped by a `PTRACE_EVENT_*` event. See
    /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
    /// currently-defined events use `SIGTRAP` as the signal; the third
    /// field is the `PTRACE_EVENT_*` value of the event.
    ///
    /// [`nix::sys::ptrace`]: ../ptrace/index.html
    /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
    #[cfg(any(target_os = "linux", target_os = "android"))]
    PtraceEvent(Pid, Signal, c_int),
    /// The traced process was stopped by execution of a system call,
    /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
    /// more information.
    ///
    /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
    #[cfg(any(target_os = "linux", target_os = "android"))]
    PtraceSyscall(Pid),
    /// The process was previously stopped but has resumed execution
    /// after receiving a `SIGCONT` signal. This is only reported if
    /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
    /// macro `WIFCONTINUED(status)`.
    Continued(Pid),
    /// There are currently no state changes to report in any awaited
    /// child process. This is only returned if `WaitPidFlag::WNOHANG`
    /// was used (otherwise `wait()` or `waitpid()` would block until
    /// there was something to report).
    StillAlive
}

impl WaitStatus {
    /// Extracts the PID from the WaitStatus unless it equals StillAlive.
    pub fn pid(&self) -> Option<Pid> {
        use self::WaitStatus::*;
        match *self {
            Exited(p, _) => Some(p),
            Signaled(p, _, _) => Some(p),
            Stopped(p, _) => Some(p),
            Continued(p) => Some(p),
            StillAlive => None,

            #[cfg(any(target_os = "linux", target_os = "android"))]
            PtraceEvent(p, _, _) => Some(p),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            PtraceSyscall(p) => Some(p),
        }
    }
}

fn exited(status: i32) -> bool {
    unsafe { libc::WIFEXITED(status) }
}

fn exit_status(status: i32) -> i32 {
    unsafe { libc::WEXITSTATUS(status) }
}

fn signaled(status: i32) -> bool {
    unsafe { libc::WIFSIGNALED(status) }
}

fn term_signal(status: i32) -> Signal {
    Signal::from_c_int(unsafe { libc::WTERMSIG(status) }).unwrap()
}

fn dumped_core(status: i32) -> bool {
    unsafe { libc::WCOREDUMP(status) }
}

fn stopped(status: i32) -> bool {
    unsafe { libc::WIFSTOPPED(status) }
}

fn stop_signal(status: i32) -> Signal {
    Signal::from_c_int(unsafe { libc::WSTOPSIG(status) }).unwrap()
}

fn syscall_stop(status: i32) -> bool {
    // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
    // of delivering SIGTRAP | 0x80 as the signal number for syscall
    // stops. This allows easily distinguishing syscall stops from
    // genuine SIGTRAP signals.
    unsafe { libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80 }
}

fn stop_additional(status: i32) -> c_int {
    (status >> 16) as c_int
}

fn continued(status: i32) -> bool {
    unsafe { libc::WIFCONTINUED(status) }
}

fn decode(pid : Pid, status: i32) -> WaitStatus {
    if exited(status) {
        WaitStatus::Exited(pid, exit_status(status))
    } else if signaled(status) {
        WaitStatus::Signaled(pid, term_signal(status), dumped_core(status))
    } else if stopped(status) {
        cfg_if! {
            if #[cfg(any(target_os = "linux", target_os = "android"))] {
                fn decode_stopped(pid: Pid, status: i32) -> WaitStatus {
                    let status_additional = stop_additional(status);
                    if syscall_stop(status) {
                        WaitStatus::PtraceSyscall(pid)
                    } else if status_additional == 0 {
                        WaitStatus::Stopped(pid, stop_signal(status))
                    } else {
                        WaitStatus::PtraceEvent(pid, stop_signal(status), stop_additional(status))
                    }
                }
            } else {
                fn decode_stopped(pid: Pid, status: i32) -> WaitStatus {
                    WaitStatus::Stopped(pid, stop_signal(status))
                }
            }
        }
        decode_stopped(pid, status)
    } else {
        assert!(continued(status));
        WaitStatus::Continued(pid)
    }
}

pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
    use self::WaitStatus::*;

    let mut status: i32 = 0;

    let option_bits = match options {
        Some(bits) => bits.bits(),
        None => 0
    };

    let res = unsafe { libc::waitpid(pid.into().unwrap_or(Pid::from_raw(-1)).into(), &mut status as *mut c_int, option_bits) };

    Ok(match try!(Errno::result(res)) {
        0 => StillAlive,
        res => decode(Pid::from_raw(res), status),
    })
}

pub fn wait() -> Result<WaitStatus> {
    waitpid(None, None)
}
<commit_msg>Omit invalid waitpid flags on OpenBSD<commit_after>use libc::{self, c_int};
use {Errno, Result};
use unistd::Pid;

use sys::signal::Signal;

libc_bitflags!(
    pub struct WaitPidFlag: c_int {
        WNOHANG;
        WUNTRACED;
        #[cfg(any(target_os = "android",
                  target_os = "freebsd",
                  target_os = "haiku",
                  target_os = "ios",
                  target_os = "linux",
                  target_os = "macos",
                  target_os = "netbsd"))]
        WEXITED;
        WCONTINUED;
        #[cfg(any(target_os = "android",
                  target_os = "freebsd",
                  target_os = "haiku",
                  target_os = "ios",
                  target_os = "linux",
                  target_os = "macos",
                  target_os = "netbsd"))]
        WSTOPPED;
        /// Don't reap, just poll status.
        #[cfg(any(target_os = "android",
                  target_os = "freebsd",
                  target_os = "haiku",
                  target_os = "ios",
                  target_os = "linux",
                  target_os = "macos",
                  target_os = "netbsd"))]
        WNOWAIT;
        /// Don't wait on children of other threads in this group
        #[cfg(any(target_os = "android", target_os = "linux"))]
        __WNOTHREAD;
        /// Wait on all children, regardless of type
        #[cfg(any(target_os = "android", target_os = "linux"))]
        __WALL;
        #[cfg(any(target_os = "android", target_os = "linux"))]
        __WCLONE;
    }
);

/// Possible return values from `wait()` or `waitpid()`.
///
/// Each status (other than `StillAlive`) describes a state transition
/// in a child process `Pid`, such as the process exiting or stopping,
/// plus additional data about the transition if any.
///
/// Note that there are two Linux-specific enum variants, `PtraceEvent`
/// and `PtraceSyscall`. Portable code should avoid exhaustively
/// matching on `WaitStatus`.
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub enum WaitStatus {
    /// The process exited normally (as with `exit()` or returning from
    /// `main`) with the given exit code. This case matches the C macro
    /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
    Exited(Pid, i32),
    /// The process was killed by the given signal. The third field
    /// indicates whether the signal generated a core dump. This case
    /// matches the C macro `WIFSIGNALED(status)`; the last two fields
    /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
    Signaled(Pid, Signal, bool),
    /// The process is alive, but was stopped by the given signal. This
    /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
    /// case matches the C macro `WIFSTOPPED(status)`; the second field
    /// is `WSTOPSIG(status)`.
    Stopped(Pid, Signal),
    /// The traced process was stopped by a `PTRACE_EVENT_*` event. See
    /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
    /// currently-defined events use `SIGTRAP` as the signal; the third
    /// field is the `PTRACE_EVENT_*` value of the event.
    ///
    /// [`nix::sys::ptrace`]: ../ptrace/index.html
    /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
    #[cfg(any(target_os = "linux", target_os = "android"))]
    PtraceEvent(Pid, Signal, c_int),
    /// The traced process was stopped by execution of a system call,
    /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
    /// more information.
    ///
    /// [`ptrace`(2)]: http://man7.org/linux/man-pages/man2/ptrace.2.html
    #[cfg(any(target_os = "linux", target_os = "android"))]
    PtraceSyscall(Pid),
    /// The process was previously stopped but has resumed execution
    /// after receiving a `SIGCONT` signal. This is only reported if
    /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
    /// macro `WIFCONTINUED(status)`.
    Continued(Pid),
    /// There are currently no state changes to report in any awaited
    /// child process. This is only returned if `WaitPidFlag::WNOHANG`
    /// was used (otherwise `wait()` or `waitpid()` would block until
    /// there was something to report).
    StillAlive
}

impl WaitStatus {
    /// Extracts the PID from the WaitStatus unless it equals StillAlive.
    pub fn pid(&self) -> Option<Pid> {
        use self::WaitStatus::*;
        match *self {
            Exited(p, _) => Some(p),
            Signaled(p, _, _) => Some(p),
            Stopped(p, _) => Some(p),
            Continued(p) => Some(p),
            StillAlive => None,

            #[cfg(any(target_os = "linux", target_os = "android"))]
            PtraceEvent(p, _, _) => Some(p),
            #[cfg(any(target_os = "linux", target_os = "android"))]
            PtraceSyscall(p) => Some(p),
        }
    }
}

fn exited(status: i32) -> bool {
    unsafe { libc::WIFEXITED(status) }
}

fn exit_status(status: i32) -> i32 {
    unsafe { libc::WEXITSTATUS(status) }
}

fn signaled(status: i32) -> bool {
    unsafe { libc::WIFSIGNALED(status) }
}

fn term_signal(status: i32) -> Signal {
    Signal::from_c_int(unsafe { libc::WTERMSIG(status) }).unwrap()
}

fn dumped_core(status: i32) -> bool {
    unsafe { libc::WCOREDUMP(status) }
}

fn stopped(status: i32) -> bool {
    unsafe { libc::WIFSTOPPED(status) }
}

fn stop_signal(status: i32) -> Signal {
    Signal::from_c_int(unsafe { libc::WSTOPSIG(status) }).unwrap()
}

fn syscall_stop(status: i32) -> bool {
    // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
    // of delivering SIGTRAP | 0x80 as the signal number for syscall
    // stops. This allows easily distinguishing syscall stops from
    // genuine SIGTRAP signals.
    unsafe { libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80 }
}

fn stop_additional(status: i32) -> c_int {
    (status >> 16) as c_int
}

fn continued(status: i32) -> bool {
    unsafe { libc::WIFCONTINUED(status) }
}

fn decode(pid : Pid, status: i32) -> WaitStatus {
    if exited(status) {
        WaitStatus::Exited(pid, exit_status(status))
    } else if signaled(status) {
        WaitStatus::Signaled(pid, term_signal(status), dumped_core(status))
    } else if stopped(status) {
        cfg_if! {
            if #[cfg(any(target_os = "linux", target_os = "android"))] {
                fn decode_stopped(pid: Pid, status: i32) -> WaitStatus {
                    let status_additional = stop_additional(status);
                    if syscall_stop(status) {
                        WaitStatus::PtraceSyscall(pid)
                    } else if status_additional == 0 {
                        WaitStatus::Stopped(pid, stop_signal(status))
                    } else {
                        WaitStatus::PtraceEvent(pid, stop_signal(status), stop_additional(status))
                    }
                }
            } else {
                fn decode_stopped(pid: Pid, status: i32) -> WaitStatus {
                    WaitStatus::Stopped(pid, stop_signal(status))
                }
            }
        }
        decode_stopped(pid, status)
    } else {
        assert!(continued(status));
        WaitStatus::Continued(pid)
    }
}

pub fn waitpid<P: Into<Option<Pid>>>(pid: P, options: Option<WaitPidFlag>) -> Result<WaitStatus> {
    use self::WaitStatus::*;

    let mut status: i32 = 0;

    let option_bits = match options {
        Some(bits) => bits.bits(),
        None => 0
    };

    let res = unsafe { libc::waitpid(pid.into().unwrap_or(Pid::from_raw(-1)).into(), &mut status as *mut c_int, option_bits) };

    Ok(match try!(Errno::result(res)) {
        0 => StillAlive,
        res => decode(Pid::from_raw(res), status),
    })
}

pub fn wait() -> Result<WaitStatus> {
    waitpid(None, None)
}
<|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/. */

//! A generic, safe mechanism by which DOM objects can be pinned and transferred
//! between threads (or intra-thread for asynchronous events). Akin to Gecko's
//! nsMainThreadPtrHandle, this uses thread-safe reference counting and ensures
//! that the actual SpiderMonkey GC integration occurs on the script thread via
//! weak refcounts. Ownership of a `Trusted<T>` object means the DOM object of
//! type T to which it points remains alive. Any other behaviour is undefined.
//! To guarantee the lifetime of a DOM object when performing asynchronous operations,
//! obtain a `Trusted<T>` from that object and pass it along with each operation.
//! A usable pointer to the original DOM object can be obtained on the script thread
//! from a `Trusted<T>` via the `root` method.
//!
//! The implementation of `Trusted<T>` is as follows:
//! The `Trusted<T>` object contains an atomic reference counted pointer to the Rust DOM object.
//! A hashtable resides in the script thread, keyed on the pointer.
//! The values in this hashtable are weak reference counts. When a `Trusted<T>` object is
//! created or cloned, the reference count is increased. When a `Trusted<T>` is dropped, the count
//! decreases. If the count hits zero, the weak reference is emptied, and is removed from
//! its hash table during the next GC. During GC, the entries of the hash table are counted
//! as JS roots.

use core::nonzero::NonZero;
use dom::bindings::conversions::ToJSValConvertible;
use dom::bindings::error::Error;
use dom::bindings::js::Root;
use dom::bindings::reflector::{DomObject, Reflector};
use dom::bindings::trace::trace_reflector;
use dom::promise::Promise;
use js::jsapi::JSAutoCompartment;
use js::jsapi::JSTracer;
use libc;
use std::cell::RefCell;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::hash::Hash;
use std::marker::PhantomData;
use std::os;
use std::rc::Rc;
use std::sync::{Arc, Weak};
use task::Task;

#[allow(missing_docs)]  // FIXME
mod dummy {  // Attributes don’t apply through the macro.
    use std::cell::RefCell;
    use std::rc::Rc;
    use super::LiveDOMReferences;
    thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> =
            Rc::new(RefCell::new(None)));
}
pub use self::dummy::LIVE_REFERENCES;

/// A pointer to a Rust DOM object that needs to be destroyed.
pub struct TrustedReference(*const libc::c_void);
unsafe impl Send for TrustedReference {}

impl TrustedReference {
    fn new<T: DomObject>(ptr: *const T) -> TrustedReference {
        TrustedReference(ptr as *const libc::c_void)
    }
}

/// A safe wrapper around a DOM Promise object that can be shared among threads for use
/// in asynchronous operations. The underlying DOM object is guaranteed to live at least
/// as long as the last outstanding `TrustedPromise` instance. These values cannot be cloned,
/// only created from existing Rc<Promise> values.
pub struct TrustedPromise {
    dom_object: *const Promise,
    owner_thread: *const libc::c_void,
}

unsafe impl Send for TrustedPromise {}

impl TrustedPromise {
    /// Create a new `TrustedPromise` instance from an existing DOM object. The object will
    /// be prevented from being GCed for the duration of the resulting `TrustedPromise` object's
    /// lifetime.
    #[allow(unrooted_must_root)]
    pub fn new(promise: Rc<Promise>) -> TrustedPromise {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let ptr = &*promise as *const Promise;
            live_references.addref_promise(promise);
            TrustedPromise {
                dom_object: ptr,
                owner_thread: (&*live_references) as *const _ as *const libc::c_void,
            }
        })
    }

    /// Obtain a usable DOM Promise from a pinned `TrustedPromise` value. Fails if used on
    /// a different thread than the original value from which this `TrustedPromise` was
    /// obtained.
    #[allow(unrooted_must_root)]
    pub fn root(self) -> Rc<Promise> {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            assert!(self.owner_thread == (&*live_references) as *const _ as *const libc::c_void);
            // Borrow-check error requires the redundant `let promise = ...; promise` here.
            let promise = match live_references.promise_table.borrow_mut().entry(self.dom_object) {
                Occupied(mut entry) => {
                    let promise = {
                        let promises = entry.get_mut();
                        promises.pop().expect("rooted promise list unexpectedly empty")
                    };
                    if entry.get().is_empty() {
                        entry.remove();
                    }
                    promise
                }
                Vacant(_) => unreachable!(),
            };
            promise
        })
    }

    /// A task which will reject the promise.
    #[allow(unrooted_must_root)]
    pub fn reject_task(self, error: Error) -> impl Send + Task {
        let this = self;
        task!(reject_promise: move || {
            debug!("Rejecting promise.");
            let this = this.root();
            let cx = this.global().get_cx();
            let _ac = JSAutoCompartment::new(cx, this.reflector().get_jsobject().get());
            this.reject_error(cx, error);
        })
    }

    /// A task which will resolve the promise.
    #[allow(unrooted_must_root)]
    pub fn resolve_task<T>(self, value: T) -> impl Send + Task
    where
        T: ToJSValConvertible + Send,
    {
        struct ResolvePromise<T>(TrustedPromise, T);
        impl<T: ToJSValConvertible> Task for ResolvePromise<T> {
            fn run(self: Box<Self>) {
                debug!("Resolving promise.");
                let this = *self;
                let promise = this.0.root();
                let cx = promise.global().get_cx();
                let _ac = JSAutoCompartment::new(cx, promise.reflector().get_jsobject().get());
                promise.resolve_native(cx, &this.1);
            }
        }
        ResolvePromise(self, value)
    }
}

/// A safe wrapper around a raw pointer to a DOM object that can be
/// shared among threads for use in asynchronous operations. The underlying
/// DOM object is guaranteed to live at least as long as the last outstanding
/// `Trusted<T>` instance.
#[allow_unrooted_interior]
pub struct Trusted<T: DomObject> {
    /// A pointer to the Rust DOM object of type T, but void to allow
    /// sending `Trusted<T>` between threads, regardless of T's sendability.
    refcount: Arc<TrustedReference>,
    owner_thread: *const libc::c_void,
    phantom: PhantomData<T>,
}

unsafe impl<T: DomObject> Send for Trusted<T> {}

impl<T: DomObject> Trusted<T> {
    /// Create a new `Trusted<T>` instance from an existing DOM pointer. The DOM object will
    /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's
    /// lifetime.
    pub fn new(ptr: &T) -> Trusted<T> {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let refcount = live_references.addref(&*ptr as *const T);
            Trusted {
                refcount: refcount,
                owner_thread: (&*live_references) as *const _ as *const libc::c_void,
                phantom: PhantomData,
            }
        })
    }

    /// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on
    /// a different thread than the original value from which this `Trusted<T>` was
    /// obtained.
    pub fn root(&self) -> Root<T> {
        assert!(LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            self.owner_thread == (&*live_references) as *const _ as *const libc::c_void
        }));
        unsafe {
            Root::new(NonZero::new_unchecked(self.refcount.0 as *const T))
        }
    }
}

impl<T: DomObject> Clone for Trusted<T> {
    fn clone(&self) -> Trusted<T> {
        Trusted {
            refcount: self.refcount.clone(),
            owner_thread: self.owner_thread,
            phantom: PhantomData,
        }
    }
}

/// The set of live, pinned DOM objects that are currently prevented
/// from being garbage collected due to outstanding references.
#[allow(unrooted_must_root)]
pub struct LiveDOMReferences {
    // keyed on pointer to Rust DOM object
    reflectable_table: RefCell<HashMap<*const libc::c_void, Weak<TrustedReference>>>,
    promise_table: RefCell<HashMap<*const Promise, Vec<Rc<Promise>>>>,
}

impl LiveDOMReferences {
    /// Set up the thread-local data required for storing the outstanding DOM references.
    pub fn initialize() {
        LIVE_REFERENCES.with(|ref r| {
            *r.borrow_mut() = Some(LiveDOMReferences {
                reflectable_table: RefCell::new(HashMap::new()),
                promise_table: RefCell::new(HashMap::new()),
            })
        });
    }

    #[allow(unrooted_must_root)]
    fn addref_promise(&self, promise: Rc<Promise>) {
        let mut table = self.promise_table.borrow_mut();
        table.entry(&*promise).or_insert(vec![]).push(promise)
    }

    fn addref<T: DomObject>(&self, ptr: *const T) -> Arc<TrustedReference> {
        let mut table = self.reflectable_table.borrow_mut();
        let capacity = table.capacity();
        let len = table.len();
        if (0 < capacity) && (capacity <= len) {
            info!("growing refcounted references by {}", len);
            remove_nulls(&mut table);
            table.reserve(len);
        }
        match table.entry(ptr as *const libc::c_void) {
            Occupied(mut entry) => match entry.get().upgrade() {
                Some(refcount) => refcount,
                None => {
                    let refcount = Arc::new(TrustedReference::new(ptr));
                    entry.insert(Arc::downgrade(&refcount));
                    refcount
                },
            },
            Vacant(entry) => {
                let refcount = Arc::new(TrustedReference::new(ptr));
                entry.insert(Arc::downgrade(&refcount));
                refcount
            }
        }
    }
}

/// Remove null entries from the live references table
fn remove_nulls<K: Eq + Hash + Clone, V> (table: &mut HashMap<K, Weak<V>>) {
    let to_remove: Vec<K> =
        table.iter()
        .filter(|&(_, value)| Weak::upgrade(value).is_none())
        .map(|(key, _)| key.clone())
        .collect();
    info!("removing {} refcounted references", to_remove.len());
    for key in to_remove {
        table.remove(&key);
    }
}

/// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES
#[allow(unrooted_must_root)]
pub unsafe extern "C" fn trace_refcounted_objects(tracer: *mut JSTracer,
                                                  _data: *mut os::raw::c_void) {
    info!("tracing live refcounted references");
    LIVE_REFERENCES.with(|ref r| {
        let r = r.borrow();
        let live_references = r.as_ref().unwrap();
        {
            let mut table = live_references.reflectable_table.borrow_mut();
            remove_nulls(&mut table);
            for obj in table.keys() {
                let reflectable = &*(*obj as *const Reflector);
                trace_reflector(tracer, "refcounted", reflectable);
            }
        }

        {
            let table = live_references.promise_table.borrow_mut();
            for promise in table.keys() {
                trace_reflector(tracer, "refcounted", (**promise).reflector());
            }
        }
    });
}
<commit_msg>Use task! to resolve promises<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/. */

//! A generic, safe mechanism by which DOM objects can be pinned and transferred
//! between threads (or intra-thread for asynchronous events). Akin to Gecko's
//! nsMainThreadPtrHandle, this uses thread-safe reference counting and ensures
//! that the actual SpiderMonkey GC integration occurs on the script thread via
//! weak refcounts. Ownership of a `Trusted<T>` object means the DOM object of
//! type T to which it points remains alive. Any other behaviour is undefined.
//! To guarantee the lifetime of a DOM object when performing asynchronous operations,
//! obtain a `Trusted<T>` from that object and pass it along with each operation.
//! A usable pointer to the original DOM object can be obtained on the script thread
//! from a `Trusted<T>` via the `root` method.
//!
//! The implementation of `Trusted<T>` is as follows:
//! The `Trusted<T>` object contains an atomic reference counted pointer to the Rust DOM object.
//! A hashtable resides in the script thread, keyed on the pointer.
//! The values in this hashtable are weak reference counts. When a `Trusted<T>` object is
//! created or cloned, the reference count is increased. When a `Trusted<T>` is dropped, the count
//! decreases. If the count hits zero, the weak reference is emptied, and is removed from
//! its hash table during the next GC. During GC, the entries of the hash table are counted
//! as JS roots.

use core::nonzero::NonZero;
use dom::bindings::conversions::ToJSValConvertible;
use dom::bindings::error::Error;
use dom::bindings::js::Root;
use dom::bindings::reflector::{DomObject, Reflector};
use dom::bindings::trace::trace_reflector;
use dom::promise::Promise;
use js::jsapi::JSAutoCompartment;
use js::jsapi::JSTracer;
use libc;
use std::cell::RefCell;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::hash::Hash;
use std::marker::PhantomData;
use std::os;
use std::rc::Rc;
use std::sync::{Arc, Weak};
use task::Task;

#[allow(missing_docs)]  // FIXME
mod dummy {  // Attributes don’t apply through the macro.
    use std::cell::RefCell;
    use std::rc::Rc;
    use super::LiveDOMReferences;
    thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> =
            Rc::new(RefCell::new(None)));
}
pub use self::dummy::LIVE_REFERENCES;

/// A pointer to a Rust DOM object that needs to be destroyed.
pub struct TrustedReference(*const libc::c_void);
unsafe impl Send for TrustedReference {}

impl TrustedReference {
    fn new<T: DomObject>(ptr: *const T) -> TrustedReference {
        TrustedReference(ptr as *const libc::c_void)
    }
}

/// A safe wrapper around a DOM Promise object that can be shared among threads for use
/// in asynchronous operations. The underlying DOM object is guaranteed to live at least
/// as long as the last outstanding `TrustedPromise` instance. These values cannot be cloned,
/// only created from existing Rc<Promise> values.
pub struct TrustedPromise {
    dom_object: *const Promise,
    owner_thread: *const libc::c_void,
}

unsafe impl Send for TrustedPromise {}

impl TrustedPromise {
    /// Create a new `TrustedPromise` instance from an existing DOM object. The object will
    /// be prevented from being GCed for the duration of the resulting `TrustedPromise` object's
    /// lifetime.
    #[allow(unrooted_must_root)]
    pub fn new(promise: Rc<Promise>) -> TrustedPromise {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let ptr = &*promise as *const Promise;
            live_references.addref_promise(promise);
            TrustedPromise {
                dom_object: ptr,
                owner_thread: (&*live_references) as *const _ as *const libc::c_void,
            }
        })
    }

    /// Obtain a usable DOM Promise from a pinned `TrustedPromise` value. Fails if used on
    /// a different thread than the original value from which this `TrustedPromise` was
    /// obtained.
    #[allow(unrooted_must_root)]
    pub fn root(self) -> Rc<Promise> {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            assert!(self.owner_thread == (&*live_references) as *const _ as *const libc::c_void);
            // Borrow-check error requires the redundant `let promise = ...; promise` here.
            let promise = match live_references.promise_table.borrow_mut().entry(self.dom_object) {
                Occupied(mut entry) => {
                    let promise = {
                        let promises = entry.get_mut();
                        promises.pop().expect("rooted promise list unexpectedly empty")
                    };
                    if entry.get().is_empty() {
                        entry.remove();
                    }
                    promise
                }
                Vacant(_) => unreachable!(),
            };
            promise
        })
    }

    /// A task which will reject the promise.
    #[allow(unrooted_must_root)]
    pub fn reject_task(self, error: Error) -> impl Send + Task {
        let this = self;
        task!(reject_promise: move || {
            debug!("Rejecting promise.");
            let this = this.root();
            let cx = this.global().get_cx();
            let _ac = JSAutoCompartment::new(cx, this.reflector().get_jsobject().get());
            this.reject_error(cx, error);
        })
    }

    /// A task which will resolve the promise.
    #[allow(unrooted_must_root)]
    pub fn resolve_task<T>(self, value: T) -> impl Send + Task
    where
        T: ToJSValConvertible + Send,
    {
        let this = self;
        task!(resolve_promise: move || {
            debug!("Resolving promise.");
            let this = this.root();
            let cx = this.global().get_cx();
            let _ac = JSAutoCompartment::new(cx, this.reflector().get_jsobject().get());
            this.resolve_native(cx, &value);
        })
    }
}

/// A safe wrapper around a raw pointer to a DOM object that can be
/// shared among threads for use in asynchronous operations. The underlying
/// DOM object is guaranteed to live at least as long as the last outstanding
/// `Trusted<T>` instance.
#[allow_unrooted_interior]
pub struct Trusted<T: DomObject> {
    /// A pointer to the Rust DOM object of type T, but void to allow
    /// sending `Trusted<T>` between threads, regardless of T's sendability.
    refcount: Arc<TrustedReference>,
    owner_thread: *const libc::c_void,
    phantom: PhantomData<T>,
}

unsafe impl<T: DomObject> Send for Trusted<T> {}

impl<T: DomObject> Trusted<T> {
    /// Create a new `Trusted<T>` instance from an existing DOM pointer. The DOM object will
    /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's
    /// lifetime.
    pub fn new(ptr: &T) -> Trusted<T> {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let refcount = live_references.addref(&*ptr as *const T);
            Trusted {
                refcount: refcount,
                owner_thread: (&*live_references) as *const _ as *const libc::c_void,
                phantom: PhantomData,
            }
        })
    }

    /// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on
    /// a different thread than the original value from which this `Trusted<T>` was
    /// obtained.
    pub fn root(&self) -> Root<T> {
        assert!(LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            self.owner_thread == (&*live_references) as *const _ as *const libc::c_void
        }));
        unsafe {
            Root::new(NonZero::new_unchecked(self.refcount.0 as *const T))
        }
    }
}

impl<T: DomObject> Clone for Trusted<T> {
    fn clone(&self) -> Trusted<T> {
        Trusted {
            refcount: self.refcount.clone(),
            owner_thread: self.owner_thread,
            phantom: PhantomData,
        }
    }
}

/// The set of live, pinned DOM objects that are currently prevented
/// from being garbage collected due to outstanding references.
#[allow(unrooted_must_root)]
pub struct LiveDOMReferences {
    // keyed on pointer to Rust DOM object
    reflectable_table: RefCell<HashMap<*const libc::c_void, Weak<TrustedReference>>>,
    promise_table: RefCell<HashMap<*const Promise, Vec<Rc<Promise>>>>,
}

impl LiveDOMReferences {
    /// Set up the thread-local data required for storing the outstanding DOM references.
    pub fn initialize() {
        LIVE_REFERENCES.with(|ref r| {
            *r.borrow_mut() = Some(LiveDOMReferences {
                reflectable_table: RefCell::new(HashMap::new()),
                promise_table: RefCell::new(HashMap::new()),
            })
        });
    }

    #[allow(unrooted_must_root)]
    fn addref_promise(&self, promise: Rc<Promise>) {
        let mut table = self.promise_table.borrow_mut();
        table.entry(&*promise).or_insert(vec![]).push(promise)
    }

    fn addref<T: DomObject>(&self, ptr: *const T) -> Arc<TrustedReference> {
        let mut table = self.reflectable_table.borrow_mut();
        let capacity = table.capacity();
        let len = table.len();
        if (0 < capacity) && (capacity <= len) {
            info!("growing refcounted references by {}", len);
            remove_nulls(&mut table);
            table.reserve(len);
        }
        match table.entry(ptr as *const libc::c_void) {
            Occupied(mut entry) => match entry.get().upgrade() {
                Some(refcount) => refcount,
                None => {
                    let refcount = Arc::new(TrustedReference::new(ptr));
                    entry.insert(Arc::downgrade(&refcount));
                    refcount
                },
            },
            Vacant(entry) => {
                let refcount = Arc::new(TrustedReference::new(ptr));
                entry.insert(Arc::downgrade(&refcount));
                refcount
            }
        }
    }
}

/// Remove null entries from the live references table
fn remove_nulls<K: Eq + Hash + Clone, V> (table: &mut HashMap<K, Weak<V>>) {
    let to_remove: Vec<K> =
        table.iter()
        .filter(|&(_, value)| Weak::upgrade(value).is_none())
        .map(|(key, _)| key.clone())
        .collect();
    info!("removing {} refcounted references", to_remove.len());
    for key in to_remove {
        table.remove(&key);
    }
}

/// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES
#[allow(unrooted_must_root)]
pub unsafe extern "C" fn trace_refcounted_objects(tracer: *mut JSTracer,
                                                  _data: *mut os::raw::c_void) {
    info!("tracing live refcounted references");
    LIVE_REFERENCES.with(|ref r| {
        let r = r.borrow();
        let live_references = r.as_ref().unwrap();
        {
            let mut table = live_references.reflectable_table.borrow_mut();
            remove_nulls(&mut table);
            for obj in table.keys() {
                let reflectable = &*(*obj as *const Reflector);
                trace_reflector(tracer, "refcounted", reflectable);
            }
        }

        {
            let table = live_references.promise_table.borrow_mut();
            for promise in table.keys() {
                trace_reflector(tracer, "refcounted", (**promise).reflector());
            }
        }
    });
}
<|endoftext|>