<commit_before>use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use std::thread;

use futures::{Future, Stream};
use futures::sync::{mpsc, oneshot};

use request::{self, Request, RequestBuilder};
use response::{self, Response};
use {async_impl, Certificate, Identity, Method, IntoUrl, Proxy, RedirectPolicy, wait};

/// A `Client` to make Requests with.
///
/// The Client has various configuration values to tweak, but the defaults
/// are set to what is usually the most commonly desired value.
///
/// The `Client` holds a connection pool internally, so it is advised that
/// you create one and reuse it.
///
/// # Examples
///
/// ```rust
/// # use reqwest::{Error, Client};
/// #
/// # fn run() -> Result<(), Error> {
/// let client = Client::new()?;
/// let resp = client.get("http://httpbin.org/")?.send()?;
/// #   drop(resp);
/// #   Ok(())
/// # }
///
/// ```
#[derive(Clone)]
pub struct Client {
    inner: ClientHandle,
}

/// A `ClientBuilder` can be used to create a `Client` with  custom configuration.
///
/// # Example
///
/// ```
/// # fn run() -> Result<(), reqwest::Error> {
/// use std::time::Duration;
///
/// let client = reqwest::Client::builder()?
///     .gzip(true)
///     .timeout(Duration::from_secs(10))
///     .build()?;
/// # Ok(())
/// # }
/// ```
pub struct ClientBuilder {
    inner: async_impl::ClientBuilder,
    timeout: Option<Duration>,
}

impl ClientBuilder {
    /// Constructs a new `ClientBuilder`
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be created.
    pub fn new() -> ::Result<ClientBuilder> {
        async_impl::ClientBuilder::new().map(|builder| ClientBuilder {
            inner: builder,
            timeout: None,
        })
    }

    /// Returns a `Client` that uses this `ClientBuilder` configuration.
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be initialized.
    ///
    /// # Panics
    ///
    /// This method consumes the internal state of the builder.
    /// Trying to use this builder again after calling `build` will panic.
    pub fn build(&mut self) -> ::Result<Client> {
        ClientHandle::new(self).map(|handle| Client {
            inner: handle,
        })
    }

    /// Add a custom root certificate.
    ///
    /// This can be used to connect to a server that has a self-signed
    /// certificate for example.
    ///
    /// # Example
    /// ```
    /// # use std::fs::File;
    /// # use std::io::Read;
    /// # fn build_client() -> Result<(), Box<std::error::Error>> {
    /// // read a local binary DER encoded certificate
    /// let mut buf = Vec::new();
    /// File::open("my-cert.der")?.read_to_end(&mut buf)?;
    ///
    /// // create a certificate
    /// let cert = reqwest::Certificate::from_der(&buf)?;
    ///
    /// // get a client builder
    /// let client = reqwest::ClientBuilder::new()?
    ///     .add_root_certificate(cert)?
    ///     .build()?;
    /// # drop(client);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails if adding root certificate was unsuccessful.
    pub fn add_root_certificate(&mut self, cert: Certificate) -> ::Result<&mut ClientBuilder> {
        self.inner.add_root_certificate(cert)?;
        Ok(self)
    }

    /// Sets the identity to be used for client certificate authentication.
    ///
    /// This can be used in mutual authentication scenarios to identify to a server
    /// with a PKCS#12 archive containing a certificate and private key for example.
    ///
    /// # Example
    /// ```
    /// # use std::fs::File;
    /// # use std::io::Read;
    /// # fn build_client() -> Result<(), Box<std::error::Error>> {
    /// // read a local PKCS12 bundle
    /// let mut buf = Vec::new();
    /// File::open("my-ident.pfx")?.read_to_end(&mut buf)?;
    ///
    /// // create an Identity from the PKCS#12 archive
    /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
    ///
    /// // get a client builder
    /// let client = reqwest::ClientBuilder::new()?
    ///     .identity(pkcs12)?
    ///     .build()?;
    /// # drop(client);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails if adding client identity was unsuccessful.
    pub fn identity(&mut self, identity: Identity) -> ::Result<&mut ClientBuilder> {
        self.inner.identity(identity)?;
        Ok(self)
    }

    /// Disable hostname verification.
    ///
    /// # Warning
    ///
    /// You should think very carefully before you use this method. If
    /// hostname verification is not used, any valid certificate for any
    /// site will be trusted for use from any other. This introduces a
    /// significant vulnerability to man-in-the-middle attacks.
    #[inline]
    pub fn danger_disable_hostname_verification(&mut self) -> &mut ClientBuilder {
        self.inner.danger_disable_hostname_verification();
        self
    }

    /// Enable hostname verification.
    ///
    /// Default is enabled.
    #[inline]
    pub fn enable_hostname_verification(&mut self) -> &mut ClientBuilder {
        self.inner.enable_hostname_verification();
        self
    }

    /// Enable auto gzip decompression by checking the ContentEncoding response header.
    ///
    /// Default is enabled.
    #[inline]
    pub fn gzip(&mut self, enable: bool) -> &mut ClientBuilder {
        self.inner.gzip(enable);
        self
    }

    /// Add a `Proxy` to the list of proxies the `Client` will use.
    #[inline]
    pub fn proxy(&mut self, proxy: Proxy) -> &mut ClientBuilder {
        self.inner.proxy(proxy);
        self
    }

    /// Set a `RedirectPolicy` for this client.
    ///
    /// Default will follow redirects up to a maximum of 10.
    #[inline]
    pub fn redirect(&mut self, policy: RedirectPolicy) -> &mut ClientBuilder {
        self.inner.redirect(policy);
        self
    }

    /// Enable or disable automatic setting of the `Referer` header.
    ///
    /// Default is `true`.
    #[inline]
    pub fn referer(&mut self, enable: bool) -> &mut ClientBuilder {
        self.inner.referer(enable);
        self
    }

    /// Set a timeout for connect, read and write operations of a `Client`.
    #[inline]
    pub fn timeout(&mut self, timeout: Duration) -> &mut ClientBuilder {
        self.timeout = Some(timeout);
        self
    }
}

impl Client {
    /// Constructs a new `Client`.
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be created or initialized.
    #[inline]
    pub fn new() -> ::Result<Client> {
        ClientBuilder::new()?.build()
    }

    /// Creates a `ClientBuilder` to configure a `Client`.
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be created.
    #[inline]
    pub fn builder() -> ::Result<ClientBuilder> {
        ClientBuilder::new()
    }

    /// Convenience method to make a `GET` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn get<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Get, url)
    }

    /// Convenience method to make a `POST` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn post<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Post, url)
    }

    /// Convenience method to make a `PUT` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn put<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Put, url)
    }

    /// Convenience method to make a `PATCH` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn patch<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Patch, url)
    }

    /// Convenience method to make a `DELETE` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn delete<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Delete, url)
    }

    /// Convenience method to make a `HEAD` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn head<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Head, url)
    }

    /// Start building a `Request` with the `Method` and `Url`.
    ///
    /// Returns a `RequestBuilder`, which will allow setting headers and
    /// request body before sending.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> ::Result<RequestBuilder> {
        let url = try_!(url.into_url());
        Ok(request::builder(self.clone(), Request::new(method, url)))
    }

    /// Executes a `Request`.
    ///
    /// A `Request` can be built manually with `Request::new()` or obtained
    /// from a RequestBuilder with `RequestBuilder::build()`.
    ///
    /// You should prefer to use the `RequestBuilder` and
    /// `RequestBuilder::send()`.
    ///
    /// # Errors
    ///
    /// This method fails if there was an error while sending request,
    /// redirect loop was detected or redirect limit was exhausted.
    pub fn execute(&self, request: Request) -> ::Result<Response> {
        self.inner.execute_request(request)
    }
}

impl fmt::Debug for Client {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Client")
            //.field("gzip", &self.inner.gzip)
            //.field("redirect_policy", &self.inner.redirect_policy)
            //.field("referer", &self.inner.referer)
            .finish()
    }
}

impl fmt::Debug for ClientBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ClientBuilder")
            .finish()
    }
}

#[derive(Clone)]
struct ClientHandle {
    timeout: Option<Duration>,
    inner: Arc<InnerClientHandle>
}

type ThreadSender = mpsc::UnboundedSender<(async_impl::Request, oneshot::Sender<::Result<async_impl::Response>>)>;

struct InnerClientHandle {
    tx: Option<ThreadSender>,
    thread: Option<thread::JoinHandle<()>>
}

impl Drop for InnerClientHandle {
    fn drop(&mut self) {
        self.tx.take();
        self.thread.take().map(|h| h.join());
    }
}

impl ClientHandle {
    fn new(builder: &mut ClientBuilder) -> ::Result<ClientHandle> {

        let timeout = builder.timeout;
        let mut builder = async_impl::client::take_builder(&mut builder.inner);
        let (tx, rx) = mpsc::unbounded();
        let (spawn_tx, spawn_rx) = oneshot::channel::<::Result<()>>();
        let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-core".into()).spawn(move || {
            use tokio_core::reactor::Core;

            let built = (|| {
                let core = try_!(Core::new());
                let handle = core.handle();
                let client = builder.build(&handle)?;
                Ok((core, handle, client))
            })();

            let (mut core, handle, client) = match built {
                Ok((a, b, c)) => {
                    if let Err(_) = spawn_tx.send(Ok(())) {
                        return;
                    }
                    (a, b, c)
                },
                Err(e) => {
                    let _ = spawn_tx.send(Err(e));
                    return;
                }
            };

            let work = rx.for_each(|(req, tx)| {
                let tx: oneshot::Sender<::Result<async_impl::Response>> = tx;
                let task = client.execute(req)
                    .then(move |x| tx.send(x).map_err(|_| ()));
                handle.spawn(task);
                Ok(())
            });

            // work is Future<(), ()>, and our closure will never return Err
            let _ = core.run(work);
        }));

        wait::timeout(spawn_rx, timeout).expect("core thread cancelled")?;

        let inner_handle = Arc::new(InnerClientHandle {
            tx: Some(tx),
            thread: Some(handle)
        });

        Ok(ClientHandle {
            timeout: timeout,
            inner: inner_handle,
        })
    }

    fn execute_request(&self, req: Request) -> ::Result<Response> {
        let (tx, rx) = oneshot::channel();
        let (req, body) = request::async(req);
        let url = req.url().clone();
        self.inner.tx
            .as_ref()
            .expect("core thread exited early")
            .unbounded_send((req, tx))
            .expect("core thread panicked");

        if let Some(body) = body {
            try_!(body.send(), &url);
        }

        let res = match wait::timeout(rx, self.timeout) {
            Ok(res) => res,
            Err(wait::Waited::TimedOut) => return Err(::error::timedout(Some(url))),
            Err(wait::Waited::Err(_canceled)) => {
                // The only possible reason there would be a Cancelled error
                // is if the thread running the Core panicked. We could return
                // an Err here, like a BrokenPipe, but the Client is not
                // recoverable. Additionally, the panic in the other thread
                // is not normal, and should likely be propagated.
                panic!("core thread panicked");
            }
        };
        res.map(|res| {
            response::new(res, self.timeout, KeepCoreThreadAlive(self.inner.clone()))
        })
    }
}

// pub(crate)

pub struct KeepCoreThreadAlive(Arc<InnerClientHandle>);
<commit_msg>change default Client timeout to 30 seconds<commit_after>use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use std::thread;

use futures::{Future, Stream};
use futures::sync::{mpsc, oneshot};

use request::{self, Request, RequestBuilder};
use response::{self, Response};
use {async_impl, Certificate, Identity, Method, IntoUrl, Proxy, RedirectPolicy, wait};

/// A `Client` to make Requests with.
///
/// The Client has various configuration values to tweak, but the defaults
/// are set to what is usually the most commonly desired value.
///
/// The `Client` holds a connection pool internally, so it is advised that
/// you create one and reuse it.
///
/// # Examples
///
/// ```rust
/// # use reqwest::{Error, Client};
/// #
/// # fn run() -> Result<(), Error> {
/// let client = Client::new()?;
/// let resp = client.get("http://httpbin.org/")?.send()?;
/// #   drop(resp);
/// #   Ok(())
/// # }
///
/// ```
#[derive(Clone)]
pub struct Client {
    inner: ClientHandle,
}

/// A `ClientBuilder` can be used to create a `Client` with  custom configuration.
///
/// # Example
///
/// ```
/// # fn run() -> Result<(), reqwest::Error> {
/// use std::time::Duration;
///
/// let client = reqwest::Client::builder()?
///     .gzip(true)
///     .timeout(Duration::from_secs(10))
///     .build()?;
/// # Ok(())
/// # }
/// ```
pub struct ClientBuilder {
    inner: async_impl::ClientBuilder,
    timeout: Timeout,
}

impl ClientBuilder {
    /// Constructs a new `ClientBuilder`
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be created.
    pub fn new() -> ::Result<ClientBuilder> {
        async_impl::ClientBuilder::new().map(|builder| ClientBuilder {
            inner: builder,
            timeout: Timeout::default(),
        })
    }

    /// Returns a `Client` that uses this `ClientBuilder` configuration.
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be initialized.
    ///
    /// # Panics
    ///
    /// This method consumes the internal state of the builder.
    /// Trying to use this builder again after calling `build` will panic.
    pub fn build(&mut self) -> ::Result<Client> {
        ClientHandle::new(self).map(|handle| Client {
            inner: handle,
        })
    }

    /// Add a custom root certificate.
    ///
    /// This can be used to connect to a server that has a self-signed
    /// certificate for example.
    ///
    /// # Example
    /// ```
    /// # use std::fs::File;
    /// # use std::io::Read;
    /// # fn build_client() -> Result<(), Box<std::error::Error>> {
    /// // read a local binary DER encoded certificate
    /// let mut buf = Vec::new();
    /// File::open("my-cert.der")?.read_to_end(&mut buf)?;
    ///
    /// // create a certificate
    /// let cert = reqwest::Certificate::from_der(&buf)?;
    ///
    /// // get a client builder
    /// let client = reqwest::ClientBuilder::new()?
    ///     .add_root_certificate(cert)?
    ///     .build()?;
    /// # drop(client);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails if adding root certificate was unsuccessful.
    pub fn add_root_certificate(&mut self, cert: Certificate) -> ::Result<&mut ClientBuilder> {
        self.inner.add_root_certificate(cert)?;
        Ok(self)
    }

    /// Sets the identity to be used for client certificate authentication.
    ///
    /// This can be used in mutual authentication scenarios to identify to a server
    /// with a PKCS#12 archive containing a certificate and private key for example.
    ///
    /// # Example
    /// ```
    /// # use std::fs::File;
    /// # use std::io::Read;
    /// # fn build_client() -> Result<(), Box<std::error::Error>> {
    /// // read a local PKCS12 bundle
    /// let mut buf = Vec::new();
    /// File::open("my-ident.pfx")?.read_to_end(&mut buf)?;
    ///
    /// // create an Identity from the PKCS#12 archive
    /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
    ///
    /// // get a client builder
    /// let client = reqwest::ClientBuilder::new()?
    ///     .identity(pkcs12)?
    ///     .build()?;
    /// # drop(client);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails if adding client identity was unsuccessful.
    pub fn identity(&mut self, identity: Identity) -> ::Result<&mut ClientBuilder> {
        self.inner.identity(identity)?;
        Ok(self)
    }

    /// Disable hostname verification.
    ///
    /// # Warning
    ///
    /// You should think very carefully before you use this method. If
    /// hostname verification is not used, any valid certificate for any
    /// site will be trusted for use from any other. This introduces a
    /// significant vulnerability to man-in-the-middle attacks.
    #[inline]
    pub fn danger_disable_hostname_verification(&mut self) -> &mut ClientBuilder {
        self.inner.danger_disable_hostname_verification();
        self
    }

    /// Enable hostname verification.
    ///
    /// Default is enabled.
    #[inline]
    pub fn enable_hostname_verification(&mut self) -> &mut ClientBuilder {
        self.inner.enable_hostname_verification();
        self
    }

    /// Enable auto gzip decompression by checking the ContentEncoding response header.
    ///
    /// Default is enabled.
    #[inline]
    pub fn gzip(&mut self, enable: bool) -> &mut ClientBuilder {
        self.inner.gzip(enable);
        self
    }

    /// Add a `Proxy` to the list of proxies the `Client` will use.
    #[inline]
    pub fn proxy(&mut self, proxy: Proxy) -> &mut ClientBuilder {
        self.inner.proxy(proxy);
        self
    }

    /// Set a `RedirectPolicy` for this client.
    ///
    /// Default will follow redirects up to a maximum of 10.
    #[inline]
    pub fn redirect(&mut self, policy: RedirectPolicy) -> &mut ClientBuilder {
        self.inner.redirect(policy);
        self
    }

    /// Enable or disable automatic setting of the `Referer` header.
    ///
    /// Default is `true`.
    #[inline]
    pub fn referer(&mut self, enable: bool) -> &mut ClientBuilder {
        self.inner.referer(enable);
        self
    }

    /// Set a timeout for connect, read and write operations of a `Client`.
    ///
    /// Default is 30 seconds.
    ///
    /// Pass `None` to disable timeout.
    #[inline]
    pub fn timeout<T>(&mut self, timeout: T) -> &mut ClientBuilder
    where T: Into<Option<Duration>>,
    {
        self.timeout = Timeout(timeout.into());
        self
    }
}

impl Client {
    /// Constructs a new `Client`.
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be created or initialized.
    #[inline]
    pub fn new() -> ::Result<Client> {
        ClientBuilder::new()?.build()
    }

    /// Creates a `ClientBuilder` to configure a `Client`.
    ///
    /// # Errors
    ///
    /// This method fails if native TLS backend cannot be created.
    #[inline]
    pub fn builder() -> ::Result<ClientBuilder> {
        ClientBuilder::new()
    }

    /// Convenience method to make a `GET` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn get<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Get, url)
    }

    /// Convenience method to make a `POST` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn post<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Post, url)
    }

    /// Convenience method to make a `PUT` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn put<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Put, url)
    }

    /// Convenience method to make a `PATCH` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn patch<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Patch, url)
    }

    /// Convenience method to make a `DELETE` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn delete<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Delete, url)
    }

    /// Convenience method to make a `HEAD` request to a URL.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn head<U: IntoUrl>(&self, url: U) -> ::Result<RequestBuilder> {
        self.request(Method::Head, url)
    }

    /// Start building a `Request` with the `Method` and `Url`.
    ///
    /// Returns a `RequestBuilder`, which will allow setting headers and
    /// request body before sending.
    ///
    /// # Errors
    ///
    /// This method fails whenever supplied `Url` cannot be parsed.
    pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> ::Result<RequestBuilder> {
        let url = try_!(url.into_url());
        Ok(request::builder(self.clone(), Request::new(method, url)))
    }

    /// Executes a `Request`.
    ///
    /// A `Request` can be built manually with `Request::new()` or obtained
    /// from a RequestBuilder with `RequestBuilder::build()`.
    ///
    /// You should prefer to use the `RequestBuilder` and
    /// `RequestBuilder::send()`.
    ///
    /// # Errors
    ///
    /// This method fails if there was an error while sending request,
    /// redirect loop was detected or redirect limit was exhausted.
    pub fn execute(&self, request: Request) -> ::Result<Response> {
        self.inner.execute_request(request)
    }
}

impl fmt::Debug for Client {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Client")
            //.field("gzip", &self.inner.gzip)
            //.field("redirect_policy", &self.inner.redirect_policy)
            //.field("referer", &self.inner.referer)
            .finish()
    }
}

impl fmt::Debug for ClientBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ClientBuilder")
            .finish()
    }
}

#[derive(Clone)]
struct ClientHandle {
    timeout: Timeout,
    inner: Arc<InnerClientHandle>
}

type ThreadSender = mpsc::UnboundedSender<(async_impl::Request, oneshot::Sender<::Result<async_impl::Response>>)>;

struct InnerClientHandle {
    tx: Option<ThreadSender>,
    thread: Option<thread::JoinHandle<()>>
}

impl Drop for InnerClientHandle {
    fn drop(&mut self) {
        self.tx.take();
        self.thread.take().map(|h| h.join());
    }
}

impl ClientHandle {
    fn new(builder: &mut ClientBuilder) -> ::Result<ClientHandle> {

        let timeout = builder.timeout;
        let mut builder = async_impl::client::take_builder(&mut builder.inner);
        let (tx, rx) = mpsc::unbounded();
        let (spawn_tx, spawn_rx) = oneshot::channel::<::Result<()>>();
        let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-core".into()).spawn(move || {
            use tokio_core::reactor::Core;

            let built = (|| {
                let core = try_!(Core::new());
                let handle = core.handle();
                let client = builder.build(&handle)?;
                Ok((core, handle, client))
            })();

            let (mut core, handle, client) = match built {
                Ok((a, b, c)) => {
                    if let Err(_) = spawn_tx.send(Ok(())) {
                        return;
                    }
                    (a, b, c)
                },
                Err(e) => {
                    let _ = spawn_tx.send(Err(e));
                    return;
                }
            };

            let work = rx.for_each(|(req, tx)| {
                let tx: oneshot::Sender<::Result<async_impl::Response>> = tx;
                let task = client.execute(req)
                    .then(move |x| tx.send(x).map_err(|_| ()));
                handle.spawn(task);
                Ok(())
            });

            // work is Future<(), ()>, and our closure will never return Err
            let _ = core.run(work);
        }));

        wait::timeout(spawn_rx, timeout.0).expect("core thread cancelled")?;

        let inner_handle = Arc::new(InnerClientHandle {
            tx: Some(tx),
            thread: Some(handle)
        });

        Ok(ClientHandle {
            timeout: timeout,
            inner: inner_handle,
        })
    }

    fn execute_request(&self, req: Request) -> ::Result<Response> {
        let (tx, rx) = oneshot::channel();
        let (req, body) = request::async(req);
        let url = req.url().clone();
        self.inner.tx
            .as_ref()
            .expect("core thread exited early")
            .unbounded_send((req, tx))
            .expect("core thread panicked");

        if let Some(body) = body {
            try_!(body.send(), &url);
        }

        let res = match wait::timeout(rx, self.timeout.0) {
            Ok(res) => res,
            Err(wait::Waited::TimedOut) => return Err(::error::timedout(Some(url))),
            Err(wait::Waited::Err(_canceled)) => {
                // The only possible reason there would be a Cancelled error
                // is if the thread running the Core panicked. We could return
                // an Err here, like a BrokenPipe, but the Client is not
                // recoverable. Additionally, the panic in the other thread
                // is not normal, and should likely be propagated.
                panic!("core thread panicked");
            }
        };
        res.map(|res| {
            response::new(res, self.timeout.0, KeepCoreThreadAlive(self.inner.clone()))
        })
    }
}

#[derive(Clone, Copy)]
struct Timeout(Option<Duration>);

impl Default for Timeout {
    #[inline]
    fn default() -> Timeout {
        // default mentioned in ClientBuilder::timeout() doc comment
        Timeout(Some(Duration::from_secs(30)))
    }
}

// pub(crate)

pub struct KeepCoreThreadAlive(Arc<InnerClientHandle>);
<|endoftext|>


<commit_before>// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]

#![feature(repr_simd, platform_intrinsics, min_const_generics)]
#![allow(non_camel_case_types)]

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct M(pub f32, pub f32, pub f32, pub f32);

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct S<const N: usize>([f32; N]);

extern "platform-intrinsic" {
    fn simd_extract<T, U>(x: T, idx: u32) -> U;
    fn simd_insert<T, U>(x: T, idx: u32, b: U) -> T;
}

// CHECK-LABEL: @extract_m
#[no_mangle]
pub unsafe fn extract_m(v: M, i: u32) -> f32  {
    // CHECK: extractelement <4 x float> %_3, i32 %i
    simd_extract(v, i)
}

// CHECK-LABEL: @extract_s
#[no_mangle]
pub unsafe fn extract_s(v: S<4>, i: u32) -> f32  {
    // CHECK: extractelement <4 x float> %_3, i32 %i
    simd_extract(v, i)
}

// CHECK-LABEL: @insert_m
#[no_mangle]
pub unsafe fn insert_m(v: M, i: u32, j: f32) -> M  {
    // CHECK: insertelement <4 x float> %_4, float %j, i32 %i
    simd_insert(v, i, j)
}

// CHECK-LABEL: @insert_s
#[no_mangle]
pub unsafe fn insert_s(v: S<4>, i: u32, j: f32) -> S<4>  {
    // CHECK: insertelement <4 x float> %_4, float %j, i32 %i
    simd_insert(v, i, j)
}
<commit_msg>args may be passed by value<commit_after>// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]

#![feature(repr_simd, platform_intrinsics, min_const_generics)]
#![allow(non_camel_case_types)]

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct M(pub f32, pub f32, pub f32, pub f32);

#[repr(simd)]
#[derive(Copy, Clone)]
pub struct S<const N: usize>([f32; N]);

extern "platform-intrinsic" {
    fn simd_extract<T, U>(x: T, idx: u32) -> U;
    fn simd_insert<T, U>(x: T, idx: u32, b: U) -> T;
}

// CHECK-LABEL: @extract_m
#[no_mangle]
pub unsafe fn extract_m(v: M, i: u32) -> f32  {
    // CHECK: extractelement <4 x float> %{{v|_3}}, i32 %i
    simd_extract(v, i)
}

// CHECK-LABEL: @extract_s
#[no_mangle]
pub unsafe fn extract_s(v: S<4>, i: u32) -> f32  {
    // CHECK: extractelement <4 x float> %{{v|_3}}, i32 %i
    simd_extract(v, i)
}

// CHECK-LABEL: @insert_m
#[no_mangle]
pub unsafe fn insert_m(v: M, i: u32, j: f32) -> M  {
    // CHECK: insertelement <4 x float> %{{v|_4}}, float %j, i32 %i
    simd_insert(v, i, j)
}

// CHECK-LABEL: @insert_s
#[no_mangle]
pub unsafe fn insert_s(v: S<4>, i: u32, j: f32) -> S<4>  {
    // CHECK: insertelement <4 x float> %{{v|_4}}, float %j, i32 %i
    simd_insert(v, i, j)
}
<|endoftext|>


<commit_before>use gfx;
use time;
use multimap::MultiMap;

use std::collections::BTreeMap as TreeMap;

use render::gfx::{Renderer, construct_opengl_renderer};

use {PuckResult, FileResources, RenderTick};
use puck_core::app::{SimSettings, Event};
use puck_core::Tick;
use super::{RenderedApp, RenderSettings};
use std::collections::Bound::Included;

pub struct ReneredAppRunner<RA, R, C, F, D> where RA : RenderedApp,
                                                  R : gfx::Resources,
                                                  C : gfx::CommandBuffer<R>,
                                                  F : gfx::Factory<R>,
                                                  D : gfx::Device {
    tick: u64,
    entities: TreeMap<RA::Id, RA::Entity>,
    renderer: Renderer<R, C, F, D>,
    render_state: RA::RenderState,
}

pub const NANOSECONDS_IN_A_SECOND : u64 = 1_000_000_000u64;

pub fn run<RA>(file_resources:FileResources, sim_settings: SimSettings, render_settings:RenderSettings, render_state: RA::RenderState) -> PuckResult<()> where RA : RenderedApp {
    let mut renderer = construct_opengl_renderer(file_resources, render_settings.dimensions, render_settings.vsync, &render_settings.title)?;

    // start file watcher
    // start sound worker

    let mut rs = render_state;

    let start_time = time::precise_time_ns();
    let mut last_time = start_time;
    let mut simulation_accu_ns = 0_u64;

    let mut last_entities : TreeMap<RA::Id, RA::Entity> = TreeMap::new();
    let mut entities : TreeMap<RA::Id, RA::Entity> = TreeMap::new();

    let mut tick = 0_u64;

    let per_tick_ns = NANOSECONDS_IN_A_SECOND / sim_settings.tick_rate;

    let mut to_route : Vec<_> = Vec::new();
    let mut render_events : Vec<_> = Vec::new();

    let mut running = true;

    while running {
        // check file watcher shit

        let (dimensions, input) = renderer.begin_frame(false, false);

        let time = time::precise_time_ns();

        let since_start_ns = time - start_time;
        let time_delta_ns = time - last_time;

        simulation_accu_ns += time_delta_ns;

        let mut input_events = RA::handle_input(&input, &dimensions, &entities);
        to_route.append(&mut input_events);

        // ROUTE THE EVENTS

        while simulation_accu_ns > per_tick_ns {
            // simulate
            let simulate_tick = Tick {
                n: tick,
                tick_duration: (per_tick_ns as f64) / (NANOSECONDS_IN_A_SECOND as f64),
                tick_rate: sim_settings.tick_rate, // per second
            };

            last_entities = entities;

            let mut entity_events = MultiMap::new();

            for ev in to_route {
                match ev  {
                    Event::Shutdown => running = false,
                    Event::SpawnEvent(id, entity) => {
                        last_entities.insert(id, entity);
                        ()
                    },
                    Event::Delete(id) => {
                        last_entities.remove(&id);
                        ()
                    },
                    Event::DeleteRange(from, to) => {
                        let mut to_delete = Vec::new();
                        for (k, _) in last_entities.range((Included(&from), Included(&to))) {
                            to_delete.push(k.clone());
                        }
                        for k in to_delete {
                            last_entities.remove(&k);
                        }
                    },
                    Event::EntityEvent(id, entity_event) => entity_events.insert(id, entity_event),
                    Event::RenderEvent(render_event) => render_events.push(render_event),
                }
            }

            to_route = Vec::new();

            entities = last_entities.iter().map(|(id, e)| {
                let mut entity = e.clone();

                // handle events from last frame
                if let Some(evs) = entity_events.get_vec(id) {
                    for event in evs {
                        let mut out = RA::handle_entity_event(event, id, &mut entity);
                        to_route.append(&mut out);
                    }
                }

                let (self_events, mut route_events) = RA::simulate(simulate_tick, &last_entities, id, &entity);

                to_route.append(&mut route_events);

                // handle self effects immediately
                for event in &self_events {
                    let mut out = RA::handle_entity_event(event, id, &mut entity);
                    to_route.append(&mut out);
                }

                (id.clone(), entity)
            }).collect();

            tick += 1;
            simulation_accu_ns -= per_tick_ns;
        }

        // render
        let render_tick = RenderTick {
            n: tick,
            accu_alpha: (simulation_accu_ns as f64) / (per_tick_ns as f64), // percentage of a frame that has accumulated
            tick_rate: sim_settings.tick_rate, // per second
        };
        RA::render(render_tick, &entities, &mut rs, &mut renderer);

        if input.close {
            running = false;
        }
    }

    Ok(())
}<commit_msg>Runner now routes events etc.<commit_after>use gfx;
use time;
use multimap::MultiMap;

use std::collections::BTreeMap as TreeMap;

use render::gfx::{Renderer, construct_opengl_renderer};

use {PuckResult, FileResources, RenderTick};
use puck_core::app::{SimSettings, Event};
use puck_core::Tick;
use super::{RenderedApp, RenderSettings};
use std::collections::Bound::Included;

pub struct ReneredAppRunner<RA, R, C, F, D> where RA : RenderedApp,
                                                  R : gfx::Resources,
                                                  C : gfx::CommandBuffer<R>,
                                                  F : gfx::Factory<R>,
                                                  D : gfx::Device {
    tick: u64,
    entities: TreeMap<RA::Id, RA::Entity>,
    renderer: Renderer<R, C, F, D>,
    render_state: RA::RenderState,
}

pub const NANOSECONDS_IN_A_SECOND : u64 = 1_000_000_000u64;

pub fn run<RA>(file_resources:FileResources, sim_settings: SimSettings, render_settings:RenderSettings, render_state: RA::RenderState) -> PuckResult<()> where RA : RenderedApp {
    let mut renderer = construct_opengl_renderer(file_resources, render_settings.dimensions, render_settings.vsync, &render_settings.title)?;

    // start file watcher
    // start sound worker

    let mut rs = render_state;

    let start_time = time::precise_time_ns();
    let mut last_time = start_time;
    let mut simulation_accu_ns = 0_u64;

    let mut last_entities : TreeMap<RA::Id, RA::Entity> = TreeMap::new();
    let mut entities : TreeMap<RA::Id, RA::Entity> = TreeMap::new();

    let mut tick = 0_u64;

    let per_tick_ns = NANOSECONDS_IN_A_SECOND / sim_settings.tick_rate;

    let mut to_route : Vec<_> = Vec::new();
    let mut render_events : Vec<_> = Vec::new();

    let mut running = true;

    while running {
        // check file watcher shit

        let (dimensions, input) = renderer.begin_frame(false, false);

        let time = time::precise_time_ns();

        let since_start_ns = time - start_time;
        let time_delta_ns = time - last_time;

        simulation_accu_ns += time_delta_ns;

        let mut input_events = RA::handle_input(&input, &dimensions, &entities);
        to_route.append(&mut input_events);

        // ROUTE THE EVENTS

        while simulation_accu_ns > per_tick_ns {
            // simulate
            let simulate_tick = Tick {
                n: tick,
                tick_duration: (per_tick_ns as f64) / (NANOSECONDS_IN_A_SECOND as f64),
                tick_rate: sim_settings.tick_rate, // per second
            };

            last_entities = entities;

            let mut entity_events = MultiMap::new();

            for ev in to_route {
                match ev  {
                    Event::Shutdown => running = false,
                    Event::SpawnEvent(id, entity) => {
                        last_entities.insert(id, entity);
                        ()
                    },
                    Event::Delete(id) => {
                        last_entities.remove(&id);
                        ()
                    },
                    Event::DeleteRange(from, to) => {
                        let mut to_delete = Vec::new();
                        for (k, _) in last_entities.range((Included(&from), Included(&to))) {
                            to_delete.push(k.clone());
                        }
                        for k in to_delete {
                            last_entities.remove(&k);
                        }
                    },
                    Event::EntityEvent(id, entity_event) => entity_events.insert(id, entity_event),
                    Event::RenderEvent(render_event) => render_events.push(render_event),
                }
            }

            to_route = Vec::new();

            entities = last_entities.iter().map(|(id, e)| {
                let mut entity = e.clone();

                // handle events from last frame
                if let Some(evs) = entity_events.get_vec(id) {
                    for event in evs {
                        let mut out = RA::handle_entity_event(event, id, &mut entity);
                        to_route.append(&mut out);
                    }
                }

                // simulate entity
                let (self_events, mut route_events) = RA::simulate(simulate_tick, &last_entities, id, &entity);

                to_route.append(&mut route_events);

                // handle self effects immediately
                for event in &self_events {
                    let mut out = RA::handle_entity_event(event, id, &mut entity);
                    to_route.append(&mut out);
                }

                (id.clone(), entity)
            }).collect();

            tick += 1;
            simulation_accu_ns -= per_tick_ns;
        }

        // render
        let render_tick = RenderTick {
            n: tick,
            accu_alpha: (simulation_accu_ns as f64) / (per_tick_ns as f64), // percentage of a frame that has accumulated
            tick_rate: sim_settings.tick_rate, // per second
        };

        for render_event in render_events {
            RA::handle_render_event(&render_event, &mut rs);
        }
        render_events = Vec::new();
        RA::render(render_tick, &entities, &mut rs, &mut renderer);

        if input.close {
            running = false;
        }
    }

    Ok(())
}<|endoftext|>


<commit_before>//! An analysis to determine which locals require allocas and
//! which do not.

use super::FunctionCx;
use crate::traits::*;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::traversal;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Location, TerminatorKind};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_target::abi::LayoutOf;

pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
    fx: &FunctionCx<'a, 'tcx, Bx>,
) -> BitSet<mir::Local> {
    let mir = fx.mir;
    let mut analyzer = LocalAnalyzer::new(fx);

    for (bb, data) in mir.basic_blocks().iter_enumerated() {
        analyzer.visit_basic_block_data(bb, data);
    }

    for (local, decl) in mir.local_decls.iter_enumerated() {
        let ty = fx.monomorphize(decl.ty);
        debug!("local {:?} has type `{}`", local, ty);
        let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span);
        if fx.cx.is_backend_immediate(layout) {
            // These sorts of types are immediates that we can store
            // in an Value without an alloca.
        } else if fx.cx.is_backend_scalar_pair(layout) {
            // We allow pairs and uses of any of their 2 fields.
        } else {
            // These sorts of types require an alloca. Note that
            // is_llvm_immediate() may *still* be true, particularly
            // for newtypes, but we currently force some types
            // (e.g., structs) into an alloca unconditionally, just so
            // that we don't have to deal with having two pathways
            // (gep vs extractvalue etc).
            analyzer.not_ssa(local);
        }
    }

    analyzer.non_ssa_locals
}

struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
    dominators: Dominators<mir::BasicBlock>,
    non_ssa_locals: BitSet<mir::Local>,
    // The location of the first visited direct assignment to each
    // local, or an invalid location (out of bounds `block` index).
    first_assignment: IndexVec<mir::Local, Location>,
}

impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
        let invalid_location = mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
        let dominators = fx.mir.dominators();
        let mut analyzer = LocalAnalyzer {
            fx,
            dominators,
            non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
            first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls),
        };

        // Arguments get assigned to by means of the function being called
        for arg in fx.mir.args_iter() {
            analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
        }

        analyzer
    }

    fn first_assignment(&self, local: mir::Local) -> Option<Location> {
        let location = self.first_assignment[local];
        if location.block.index() < self.fx.mir.basic_blocks().len() {
            Some(location)
        } else {
            None
        }
    }

    fn not_ssa(&mut self, local: mir::Local) {
        debug!("marking {:?} as non-SSA", local);
        self.non_ssa_locals.insert(local);
    }

    fn assign(&mut self, local: mir::Local, location: Location) {
        if self.first_assignment(local).is_some() {
            self.not_ssa(local);
        } else {
            self.first_assignment[local] = location;
        }
    }

    fn process_place(
        &mut self,
        place_ref: &mir::PlaceRef<'tcx>,
        context: PlaceContext,
        location: Location,
    ) {
        let cx = self.fx.cx;

        if let Some((place_base, elem)) = place_ref.last_projection() {
            let mut base_context = if context.is_mutating_use() {
                PlaceContext::MutatingUse(MutatingUseContext::Projection)
            } else {
                PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
            };

            // Allow uses of projections that are ZSTs or from scalar fields.
            let is_consume = matches!(
                context,
                PlaceContext::NonMutatingUse(
                    NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
                )
            );
            if is_consume {
                let base_ty = place_base.ty(self.fx.mir, cx.tcx());
                let base_ty = self.fx.monomorphize(base_ty);

                // ZSTs don't require any actual memory access.
                let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(elem)).ty;
                let span = self.fx.mir.local_decls[place_ref.local].source_info.span;
                if cx.spanned_layout_of(elem_ty, span).is_zst() {
                    return;
                }

                if let mir::ProjectionElem::Field(..) = elem {
                    let layout = cx.spanned_layout_of(base_ty.ty, span);
                    if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
                        // Recurse with the same context, instead of `Projection`,
                        // potentially stopping at non-operand projections,
                        // which would trigger `not_ssa` on locals.
                        base_context = context;
                    }
                }
            }

            if let mir::ProjectionElem::Deref = elem {
                // Deref projections typically only read the pointer.
                base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
            }

            self.process_place(&place_base, base_context, location);
            // HACK(eddyb) this emulates the old `visit_projection_elem`, this
            // entire `visit_place`-like `process_place` method should be rewritten,
            // now that we have moved to the "slice of projections" representation.
            if let mir::ProjectionElem::Index(local) = elem {
                self.visit_local(
                    &local,
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                    location,
                );
            }
        } else {
            self.visit_local(&place_ref.local, context, location);
        }
    }
}

impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
    for LocalAnalyzer<'mir, 'a, 'tcx, Bx>
{
    fn visit_assign(
        &mut self,
        place: &mir::Place<'tcx>,
        rvalue: &mir::Rvalue<'tcx>,
        location: Location,
    ) {
        debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);

        if let Some(index) = place.as_local() {
            self.assign(index, location);
            let decl_span = self.fx.mir.local_decls[index].source_info.span;
            if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
                self.not_ssa(index);
            }
        } else {
            self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
        }

        self.visit_rvalue(rvalue, location);
    }

    fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
        debug!("visit_place(place={:?}, context={:?})", place, context);
        self.process_place(&place.as_ref(), context, location);
    }

    fn visit_local(&mut self, &local: &mir::Local, context: PlaceContext, location: Location) {
        match context {
            PlaceContext::MutatingUse(MutatingUseContext::Call)
            | PlaceContext::MutatingUse(MutatingUseContext::Yield) => {
                self.assign(local, location);
            }

            PlaceContext::NonUse(_) | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

            PlaceContext::NonMutatingUse(
                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
            ) => {
                // Reads from uninitialized variables (e.g., in dead code, after
                // optimizations) require locals to be in (uninitialized) memory.
                // N.B., there can be uninitialized reads of a local visited after
                // an assignment to that local, if they happen on disjoint paths.
                let ssa_read = match self.first_assignment(local) {
                    Some(assignment_location) => {
                        assignment_location.dominates(location, &self.dominators)
                    }
                    None => {
                        debug!("No first assignment found for {:?}", local);
                        // We have not seen any assignment to the local yet,
                        // but before marking not_ssa, check if it is a ZST,
                        // in which case we don't need to initialize the local.
                        let ty = self.fx.mir.local_decls[local].ty;
                        let ty = self.fx.monomorphize(ty);

                        let is_zst = self.fx.cx.layout_of(ty).is_zst();
                        debug!("is_zst: {}", is_zst);
                        is_zst
                    }
                };
                if !ssa_read {
                    self.not_ssa(local);
                }
            }

            PlaceContext::MutatingUse(
                MutatingUseContext::Store
                | MutatingUseContext::AsmOutput
                | MutatingUseContext::Borrow
                | MutatingUseContext::AddressOf
                | MutatingUseContext::Projection,
            )
            | PlaceContext::NonMutatingUse(
                NonMutatingUseContext::Inspect
                | NonMutatingUseContext::SharedBorrow
                | NonMutatingUseContext::UniqueBorrow
                | NonMutatingUseContext::ShallowBorrow
                | NonMutatingUseContext::AddressOf
                | NonMutatingUseContext::Projection,
            ) => {
                self.not_ssa(local);
            }

            PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
                let ty = self.fx.mir.local_decls[local].ty;
                let ty = self.fx.monomorphize(ty);

                // Only need the place if we're actually dropping it.
                if self.fx.cx.type_needs_drop(ty) {
                    self.not_ssa(local);
                }
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
    NotCleanup,
    Funclet,
    Internal { funclet: mir::BasicBlock },
}

impl CleanupKind {
    pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
        match self {
            CleanupKind::NotCleanup => None,
            CleanupKind::Funclet => Some(for_bb),
            CleanupKind::Internal { funclet } => Some(funclet),
        }
    }
}

pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKind> {
    fn discover_masters<'tcx>(
        result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
        mir: &mir::Body<'tcx>,
    ) {
        for (bb, data) in mir.basic_blocks().iter_enumerated() {
            match data.terminator().kind {
                TerminatorKind::Goto { .. }
                | TerminatorKind::Resume
                | TerminatorKind::Abort
                | TerminatorKind::Return
                | TerminatorKind::GeneratorDrop
                | TerminatorKind::Unreachable
                | TerminatorKind::SwitchInt { .. }
                | TerminatorKind::Yield { .. }
                | TerminatorKind::FalseEdge { .. }
                | TerminatorKind::FalseUnwind { .. }
                | TerminatorKind::InlineAsm { .. } => { /* nothing to do */ }
                TerminatorKind::Call { cleanup: unwind, .. }
                | TerminatorKind::Assert { cleanup: unwind, .. }
                | TerminatorKind::DropAndReplace { unwind, .. }
                | TerminatorKind::Drop { unwind, .. } => {
                    if let Some(unwind) = unwind {
                        debug!(
                            "cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                            bb, data, unwind
                        );
                        result[unwind] = CleanupKind::Funclet;
                    }
                }
            }
        }
    }

    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, mir: &mir::Body<'tcx>) {
        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());

        let mut set_successor = |funclet: mir::BasicBlock, succ| match funclet_succs[funclet] {
            ref mut s @ None => {
                debug!("set_successor: updating successor of {:?} to {:?}", funclet, succ);
                *s = Some(succ);
            }
            Some(s) => {
                if s != succ {
                    span_bug!(
                        mir.span,
                        "funclet {:?} has 2 parents - {:?} and {:?}",
                        funclet,
                        s,
                        succ
                    );
                }
            }
        };

        for (bb, data) in traversal::reverse_postorder(mir) {
            let funclet = match result[bb] {
                CleanupKind::NotCleanup => continue,
                CleanupKind::Funclet => bb,
                CleanupKind::Internal { funclet } => funclet,
            };

            debug!(
                "cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
                bb, data, result[bb], funclet
            );

            for &succ in data.terminator().successors() {
                let kind = result[succ];
                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", funclet, succ, kind);
                match kind {
                    CleanupKind::NotCleanup => {
                        result[succ] = CleanupKind::Internal { funclet };
                    }
                    CleanupKind::Funclet => {
                        if funclet != succ {
                            set_successor(funclet, succ);
                        }
                    }
                    CleanupKind::Internal { funclet: succ_funclet } => {
                        if funclet != succ_funclet {
                            // `succ` has 2 different funclet going into it, so it must
                            // be a funclet by itself.

                            debug!(
                                "promoting {:?} to a funclet and updating {:?}",
                                succ, succ_funclet
                            );
                            result[succ] = CleanupKind::Funclet;
                            set_successor(succ_funclet, succ);
                            set_successor(funclet, succ);
                        }
                    }
                }
            }
        }
    }

    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());

    discover_masters(&mut result, mir);
    propagate(&mut result, mir);
    debug!("cleanup_kinds: result={:?}", result);
    result
}
<commit_msg>Auto merge of #85741 - tmiasko:ssa, r=nagisa<commit_after>//! An analysis to determine which locals require allocas and
//! which do not.

use super::FunctionCx;
use crate::traits::*;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::traversal;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Location, TerminatorKind};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_target::abi::LayoutOf;

pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
    fx: &FunctionCx<'a, 'tcx, Bx>,
) -> BitSet<mir::Local> {
    let mir = fx.mir;
    let mut analyzer = LocalAnalyzer::new(fx);

    // If there exists a local definition that dominates all uses of that local,
    // the definition should be visited first. Traverse blocks in preorder which
    // is a topological sort of dominance partial order.
    for (bb, data) in traversal::preorder(&mir) {
        analyzer.visit_basic_block_data(bb, data);
    }

    for (local, decl) in mir.local_decls.iter_enumerated() {
        let ty = fx.monomorphize(decl.ty);
        debug!("local {:?} has type `{}`", local, ty);
        let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span);
        if fx.cx.is_backend_immediate(layout) {
            // These sorts of types are immediates that we can store
            // in an Value without an alloca.
        } else if fx.cx.is_backend_scalar_pair(layout) {
            // We allow pairs and uses of any of their 2 fields.
        } else {
            // These sorts of types require an alloca. Note that
            // is_llvm_immediate() may *still* be true, particularly
            // for newtypes, but we currently force some types
            // (e.g., structs) into an alloca unconditionally, just so
            // that we don't have to deal with having two pathways
            // (gep vs extractvalue etc).
            analyzer.not_ssa(local);
        }
    }

    analyzer.non_ssa_locals
}

struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
    dominators: Dominators<mir::BasicBlock>,
    non_ssa_locals: BitSet<mir::Local>,
    // The location of the first visited direct assignment to each
    // local, or an invalid location (out of bounds `block` index).
    first_assignment: IndexVec<mir::Local, Location>,
}

impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
        let invalid_location = mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
        let dominators = fx.mir.dominators();
        let mut analyzer = LocalAnalyzer {
            fx,
            dominators,
            non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
            first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls),
        };

        // Arguments get assigned to by means of the function being called
        for arg in fx.mir.args_iter() {
            analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
        }

        analyzer
    }

    fn first_assignment(&self, local: mir::Local) -> Option<Location> {
        let location = self.first_assignment[local];
        if location.block.index() < self.fx.mir.basic_blocks().len() {
            Some(location)
        } else {
            None
        }
    }

    fn not_ssa(&mut self, local: mir::Local) {
        debug!("marking {:?} as non-SSA", local);
        self.non_ssa_locals.insert(local);
    }

    fn assign(&mut self, local: mir::Local, location: Location) {
        if self.first_assignment(local).is_some() {
            self.not_ssa(local);
        } else {
            self.first_assignment[local] = location;
        }
    }

    fn process_place(
        &mut self,
        place_ref: &mir::PlaceRef<'tcx>,
        context: PlaceContext,
        location: Location,
    ) {
        let cx = self.fx.cx;

        if let Some((place_base, elem)) = place_ref.last_projection() {
            let mut base_context = if context.is_mutating_use() {
                PlaceContext::MutatingUse(MutatingUseContext::Projection)
            } else {
                PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
            };

            // Allow uses of projections that are ZSTs or from scalar fields.
            let is_consume = matches!(
                context,
                PlaceContext::NonMutatingUse(
                    NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
                )
            );
            if is_consume {
                let base_ty = place_base.ty(self.fx.mir, cx.tcx());
                let base_ty = self.fx.monomorphize(base_ty);

                // ZSTs don't require any actual memory access.
                let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(elem)).ty;
                let span = self.fx.mir.local_decls[place_ref.local].source_info.span;
                if cx.spanned_layout_of(elem_ty, span).is_zst() {
                    return;
                }

                if let mir::ProjectionElem::Field(..) = elem {
                    let layout = cx.spanned_layout_of(base_ty.ty, span);
                    if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
                        // Recurse with the same context, instead of `Projection`,
                        // potentially stopping at non-operand projections,
                        // which would trigger `not_ssa` on locals.
                        base_context = context;
                    }
                }
            }

            if let mir::ProjectionElem::Deref = elem {
                // Deref projections typically only read the pointer.
                base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
            }

            self.process_place(&place_base, base_context, location);
            // HACK(eddyb) this emulates the old `visit_projection_elem`, this
            // entire `visit_place`-like `process_place` method should be rewritten,
            // now that we have moved to the "slice of projections" representation.
            if let mir::ProjectionElem::Index(local) = elem {
                self.visit_local(
                    &local,
                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
                    location,
                );
            }
        } else {
            self.visit_local(&place_ref.local, context, location);
        }
    }
}

impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
    for LocalAnalyzer<'mir, 'a, 'tcx, Bx>
{
    fn visit_assign(
        &mut self,
        place: &mir::Place<'tcx>,
        rvalue: &mir::Rvalue<'tcx>,
        location: Location,
    ) {
        debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);

        if let Some(index) = place.as_local() {
            self.assign(index, location);
            let decl_span = self.fx.mir.local_decls[index].source_info.span;
            if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
                self.not_ssa(index);
            }
        } else {
            self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
        }

        self.visit_rvalue(rvalue, location);
    }

    fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
        debug!("visit_place(place={:?}, context={:?})", place, context);
        self.process_place(&place.as_ref(), context, location);
    }

    fn visit_local(&mut self, &local: &mir::Local, context: PlaceContext, location: Location) {
        match context {
            PlaceContext::MutatingUse(MutatingUseContext::Call)
            | PlaceContext::MutatingUse(MutatingUseContext::Yield) => {
                self.assign(local, location);
            }

            PlaceContext::NonUse(_) | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

            PlaceContext::NonMutatingUse(
                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
            ) => {
                // Reads from uninitialized variables (e.g., in dead code, after
                // optimizations) require locals to be in (uninitialized) memory.
                // N.B., there can be uninitialized reads of a local visited after
                // an assignment to that local, if they happen on disjoint paths.
                let ssa_read = match self.first_assignment(local) {
                    Some(assignment_location) => {
                        assignment_location.dominates(location, &self.dominators)
                    }
                    None => {
                        debug!("No first assignment found for {:?}", local);
                        // We have not seen any assignment to the local yet,
                        // but before marking not_ssa, check if it is a ZST,
                        // in which case we don't need to initialize the local.
                        let ty = self.fx.mir.local_decls[local].ty;
                        let ty = self.fx.monomorphize(ty);

                        let is_zst = self.fx.cx.layout_of(ty).is_zst();
                        debug!("is_zst: {}", is_zst);
                        is_zst
                    }
                };
                if !ssa_read {
                    self.not_ssa(local);
                }
            }

            PlaceContext::MutatingUse(
                MutatingUseContext::Store
                | MutatingUseContext::AsmOutput
                | MutatingUseContext::Borrow
                | MutatingUseContext::AddressOf
                | MutatingUseContext::Projection,
            )
            | PlaceContext::NonMutatingUse(
                NonMutatingUseContext::Inspect
                | NonMutatingUseContext::SharedBorrow
                | NonMutatingUseContext::UniqueBorrow
                | NonMutatingUseContext::ShallowBorrow
                | NonMutatingUseContext::AddressOf
                | NonMutatingUseContext::Projection,
            ) => {
                self.not_ssa(local);
            }

            PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
                let ty = self.fx.mir.local_decls[local].ty;
                let ty = self.fx.monomorphize(ty);

                // Only need the place if we're actually dropping it.
                if self.fx.cx.type_needs_drop(ty) {
                    self.not_ssa(local);
                }
            }
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
    NotCleanup,
    Funclet,
    Internal { funclet: mir::BasicBlock },
}

impl CleanupKind {
    pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
        match self {
            CleanupKind::NotCleanup => None,
            CleanupKind::Funclet => Some(for_bb),
            CleanupKind::Internal { funclet } => Some(funclet),
        }
    }
}

pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKind> {
    fn discover_masters<'tcx>(
        result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
        mir: &mir::Body<'tcx>,
    ) {
        for (bb, data) in mir.basic_blocks().iter_enumerated() {
            match data.terminator().kind {
                TerminatorKind::Goto { .. }
                | TerminatorKind::Resume
                | TerminatorKind::Abort
                | TerminatorKind::Return
                | TerminatorKind::GeneratorDrop
                | TerminatorKind::Unreachable
                | TerminatorKind::SwitchInt { .. }
                | TerminatorKind::Yield { .. }
                | TerminatorKind::FalseEdge { .. }
                | TerminatorKind::FalseUnwind { .. }
                | TerminatorKind::InlineAsm { .. } => { /* nothing to do */ }
                TerminatorKind::Call { cleanup: unwind, .. }
                | TerminatorKind::Assert { cleanup: unwind, .. }
                | TerminatorKind::DropAndReplace { unwind, .. }
                | TerminatorKind::Drop { unwind, .. } => {
                    if let Some(unwind) = unwind {
                        debug!(
                            "cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                            bb, data, unwind
                        );
                        result[unwind] = CleanupKind::Funclet;
                    }
                }
            }
        }
    }

    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, mir: &mir::Body<'tcx>) {
        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());

        let mut set_successor = |funclet: mir::BasicBlock, succ| match funclet_succs[funclet] {
            ref mut s @ None => {
                debug!("set_successor: updating successor of {:?} to {:?}", funclet, succ);
                *s = Some(succ);
            }
            Some(s) => {
                if s != succ {
                    span_bug!(
                        mir.span,
                        "funclet {:?} has 2 parents - {:?} and {:?}",
                        funclet,
                        s,
                        succ
                    );
                }
            }
        };

        for (bb, data) in traversal::reverse_postorder(mir) {
            let funclet = match result[bb] {
                CleanupKind::NotCleanup => continue,
                CleanupKind::Funclet => bb,
                CleanupKind::Internal { funclet } => funclet,
            };

            debug!(
                "cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
                bb, data, result[bb], funclet
            );

            for &succ in data.terminator().successors() {
                let kind = result[succ];
                debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", funclet, succ, kind);
                match kind {
                    CleanupKind::NotCleanup => {
                        result[succ] = CleanupKind::Internal { funclet };
                    }
                    CleanupKind::Funclet => {
                        if funclet != succ {
                            set_successor(funclet, succ);
                        }
                    }
                    CleanupKind::Internal { funclet: succ_funclet } => {
                        if funclet != succ_funclet {
                            // `succ` has 2 different funclet going into it, so it must
                            // be a funclet by itself.

                            debug!(
                                "promoting {:?} to a funclet and updating {:?}",
                                succ, succ_funclet
                            );
                            result[succ] = CleanupKind::Funclet;
                            set_successor(succ_funclet, succ);
                            set_successor(funclet, succ);
                        }
                    }
                }
            }
        }
    }

    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());

    discover_masters(&mut result, mir);
    propagate(&mut result, mir);
    debug!("cleanup_kinds: result={:?}", result);
    result
}
<|endoftext|>


<commit_before>/// srs: A model for spaced repetition system learning.
///
/// This crate maintains a database of problems, and some notions of intervals and a bit of history
/// in order to allow them to be used for spaced repetition learning.

extern crate rand;
extern crate rusqlite;

use rand::{Rng, thread_rng};
use rusqlite::{Connection, Transaction};
use std::error;
use std::path::Path;
use std::result;
use std::time::{SystemTime, UNIX_EPOCH};

// For now, just box the errors.  TODO: Make a real error type.
pub type Result<T> = result::Result<T, Box<error::Error + Send + Sync>>;

/// A Store holds problems in a database (and holds the handle to the database).
pub struct Store {
    /// The connection to the database.
    conn: Connection,
}

impl Store {
    /// Create a new store at the given path.  Will return an error if the database has already
    /// been created.
    pub fn create<P: AsRef<Path>>(path: P) -> Result<Store> {
        let mut conn = Connection::open(path)?;

        {
            let tx = conn.transaction()?;
            tx.execute("CREATE TABLE probs (id INTEGER PRIMARY KEY,
                question TEXT UNIQUE,
                answer TEXT NOT NULL)", &[])?;
            tx.execute("CREATE TABLE learning (probid INTEGER PRIMARY KEY REFERENCES probs (id),
                next REAL NOT NULL,
                interval REAL NOT NULL)", &[])?;
            tx.execute("CREATE INDEX learning_next ON learning (next)", &[])?;
            tx.execute("CREATE TABLE schema_version (version TEXT NOT NULL)", &[])?;
            tx.execute("INSERT INTO schema_version VALUES (?)", &[&"20170408B"])?;
            tx.commit()?;
        }

        conn.execute("PRAGMA foreign_keys = ON", &[])?;

        Ok(Store {
            conn: conn,
        })
    }

    pub fn open<P: AsRef<Path>>(path: P) -> Result<Store> {
        let conn = Connection::open(path)?;
        {
            let mut stmt = conn.prepare(" SELECT version FROM schema_version")?;
            let mut rows = stmt.query_map(&[], |row| {
                let vers: String = row.get(0);
                vers
            })?;
            match rows.next() {
                Some(text) => {
                    let text = text?;
                    if text != "20170408B" {
                        panic!("schema version mismatch {}", text);
                    }
                }
                None => panic!("No schema present"),
            }
            match rows.next() {
                Some(_) => panic!("Multiple rows in schema_version"),
                None => (),
            }
        }
        Ok(Store {
            conn: conn,
        })
    }

    /// Return a populator that can be used to more rapidly populate the data.  The population will
    /// be done within the context of a single sqlite3 database transaction.
    pub fn populate(&mut self) -> Result<Populator> {
        let tx = self.conn.transaction()?;
        Ok(Populator {
            tx: tx,
        })
    }

    /// Query for the next problem that has expired.  If Some, then this is the next problem that
    /// should be asked.
    pub fn get_next(&mut self) -> Result<Option<Problem>> {
        let mut stmt = self.conn.prepare("
            SELECT id, question, answer, next, interval
            FROM probs JOIN learning
            WHERE probs.id = learning.probid
                AND next <= ?
            ORDER BY next
            LIMIT 1")?;
        let mut rows = stmt.query_map(&[&now()], |row| {
            Problem {
                id: row.get(0),
                question: row.get(1),
                answer: row.get(2),
                next: row.get(3),
                interval: row.get(4),
            }
        })?;
        match rows.next() {
            Some(row) => Ok(Some(row?)),
            None => Ok(None),
        }
    }

    /// Get a problem that hasn't started being learned.  The interval and "next" will be set
    /// appropriately for a new word.
    pub fn get_new(&mut self) -> Result<Option<Problem>> {
        let mut stmt = self.conn.prepare("
            SELECT id, question, answer
            FROM probs
            WHERE ID NOT IN (SELECT probid FROM learning)
            ORDER BY id
            LIMIT 1")?;
        let mut rows = stmt.query_map(&[], |row| {
            Problem {
                id: row.get(0),
                question: row.get(1),
                answer: row.get(2),
                next: now(),
                interval: 5.0,
            }
        })?;
        match rows.next() {
            Some(row) => Ok(Some(row?)),
            None => Ok(None),
        }
    }

    /// Update a word, based on a learning factor.  The scale is 1..4, with 1 being totally
    /// incorrect, and 4 being totally correct.
    pub fn update(&mut self, prob: Problem, factor: u8) -> Result<()> {
        let factor = match factor {
            1 => 0.25,
            2 => 0.9,
            3 => 1.2,
            4 => 2.2,
            _ => panic!("Invalid factor: {}", factor),
        };

        let mut prob = prob;

        let mut rng = thread_rng();
        let interval = prob.interval;
        // Compute the interval, capping with a minimum of 5 seconds.
        prob.interval = (interval * factor * rng.gen_range(0.75, 1.25)).max(5.0);
        prob.next = now() + prob.interval;

        let tx = self.conn.transaction()?;
        tx.execute("INSERT OR REPLACE INTO learning VALUES (?, ?, ?)",
            &[&prob.id, &prob.next, &prob.interval])?;
        tx.commit()?;

        Ok(())
    }

    /// Retrieve statistics about the words available.
    pub fn get_counts(&self) -> Result<Counts> {
        Ok(Counts {
            active: 0,
            later: 0,
            unlearned: 0,
            buckets: vec![],
        })
    }
}

pub struct Counts {
    pub active: usize,
    pub later: usize,
    pub unlearned: usize,

    pub buckets: Vec<Bucket>,
}

pub struct Bucket {
    pub name: &'static str,
    pub count: usize,
}

/// A single problem retrieved.
pub struct Problem {
    id: i64,
    pub question: String,
    pub answer: String,
    pub next: f64, // TODO: Make these private, and provide a query.
    pub interval: f64,
}

pub struct Populator<'a> {
    tx: Transaction<'a>,
}

impl<'a> Populator<'a> {
    /// Add a single unlearned problem to the store.
    pub fn add_problem(&mut self, question: &str, answer: &str) -> Result<()> {
        self.tx.execute("INSERT INTO probs (question, answer) VALUES (?, ?)",
            &[&question, &answer])?;
        Ok(())
    }

    /// Add a problem that is in the process of being learned.  The 'next' value is the unix time
    /// that the question should be asked again, and 'interval' is the current interval.
    pub fn add_learning_problem(&mut self, question: &str, answer: &str, next: f64, interval: f64) -> Result<()> {
        self.tx.execute("INSERT INTO probs (question, answer) VALUES (?, ?)",
            &[&question, &answer])?;
        self.tx.execute("INSERT INTO learning VALUES (?, ?, ?)",
            &[&self.tx.last_insert_rowid(), &next, &interval])?;
        Ok(())
    }

    pub fn commit(self) -> Result<()> {
        self.tx.commit()?;
        Ok(())
    }
}

// Get the current time as ticks.
pub fn now() -> f64 {
    let stamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let sec = stamp.as_secs();
    let nsec = stamp.subsec_nanos();

    sec as f64 + (nsec as f64 / 1.0e9)
}
<commit_msg>Implement statistics<commit_after>/// srs: A model for spaced repetition system learning.
///
/// This crate maintains a database of problems, and some notions of intervals and a bit of history
/// in order to allow them to be used for spaced repetition learning.

extern crate rand;
extern crate rusqlite;

use rand::{Rng, thread_rng};
use rusqlite::{Connection, Transaction};
use std::error;
use std::path::Path;
use std::result;
use std::time::{SystemTime, UNIX_EPOCH};

// For now, just box the errors.  TODO: Make a real error type.
pub type Result<T> = result::Result<T, Box<error::Error + Send + Sync>>;

/// A Store holds problems in a database (and holds the handle to the database).
pub struct Store {
    /// The connection to the database.
    conn: Connection,
}

impl Store {
    /// Create a new store at the given path.  Will return an error if the database has already
    /// been created.
    pub fn create<P: AsRef<Path>>(path: P) -> Result<Store> {
        let mut conn = Connection::open(path)?;

        {
            let tx = conn.transaction()?;
            tx.execute("CREATE TABLE probs (id INTEGER PRIMARY KEY,
                question TEXT UNIQUE,
                answer TEXT NOT NULL)", &[])?;
            tx.execute("CREATE TABLE learning (probid INTEGER PRIMARY KEY REFERENCES probs (id),
                next REAL NOT NULL,
                interval REAL NOT NULL)", &[])?;
            tx.execute("CREATE INDEX learning_next ON learning (next)", &[])?;
            tx.execute("CREATE TABLE schema_version (version TEXT NOT NULL)", &[])?;
            tx.execute("INSERT INTO schema_version VALUES (?)", &[&"20170408B"])?;
            tx.commit()?;
        }

        conn.execute("PRAGMA foreign_keys = ON", &[])?;

        Ok(Store {
            conn: conn,
        })
    }

    pub fn open<P: AsRef<Path>>(path: P) -> Result<Store> {
        let conn = Connection::open(path)?;
        {
            let mut stmt = conn.prepare(" SELECT version FROM schema_version")?;
            let mut rows = stmt.query_map(&[], |row| {
                let vers: String = row.get(0);
                vers
            })?;
            match rows.next() {
                Some(text) => {
                    let text = text?;
                    if text != "20170408B" {
                        panic!("schema version mismatch {}", text);
                    }
                }
                None => panic!("No schema present"),
            }
            match rows.next() {
                Some(_) => panic!("Multiple rows in schema_version"),
                None => (),
            }
        }
        Ok(Store {
            conn: conn,
        })
    }

    /// Return a populator that can be used to more rapidly populate the data.  The population will
    /// be done within the context of a single sqlite3 database transaction.
    pub fn populate(&mut self) -> Result<Populator> {
        let tx = self.conn.transaction()?;
        Ok(Populator {
            tx: tx,
        })
    }

    /// Query for the next problem that has expired.  If Some, then this is the next problem that
    /// should be asked.
    pub fn get_next(&mut self) -> Result<Option<Problem>> {
        let mut stmt = self.conn.prepare("
            SELECT id, question, answer, next, interval
            FROM probs JOIN learning
            WHERE probs.id = learning.probid
                AND next <= ?
            ORDER BY next
            LIMIT 1")?;
        let mut rows = stmt.query_map(&[&now()], |row| {
            Problem {
                id: row.get(0),
                question: row.get(1),
                answer: row.get(2),
                next: row.get(3),
                interval: row.get(4),
            }
        })?;
        match rows.next() {
            Some(row) => Ok(Some(row?)),
            None => Ok(None),
        }
    }

    /// Get a problem that hasn't started being learned.  The interval and "next" will be set
    /// appropriately for a new word.
    pub fn get_new(&mut self) -> Result<Option<Problem>> {
        let mut stmt = self.conn.prepare("
            SELECT id, question, answer
            FROM probs
            WHERE ID NOT IN (SELECT probid FROM learning)
            ORDER BY id
            LIMIT 1")?;
        let mut rows = stmt.query_map(&[], |row| {
            Problem {
                id: row.get(0),
                question: row.get(1),
                answer: row.get(2),
                next: now(),
                interval: 5.0,
            }
        })?;
        match rows.next() {
            Some(row) => Ok(Some(row?)),
            None => Ok(None),
        }
    }

    /// Update a word, based on a learning factor.  The scale is 1..4, with 1 being totally
    /// incorrect, and 4 being totally correct.
    pub fn update(&mut self, prob: Problem, factor: u8) -> Result<()> {
        let factor = match factor {
            1 => 0.25,
            2 => 0.9,
            3 => 1.2,
            4 => 2.2,
            _ => panic!("Invalid factor: {}", factor),
        };

        let mut prob = prob;

        let mut rng = thread_rng();
        let interval = prob.interval;
        // Compute the interval, capping with a minimum of 5 seconds.
        prob.interval = (interval * factor * rng.gen_range(0.75, 1.25)).max(5.0);
        prob.next = now() + prob.interval;

        let tx = self.conn.transaction()?;
        tx.execute("INSERT OR REPLACE INTO learning VALUES (?, ?, ?)",
            &[&prob.id, &prob.next, &prob.interval])?;
        tx.commit()?;

        Ok(())
    }

    /// Retrieve statistics about the words available.
    pub fn get_counts(&self) -> Result<Counts> {
        let unlearned: i64 = self.conn.query_row("
                SELECT COUNT(*)
                FROM probs
                WHERE id NOT IN (SELECT probid FROM learning)", &[],
            |row| { row.get(0) })?;

        let cur = now();

        let active: i64 = self.conn.query_row("
                SELECT COUNT(*)
                FROM probs JOIN learning
                WHERE probs.id = learning.probid
                    AND next <= ?", &[&cur], |row| { row.get(0) })?;

        let mut interval = 1.0;
        let mut prior = 0.0;
        let buckets: Vec<_> = COUNT_BUCKETS.iter().map(|buk| {
            interval *= buk.limit;
            let count: i64 = self.conn.query_row("
                    SELECT COUNT(*)
                    FROM probs JOIN learning
                    WHERE probs.id = learning.probid
                        AND interval <= ? AND interval > ?",
                &[&interval, &prior], |row| { row.get(0) }).unwrap();
            prior = interval;
            Bucket {
                name: buk.name,
                count: count as usize,
            }
        }).collect();

        Ok(Counts {
            active: active as usize,
            later: (unlearned - active) as usize,
            unlearned: unlearned as usize,
            buckets: buckets,
        })
    }
}

pub struct Counts {
    pub active: usize,
    pub later: usize,
    pub unlearned: usize,

    pub buckets: Vec<Bucket>,
}

pub struct Bucket {
    pub name: &'static str,
    pub count: usize,
}

struct BucketBin {
    name: &'static str,
    limit: f64,
}

static COUNT_BUCKETS: &'static [BucketBin] = &[
    BucketBin{ name: "sec", limit: 60.0 },
    BucketBin{ name: "min", limit: 60.0 },
    BucketBin{ name: "hr", limit: 24.0 },
    BucketBin{ name: "day", limit: 30.0 },
    BucketBin{ name: "mon", limit: 1.0e30 },
];

/// A single problem retrieved.
pub struct Problem {
    id: i64,
    pub question: String,
    pub answer: String,
    pub next: f64, // TODO: Make these private, and provide a query.
    pub interval: f64,
}

pub struct Populator<'a> {
    tx: Transaction<'a>,
}

impl<'a> Populator<'a> {
    /// Add a single unlearned problem to the store.
    pub fn add_problem(&mut self, question: &str, answer: &str) -> Result<()> {
        self.tx.execute("INSERT INTO probs (question, answer) VALUES (?, ?)",
            &[&question, &answer])?;
        Ok(())
    }

    /// Add a problem that is in the process of being learned.  The 'next' value is the unix time
    /// that the question should be asked again, and 'interval' is the current interval.
    pub fn add_learning_problem(&mut self, question: &str, answer: &str, next: f64, interval: f64) -> Result<()> {
        self.tx.execute("INSERT INTO probs (question, answer) VALUES (?, ?)",
            &[&question, &answer])?;
        self.tx.execute("INSERT INTO learning VALUES (?, ?, ?)",
            &[&self.tx.last_insert_rowid(), &next, &interval])?;
        Ok(())
    }

    pub fn commit(self) -> Result<()> {
        self.tx.commit()?;
        Ok(())
    }
}

// Get the current time as ticks.
pub fn now() -> f64 {
    let stamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
    let sec = stamp.as_secs();
    let nsec = stamp.subsec_nanos();

    sec as f64 + (nsec as f64 / 1.0e9)
}
<|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/. */

#![allow(non_snake_case)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts,
    FcConfigSubstitute, FcDefaultSubstitute,
    FcFontMatch,
    FcNameParse, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy,
    FcMatchPattern,
    FcPatternCreate, FcPatternAddString,
    FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use util::str::c_str_to_string;

use libc;
use libc::{c_int, c_char};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::ptr;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";

pub fn get_available_families<F>(mut callback: F) where F: FnMut(String) {
    unsafe {
        let config = FcConfigGetCurrent();
        let fontSet = FcConfigGetFonts(config, FcSetSystem);
        for i in 0..((*fontSet).nfont as isize) {
            let font = (*fontSet).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::null_mut();
            let mut v: c_int = 0;
            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
                let family_name = c_str_to_string(family as *const c_char);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn get_variations_for_family<F>(family_name: &str, mut callback: F)
    where F: FnMut(String)
{
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(!pattern.is_null());
        let family_name_c = CString::new(family_name).unwrap();
        let family_name = family_name_c.as_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(!object_set.is_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut c_char);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut c_char);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in 0..((*matches).nfont as isize) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::null_mut();
            let file = if FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file) == FcResultMatch {
                c_str_to_string(file as *const c_char)
            } else {
                panic!();
            };
            let mut index: libc::c_int = 0;
            let index = if FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index) == FcResultMatch {
                index
            } else {
                panic!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn get_system_default_family(generic_name: &str) -> Option<String> {
    let generic_name_c = CString::new(generic_name).unwrap();
    let generic_name_ptr = generic_name_c.as_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::null_mut(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::null_mut();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
            let result = c_str_to_string(match_string as *const c_char);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os="linux")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_owned(),
        "DejaVu Sans".to_owned(),
        "Arial".to_owned()
    )
}

#[cfg(target_os="android")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!("Roboto".to_owned())
}
<commit_msg>Auto merge of #6143 - michaelwu:truetype-only, r=pcwalton<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/. */

#![allow(non_snake_case)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts,
    FcConfigSubstitute, FcDefaultSubstitute,
    FcFontMatch,
    FcNameParse, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy,
    FcMatchPattern,
    FcPatternCreate, FcPatternAddString,
    FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use util::str::c_str_to_string;

use libc;
use libc::{c_int, c_char};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::ptr;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";
static FC_FONTFORMAT: &'static [u8] = b"fontformat\0";

pub fn get_available_families<F>(mut callback: F) where F: FnMut(String) {
    unsafe {
        let config = FcConfigGetCurrent();
        let fontSet = FcConfigGetFonts(config, FcSetSystem);
        for i in 0..((*fontSet).nfont as isize) {
            let font = (*fontSet).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::null_mut();
            let mut format: *mut FcChar8 = ptr::null_mut();
            let mut v: c_int = 0;
            if FcPatternGetString(*font, FC_FONTFORMAT.as_ptr() as *mut c_char, v, &mut format) != FcResultMatch {
                continue;
            }

            // Skip bitmap fonts. They aren't supported by FreeType.
            let fontformat = c_str_to_string(format as *const c_char);
            if fontformat != "TrueType" &&
               fontformat != "CFF" &&
               fontformat != "Type 1" {
                continue;
            }

            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut c_char, v, &mut family) == FcResultMatch {
                let family_name = c_str_to_string(family as *const c_char);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn get_variations_for_family<F>(family_name: &str, mut callback: F)
    where F: FnMut(String)
{
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(!pattern.is_null());
        let family_name_c = CString::new(family_name).unwrap();
        let family_name = family_name_c.as_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut c_char, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(!object_set.is_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut c_char);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut c_char);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in 0..((*matches).nfont as isize) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::null_mut();
            let file = if FcPatternGetString(*font, FC_FILE.as_ptr() as *mut c_char, 0, &mut file) == FcResultMatch {
                c_str_to_string(file as *const c_char)
            } else {
                panic!();
            };
            let mut index: libc::c_int = 0;
            let index = if FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut c_char, 0, &mut index) == FcResultMatch {
                index
            } else {
                panic!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn get_system_default_family(generic_name: &str) -> Option<String> {
    let generic_name_c = CString::new(generic_name).unwrap();
    let generic_name_ptr = generic_name_c.as_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::null_mut(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::null_mut(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::null_mut();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut c_char, 0, &mut match_string);
            let result = c_str_to_string(match_string as *const c_char);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os="linux")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_owned(),
        "DejaVu Sans".to_owned(),
        "Arial".to_owned()
    )
}

#[cfg(target_os="android")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!("Roboto".to_owned())
}
<|endoftext|>