<commit_before>use http::Method;
use std::future::Future;
use wasm_bindgen::UnwrapThrowExt as _;
use url::Url;

use super::{Request, RequestBuilder, Response};
use crate::IntoUrl;

/// dox
#[derive(Clone, Debug)]
pub struct Client(());

/// dox
#[derive(Debug)]
pub struct ClientBuilder(());

impl Client {
    /// dox
    pub fn new() -> Self {
        Client::builder().build().unwrap_throw()
    }

    /// dox
    pub fn builder() -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> RequestBuilder {
        let req = url.into_url().map(move |url| Request::new(method, url));
        RequestBuilder::new(self.clone(), req)
    }

    /// 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,
    ) -> impl Future<Output = Result<Response, crate::Error>> {
        self.execute_request(request)
    }

    pub(super) fn execute_request(
        &self,
        req: Request,
    ) -> impl Future<Output = crate::Result<Response>> {
        fetch(req)
    }
}

impl Default for Client {
    fn default() -> Self {
        Self::new()
    }
}

async fn fetch(req: Request) -> crate::Result<Response> {
    // Build the js Request
    let mut init = web_sys::RequestInit::new();
    init.method(req.method().as_str());

    let js_headers = web_sys::Headers::new()
        .map_err(crate::error::wasm)
        .map_err(crate::error::builder)?;

    for (name, value) in req.headers() {
        js_headers
            .append(
                name.as_str(),
                value.to_str().map_err(crate::error::builder)?,
            )
            .map_err(crate::error::wasm)
            .map_err(crate::error::builder)?;
    }
    init.headers(&js_headers.into());

    // When req.cors is true, do nothing because the default mode is 'cors'
    if !req.cors {
        init.mode(web_sys::RequestMode::NoCors);
    }

    if let Some(body) = req.body() {
        init.body(Some(&body.to_js_value()?.as_ref().as_ref()));
    }

    let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)
        .map_err(crate::error::wasm)
        .map_err(crate::error::builder)?;

    // Await the fetch() promise
    let p = web_sys::window()
        .expect("window should exist")
        .fetch_with_request(&js_req);
    let js_resp = super::promise::<web_sys::Response>(p)
        .await
        .map_err(crate::error::request)?;

    // Convert from the js Response
    let mut resp = http::Response::builder()
        .status(js_resp.status());

    let url = Url::parse(&js_resp.url()).expect_throw("url parse");

    let js_headers = js_resp.headers();
    let js_iter = js_sys::try_iter(&js_headers)
        .expect_throw("headers try_iter")
        .expect_throw("headers have an iterator");

    for item in js_iter {
        let item = item.expect_throw("headers iterator doesn't throw");
        let v: Vec<String> = item.into_serde().expect_throw("headers into_serde");
        resp = resp.header(
            v.get(0).expect_throw("headers name"),
            v.get(1).expect_throw("headers value"),
        );
    }

    resp.body(js_resp)
        .map(|resp| Response::new(resp, url))
        .map_err(crate::error::request)
}

// ===== impl ClientBuilder =====

impl ClientBuilder {
    /// dox
    pub fn new() -> Self {
        ClientBuilder(())
    }

    /// dox
    pub fn build(self) -> Result<Client, crate::Error> {
        Ok(Client(()))
    }
}

impl Default for ClientBuilder {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>Do not assume that `window` exists in WASM (#990)<commit_after>use http::Method;
use std::future::Future;
use wasm_bindgen::prelude::{wasm_bindgen, UnwrapThrowExt as _};
use js_sys::Promise;
use url::Url;

use super::{Request, RequestBuilder, Response};
use crate::IntoUrl;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_name = fetch)]
    fn fetch_with_request(input: &web_sys::Request) -> Promise;
}

/// dox
#[derive(Clone, Debug)]
pub struct Client(());

/// dox
#[derive(Debug)]
pub struct ClientBuilder(());

impl Client {
    /// dox
    pub fn new() -> Self {
        Client::builder().build().unwrap_throw()
    }

    /// dox
    pub fn builder() -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> RequestBuilder {
        let req = url.into_url().map(move |url| Request::new(method, url));
        RequestBuilder::new(self.clone(), req)
    }

    /// 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,
    ) -> impl Future<Output = Result<Response, crate::Error>> {
        self.execute_request(request)
    }

    pub(super) fn execute_request(
        &self,
        req: Request,
    ) -> impl Future<Output = crate::Result<Response>> {
        fetch(req)
    }
}

impl Default for Client {
    fn default() -> Self {
        Self::new()
    }
}

async fn fetch(req: Request) -> crate::Result<Response> {
    // Build the js Request
    let mut init = web_sys::RequestInit::new();
    init.method(req.method().as_str());

    let js_headers = web_sys::Headers::new()
        .map_err(crate::error::wasm)
        .map_err(crate::error::builder)?;

    for (name, value) in req.headers() {
        js_headers
            .append(
                name.as_str(),
                value.to_str().map_err(crate::error::builder)?,
            )
            .map_err(crate::error::wasm)
            .map_err(crate::error::builder)?;
    }
    init.headers(&js_headers.into());

    // When req.cors is true, do nothing because the default mode is 'cors'
    if !req.cors {
        init.mode(web_sys::RequestMode::NoCors);
    }

    if let Some(body) = req.body() {
        init.body(Some(&body.to_js_value()?.as_ref().as_ref()));
    }

    let js_req = web_sys::Request::new_with_str_and_init(req.url().as_str(), &init)
        .map_err(crate::error::wasm)
        .map_err(crate::error::builder)?;

    // Await the fetch() promise
    let p = fetch_with_request(&js_req);
    let js_resp = super::promise::<web_sys::Response>(p)
        .await
        .map_err(crate::error::request)?;

    // Convert from the js Response
    let mut resp = http::Response::builder()
        .status(js_resp.status());

    let url = Url::parse(&js_resp.url()).expect_throw("url parse");

    let js_headers = js_resp.headers();
    let js_iter = js_sys::try_iter(&js_headers)
        .expect_throw("headers try_iter")
        .expect_throw("headers have an iterator");

    for item in js_iter {
        let item = item.expect_throw("headers iterator doesn't throw");
        let v: Vec<String> = item.into_serde().expect_throw("headers into_serde");
        resp = resp.header(
            v.get(0).expect_throw("headers name"),
            v.get(1).expect_throw("headers value"),
        );
    }

    resp.body(js_resp)
        .map(|resp| Response::new(resp, url))
        .map_err(crate::error::request)
}

// ===== impl ClientBuilder =====

impl ClientBuilder {
    /// dox
    pub fn new() -> Self {
        ClientBuilder(())
    }

    /// dox
    pub fn build(self) -> Result<Client, crate::Error> {
        Ok(Client(()))
    }
}

impl Default for ClientBuilder {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


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

use std::io;
use std::collections::VecDeque;
use std::rc::{Rc, Weak};
use std::cell::RefCell;
use futures::{self, task, Async, Future, Poll};
use futures::sync::oneshot;

use capnp::{Error};

use serialize::{self, AsOutputSegments};

enum State<W, M> where W: io::Write, M: AsOutputSegments {
    Writing(serialize::Write<W, M>, oneshot::Sender<M>),
    BetweenWrites(W),
    Empty,
}

/// A queue of messages being written.
pub struct WriteQueue<W, M> where W: io::Write, M: AsOutputSegments {
    inner: Rc<RefCell<Inner<M>>>,
    state: State<W, M>,
}

struct Inner<M> {
    queue: VecDeque<(M, oneshot::Sender<M>)>,
    sender_count: usize,
    task: Option<task::Task>,

    // If set, then the queue has been requested to end, and we should complete the oneshot once
    // the queue has been emptied.
    end_notifier: Option<oneshot::Sender<()>>
}

/// A handle that allows message to be sent to a `WriteQueue`.
pub struct Sender<M> where M: AsOutputSegments {
    inner: Weak<RefCell<Inner<M>>>,
}

impl <M> Clone for Sender<M> where M: AsOutputSegments {
    fn clone(&self) -> Sender<M> {
        match self.inner.upgrade() {
            None => (),
            Some(inner) => {
                inner.borrow_mut().sender_count += 1;
            }
        }
        Sender { inner: self.inner.clone() }
    }
}

impl <M> Drop for Sender<M> where M: AsOutputSegments {
    fn drop(&mut self) {
        match self.inner.upgrade() {
            None => (),
            Some(inner) => {
                inner.borrow_mut().sender_count -= 1;
            }
        }
    }
}

pub fn write_queue<W, M>(writer: W) -> (Sender<M>, WriteQueue<W, M>)
    where W: io::Write, M: AsOutputSegments
{
    let inner = Rc::new(RefCell::new(Inner {
        queue: VecDeque::new(),
        task: None,
        sender_count: 1,
        end_notifier: None,
    }));

    let sender = Sender { inner: Rc::downgrade(&inner) };

    let queue = WriteQueue {
        inner: inner,
        state: State::BetweenWrites(writer),
    };

    (sender, queue)
}

impl <M> Sender<M> where M: AsOutputSegments {
    /// Enqueues a message to be written.
    pub fn send(&mut self, message: M) -> oneshot::Receiver<M> {
        let (complete, oneshot) = futures::oneshot();

        match self.inner.upgrade() {
            None => (),
            Some(rc_inner) => {
                if rc_inner.borrow().end_notifier.is_some() {
                    drop(complete)
                } else {
                    rc_inner.borrow_mut().queue.push_back((message, complete));
                }

                match rc_inner.borrow_mut().task.take() {
                    Some(t) => t.unpark(),
                    None => (),
                }
            }
        }

        oneshot
    }

    /// Returns the number of messages queued to be written, not including any in-progress write.
    pub fn len(&mut self) -> usize {
        match self.inner.upgrade() {
            None => 0,
            Some(rc_inner) => rc_inner.borrow().queue.len(),
        }

    }

    /// Commands the queue to stop writing messages once it is empty. After this method has been called,
    /// any new calls to `send()` will return a future that immediately resolves to an error.
    pub fn end(&mut self) -> oneshot::Receiver<()> {
        let (complete, receiver) = futures::oneshot();

        match self.inner.upgrade() {
            None => (),
            Some(rc_inner) => {
                // TODO: what if end_notifier is already full? Maybe it should be a vector?
                rc_inner.borrow_mut().end_notifier = Some(complete);

                match rc_inner.borrow_mut().task.take() {
                    Some(t) => t.unpark(),
                    None => (),
                }
            }
        }

        receiver
    }
}

enum IntermediateState<W, M> where W: io::Write, M: AsOutputSegments {
    WriteDone(M, W),
    StartWrite(M, oneshot::Sender<M>),
    Resolve,
}

impl <W, M> Future for WriteQueue<W, M> where W: io::Write, M: AsOutputSegments {
    type Item = W; // Resolves when all senders have been dropped and all messages written.
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let next = match self.state {
                State::Writing(ref mut write, ref mut _complete) => {
                    let (w, m) = try_ready!(Future::poll(write));
                    IntermediateState::WriteDone(m, w)
                }
                State::BetweenWrites(ref mut _writer) => {
                    let front = self.inner.borrow_mut().queue.pop_front();
                    match front {
                        Some((m, complete)) => {
                            IntermediateState::StartWrite(m, complete)
                        }
                        None => {
                            let count = self.inner.borrow().sender_count;
                            let ended = self.inner.borrow().end_notifier.is_some();
                            if count == 0 || ended {
                                IntermediateState::Resolve
                            } else {
                                self.inner.borrow_mut().task = Some(task::park());
                                return Ok(Async::NotReady)
                            }
                        }
                    }
                }
                State::Empty => unreachable!(),
            };

            match next {
                IntermediateState::WriteDone(m, w) => {
                    match ::std::mem::replace(&mut self.state, State::BetweenWrites(w)) {
                        State::Writing(_, complete) => {
                            complete.complete(m)
                        }
                        _ => unreachable!(),
                    }
                }
                IntermediateState::StartWrite(m, c) => {
                    let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(w) => {
                            State::Writing(::serialize::write_message(w, m), c)
                        }
                        _ => unreachable!(),
                    };
                    self.state = new_state;
                }
                IntermediateState::Resolve => {
                    let ender = self.inner.borrow_mut().end_notifier.take();
                    match ender {
                        None => (),
                        Some(complete) => complete.complete(()),
                    }
                    match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(w) => {
                            return Ok(Async::Ready(w))
                        }
                        _ => unreachable!(),
                    }
                }
            }
        }
    }
}
<commit_msg>Error::disconnected() for send() and end().<commit_after>// Copyright (c) 2016 Sandstorm Development Group, Inc. and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::io;
use std::collections::VecDeque;
use std::rc::{Rc, Weak};
use std::cell::RefCell;
use futures::{self, task, Async, Future, Poll};
use futures::sync::oneshot;

use capnp::{Error};

use serialize::{self, AsOutputSegments};

enum State<W, M> where W: io::Write, M: AsOutputSegments {
    Writing(serialize::Write<W, M>, oneshot::Sender<M>),
    BetweenWrites(W),
    Empty,
}

/// A queue of messages being written.
pub struct WriteQueue<W, M> where W: io::Write, M: AsOutputSegments {
    inner: Rc<RefCell<Inner<M>>>,
    state: State<W, M>,
}

struct Inner<M> {
    queue: VecDeque<(M, oneshot::Sender<M>)>,
    sender_count: usize,
    task: Option<task::Task>,

    // If set, then the queue has been requested to end, and we should complete the oneshot once
    // the queue has been emptied.
    end_notifier: Option<oneshot::Sender<()>>
}

/// A handle that allows message to be sent to a `WriteQueue`.
pub struct Sender<M> where M: AsOutputSegments {
    inner: Weak<RefCell<Inner<M>>>,
}

impl <M> Clone for Sender<M> where M: AsOutputSegments {
    fn clone(&self) -> Sender<M> {
        match self.inner.upgrade() {
            None => (),
            Some(inner) => {
                inner.borrow_mut().sender_count += 1;
            }
        }
        Sender { inner: self.inner.clone() }
    }
}

impl <M> Drop for Sender<M> where M: AsOutputSegments {
    fn drop(&mut self) {
        match self.inner.upgrade() {
            None => (),
            Some(inner) => {
                inner.borrow_mut().sender_count -= 1;
            }
        }
    }
}

pub fn write_queue<W, M>(writer: W) -> (Sender<M>, WriteQueue<W, M>)
    where W: io::Write, M: AsOutputSegments
{
    let inner = Rc::new(RefCell::new(Inner {
        queue: VecDeque::new(),
        task: None,
        sender_count: 1,
        end_notifier: None,
    }));

    let sender = Sender { inner: Rc::downgrade(&inner) };

    let queue = WriteQueue {
        inner: inner,
        state: State::BetweenWrites(writer),
    };

    (sender, queue)
}

impl <M> Sender<M> where M: AsOutputSegments + 'static {
    /// Enqueues a message to be written.
    pub fn send(&mut self, message: M) -> Box<Future<Item=M, Error=Error>> {
        let (complete, oneshot) = futures::oneshot();

        match self.inner.upgrade() {
            None => (),
            Some(rc_inner) => {
                if rc_inner.borrow().end_notifier.is_some() {
                    drop(complete)
                } else {
                    rc_inner.borrow_mut().queue.push_back((message, complete));
                }

                match rc_inner.borrow_mut().task.take() {
                    Some(t) => t.unpark(),
                    None => (),
                }
            }
        }

        Box::new(
            oneshot.map_err(
                |oneshot::Canceled| Error::disconnected("WriteQueue has terminated".into())))
    }

    /// Returns the number of messages queued to be written, not including any in-progress write.
    pub fn len(&mut self) -> usize {
        match self.inner.upgrade() {
            None => 0,
            Some(rc_inner) => rc_inner.borrow().queue.len(),
        }

    }

    /// Commands the queue to stop writing messages once it is empty. After this method has been called,
    /// any new calls to `send()` will return a future that immediately resolves to an error.
    pub fn end(&mut self) -> Box<Future<Item=(), Error=Error>> {
        let (complete, receiver) = futures::oneshot();

        match self.inner.upgrade() {
            None => (),
            Some(rc_inner) => {
                // TODO: what if end_notifier is already full? Maybe it should be a vector?
                rc_inner.borrow_mut().end_notifier = Some(complete);

                match rc_inner.borrow_mut().task.take() {
                    Some(t) => t.unpark(),
                    None => (),
                }
            }
        }

        Box::new(
            receiver.map_err(
                |oneshot::Canceled| Error::disconnected("WriteQueue has terminated".into())))
    }
}

enum IntermediateState<W, M> where W: io::Write, M: AsOutputSegments {
    WriteDone(M, W),
    StartWrite(M, oneshot::Sender<M>),
    Resolve,
}

impl <W, M> Future for WriteQueue<W, M> where W: io::Write, M: AsOutputSegments {
    type Item = W; // Resolves when all senders have been dropped and all messages written.
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let next = match self.state {
                State::Writing(ref mut write, ref mut _complete) => {
                    let (w, m) = try_ready!(Future::poll(write));
                    IntermediateState::WriteDone(m, w)
                }
                State::BetweenWrites(ref mut _writer) => {
                    let front = self.inner.borrow_mut().queue.pop_front();
                    match front {
                        Some((m, complete)) => {
                            IntermediateState::StartWrite(m, complete)
                        }
                        None => {
                            let count = self.inner.borrow().sender_count;
                            let ended = self.inner.borrow().end_notifier.is_some();
                            if count == 0 || ended {
                                IntermediateState::Resolve
                            } else {
                                self.inner.borrow_mut().task = Some(task::park());
                                return Ok(Async::NotReady)
                            }
                        }
                    }
                }
                State::Empty => unreachable!(),
            };

            match next {
                IntermediateState::WriteDone(m, w) => {
                    match ::std::mem::replace(&mut self.state, State::BetweenWrites(w)) {
                        State::Writing(_, complete) => {
                            complete.complete(m)
                        }
                        _ => unreachable!(),
                    }
                }
                IntermediateState::StartWrite(m, c) => {
                    let new_state = match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(w) => {
                            State::Writing(::serialize::write_message(w, m), c)
                        }
                        _ => unreachable!(),
                    };
                    self.state = new_state;
                }
                IntermediateState::Resolve => {
                    let ender = self.inner.borrow_mut().end_notifier.take();
                    match ender {
                        None => (),
                        Some(complete) => complete.complete(()),
                    }
                    match ::std::mem::replace(&mut self.state, State::Empty) {
                        State::BetweenWrites(w) => {
                            return Ok(Async::Ready(w))
                        }
                        _ => unreachable!(),
                    }
                }
            }
        }
    }
}
<|endoftext|>


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

//! vulkano_gralloc: Implements swapchain allocation and memory mapping
//! using Vulkano.
//!
//! External code found at https://github.com/vulkano-rs/vulkano.

#![cfg(feature = "vulkano")]

use std::collections::BTreeMap as Map;
use std::convert::TryInto;
use std::iter::Empty;
use std::sync::Arc;

use base::MappedRegion;

use crate::rutabaga_gralloc::gralloc::{Gralloc, ImageAllocationInfo, ImageMemoryRequirements};
use crate::rutabaga_utils::*;

use vulkano::device::physical::{MemoryType, PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceCreationError, DeviceExtensions};
use vulkano::image::{
    sys, ImageCreateFlags, ImageCreationError, ImageDimensions, ImageUsage, SampleCount,
};

use vulkano::instance::{Instance, InstanceCreationError, InstanceExtensions, Version};

use vulkano::memory::{
    DedicatedAlloc, DeviceMemoryAllocError, DeviceMemoryBuilder, DeviceMemoryMapping,
    ExternalMemoryHandleType, MemoryRequirements,
};

use vulkano::memory::pool::AllocFromRequirementsFilter;
use vulkano::sync::Sharing;

/// A gralloc implementation capable of allocation `VkDeviceMemory`.
pub struct VulkanoGralloc {
    devices: Map<PhysicalDeviceType, Arc<Device>>,
    has_integrated_gpu: bool,
}

struct VulkanoMapping {
    mapping: DeviceMemoryMapping,
    size: usize,
}

impl VulkanoMapping {
    pub fn new(mapping: DeviceMemoryMapping, size: usize) -> VulkanoMapping {
        VulkanoMapping { mapping, size }
    }
}

unsafe impl MappedRegion for VulkanoMapping {
    /// Used for passing this region for hypervisor memory mappings.  We trust crosvm to use this
    /// safely.
    fn as_ptr(&self) -> *mut u8 {
        unsafe { self.mapping.as_ptr() }
    }

    /// Returns the size of the memory region in bytes.
    fn size(&self) -> usize {
        self.size
    }
}

impl VulkanoGralloc {
    /// Returns a new `VulkanGralloc' instance upon success.
    pub fn init() -> RutabagaResult<Box<dyn Gralloc>> {
        // Initialization copied from triangle.rs in Vulkano.  Look there for a more detailed
        // explanation of VK initialization.
        let instance_extensions = InstanceExtensions {
            khr_external_memory_capabilities: true,
            khr_get_physical_device_properties2: true,
            ..InstanceExtensions::none()
        };
        let instance = Instance::new(None, Version::V1_1, &instance_extensions, None)?;

        let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default();
        let mut has_integrated_gpu = false;

        for physical in PhysicalDevice::enumerate(&instance) {
            let queue_family = physical
                .queue_families()
                .find(|&q| {
                    // We take the first queue family that supports graphics.
                    q.supports_graphics()
                })
                .ok_or(RutabagaError::SpecViolation(
                    "need graphics queue family to proceed",
                ))?;

            let supported_extensions = physical.supported_extensions();

            let desired_extensions = DeviceExtensions {
                khr_dedicated_allocation: true,
                khr_get_memory_requirements2: true,
                khr_external_memory: true,
                khr_external_memory_fd: true,
                ext_external_memory_dma_buf: true,
                ..DeviceExtensions::none()
            };

            let intersection = supported_extensions.intersection(&desired_extensions);

            let (device, mut _queues) = Device::new(
                physical,
                physical.supported_features(),
                &intersection,
                [(queue_family, 0.5)].iter().cloned(),
            )?;

            let device_type = device.physical_device().properties().device_type;
            if device_type == PhysicalDeviceType::IntegratedGpu {
                has_integrated_gpu = true
            }

            // If we have two devices of the same type (two integrated GPUs), the old value is
            // dropped.  Vulkano is verbose enough such that a keener selection algorithm may
            // be used, but the need for such complexity does not seem to exist now.
            devices.insert(device_type, device);
        }

        Ok(Box::new(VulkanoGralloc {
            devices,
            has_integrated_gpu,
        }))
    }

    // This function is used safely in this module because gralloc does not:
    //
    //  (1) bind images to any memory.
    //  (2) transition the layout of images.
    //  (3) transfer ownership of images between queues.
    //
    // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec.
    unsafe fn create_image(
        &mut self,
        info: ImageAllocationInfo,
    ) -> RutabagaResult<(sys::UnsafeImage, MemoryRequirements)> {
        let device = if self.has_integrated_gpu {
            self.devices
                .get(&PhysicalDeviceType::IntegratedGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        } else {
            self.devices
                .get(&PhysicalDeviceType::DiscreteGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        };

        let usage = match info.flags.uses_rendering() {
            true => ImageUsage {
                color_attachment: true,
                ..ImageUsage::none()
            },
            false => ImageUsage {
                sampled: true,
                ..ImageUsage::none()
            },
        };

        // Reasonable bounds on image width.
        if info.width == 0 || info.width > 4096 {
            return Err(RutabagaError::InvalidGrallocDimensions);
        }

        // Reasonable bounds on image height.
        if info.height == 0 || info.height > 4096 {
            return Err(RutabagaError::InvalidGrallocDimensions);
        }

        let vulkan_format = info.drm_format.vulkan_format()?;
        let (unsafe_image, memory_requirements) = sys::UnsafeImage::new(
            device.clone(),
            usage,
            vulkan_format,
            ImageCreateFlags::none(),
            ImageDimensions::Dim2d {
                width: info.width,
                height: info.height,
                array_layers: 1,
            },
            SampleCount::Sample1,
            1, /* mipmap count */
            Sharing::Exclusive::<Empty<_>>,
            true,  /* linear images only currently */
            false, /* not preinitialized */
        )?;

        Ok((unsafe_image, memory_requirements))
    }
}

impl Gralloc for VulkanoGralloc {
    fn supports_external_gpu_memory(&self) -> bool {
        for device in self.devices.values() {
            if !device.enabled_extensions().khr_external_memory {
                return false;
            }
        }

        true
    }

    fn supports_dmabuf(&self) -> bool {
        for device in self.devices.values() {
            if !device.enabled_extensions().ext_external_memory_dma_buf {
                return false;
            }
        }

        true
    }

    fn get_image_memory_requirements(
        &mut self,
        info: ImageAllocationInfo,
    ) -> RutabagaResult<ImageMemoryRequirements> {
        let mut reqs: ImageMemoryRequirements = Default::default();

        let (unsafe_image, memory_requirements) = unsafe { self.create_image(info)? };

        let device = if self.has_integrated_gpu {
            self.devices
                .get(&PhysicalDeviceType::IntegratedGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        } else {
            self.devices
                .get(&PhysicalDeviceType::DiscreteGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        };

        let planar_layout = info.drm_format.planar_layout()?;

        // Safe because we created the image with the linear bit set and verified the format is
        // not a depth or stencil format.  We are also using the correct image aspect.  Vulkano
        // will panic if we are not.
        for plane in 0..planar_layout.num_planes {
            let aspect = info.drm_format.vulkan_image_aspect(plane)?;
            let layout = unsafe { unsafe_image.multiplane_color_layout(aspect) };
            reqs.strides[plane] = layout.row_pitch as u32;
            reqs.offsets[plane] = layout.offset as u32;
        }

        let need_visible = info.flags.host_visible();
        let want_cached = info.flags.host_cached();

        let memory_type = {
            let filter = |current_type: MemoryType| {
                if need_visible && !current_type.is_host_visible() {
                    return AllocFromRequirementsFilter::Forbidden;
                }

                if !need_visible && current_type.is_device_local() {
                    return AllocFromRequirementsFilter::Preferred;
                }

                if need_visible && want_cached && current_type.is_host_cached() {
                    return AllocFromRequirementsFilter::Preferred;
                }

                if need_visible
                    && !want_cached
                    && current_type.is_host_coherent()
                    && !current_type.is_host_cached()
                {
                    return AllocFromRequirementsFilter::Preferred;
                }

                AllocFromRequirementsFilter::Allowed
            };

            let first_loop = device
                .physical_device()
                .memory_types()
                .map(|t| (t, AllocFromRequirementsFilter::Preferred));
            let second_loop = device
                .physical_device()
                .memory_types()
                .map(|t| (t, AllocFromRequirementsFilter::Allowed));
            first_loop
                .chain(second_loop)
                .filter(|&(t, _)| (memory_requirements.memory_type_bits & (1 << t.id())) != 0)
                .find(|&(t, rq)| filter(t) == rq)
                .ok_or(RutabagaError::SpecViolation(
                    "unable to find required memory type",
                ))?
                .0
        };

        reqs.info = info;
        reqs.size = memory_requirements.size as u64;

        if memory_type.is_host_visible() {
            if memory_type.is_host_cached() {
                reqs.map_info = RUTABAGA_MAP_CACHE_CACHED;
            } else if memory_type.is_host_coherent() {
                reqs.map_info = RUTABAGA_MAP_CACHE_WC;
            }
        }

        reqs.vulkan_info = Some(VulkanInfo {
            memory_idx: memory_type.id() as u32,
            physical_device_idx: device.physical_device().index() as u32,
        });

        Ok(reqs)
    }

    fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
        let (unsafe_image, memory_requirements) = unsafe { self.create_image(reqs.info)? };

        let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?;

        let device = if self.has_integrated_gpu {
            self.devices
                .get(&PhysicalDeviceType::IntegratedGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        } else {
            self.devices
                .get(&PhysicalDeviceType::DiscreteGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        };

        let memory_type = device
            .physical_device()
            .memory_type_by_id(vulkan_info.memory_idx)
            .ok_or(RutabagaError::InvalidVulkanInfo)?;

        let (handle_type, rutabaga_type) =
            match device.enabled_extensions().ext_external_memory_dma_buf {
                true => (
                    ExternalMemoryHandleType {
                        dma_buf: true,
                        ..ExternalMemoryHandleType::none()
                    },
                    RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
                ),
                false => (
                    ExternalMemoryHandleType {
                        opaque_fd: true,
                        ..ExternalMemoryHandleType::none()
                    },
                    RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD,
                ),
            };

        let dedicated = match device.enabled_extensions().khr_dedicated_allocation {
            true => {
                if memory_requirements.prefer_dedicated {
                    DedicatedAlloc::Image(&unsafe_image)
                } else {
                    DedicatedAlloc::None
                }
            }
            false => DedicatedAlloc::None,
        };

        let device_memory = DeviceMemoryBuilder::new(device.clone(), memory_type.id(), reqs.size)
            .dedicated_info(dedicated)
            .export_info(handle_type)
            .build()?;

        let descriptor = device_memory.export_fd(handle_type)?.into();

        Ok(RutabagaHandle {
            os_handle: descriptor,
            handle_type: rutabaga_type,
        })
    }

    /// Implementations must map the memory associated with the `resource_id` upon success.
    fn import_and_map(
        &mut self,
        handle: RutabagaHandle,
        vulkan_info: VulkanInfo,
        size: u64,
    ) -> RutabagaResult<Box<dyn MappedRegion>> {
        let device = self
            .devices
            .values()
            .find(|device| {
                device.physical_device().index() as u32 == vulkan_info.physical_device_idx
            })
            .ok_or(RutabagaError::InvalidVulkanInfo)?;

        let handle_type = match handle.handle_type {
            RUTABAGA_MEM_HANDLE_TYPE_DMABUF => ExternalMemoryHandleType {
                dma_buf: true,
                ..ExternalMemoryHandleType::none()
            },
            RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD => ExternalMemoryHandleType {
                opaque_fd: true,
                ..ExternalMemoryHandleType::none()
            },
            _ => return Err(RutabagaError::InvalidRutabagaHandle),
        };

        let device_memory = DeviceMemoryBuilder::new(device.clone(), vulkan_info.memory_idx, size)
            .import_info(handle.os_handle.into(), handle_type)
            .build()?;
        let mapping = DeviceMemoryMapping::new(device.clone(), device_memory.clone(), 0, size, 0)?;

        Ok(Box::new(VulkanoMapping::new(mapping, size.try_into()?)))
    }
}

// Vulkano should really define an universal type that wraps all these errors, say
// "VulkanoError(e)".
impl From<InstanceCreationError> for RutabagaError {
    fn from(e: InstanceCreationError) -> RutabagaError {
        RutabagaError::VkInstanceCreationError(e)
    }
}

impl From<ImageCreationError> for RutabagaError {
    fn from(e: ImageCreationError) -> RutabagaError {
        RutabagaError::VkImageCreationError(e)
    }
}

impl From<DeviceCreationError> for RutabagaError {
    fn from(e: DeviceCreationError) -> RutabagaError {
        RutabagaError::VkDeviceCreationError(e)
    }
}

impl From<DeviceMemoryAllocError> for RutabagaError {
    fn from(e: DeviceMemoryAllocError) -> RutabagaError {
        RutabagaError::VkDeviceMemoryAllocError(e)
    }
}
<commit_msg>rutabaga_gfx: rutabaga_gralloc: Handle device creation failure<commit_after>// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! vulkano_gralloc: Implements swapchain allocation and memory mapping
//! using Vulkano.
//!
//! External code found at https://github.com/vulkano-rs/vulkano.

#![cfg(feature = "vulkano")]

use std::collections::BTreeMap as Map;
use std::convert::TryInto;
use std::iter::Empty;
use std::sync::Arc;

use base::MappedRegion;

use crate::rutabaga_gralloc::gralloc::{Gralloc, ImageAllocationInfo, ImageMemoryRequirements};
use crate::rutabaga_utils::*;

use vulkano::device::physical::{MemoryType, PhysicalDevice, PhysicalDeviceType};
use vulkano::device::{Device, DeviceCreationError, DeviceExtensions};
use vulkano::image::{
    sys, ImageCreateFlags, ImageCreationError, ImageDimensions, ImageUsage, SampleCount,
};

use vulkano::instance::{Instance, InstanceCreationError, InstanceExtensions, Version};

use vulkano::memory::{
    DedicatedAlloc, DeviceMemoryAllocError, DeviceMemoryBuilder, DeviceMemoryMapping,
    ExternalMemoryHandleType, MemoryRequirements,
};

use vulkano::memory::pool::AllocFromRequirementsFilter;
use vulkano::sync::Sharing;

/// A gralloc implementation capable of allocation `VkDeviceMemory`.
pub struct VulkanoGralloc {
    devices: Map<PhysicalDeviceType, Arc<Device>>,
    has_integrated_gpu: bool,
}

struct VulkanoMapping {
    mapping: DeviceMemoryMapping,
    size: usize,
}

impl VulkanoMapping {
    pub fn new(mapping: DeviceMemoryMapping, size: usize) -> VulkanoMapping {
        VulkanoMapping { mapping, size }
    }
}

unsafe impl MappedRegion for VulkanoMapping {
    /// Used for passing this region for hypervisor memory mappings.  We trust crosvm to use this
    /// safely.
    fn as_ptr(&self) -> *mut u8 {
        unsafe { self.mapping.as_ptr() }
    }

    /// Returns the size of the memory region in bytes.
    fn size(&self) -> usize {
        self.size
    }
}

impl VulkanoGralloc {
    /// Returns a new `VulkanGralloc' instance upon success.
    pub fn init() -> RutabagaResult<Box<dyn Gralloc>> {
        // Initialization copied from triangle.rs in Vulkano.  Look there for a more detailed
        // explanation of VK initialization.
        let instance_extensions = InstanceExtensions {
            khr_external_memory_capabilities: true,
            khr_get_physical_device_properties2: true,
            ..InstanceExtensions::none()
        };
        let instance = Instance::new(None, Version::V1_1, &instance_extensions, None)?;

        let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default();
        let mut has_integrated_gpu = false;

        for physical in PhysicalDevice::enumerate(&instance) {
            let queue_family = physical
                .queue_families()
                .find(|&q| {
                    // We take the first queue family that supports graphics.
                    q.supports_graphics()
                })
                .ok_or(RutabagaError::SpecViolation(
                    "need graphics queue family to proceed",
                ))?;

            let supported_extensions = physical.supported_extensions();

            let desired_extensions = DeviceExtensions {
                khr_dedicated_allocation: true,
                khr_get_memory_requirements2: true,
                khr_external_memory: true,
                khr_external_memory_fd: true,
                ext_external_memory_dma_buf: true,
                ..DeviceExtensions::none()
            };

            let intersection = supported_extensions.intersection(&desired_extensions);

            if let Ok(device, mut _queues) = Device::new(
                physical,
                physical.supported_features(),
                &intersection,
                [(queue_family, 0.5)].iter().cloned(),
            ) {
                let device_type = device.physical_device().properties().device_type;
                if device_type == PhysicalDeviceType::IntegratedGpu {
                    has_integrated_gpu = true
                }

                // If we have two devices of the same type (two integrated GPUs), the old value is
                // dropped.  Vulkano is verbose enough such that a keener selection algorithm may
                // be used, but the need for such complexity does not seem to exist now.
                devices.insert(device_type, device);
            };
        }

        if devices.is_empty() {
            return Err(RutabagaError::SpecViolation(
                "no matching VK devices available",
            ));
        }

        Ok(Box::new(VulkanoGralloc {
            devices,
            has_integrated_gpu,
        }))
    }

    // This function is used safely in this module because gralloc does not:
    //
    //  (1) bind images to any memory.
    //  (2) transition the layout of images.
    //  (3) transfer ownership of images between queues.
    //
    // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec.
    unsafe fn create_image(
        &mut self,
        info: ImageAllocationInfo,
    ) -> RutabagaResult<(sys::UnsafeImage, MemoryRequirements)> {
        let device = if self.has_integrated_gpu {
            self.devices
                .get(&PhysicalDeviceType::IntegratedGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        } else {
            self.devices
                .get(&PhysicalDeviceType::DiscreteGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        };

        let usage = match info.flags.uses_rendering() {
            true => ImageUsage {
                color_attachment: true,
                ..ImageUsage::none()
            },
            false => ImageUsage {
                sampled: true,
                ..ImageUsage::none()
            },
        };

        // Reasonable bounds on image width.
        if info.width == 0 || info.width > 4096 {
            return Err(RutabagaError::InvalidGrallocDimensions);
        }

        // Reasonable bounds on image height.
        if info.height == 0 || info.height > 4096 {
            return Err(RutabagaError::InvalidGrallocDimensions);
        }

        let vulkan_format = info.drm_format.vulkan_format()?;
        let (unsafe_image, memory_requirements) = sys::UnsafeImage::new(
            device.clone(),
            usage,
            vulkan_format,
            ImageCreateFlags::none(),
            ImageDimensions::Dim2d {
                width: info.width,
                height: info.height,
                array_layers: 1,
            },
            SampleCount::Sample1,
            1, /* mipmap count */
            Sharing::Exclusive::<Empty<_>>,
            true,  /* linear images only currently */
            false, /* not preinitialized */
        )?;

        Ok((unsafe_image, memory_requirements))
    }
}

impl Gralloc for VulkanoGralloc {
    fn supports_external_gpu_memory(&self) -> bool {
        for device in self.devices.values() {
            if !device.enabled_extensions().khr_external_memory {
                return false;
            }
        }

        true
    }

    fn supports_dmabuf(&self) -> bool {
        for device in self.devices.values() {
            if !device.enabled_extensions().ext_external_memory_dma_buf {
                return false;
            }
        }

        true
    }

    fn get_image_memory_requirements(
        &mut self,
        info: ImageAllocationInfo,
    ) -> RutabagaResult<ImageMemoryRequirements> {
        let mut reqs: ImageMemoryRequirements = Default::default();

        let (unsafe_image, memory_requirements) = unsafe { self.create_image(info)? };

        let device = if self.has_integrated_gpu {
            self.devices
                .get(&PhysicalDeviceType::IntegratedGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        } else {
            self.devices
                .get(&PhysicalDeviceType::DiscreteGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        };

        let planar_layout = info.drm_format.planar_layout()?;

        // Safe because we created the image with the linear bit set and verified the format is
        // not a depth or stencil format.  We are also using the correct image aspect.  Vulkano
        // will panic if we are not.
        for plane in 0..planar_layout.num_planes {
            let aspect = info.drm_format.vulkan_image_aspect(plane)?;
            let layout = unsafe { unsafe_image.multiplane_color_layout(aspect) };
            reqs.strides[plane] = layout.row_pitch as u32;
            reqs.offsets[plane] = layout.offset as u32;
        }

        let need_visible = info.flags.host_visible();
        let want_cached = info.flags.host_cached();

        let memory_type = {
            let filter = |current_type: MemoryType| {
                if need_visible && !current_type.is_host_visible() {
                    return AllocFromRequirementsFilter::Forbidden;
                }

                if !need_visible && current_type.is_device_local() {
                    return AllocFromRequirementsFilter::Preferred;
                }

                if need_visible && want_cached && current_type.is_host_cached() {
                    return AllocFromRequirementsFilter::Preferred;
                }

                if need_visible
                    && !want_cached
                    && current_type.is_host_coherent()
                    && !current_type.is_host_cached()
                {
                    return AllocFromRequirementsFilter::Preferred;
                }

                AllocFromRequirementsFilter::Allowed
            };

            let first_loop = device
                .physical_device()
                .memory_types()
                .map(|t| (t, AllocFromRequirementsFilter::Preferred));
            let second_loop = device
                .physical_device()
                .memory_types()
                .map(|t| (t, AllocFromRequirementsFilter::Allowed));
            first_loop
                .chain(second_loop)
                .filter(|&(t, _)| (memory_requirements.memory_type_bits & (1 << t.id())) != 0)
                .find(|&(t, rq)| filter(t) == rq)
                .ok_or(RutabagaError::SpecViolation(
                    "unable to find required memory type",
                ))?
                .0
        };

        reqs.info = info;
        reqs.size = memory_requirements.size as u64;

        if memory_type.is_host_visible() {
            if memory_type.is_host_cached() {
                reqs.map_info = RUTABAGA_MAP_CACHE_CACHED;
            } else if memory_type.is_host_coherent() {
                reqs.map_info = RUTABAGA_MAP_CACHE_WC;
            }
        }

        reqs.vulkan_info = Some(VulkanInfo {
            memory_idx: memory_type.id() as u32,
            physical_device_idx: device.physical_device().index() as u32,
        });

        Ok(reqs)
    }

    fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
        let (unsafe_image, memory_requirements) = unsafe { self.create_image(reqs.info)? };

        let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?;

        let device = if self.has_integrated_gpu {
            self.devices
                .get(&PhysicalDeviceType::IntegratedGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        } else {
            self.devices
                .get(&PhysicalDeviceType::DiscreteGpu)
                .ok_or(RutabagaError::InvalidGrallocGpuType)?
        };

        let memory_type = device
            .physical_device()
            .memory_type_by_id(vulkan_info.memory_idx)
            .ok_or(RutabagaError::InvalidVulkanInfo)?;

        let (handle_type, rutabaga_type) =
            match device.enabled_extensions().ext_external_memory_dma_buf {
                true => (
                    ExternalMemoryHandleType {
                        dma_buf: true,
                        ..ExternalMemoryHandleType::none()
                    },
                    RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
                ),
                false => (
                    ExternalMemoryHandleType {
                        opaque_fd: true,
                        ..ExternalMemoryHandleType::none()
                    },
                    RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD,
                ),
            };

        let dedicated = match device.enabled_extensions().khr_dedicated_allocation {
            true => {
                if memory_requirements.prefer_dedicated {
                    DedicatedAlloc::Image(&unsafe_image)
                } else {
                    DedicatedAlloc::None
                }
            }
            false => DedicatedAlloc::None,
        };

        let device_memory = DeviceMemoryBuilder::new(device.clone(), memory_type.id(), reqs.size)
            .dedicated_info(dedicated)
            .export_info(handle_type)
            .build()?;

        let descriptor = device_memory.export_fd(handle_type)?.into();

        Ok(RutabagaHandle {
            os_handle: descriptor,
            handle_type: rutabaga_type,
        })
    }

    /// Implementations must map the memory associated with the `resource_id` upon success.
    fn import_and_map(
        &mut self,
        handle: RutabagaHandle,
        vulkan_info: VulkanInfo,
        size: u64,
    ) -> RutabagaResult<Box<dyn MappedRegion>> {
        let device = self
            .devices
            .values()
            .find(|device| {
                device.physical_device().index() as u32 == vulkan_info.physical_device_idx
            })
            .ok_or(RutabagaError::InvalidVulkanInfo)?;

        let handle_type = match handle.handle_type {
            RUTABAGA_MEM_HANDLE_TYPE_DMABUF => ExternalMemoryHandleType {
                dma_buf: true,
                ..ExternalMemoryHandleType::none()
            },
            RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD => ExternalMemoryHandleType {
                opaque_fd: true,
                ..ExternalMemoryHandleType::none()
            },
            _ => return Err(RutabagaError::InvalidRutabagaHandle),
        };

        let device_memory = DeviceMemoryBuilder::new(device.clone(), vulkan_info.memory_idx, size)
            .import_info(handle.os_handle.into(), handle_type)
            .build()?;
        let mapping = DeviceMemoryMapping::new(device.clone(), device_memory.clone(), 0, size, 0)?;

        Ok(Box::new(VulkanoMapping::new(mapping, size.try_into()?)))
    }
}

// Vulkano should really define an universal type that wraps all these errors, say
// "VulkanoError(e)".
impl From<InstanceCreationError> for RutabagaError {
    fn from(e: InstanceCreationError) -> RutabagaError {
        RutabagaError::VkInstanceCreationError(e)
    }
}

impl From<ImageCreationError> for RutabagaError {
    fn from(e: ImageCreationError) -> RutabagaError {
        RutabagaError::VkImageCreationError(e)
    }
}

impl From<DeviceCreationError> for RutabagaError {
    fn from(e: DeviceCreationError) -> RutabagaError {
        RutabagaError::VkDeviceCreationError(e)
    }
}

impl From<DeviceMemoryAllocError> for RutabagaError {
    fn from(e: DeviceMemoryAllocError) -> RutabagaError {
        RutabagaError::VkDeviceMemoryAllocError(e)
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use ra_db::Cancelable;
use ra_syntax::ast::{self, NameOwner, StructFlavor};

use crate::{
    DefId, Name, AsName,
    db::HirDatabase,
    type_ref::TypeRef,
};

pub struct Struct {
    def_id: DefId,
}

impl Struct {
    pub(crate) fn new(def_id: DefId) -> Self {
        Struct { def_id }
    }

    pub fn def_id(&self) -> DefId {
        self.def_id
    }

    pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
        Ok(db.struct_data(self.def_id)?.variant_data.clone())
    }

    pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<StructData>> {
        Ok(db.struct_data(self.def_id)?)
    }

    pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
        Ok(db.struct_data(self.def_id)?.name.clone())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
    name: Option<Name>,
    variant_data: Arc<VariantData>,
}

impl StructData {
    pub(crate) fn new(struct_def: &ast::StructDef) -> StructData {
        let name = struct_def.name().map(|n| n.as_name());
        let variant_data = VariantData::new(struct_def.flavor());
        let variant_data = Arc::new(variant_data);
        StructData { name, variant_data }
    }

    pub fn name(&self) -> Option<&Name> {
        self.name.as_ref()
    }

    pub fn variant_data(&self) -> &Arc<VariantData> {
        &self.variant_data
    }
}

pub struct Enum {
    def_id: DefId,
}

impl Enum {
    pub(crate) fn new(def_id: DefId) -> Self {
        Enum { def_id }
    }

    pub fn def_id(&self) -> DefId {
        self.def_id
    }

    pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
        Ok(db.enum_data(self.def_id)?.name.clone())
    }

    pub fn variants(&self, db: &impl HirDatabase) -> Cancelable<Vec<(Name, Arc<VariantData>)>> {
        Ok(db.enum_data(self.def_id)?.variants.clone())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
    name: Option<Name>,
    variants: Vec<(Name, Arc<VariantData>)>,
}

impl EnumData {
    pub(crate) fn new(enum_def: &ast::EnumDef) -> Self {
        let name = enum_def.name().map(|n| n.as_name());
        let variants = if let Some(evl) = enum_def.variant_list() {
            evl.variants()
                .map(|v| {
                    (
                        v.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
                        Arc::new(VariantData::new(v.flavor())),
                    )
                })
                .collect()
        } else {
            Vec::new()
        };
        EnumData { name, variants }
    }
}

/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructField {
    name: Name,
    type_ref: TypeRef,
}

impl StructField {
    pub fn name(&self) -> Name {
        self.name.clone()
    }
    pub fn type_ref(&self) -> &TypeRef {
        &self.type_ref
    }
}

/// Fields of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
    Struct(Vec<StructField>),
    Tuple(Vec<StructField>),
    Unit,
}

impl VariantData {
    pub fn new(flavor: StructFlavor) -> Self {
        match flavor {
            StructFlavor::Tuple(fl) => {
                let fields = fl
                    .fields()
                    .enumerate()
                    .map(|(i, fd)| StructField {
                        name: Name::tuple_field_name(i),
                        type_ref: TypeRef::from_ast_opt(fd.type_ref()),
                    })
                    .collect();
                VariantData::Tuple(fields)
            }
            StructFlavor::Named(fl) => {
                let fields = fl
                    .fields()
                    .map(|fd| StructField {
                        name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
                        type_ref: TypeRef::from_ast_opt(fd.type_ref()),
                    })
                    .collect();
                VariantData::Struct(fields)
            }
            StructFlavor::Unit => VariantData::Unit,
        }
    }

    pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
        self.fields()
            .iter()
            .find(|f| f.name == *field_name)
            .map(|f| &f.type_ref)
    }

    pub fn fields(&self) -> &[StructField] {
        match *self {
            VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
            _ => &[],
        }
    }
    pub fn is_struct(&self) -> bool {
        if let VariantData::Struct(..) = *self {
            true
        } else {
            false
        }
    }
    pub fn is_tuple(&self) -> bool {
        if let VariantData::Tuple(..) = *self {
            true
        } else {
            false
        }
    }
    pub fn is_unit(&self) -> bool {
        if let VariantData::Unit = *self {
            true
        } else {
            false
        }
    }
}
<commit_msg>convert some if-lets to match<commit_after>use std::sync::Arc;

use ra_db::Cancelable;
use ra_syntax::ast::{self, NameOwner, StructFlavor};

use crate::{
    DefId, Name, AsName,
    db::HirDatabase,
    type_ref::TypeRef,
};

pub struct Struct {
    def_id: DefId,
}

impl Struct {
    pub(crate) fn new(def_id: DefId) -> Self {
        Struct { def_id }
    }

    pub fn def_id(&self) -> DefId {
        self.def_id
    }

    pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
        Ok(db.struct_data(self.def_id)?.variant_data.clone())
    }

    pub fn struct_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<StructData>> {
        Ok(db.struct_data(self.def_id)?)
    }

    pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
        Ok(db.struct_data(self.def_id)?.name.clone())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructData {
    name: Option<Name>,
    variant_data: Arc<VariantData>,
}

impl StructData {
    pub(crate) fn new(struct_def: &ast::StructDef) -> StructData {
        let name = struct_def.name().map(|n| n.as_name());
        let variant_data = VariantData::new(struct_def.flavor());
        let variant_data = Arc::new(variant_data);
        StructData { name, variant_data }
    }

    pub fn name(&self) -> Option<&Name> {
        self.name.as_ref()
    }

    pub fn variant_data(&self) -> &Arc<VariantData> {
        &self.variant_data
    }
}

pub struct Enum {
    def_id: DefId,
}

impl Enum {
    pub(crate) fn new(def_id: DefId) -> Self {
        Enum { def_id }
    }

    pub fn def_id(&self) -> DefId {
        self.def_id
    }

    pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
        Ok(db.enum_data(self.def_id)?.name.clone())
    }

    pub fn variants(&self, db: &impl HirDatabase) -> Cancelable<Vec<(Name, Arc<VariantData>)>> {
        Ok(db.enum_data(self.def_id)?.variants.clone())
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumData {
    name: Option<Name>,
    variants: Vec<(Name, Arc<VariantData>)>,
}

impl EnumData {
    pub(crate) fn new(enum_def: &ast::EnumDef) -> Self {
        let name = enum_def.name().map(|n| n.as_name());
        let variants = if let Some(evl) = enum_def.variant_list() {
            evl.variants()
                .map(|v| {
                    (
                        v.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
                        Arc::new(VariantData::new(v.flavor())),
                    )
                })
                .collect()
        } else {
            Vec::new()
        };
        EnumData { name, variants }
    }
}

/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructField {
    name: Name,
    type_ref: TypeRef,
}

impl StructField {
    pub fn name(&self) -> Name {
        self.name.clone()
    }
    pub fn type_ref(&self) -> &TypeRef {
        &self.type_ref
    }
}

/// Fields of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
    Struct(Vec<StructField>),
    Tuple(Vec<StructField>),
    Unit,
}

impl VariantData {
    pub fn new(flavor: StructFlavor) -> Self {
        match flavor {
            StructFlavor::Tuple(fl) => {
                let fields = fl
                    .fields()
                    .enumerate()
                    .map(|(i, fd)| StructField {
                        name: Name::tuple_field_name(i),
                        type_ref: TypeRef::from_ast_opt(fd.type_ref()),
                    })
                    .collect();
                VariantData::Tuple(fields)
            }
            StructFlavor::Named(fl) => {
                let fields = fl
                    .fields()
                    .map(|fd| StructField {
                        name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
                        type_ref: TypeRef::from_ast_opt(fd.type_ref()),
                    })
                    .collect();
                VariantData::Struct(fields)
            }
            StructFlavor::Unit => VariantData::Unit,
        }
    }

    pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
        self.fields()
            .iter()
            .find(|f| f.name == *field_name)
            .map(|f| &f.type_ref)
    }

    pub fn fields(&self) -> &[StructField] {
        match *self {
            VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
            _ => &[],
        }
    }
    pub fn is_struct(&self) -> bool {
        match self {
            VariantData::Struct(..) => true,
            _ => false,
        }
    }
    pub fn is_tuple(&self) -> bool {
        match self {
            VariantData::Tuple(..) => true,
            _ => false,
        }
    }
    pub fn is_unit(&self) -> bool {
        match self {
            VariantData::Unit => true,
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>use core::prelude::*;

use aspace::mapflag;
use con;
use con::write;
use cpu;
use process;
use process::Handle;
use process::Process;
use start32::kernel_base;
use util::abort;

static log_syscall : bool = false;
static log_transfer_message : bool = false;
static log_portio : bool = false;
static log_hmod : bool = false;

static log_recv : bool = false;
static log_ipc : bool = false;

pub mod nr {
    #![allow(dead_code)]
    pub static RECV : uint = 0;
    pub static MAP : uint = 1;
    pub static PFAULT : uint = 2;
    pub static UNMAP : uint = 3;
    pub static HMOD : uint = 4;
    pub static NEWPROC : uint = 5;
    pub static WRITE : uint = 6;
    pub static PORTIO : uint = 7;
    pub static GRANT : uint = 8;
    pub static PULSE : uint = 9;

    pub static USER : uint = 16;

    pub static MSG_MASK : uint = 0xff;
    pub static MSG_KIND_MASK : uint = 0x300;
    pub static MSG_KIND_SEND : uint = 0x000;
    pub static MSG_KIND_CALL : uint = 0x100;
}

// Note: tail-called from the syscall code, "return" by switching to a process.
#[no_mangle]
pub fn syscall(
    arg0: uint,
    arg1: uint,
    arg2: uint,
    arg3: uint,
    arg4: uint,
    arg5: uint,
    nr : uint, // saved_rax
) -> ! {
    use syscall::nr::*;

    let p = unsafe { cpu().get_process() };
    // FIXME cpu.leave_proc?
    p.unset(process::Running);
    p.set(process::FastRet);

    if log_syscall {
        write("syscall! nr=");
        con::writeUInt(nr);
        write(" from process ");
        con::writeMutPtr(p);
        con::newline();
    }

    match nr {
    RECV => ipc_recv(p, arg0),
    MAP => syscall_map(p, arg0, arg1, arg2, arg3, arg4),
    PFAULT => syscall_pfault(p, arg1, arg2), // arg0 is always 0
    HMOD => syscall_hmod(p, arg0, arg1, arg2),
    PORTIO => syscall_portio(p, arg0, arg1, arg2),
    WRITE => {
        con::putc(arg0 as u8 as char);
        cpu().syscall_return(p, 0);
    },
    _ if nr >= USER => {
        match nr & MSG_KIND_MASK {
            MSG_KIND_CALL => ipc_call(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            MSG_KIND_SEND => ipc_send(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            _ => abort("Unknown IPC kind")
        }
    },
    _ => abort("Unhandled syscall"),
    }

    if p.is_runnable() {
        abort("process not blocked at return");
    }

    unsafe { cpu().run(); }
}

fn ipc_call(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
    arg3: uint, arg4: uint, arg5: uint) {
    if log_ipc {
        con::writeMutPtr(p);
        write(" ipc_call to ");
        con::writeUInt(to);
    }

    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        if log_ipc {
            write("==> process ");
            con::writeMutPtr(h.process());
            con::newline();
        }

        p.set(process::InSend);
        p.set(process::InRecv);
        p.regs.rdi = to;
        send_or_block(p, h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_call: no recipient")
    }
    if log_ipc {
        write("ipc_call: blocked\n");
    }
}

fn transfer_set_handle(target: &mut Process, source: &mut Process) {
    let mut rcpt = target.regs.rdi;
    let from = source.regs.rdi;

    let h = source.find_handle(from).unwrap();
    if rcpt == 0 {
        rcpt = h.other().unwrap().id();
    } else if !target.find_handle(rcpt).is_some() {
        match h.other() {
            // Already associated, "junk" the fresh handle and just update the
            // recipient-side handle.
            Some(g) => rcpt = g.id(),
            // Associate handles now.
            None => {
                let g = target.new_handle(rcpt, source);
                g.associate(h);
            },
        }
    } else {
        // TODO Assert that rcpt <-> from. (But the caller is responsible for
        // checking that first.)
    }
    if log_transfer_message {
        write("transfer_set_handle: rcpt=");
        con::writeHex(rcpt);
        write(" for ");
        con::writeHex(target.regs.rdi);
        con::newline();
    }
    target.regs.rdi = rcpt;
}

fn transfer_message(target: &mut Process, source: &mut Process) -> ! {
    transfer_set_handle(target, source);

    if log_transfer_message {
        write("transfer_message ");
        con::writeMutPtr(target);
        write(" <- ");
        con::writeMutPtr(source);
        con::newline();
    }

    target.regs.rax = source.regs.rax;
    target.regs.rdi = source.regs.rdi;
    target.regs.rsi = source.regs.rsi;
    target.regs.rdx = source.regs.rdx;
    target.regs.r8 = source.regs.r8;
    target.regs.r9 = source.regs.r9;
    target.regs.r10 = source.regs.r10;

    target.unset(process::InRecv);
    source.unset(process::InSend);

    let c = cpu();
    c.queue(target);
    if source.ipc_state() == 0 {
        c.queue(source);
    }
    unsafe { c.run(); }
}

fn send_or_block(sender : &mut Process, h : &mut Handle, msg: uint,
        arg1: uint, arg2: uint, arg3: uint, arg4: uint, arg5: uint) {
    let p = h.process();

    // Save regs - either we'll copy these in transfer_message or we'll
    // need to store them until later on when the transfer can finish.
    sender.regs.rax = msg;
    sender.regs.rdi = h.id();
    sender.regs.rsi = arg1;
    sender.regs.rdx = arg2;
    sender.regs.r10 = arg3;
    sender.regs.r8 = arg4;
    sender.regs.r9 = arg5;

    let other_id = match h.other() {
        Some(g) => g.id(),
        None => 0,
    };

    // p is the recipient, the sender is in g.process().
    if p.ipc_state() == process::InRecv.mask() {
        let rcpt = h.process().regs.rdi;
        // Check the receiving process' receipt handle
        //   0 ==> transfer
        //   !0, connected to our handle ==> transfer
        //   !0, fresh ==> transfer
        //   !0 otherwise ==> block
        if rcpt == other_id || !p.find_handle(rcpt).is_some() {
            transfer_message(p, sender);
        }
    }

    if log_ipc {
        write("send_or_block: ");
        con::writeMutPtr(sender);
        write(" waits for ");
        con::writeMutPtr(p);
        con::newline();
    }
    p.add_waiter(sender)
}

fn ipc_send(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
        arg3: uint, arg4: uint, arg5: uint) {
    if log_ipc {
        con::writeMutPtr(p);
        write(" ipc_send to ");
        con::writeUInt(to);
        con::newline();
    }

    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        p.set(process::InSend);
        send_or_block(p, h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_send: no recipient")
    }
}

fn ipc_recv(p : &mut Process, from : uint) {
    let mut handle = None;
    if from != 0 {
        handle = p.find_handle(from);
    }

    if log_recv {
        con::writeMutPtr(p);
        write(" recv from ");
        con::writeUInt(from);
    }

    p.set(process::InRecv);
    p.regs.rdi = from;
    match handle {
        Some(h) => {
            if log_recv {
                write(" ==> process ");
                con::writeMutPtr(h.process());
                con::newline();
            }
            recv(p, h)
        },
        None => {
            if log_recv && from != 0 {
                write(" ==> fresh\n");
            }
            recv_from_any(p, from)
        }
    }
}

fn recv(p: &mut Process, handle: &mut Handle) {
    let rcpt = handle.process();
    if rcpt.is(process::InSend) {
        abort("recv-from-specific not implemented");
    } else {
        rcpt.add_waiter(p);
    }
}

fn recv_from_any(p : &mut Process, _id: uint) {
    let mut sender = None;
    for waiter in p.waiters.iter() {
        if waiter.is(process::InSend) {
            sender = Some(waiter);
            break;
        }
    }
    match sender {
        Some(s) => {
            p.remove_waiter(s);
            transfer_message(p, s);
        },
        None => ()
    }

    if log_recv {
        write("recv: no waiters for ");
        con::writeMutPtr(p);
        con::newline();
    }

    // TODO Look for pending pulse
    // 2. Look for pending pulses
    // 3. Switch next
}

fn syscall_map(p: &mut Process, handle: uint, mut prot: uint, addr: uint, mut offset: uint, size: uint) {
    prot &= mapflag::UserAllowed;
    // TODO Check (and return failure) on:
    // * unaligned addr, offset, size (must be page-aligned)
    if (prot & mapflag::DMA) == mapflag::DMA {
        offset = match cpu().memory.alloc_frame() {
            None => 0,
            Some(p) => p as uint - kernel_base,
        }
    }

    p.aspace().map_range(addr, addr + size, handle, (offset - addr) | prot);

    if (prot & mapflag::Phys) == 0 {
        offset = 0;
    }
    cpu().syscall_return(p, offset);
}

fn syscall_pfault(p : &mut Process, mut vaddr: uint, access: uint) {
    vaddr &= !0xfff;

    // set fault address
    p.fault_addr = vaddr;
    p.regs.rsi = vaddr;
    p.regs.rdx = access & mapflag::RWX;
    // Look up vaddr, get handle, offset and flags
    let card = p.aspace().mapcard_find_def(vaddr);
    p.regs.rsi += card.offset; // proc.rsi is now translated into offset
    p.regs.rdi = card.handle;
    p.set(process::PFault);

    // Now do the equivalent of sendrcv with rdi=handle, rsi=offset, rdx=flags
}

fn syscall_hmod(p : &mut Process, id: uint, rename: uint, copy: uint) {
    let handle = p.find_handle(id);
    if log_hmod {
        con::writeMutPtr(p);
        write(" hmod: id="); con::writeHex(id);
        write(" rename="); con::writeHex(rename);
        write(" copy="); con::writeHex(copy);
        con::newline();
    }
    match handle {
    None => (),
    Some(h) => {
        // Fresh/dissociated handle for the same process as the original
        if copy != 0 {
            p.new_handle(copy, h.process());
        }
        if rename == 0 {
            p.delete_handle(h);
        } else if rename != id {
            p.rename_handle(h, rename);
        }
    }
    }
    cpu().syscall_return(p, 0);
}

fn syscall_portio(p : &mut Process, port : uint, op : uint, data: uint) -> ! {
    let mut res : uint = 0;
    unsafe { match op {
    0x01 => asm!("inb %dx, %al" : "={al}"(res) : "{dx}"(port)),
    0x02 => asm!("inw %dx, %ax" : "={ax}"(res) : "{dx}"(port)),
    0x04 => asm!("inl %dx, %eax" : "={eax}"(res) : "{dx}"(port)),
    0x11 => asm!("outb %al, %dx" :: "{al}"(data), "{dx}"(port)),
    0x12 => asm!("outw %ax, %dx" :: "{ax}"(data), "{dx}"(port)),
    0x14 => asm!("outl %eax, %dx" :: "{eax}"(data), "{dx}"(port)),
    _ => abort("unhandled portio operation")
    } }
    if log_portio {
        write("portio: port="); con::writeHex(port & 0xffff);
        write(" op="); con::writeUInt(op);
        write(" data="); con::writeHex(data);
        write(" res="); con::writeHex(res);
        con::newline();
    }
    cpu().syscall_return(p, res);
}
<commit_msg>Don't fastret from transfer_message, it won't set message regs<commit_after>use core::prelude::*;

use aspace::mapflag;
use con;
use con::write;
use cpu;
use process;
use process::Handle;
use process::Process;
use start32::kernel_base;
use util::abort;

static log_syscall : bool = false;
static log_transfer_message : bool = false;
static log_portio : bool = false;
static log_hmod : bool = false;

static log_recv : bool = false;
static log_ipc : bool = false;

pub mod nr {
    #![allow(dead_code)]
    pub static RECV : uint = 0;
    pub static MAP : uint = 1;
    pub static PFAULT : uint = 2;
    pub static UNMAP : uint = 3;
    pub static HMOD : uint = 4;
    pub static NEWPROC : uint = 5;
    pub static WRITE : uint = 6;
    pub static PORTIO : uint = 7;
    pub static GRANT : uint = 8;
    pub static PULSE : uint = 9;

    pub static USER : uint = 16;

    pub static MSG_MASK : uint = 0xff;
    pub static MSG_KIND_MASK : uint = 0x300;
    pub static MSG_KIND_SEND : uint = 0x000;
    pub static MSG_KIND_CALL : uint = 0x100;
}

// Note: tail-called from the syscall code, "return" by switching to a process.
#[no_mangle]
pub fn syscall(
    arg0: uint,
    arg1: uint,
    arg2: uint,
    arg3: uint,
    arg4: uint,
    arg5: uint,
    nr : uint, // saved_rax
) -> ! {
    use syscall::nr::*;

    let p = unsafe { cpu().get_process() };
    // FIXME cpu.leave_proc?
    p.unset(process::Running);
    p.set(process::FastRet);

    if log_syscall {
        write("syscall! nr=");
        con::writeUInt(nr);
        write(" from process ");
        con::writeMutPtr(p);
        con::newline();
    }

    match nr {
    RECV => ipc_recv(p, arg0),
    MAP => syscall_map(p, arg0, arg1, arg2, arg3, arg4),
    PFAULT => syscall_pfault(p, arg1, arg2), // arg0 is always 0
    HMOD => syscall_hmod(p, arg0, arg1, arg2),
    PORTIO => syscall_portio(p, arg0, arg1, arg2),
    WRITE => {
        con::putc(arg0 as u8 as char);
        cpu().syscall_return(p, 0);
    },
    _ if nr >= USER => {
        match nr & MSG_KIND_MASK {
            MSG_KIND_CALL => ipc_call(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            MSG_KIND_SEND => ipc_send(p, nr & MSG_MASK, arg0, arg1, arg2, arg3, arg4, arg5),
            _ => abort("Unknown IPC kind")
        }
    },
    _ => abort("Unhandled syscall"),
    }

    if p.is_runnable() {
        abort("process not blocked at return");
    }

    unsafe { cpu().run(); }
}

fn ipc_call(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
    arg3: uint, arg4: uint, arg5: uint) {
    if log_ipc {
        con::writeMutPtr(p);
        write(" ipc_call to ");
        con::writeUInt(to);
    }

    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        if log_ipc {
            write("==> process ");
            con::writeMutPtr(h.process());
            con::newline();
        }

        p.set(process::InSend);
        p.set(process::InRecv);
        p.regs.rdi = to;
        send_or_block(p, h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_call: no recipient")
    }
    if log_ipc {
        write("ipc_call: blocked\n");
    }
}

fn transfer_set_handle(target: &mut Process, source: &mut Process) {
    let mut rcpt = target.regs.rdi;
    let from = source.regs.rdi;

    let h = source.find_handle(from).unwrap();
    if rcpt == 0 {
        rcpt = h.other().unwrap().id();
    } else if !target.find_handle(rcpt).is_some() {
        match h.other() {
            // Already associated, "junk" the fresh handle and just update the
            // recipient-side handle.
            Some(g) => rcpt = g.id(),
            // Associate handles now.
            None => {
                let g = target.new_handle(rcpt, source);
                g.associate(h);
            },
        }
    } else {
        // TODO Assert that rcpt <-> from. (But the caller is responsible for
        // checking that first.)
    }
    if log_transfer_message {
        write("transfer_set_handle: rcpt=");
        con::writeHex(rcpt);
        write(" for ");
        con::writeHex(target.regs.rdi);
        con::newline();
    }
    target.regs.rdi = rcpt;
}

fn transfer_message(target: &mut Process, source: &mut Process) -> ! {
    transfer_set_handle(target, source);

    if log_transfer_message {
        write("transfer_message ");
        con::writeMutPtr(target);
        write(" <- ");
        con::writeMutPtr(source);
        con::newline();
    }

    // FIXME Should use special fastret for message passing instead of
    // unsetting fastret and setting everything via the process struct.
    // Probably something like:
    // c.ipc_return(target, transfer_set_handle(...), source)
    // after updating source process runnability and state.

    target.regs.rax = source.regs.rax;
    // rdi is set by transfer_set_handle
    target.regs.rsi = source.regs.rsi;
    target.regs.rdx = source.regs.rdx;
    target.regs.r8 = source.regs.r8;
    target.regs.r9 = source.regs.r9;
    target.regs.r10 = source.regs.r10;

    target.unset(process::InRecv);
    target.unset(process::FastRet);
    source.unset(process::InSend);

    let c = cpu();
    c.queue(target);

    // FIXME If source was on target's wait queue, we don't clear it here.
    if source.ipc_state() == 0 {
        c.queue(source);
    }
    unsafe { c.run(); }
}

fn send_or_block(sender : &mut Process, h : &mut Handle, msg: uint,
        arg1: uint, arg2: uint, arg3: uint, arg4: uint, arg5: uint) {
    let p = h.process();

    // Save regs - either we'll copy these in transfer_message or we'll
    // need to store them until later on when the transfer can finish.
    sender.regs.rax = msg;
    sender.regs.rdi = h.id();
    sender.regs.rsi = arg1;
    sender.regs.rdx = arg2;
    sender.regs.r10 = arg3;
    sender.regs.r8 = arg4;
    sender.regs.r9 = arg5;

    let other_id = match h.other() {
        Some(g) => g.id(),
        None => 0,
    };

    // p is the recipient, the sender is in g.process().
    if p.ipc_state() == process::InRecv.mask() {
        let rcpt = h.process().regs.rdi;
        // Check the receiving process' receipt handle
        //   0 ==> transfer
        //   !0, connected to our handle ==> transfer
        //   !0, fresh ==> transfer
        //   !0 otherwise ==> block
        if rcpt == other_id || !p.find_handle(rcpt).is_some() {
            transfer_message(p, sender);
        }
    }

    if log_ipc {
        write("send_or_block: ");
        con::writeMutPtr(sender);
        write(" waits for ");
        con::writeMutPtr(p);
        con::newline();
    }
    p.add_waiter(sender)
}

fn ipc_send(p : &mut Process, msg : uint, to : uint, arg1: uint, arg2: uint,
        arg3: uint, arg4: uint, arg5: uint) {
    if log_ipc {
        con::writeMutPtr(p);
        write(" ipc_send to ");
        con::writeUInt(to);
        con::newline();
    }

    let handle = p.find_handle(to);
    match handle {
    Some(h) => {
        p.set(process::InSend);
        send_or_block(p, h, msg, arg1, arg2, arg3, arg4, arg5);
    },
    None => abort("ipc_send: no recipient")
    }
}

fn ipc_recv(p : &mut Process, from : uint) {
    let mut handle = None;
    if from != 0 {
        handle = p.find_handle(from);
    }

    if log_recv {
        con::writeMutPtr(p);
        write(" recv from ");
        con::writeUInt(from);
    }

    p.set(process::InRecv);
    p.regs.rdi = from;
    match handle {
        Some(h) => {
            if log_recv {
                write(" ==> process ");
                con::writeMutPtr(h.process());
                con::newline();
            }
            recv(p, h)
        },
        None => {
            if log_recv && from != 0 {
                write(" ==> fresh\n");
            }
            recv_from_any(p, from)
        }
    }
}

fn recv(p: &mut Process, handle: &mut Handle) {
    let rcpt = handle.process();
    if rcpt.is(process::InSend) {
        abort("recv-from-specific not implemented");
    } else {
        rcpt.add_waiter(p);
    }
}

fn recv_from_any(p : &mut Process, _id: uint) {
    let mut sender = None;
    for waiter in p.waiters.iter() {
        if waiter.is(process::InSend) {
            sender = Some(waiter);
            break;
        }
    }
    match sender {
        Some(s) => {
            p.remove_waiter(s);
            transfer_message(p, s);
        },
        None => ()
    }

    if log_recv {
        write("recv: no waiters for ");
        con::writeMutPtr(p);
        con::newline();
    }

    // TODO Look for pending pulse
    // 2. Look for pending pulses
    // 3. Switch next
}

fn syscall_map(p: &mut Process, handle: uint, mut prot: uint, addr: uint, mut offset: uint, size: uint) {
    prot &= mapflag::UserAllowed;
    // TODO Check (and return failure) on:
    // * unaligned addr, offset, size (must be page-aligned)
    if (prot & mapflag::DMA) == mapflag::DMA {
        offset = match cpu().memory.alloc_frame() {
            None => 0,
            Some(p) => p as uint - kernel_base,
        }
    }

    p.aspace().map_range(addr, addr + size, handle, (offset - addr) | prot);

    if (prot & mapflag::Phys) == 0 {
        offset = 0;
    }
    cpu().syscall_return(p, offset);
}

fn syscall_pfault(p : &mut Process, mut vaddr: uint, access: uint) {
    vaddr &= !0xfff;

    // set fault address
    p.fault_addr = vaddr;
    p.regs.rsi = vaddr;
    p.regs.rdx = access & mapflag::RWX;
    // Look up vaddr, get handle, offset and flags
    let card = p.aspace().mapcard_find_def(vaddr);
    p.regs.rsi += card.offset; // proc.rsi is now translated into offset
    p.regs.rdi = card.handle;
    p.set(process::PFault);

    // Now do the equivalent of sendrcv with rdi=handle, rsi=offset, rdx=flags
}

fn syscall_hmod(p : &mut Process, id: uint, rename: uint, copy: uint) {
    let handle = p.find_handle(id);
    if log_hmod {
        con::writeMutPtr(p);
        write(" hmod: id="); con::writeHex(id);
        write(" rename="); con::writeHex(rename);
        write(" copy="); con::writeHex(copy);
        con::newline();
    }
    match handle {
    None => (),
    Some(h) => {
        // Fresh/dissociated handle for the same process as the original
        if copy != 0 {
            p.new_handle(copy, h.process());
        }
        if rename == 0 {
            p.delete_handle(h);
        } else if rename != id {
            p.rename_handle(h, rename);
        }
    }
    }
    cpu().syscall_return(p, 0);
}

fn syscall_portio(p : &mut Process, port : uint, op : uint, data: uint) -> ! {
    let mut res : uint = 0;
    unsafe { match op {
    0x01 => asm!("inb %dx, %al" : "={al}"(res) : "{dx}"(port)),
    0x02 => asm!("inw %dx, %ax" : "={ax}"(res) : "{dx}"(port)),
    0x04 => asm!("inl %dx, %eax" : "={eax}"(res) : "{dx}"(port)),
    0x11 => asm!("outb %al, %dx" :: "{al}"(data), "{dx}"(port)),
    0x12 => asm!("outw %ax, %dx" :: "{ax}"(data), "{dx}"(port)),
    0x14 => asm!("outl %eax, %dx" :: "{eax}"(data), "{dx}"(port)),
    _ => abort("unhandled portio operation")
    } }
    if log_portio {
        write("portio: port="); con::writeHex(port & 0xffff);
        write(" op="); con::writeUInt(op);
        write(" data="); con::writeHex(data);
        write(" res="); con::writeHex(res);
        con::newline();
    }
    cpu().syscall_return(p, res);
}
<|endoftext|>


<commit_before>use bifrost::raft::client::{RaftClient, SubscriptionError};
use bifrost::raft::state_machine::master::ExecError;

use std::collections::HashMap;
use parking_lot::{RwLock};

use std::sync::{Arc};
use std::string::String;
use core::borrow::Borrow;

pub mod sm;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Schema {
    pub id: u32,
    pub name: String,
    pub key_field: Option<String>,
    pub fields: Field
}

impl Schema {
    pub fn new(name: String, key_field: Option<String>, fields: Field) -> Schema {
        Schema {
            id: 0,
            name: name,
            key_field: key_field,
            fields: fields
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Field {
    pub type_id: u32,
    pub name: String,
    pub nullable: bool,
    pub is_array: bool,
    pub sub_fields: Option<Vec<Field>>
}

pub struct SchemasMap {
    schema_map: HashMap<u32, Arc<Schema>>,
    name_map: HashMap<String, u32>,
    id_counter: u32,
}

pub struct SchemasServer {
    map: Arc<RwLock<SchemasMap>>,
    sm: Option<sm::client::SMClient>,
}

impl SchemasServer {
    pub fn new(raft_client: Option<&Arc<RaftClient>>) -> Arc<SchemasServer> {
        let map = Arc::new(RwLock::new(SchemasMap::new()));
        let sm = match raft_client {
            Some(raft) => {
                let m1 = map.clone();
                let m2 = map.clone();
                let sm = sm::client::SMClient::new(sm::DEFAULT_SM_ID, raft);
                let new_sub = sm.on_schema_added(move |r| {
                    let mut m1 = m1.write();
                    m1.new_schema(&r.unwrap());
                });
                let del_sub = sm.on_schema_deleted(move |r| {
                    let mut m2 = m2.write();
                    m2.del_schema(&r.unwrap());
                });
                Some(sm)
            },
            None => None
        };
        let schemas = Arc::new(SchemasServer {
            map: map,
            sm: sm
        });
        return schemas;
    }
    pub fn get(&self, id: &u32) -> Option<Arc<Schema>> {
        let m = self.map.read();
        m.get(id)
    }
    pub fn new_schema(&self, schema: &Schema) { // for debug only
        let mut m = self.map.write();
        m.new_schema(schema)
    }
}

impl SchemasMap {
    pub fn new() -> SchemasMap {
        SchemasMap {
            schema_map: HashMap::new(),
            name_map: HashMap::new(),
            id_counter: 0,
        }
    }
    pub fn new_schema(&mut self, schema: &Schema) {
        let name = schema.name.clone();
        let id = schema.id;
        self.schema_map.insert(id, Arc::new(schema.clone()));
        self.name_map.insert(name, id);
    }
    pub fn del_schema(&mut self, name: &String) -> Result<(), ()> {
        if let Some(id) = self.name_map.get(name) {
            self.schema_map.remove(&id);
        }
        self.name_map.remove(name);
        Ok(())
    }
    pub fn get_by_name(&self, name: &String) -> Option<Arc<Schema>> {
        if let Some(id) = self.name_map.get(name) {
            return self.get(&id)
        }
        return None;
    }
    pub fn get(&self, id: &u32) -> Option<Arc<Schema>> {
        if let Some(schema) = self.schema_map.get(id) {
            return Some(schema.clone())
        }
        return None;
    }
    fn next_id(&mut self) -> u32 {
        self.id_counter += 1;
        self.id_counter
    }
    fn get_all(&self) -> Vec<Schema> {
        self.schema_map
            .values()
            .map(|s_ref| {
                let arc = s_ref.clone();
                let r: &Schema = arc.borrow();
                r.clone()
            })
            .collect()
    }
    fn load_from_list(&mut self, data: &Vec<Schema>) {
         for schema in data {
             let id = schema.id;
             self.name_map.insert(schema.name.clone(), id);
             self.schema_map.insert(id, Arc::new(schema.clone()));
         }
    }
}<commit_msg>optimize layout<commit_after>use bifrost::raft::client::{RaftClient, SubscriptionError};
use bifrost::raft::state_machine::master::ExecError;

use std::collections::HashMap;
use parking_lot::{RwLock};

use std::sync::{Arc};
use std::string::String;
use core::borrow::Borrow;

pub mod sm;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Schema {
    pub id: u32,
    pub name: String,
    pub key_field: Option<String>,
    pub fields: Field
}

impl Schema {
    pub fn new(name: String, key_field: Option<String>, fields: Field) -> Schema {
        Schema {
            id: 0,
            name: name,
            key_field: key_field,
            fields: fields
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Field {
    pub type_id: u32,
    pub nullable: bool,
    pub is_array: bool,
    pub sub_fields: Option<Vec<Field>>,
    pub name: String,
}

pub struct SchemasMap {
    schema_map: HashMap<u32, Arc<Schema>>,
    name_map: HashMap<String, u32>,
    id_counter: u32,
}

pub struct SchemasServer {
    map: Arc<RwLock<SchemasMap>>,
    sm: Option<sm::client::SMClient>,
}

impl SchemasServer {
    pub fn new(raft_client: Option<&Arc<RaftClient>>) -> Arc<SchemasServer> {
        let map = Arc::new(RwLock::new(SchemasMap::new()));
        let sm = match raft_client {
            Some(raft) => {
                let m1 = map.clone();
                let m2 = map.clone();
                let sm = sm::client::SMClient::new(sm::DEFAULT_SM_ID, raft);
                let new_sub = sm.on_schema_added(move |r| {
                    let mut m1 = m1.write();
                    m1.new_schema(&r.unwrap());
                });
                let del_sub = sm.on_schema_deleted(move |r| {
                    let mut m2 = m2.write();
                    m2.del_schema(&r.unwrap());
                });
                Some(sm)
            },
            None => None
        };
        let schemas = Arc::new(SchemasServer {
            map: map,
            sm: sm
        });
        return schemas;
    }
    pub fn get(&self, id: &u32) -> Option<Arc<Schema>> {
        let m = self.map.read();
        m.get(id)
    }
    pub fn new_schema(&self, schema: &Schema) { // for debug only
        let mut m = self.map.write();
        m.new_schema(schema)
    }
}

impl SchemasMap {
    pub fn new() -> SchemasMap {
        SchemasMap {
            schema_map: HashMap::new(),
            name_map: HashMap::new(),
            id_counter: 0,
        }
    }
    pub fn new_schema(&mut self, schema: &Schema) {
        let name = schema.name.clone();
        let id = schema.id;
        self.schema_map.insert(id, Arc::new(schema.clone()));
        self.name_map.insert(name, id);
    }
    pub fn del_schema(&mut self, name: &String) -> Result<(), ()> {
        if let Some(id) = self.name_map.get(name) {
            self.schema_map.remove(&id);
        }
        self.name_map.remove(name);
        Ok(())
    }
    pub fn get_by_name(&self, name: &String) -> Option<Arc<Schema>> {
        if let Some(id) = self.name_map.get(name) {
            return self.get(&id)
        }
        return None;
    }
    pub fn get(&self, id: &u32) -> Option<Arc<Schema>> {
        if let Some(schema) = self.schema_map.get(id) {
            return Some(schema.clone())
        }
        return None;
    }
    fn next_id(&mut self) -> u32 {
        self.id_counter += 1;
        self.id_counter
    }
    fn get_all(&self) -> Vec<Schema> {
        self.schema_map
            .values()
            .map(|s_ref| {
                let arc = s_ref.clone();
                let r: &Schema = arc.borrow();
                r.clone()
            })
            .collect()
    }
    fn load_from_list(&mut self, data: &Vec<Schema>) {
         for schema in data {
             let id = schema.id;
             self.name_map.insert(schema.name.clone(), id);
             self.schema_map.insert(id, Arc::new(schema.clone()));
         }
    }
}<|endoftext|>