<commit_before>extern crate pqbus;
extern crate env_logger;
extern crate postgres;
extern crate retry;

use postgres::{Connection, SslMode};
use retry::retry;

use std::time::Duration;
use std::env;
use std::sync::{Arc, Mutex};
use std::str::FromStr;
use std::thread;

use pqbus::Queue;
use pqbus::messages::StringMessage;
use pqbus::error::Error;

struct TestInit;

fn test_setup() -> TestInit {
    let _ = env_logger::init();
    TestInit
}

fn db_uri() -> String {
    env::var("TEST_DB_URI").unwrap_or("postgres://postgres@localhost/pqbus_test".to_string())
}

fn conn() -> Result<Connection, retry::RetryError> {
    match retry(10,
                100,
                || Connection::connect(db_uri().as_ref(), SslMode::None),
                |e| {
                    println!("Connection failed");
                    e.is_ok()
                }) {
        Err(e) => {
            println!("Failed to connect: {}", e);
            Err(e)
        }
        Ok(c) => Ok(c.unwrap()),
    }
}

fn drop_table(name: &str) {
    conn().unwrap().execute(&format!("drop table if exists {} cascade", name), &[]).unwrap();
}

#[test]
fn test_connect_fail() {
    test_setup();
    let bus = pqbus::new("bla", "work");
    assert!(bus.is_err());
}

#[test]
fn test_valid_bus_name() {
    test_setup();
    let bus = pqbus::new(db_uri(), "bad-name");
    match bus {
        Err(Error::InvalidBusName(n)) => assert_eq!("bad-name", &n),
        _ => unreachable!(),
    }
}

#[test]
fn test_valid_queue_name() {
    test_setup();
    let bus = pqbus::new(db_uri(), "work").unwrap();
    let queue = bus.queue("bad-name");
    match queue.as_ref() {
        Err(&Error::InvalidQueueName(ref n)) => assert_eq!("bad-name", n),
        _ => unreachable!(),
    }
    if queue.is_ok() {
        queue.unwrap().push(StringMessage::new("a")).unwrap();
    }
}

#[test]
fn test_push() {
    test_setup();
    drop_table("pqbus_push_a_queue");
    let bus = pqbus::new(db_uri(), "push").unwrap();
    let queue = bus.queue("a").unwrap();

    queue.push(StringMessage::new("a")).unwrap();

}

#[test]
fn test_empty() {
    test_setup();
    drop_table("pqbus_test_empty_test_queue_queue");
    let bus = pqbus::new(db_uri(), "test_empty").unwrap();
    let queue = bus.queue("test_queue").unwrap();

    assert!(queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert!(!queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert!(!queue.is_empty().unwrap());
}

#[test]
fn test_size() {
    test_setup();
    drop_table("pqbus_test_size_test_queue_queue");
    let bus = pqbus::new(db_uri(), "test_size").unwrap();
    let queue = bus.queue("test_queue").unwrap();

    assert_eq!(0, queue.size().unwrap());
    assert!(queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert_eq!(1, queue.size().unwrap());
    assert!(!queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert_eq!(2, queue.size().unwrap());
    assert!(!queue.is_empty().unwrap());
}

#[test]
fn test_sequential_push_pop() {
    test_setup();
    drop_table("pqbus_test_sequential_push_pop_test_queue_queue");
    let bus = pqbus::new(db_uri(), "test_sequential_push_pop").unwrap();
    let queue = bus.queue("test_queue").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("Hello World!")).unwrap();
    let result = queue.pop_blocking().unwrap();
    assert_eq!("Hello World!", result.body());
}

#[test]
fn test_pop_blocking() {
    test_setup();
    drop_table("pqbus_attempt_pop_a_queue");
    let bus = pqbus::new(db_uri(), "attempt_pop").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("Hello World!")).unwrap();

    let result = queue.pop_blocking().unwrap();
    assert_eq!("Hello World!", result.body());
}

#[test]
fn test_one_bus_duel_queue_push_pop_in_order() {
    test_setup();
    drop_table("pqbus_test_sequential_push_pop_test_queue_a_queue");
    drop_table("pqbus_test_sequential_push_pop_test_queue_b_queue");

    let bus = pqbus::new(db_uri(), "test_sequential_push_pop").unwrap();
    let queue_a = bus.queue("test_queue_a").unwrap();
    let queue_b = bus.queue("test_queue_b").unwrap();
    assert!(queue_a.is_empty().unwrap());
    assert!(queue_b.is_empty().unwrap());

    queue_a.push(StringMessage::new("a")).unwrap();
    queue_b.push(StringMessage::new("b")).unwrap();

    let result_a = queue_a.pop().unwrap().unwrap();
    let result_b = queue_b.pop().unwrap().unwrap();

    assert_eq!("a", result_a.body());
    assert_eq!("b", result_b.body());
}

#[test]
fn test_one_bus_duel_queue_push_pop_unorder() {
    test_setup();
    drop_table("pqbus_one_bus_duel_queue_push_pop_unorder_test_queue_a_queue");
    drop_table("pqbus_one_bus_duel_queue_push_pop_unorder_test_queue_b_queue");

    let bus = pqbus::new(db_uri(), "one_bus_duel_queue_push_pop_unorder").unwrap();
    let queue_a = bus.queue("test_queue_a").unwrap();
    let queue_b = bus.queue("test_queue_b").unwrap();
    assert!(queue_a.is_empty().unwrap());
    assert!(queue_b.is_empty().unwrap());

    queue_a.push(StringMessage::new("a")).unwrap();
    queue_b.push(StringMessage::new("b")).unwrap();

    let result_b = queue_b.pop().unwrap().unwrap();
    let result_a = queue_a.pop().unwrap().unwrap();

    assert_eq!("a", result_a.body());
    assert_eq!("b", result_b.body());
}

#[test]
fn test_multithread_push_pop() {
    test_setup();
    drop_table("pqbus_multithread_push_pop_a_queue");

    let bus = pqbus::new(db_uri(), "multithread_push_pop").unwrap();
    let queue: Queue<StringMessage> = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let child = thread::spawn(move || {
        let bus = pqbus::new(db_uri(), "multithread_push_pop").unwrap();
        let queue: Queue<StringMessage> = bus.queue("a").unwrap();
        queue.pop_blocking()
    });

    queue.push(StringMessage::new("a")).unwrap();
    let res = child.join().unwrap();

    assert_eq!("a", res.unwrap().body());
}

#[test]
fn test_multithread_push_pop_many() {
    test_setup();
    drop_table("pqbus_multithread_push_pop_many_a_queue");
    let bus = pqbus::new(db_uri(), "multithread_push_pop_many").unwrap();
    let queue: Queue<StringMessage> = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let publisher_count = 10;
    let work_per_publisher = 100;
    let worker_count = 5;
    let work_per_worker = 200;

    assert_eq!(worker_count * work_per_worker,
               publisher_count * work_per_publisher);

    let mut threads = vec![];

    for i in 0..publisher_count {
        threads.push(thread::spawn(move || {
            let bus = pqbus::new(db_uri(), "multithread_push_pop_many").unwrap();
            let queue = bus.queue("a").unwrap();
            for j in 0..work_per_publisher {
                let n = (i * work_per_publisher) + j;
                queue.push(StringMessage::new(format!("{}", n))).unwrap();
            }
        }));
    }

    let results = Arc::new(Mutex::new(vec![]));

    for _i in 0..worker_count {
        let results = results.clone();
        threads.push(thread::spawn(move || {
            let bus = pqbus::new(db_uri(), "multithread_push_pop_many").unwrap();
            let queue = bus.queue("a").unwrap();
            for _i in 0..work_per_worker {
                let r: StringMessage = queue.pop_blocking().unwrap();
                let mut mine = results.lock().unwrap();
                let n: i32 = FromStr::from_str(r.body()).unwrap();
                mine.push(n);
            }
        }));
    }

    for t in threads {
        t.join().unwrap();
    }

    let mut results = results.lock().unwrap();
    assert_eq!(publisher_count * work_per_publisher, results.len());
    results.sort();
    for i in 0..(publisher_count * work_per_publisher) {
        println!("Check {} == {}", i, results[i]);
        assert_eq!(i as i32, results[i]);
    }
}

#[test]
fn test_pop_wait_none() {
    test_setup();
    drop_table("pqbus_pop_wait_none_a_queue");
    let bus = pqbus::new(db_uri(), "pop_wait_none").unwrap();
    let queue: Queue<StringMessage> = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let result = queue.pop_wait(Duration::new(1, 0));

    assert!(result.is_ok());
    let result = result.unwrap();
    assert!(result.is_none());
}

#[test]
fn test_pop_wait_some() {
    test_setup();
    drop_table("pqbus_pop_wait_some_a_queue");
    let bus = pqbus::new(db_uri(), "pop_wait_some").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let child = thread::spawn(|| {
        let bus = pqbus::new(db_uri(), "pop_wait_some").unwrap();
        let queue: Queue<StringMessage> = bus.queue("a").unwrap();
        queue.pop_wait(Duration::new(2, 0))
    });

    // crude
    thread::sleep(Duration::new(1, 0));

    queue.push(StringMessage::new("test")).unwrap();
    let result = child.join().unwrap().unwrap();

    assert!(result.is_some());
    assert_eq!("test", result.unwrap().body());
}

#[test]
fn test_messages_iter_nth() {
    test_setup();
    drop_table("pqbus_messages_iter_nth_a_queue");
    let bus = pqbus::new(db_uri(), "messages_iter_nth").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("1")).unwrap();
    queue.push(StringMessage::new("2")).unwrap();
    queue.push(StringMessage::new("3")).unwrap();
    queue.push(StringMessage::new("4")).unwrap();

    let third = queue.messages().nth(2).unwrap();
    assert_eq!("3", third.unwrap().body());
}

#[test]
fn test_messages_waiting_iter() {
    test_setup();
    drop_table("pqbus_iter_nth_a_queue");
    let bus = pqbus::new(db_uri(), "iter_nth").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("1")).unwrap();
    queue.push(StringMessage::new("2")).unwrap();
    queue.push(StringMessage::new("3")).unwrap();
    queue.push(StringMessage::new("4")).unwrap();

    let mut i = 0;
    for _message in queue.messages() {
        i += 1;
    }

    assert_eq!(4, i);
}
<commit_msg>Implement logging<commit_after>extern crate pqbus;
extern crate env_logger;
extern crate postgres;
extern crate retry;

use postgres::{Connection, SslMode};
use retry::retry;

use std::time::Duration;
use std::env;
use std::sync::{Arc, Mutex};
use std::str::FromStr;
use std::thread;

use pqbus::Queue;
use pqbus::messages::StringMessage;
use pqbus::error::Error;

struct TestInit;

fn test_setup() -> TestInit {
    let _ = env_logger::init();
    TestInit
}

fn db_uri() -> String {
    env::var("TEST_DB_URI").unwrap_or("postgres://postgres@localhost/pqbus_test".to_string())
}

fn conn() -> Result<Connection, retry::RetryError> {
    match retry(10,
                100,
                || Connection::connect(db_uri().as_ref(), SslMode::None),
                |e| {
                    println!("Connection failed");
                    e.is_ok()
                }) {
        Err(e) => {
            println!("Failed to connect: {}", e);
            Err(e)
        }
        Ok(c) => Ok(c.unwrap()),
    }
}

fn drop_table(name: &str) {
    conn().unwrap().execute(&format!("drop table if exists {} cascade", name), &[]).unwrap();
}

#[test]
fn test_connect_fail() {
    test_setup();
    let bus = pqbus::new("bla", "work");
    assert!(bus.is_err());
}

#[test]
fn test_valid_bus_name() {
    test_setup();
    let bus = pqbus::new(db_uri(), "bad-name");
    match bus {
        Err(Error::InvalidBusName(n)) => assert_eq!("bad-name", &n),
        _ => unreachable!(),
    }
}

#[test]
fn test_valid_queue_name() {
    test_setup();
    let bus = pqbus::new(db_uri(), "work").unwrap();
    let queue: Result<Queue<StringMessage>, pqbus::error::Error> = bus.queue("bad-name");
    match queue.as_ref() {
        Err(&Error::InvalidQueueName(ref n)) => assert_eq!("bad-name", n),
        _ => unreachable!(),
    }
}

#[test]
fn test_push() {
    test_setup();
    drop_table("pqbus_push_a_queue");
    let bus = pqbus::new(db_uri(), "push").unwrap();
    let queue = bus.queue("a").unwrap();
    queue.push(StringMessage::new("a")).unwrap();
}

#[test]
fn test_empty() {
    test_setup();
    drop_table("pqbus_test_empty_test_queue_queue");
    let bus = pqbus::new(db_uri(), "test_empty").unwrap();
    let queue = bus.queue("test_queue").unwrap();

    assert!(queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert!(!queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert!(!queue.is_empty().unwrap());
}

#[test]
fn test_size() {
    test_setup();
    drop_table("pqbus_test_size_test_queue_queue");
    let bus = pqbus::new(db_uri(), "test_size").unwrap();
    let queue = bus.queue("test_queue").unwrap();

    assert_eq!(0, queue.size().unwrap());
    assert!(queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert_eq!(1, queue.size().unwrap());
    assert!(!queue.is_empty().unwrap());
    queue.push(StringMessage::new("a")).unwrap();
    assert_eq!(2, queue.size().unwrap());
    assert!(!queue.is_empty().unwrap());
}

#[test]
fn test_sequential_push_pop() {
    test_setup();
    drop_table("pqbus_test_sequential_push_pop_test_queue_queue");
    let bus = pqbus::new(db_uri(), "test_sequential_push_pop").unwrap();
    let queue = bus.queue("test_queue").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("Hello World!")).unwrap();
    let result = queue.pop_blocking().unwrap();
    assert_eq!("Hello World!", result.body());
}

#[test]
fn test_pop_blocking() {
    test_setup();
    drop_table("pqbus_attempt_pop_a_queue");
    let bus = pqbus::new(db_uri(), "attempt_pop").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("Hello World!")).unwrap();

    let result = queue.pop_blocking().unwrap();
    assert_eq!("Hello World!", result.body());
}

#[test]
fn test_one_bus_duel_queue_push_pop_in_order() {
    test_setup();
    drop_table("pqbus_test_sequential_push_pop_test_queue_a_queue");
    drop_table("pqbus_test_sequential_push_pop_test_queue_b_queue");

    let bus = pqbus::new(db_uri(), "test_sequential_push_pop").unwrap();
    let queue_a = bus.queue("test_queue_a").unwrap();
    let queue_b = bus.queue("test_queue_b").unwrap();
    assert!(queue_a.is_empty().unwrap());
    assert!(queue_b.is_empty().unwrap());

    queue_a.push(StringMessage::new("a")).unwrap();
    queue_b.push(StringMessage::new("b")).unwrap();

    let result_a = queue_a.pop().unwrap().unwrap();
    let result_b = queue_b.pop().unwrap().unwrap();

    assert_eq!("a", result_a.body());
    assert_eq!("b", result_b.body());
}

#[test]
fn test_one_bus_duel_queue_push_pop_unorder() {
    test_setup();
    drop_table("pqbus_one_bus_duel_queue_push_pop_unorder_test_queue_a_queue");
    drop_table("pqbus_one_bus_duel_queue_push_pop_unorder_test_queue_b_queue");

    let bus = pqbus::new(db_uri(), "one_bus_duel_queue_push_pop_unorder").unwrap();
    let queue_a = bus.queue("test_queue_a").unwrap();
    let queue_b = bus.queue("test_queue_b").unwrap();
    assert!(queue_a.is_empty().unwrap());
    assert!(queue_b.is_empty().unwrap());

    queue_a.push(StringMessage::new("a")).unwrap();
    queue_b.push(StringMessage::new("b")).unwrap();

    let result_b = queue_b.pop().unwrap().unwrap();
    let result_a = queue_a.pop().unwrap().unwrap();

    assert_eq!("a", result_a.body());
    assert_eq!("b", result_b.body());
}

#[test]
fn test_multithread_push_pop() {
    test_setup();
    drop_table("pqbus_multithread_push_pop_a_queue");

    let bus = pqbus::new(db_uri(), "multithread_push_pop").unwrap();
    let queue: Queue<StringMessage> = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let child = thread::spawn(move || {
        let bus = pqbus::new(db_uri(), "multithread_push_pop").unwrap();
        let queue: Queue<StringMessage> = bus.queue("a").unwrap();
        queue.pop_blocking()
    });

    queue.push(StringMessage::new("a")).unwrap();
    let res = child.join().unwrap();

    assert_eq!("a", res.unwrap().body());
}

#[test]
fn test_multithread_push_pop_many() {
    test_setup();
    drop_table("pqbus_multithread_push_pop_many_a_queue");
    let bus = pqbus::new(db_uri(), "multithread_push_pop_many").unwrap();
    let queue: Queue<StringMessage> = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let publisher_count = 10;
    let work_per_publisher = 100;
    let worker_count = 5;
    let work_per_worker = 200;

    assert_eq!(worker_count * work_per_worker,
               publisher_count * work_per_publisher);

    let mut threads = vec![];

    for i in 0..publisher_count {
        threads.push(thread::spawn(move || {
            let bus = pqbus::new(db_uri(), "multithread_push_pop_many").unwrap();
            let queue = bus.queue("a").unwrap();
            for j in 0..work_per_publisher {
                let n = (i * work_per_publisher) + j;
                queue.push(StringMessage::new(format!("{}", n))).unwrap();
            }
        }));
    }

    let results = Arc::new(Mutex::new(vec![]));

    for _i in 0..worker_count {
        let results = results.clone();
        threads.push(thread::spawn(move || {
            let bus = pqbus::new(db_uri(), "multithread_push_pop_many").unwrap();
            let queue = bus.queue("a").unwrap();
            for _i in 0..work_per_worker {
                let r: StringMessage = queue.pop_blocking().unwrap();
                let mut mine = results.lock().unwrap();
                let n: i32 = FromStr::from_str(r.body()).unwrap();
                mine.push(n);
            }
        }));
    }

    for t in threads {
        t.join().unwrap();
    }

    let mut results = results.lock().unwrap();
    assert_eq!(publisher_count * work_per_publisher, results.len());
    results.sort();
    for i in 0..(publisher_count * work_per_publisher) {
        println!("Check {} == {}", i, results[i]);
        assert_eq!(i as i32, results[i]);
    }
}

#[test]
fn test_pop_wait_none() {
    test_setup();
    drop_table("pqbus_pop_wait_none_a_queue");
    let bus = pqbus::new(db_uri(), "pop_wait_none").unwrap();
    let queue: Queue<StringMessage> = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let result = queue.pop_wait(Duration::new(1, 0));

    assert!(result.is_ok());
    let result = result.unwrap();
    assert!(result.is_none());
}

#[test]
fn test_pop_wait_some() {
    test_setup();
    drop_table("pqbus_pop_wait_some_a_queue");
    let bus = pqbus::new(db_uri(), "pop_wait_some").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    let child = thread::spawn(|| {
        let bus = pqbus::new(db_uri(), "pop_wait_some").unwrap();
        let queue: Queue<StringMessage> = bus.queue("a").unwrap();
        queue.pop_wait(Duration::new(2, 0))
    });

    // crude
    thread::sleep(Duration::new(1, 0));

    queue.push(StringMessage::new("test")).unwrap();
    let result = child.join().unwrap().unwrap();

    assert!(result.is_some());
    assert_eq!("test", result.unwrap().body());
}

#[test]
fn test_messages_iter_nth() {
    test_setup();
    drop_table("pqbus_messages_iter_nth_a_queue");
    let bus = pqbus::new(db_uri(), "messages_iter_nth").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("1")).unwrap();
    queue.push(StringMessage::new("2")).unwrap();
    queue.push(StringMessage::new("3")).unwrap();
    queue.push(StringMessage::new("4")).unwrap();

    let third = queue.messages().nth(2).unwrap();
    assert_eq!("3", third.unwrap().body());
}

#[test]
fn test_messages_waiting_iter() {
    test_setup();
    drop_table("pqbus_iter_nth_a_queue");
    let bus = pqbus::new(db_uri(), "iter_nth").unwrap();
    let queue = bus.queue("a").unwrap();
    assert!(queue.is_empty().unwrap());

    queue.push(StringMessage::new("1")).unwrap();
    queue.push(StringMessage::new("2")).unwrap();
    queue.push(StringMessage::new("3")).unwrap();
    queue.push(StringMessage::new("4")).unwrap();

    let mut i = 0;
    for _message in queue.messages() {
        i += 1;
    }

    assert_eq!(4, i);
}
<|endoftext|>


<commit_before>pub struct Queue<T> {
    older: Vec<T>,
    younger: Vec<T>
}

impl<T> Queue<T> {
    pub fn new() -> Queue<T> {
        Queue { older: Vec::new(), younger: Vec::new() }
    }

    pub fn push(&mut self, t: T) {
        self.younger.push(t);
    }

    pub fn is_empty(&self) -> bool {
        self.younger.is_empty() == self.older.is_empty()
    }
}
<commit_msg>rename Self -> Queue<T><commit_after>pub struct Queue<T> {
    older: Vec<T>,
    younger: Vec<T>
}

impl<T> Queue<T> {
    pub fn new() -> Self {
        Queue { older: Vec::new(), younger: Vec::new() }
    }

    pub fn push(&mut self, t: T) {
        self.younger.push(t);
    }

    pub fn is_empty(&self) -> bool {
        self.younger.is_empty() == self.older.is_empty()
    }
}
<|endoftext|>


<commit_before>use crate::*;
use rocket::fairing::AdHoc;
use rocket::http::hyper::header::AccessControlAllowOrigin;
use rocket::http::Status;
use rocket::request::{self, FromRequest, Request};
use rocket::response::content;
use rocket::response::Response;
use rocket::State;
use std::path::PathBuf;

#[cfg(feature = "webcontent")]
use ::{
    turingarena_contest_webcontent::WebContent,
    std::ffi::OsStr,
    std::io::Cursor,
    rocket::http::ContentType,
};

struct Authorization(Option<String>);

impl<'a, 'r> FromRequest<'a, 'r> for Authorization {
    type Error = String;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        request::Outcome::Success(Authorization(
            match request.headers().get_one("Authorization") {
                Some(token) => Some(token.to_owned()),
                None => None,
            },
        ))
    }
}

#[rocket::get("/graphiql")]
fn graphiql() -> content::Html<String> {
    juniper_rocket::graphiql_source("/graphql")
}

#[rocket::options("/graphql")]
fn options_graphql<'a>() -> Response<'a> {
    Response::build()
        .raw_header("Access-Control-Allow-Origin", "*")
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header(
            "Access-Control-Allow-Headers",
            "Content-Type, Authorization",
        )
        .finalize()
}

#[rocket::post("/graphql", data = "<request>")]
fn post_graphql_handler(
    request: juniper_rocket::GraphQLRequest,
    schema: State<Schema>,
    auth: Authorization,
    context: State<Context>,
) -> juniper_rocket::GraphQLResponse {
    let claims = context.secret.as_ref().and_then(|secret| {
        auth.0
            .and_then(|token| match auth::validate(&token, secret) {
                Ok(claims) => Some(claims),
                Err(_) => panic!("Invalid token"),
            })
    });
    let context = context.clone().with_jwt_data(claims);
    request.execute(&schema, &context)
}

#[rocket::get("/")]
fn index<'r>() -> rocket::response::Result<'r> {
    dist(Some(PathBuf::from("index.html")))
}

#[cfg(not(feature = "webcontent"))]
#[rocket::get("/<_file_option..>")]
fn dist<'r>(_file_option: Option<PathBuf>) -> rocket::response::Result<'r> {
    Err(Status::new(
        404,
        "Static files not embedded. Enable feature `webcontent`",
    ))
}

#[cfg(feature = "webcontent")]
#[rocket::get("/<file_option..>")]
fn dist<'r>(file_option: Option<PathBuf>) -> rocket::response::Result<'r> {
    let file = file_option.unwrap_or(PathBuf::new());
    let filename = file.display().to_string();
    let content = WebContent::get(&filename)
        .or(WebContent::get("index.html"))
        .unwrap();
    let ext = file
        .as_path()
        .extension()
        .and_then(OsStr::to_str)
        .unwrap_or("html");
    let content_type = ContentType::from_extension(ext)
        .ok_or(Status::new(400, "Could not get file content type"))?;
    response::Response::build()
        .header(content_type)
        .sized_body(Cursor::new(content))
        .ok()
}

pub fn generate_schema(context: Context) {
    let (schema, _errors) = juniper::introspect(
        &Schema::new(contest::ContestQueries {}, contest::ContestQueries {}),
        &context,
        juniper::IntrospectionFormat::All,
    )
    .unwrap();
    println!("{}", serde_json::to_string_pretty(&schema).unwrap());
}

pub fn run_server(host: String, port: u16, context: Context) {
    let config = rocket::Config::build(rocket::config::Environment::active().unwrap())
        .port(port)
        .address(host)
        .finalize()
        .unwrap();

    rocket::custom(config)
        .manage(Schema::new(
            contest::ContestQueries {},
            contest::ContestQueries {},
        ))
        .manage(context)
        .attach(AdHoc::on_response("Cors header", |_, res| {
            res.set_header(AccessControlAllowOrigin::Any);
        }))
        .mount(
            "/",
            rocket::routes![graphiql, options_graphql, post_graphql_handler, index, dist],
        )
        .launch();
}
<commit_msg>rust: minor fix<commit_after>use crate::*;
use rocket::fairing::AdHoc;
use rocket::http::hyper::header::AccessControlAllowOrigin;
use rocket::http::Status;
use rocket::request::{self, FromRequest, Request};
use rocket::response::content;
use rocket::response::Response;
use rocket::State;
use std::path::PathBuf;

#[cfg(feature = "webcontent")]
use ::{
    turingarena_contest_webcontent::WebContent,
    std::ffi::OsStr,
    std::io::Cursor,
    rocket::http::ContentType,
};

struct Authorization(Option<String>);

impl<'a, 'r> FromRequest<'a, 'r> for Authorization {
    type Error = String;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        request::Outcome::Success(Authorization(
            match request.headers().get_one("Authorization") {
                Some(token) => Some(token.to_owned()),
                None => None,
            },
        ))
    }
}

#[rocket::get("/graphiql")]
fn graphiql() -> content::Html<String> {
    juniper_rocket::graphiql_source("/graphql")
}

#[rocket::options("/graphql")]
fn options_graphql<'a>() -> Response<'a> {
    Response::build()
        .raw_header("Access-Control-Allow-Origin", "*")
        .raw_header("Access-Control-Allow-Methods", "OPTIONS, POST")
        .raw_header(
            "Access-Control-Allow-Headers",
            "Content-Type, Authorization",
        )
        .finalize()
}

#[rocket::post("/graphql", data = "<request>")]
fn post_graphql_handler(
    request: juniper_rocket::GraphQLRequest,
    schema: State<Schema>,
    auth: Authorization,
    context: State<Context>,
) -> juniper_rocket::GraphQLResponse {
    let claims = context.secret.as_ref().and_then(|secret| {
        auth.0
            .and_then(|token| match auth::validate(&token, secret) {
                Ok(claims) => Some(claims),
                Err(_) => panic!("Invalid token"),
            })
    });
    let context = context.clone().with_jwt_data(claims);
    request.execute(&schema, &context)
}

#[rocket::get("/")]
fn index<'r>() -> rocket::response::Result<'r> {
    dist(Some(PathBuf::from("index.html")))
}

#[cfg(not(feature = "webcontent"))]
#[rocket::get("/<_file_option..>")]
fn dist<'r>(_file_option: Option<PathBuf>) -> rocket::response::Result<'r> {
    Err(Status::new(
        404,
        "Static files not embedded. Enable feature `webcontent`",
    ))
}

#[cfg(feature = "webcontent")]
#[rocket::get("/<file_option..>")]
fn dist<'r>(file_option: Option<PathBuf>) -> rocket::response::Result<'r> {
    let file = file_option.unwrap_or(PathBuf::new());
    let filename = file.display().to_string();
    let content = WebContent::get(&filename)
        .or(WebContent::get("index.html"))
        .unwrap();
    let ext = file
        .as_path()
        .extension()
        .and_then(OsStr::to_str)
        .unwrap_or("html");
    let content_type = ContentType::from_extension(ext)
        .ok_or(Status::new(400, "Could not get file content type"))?;
    Response::build()
        .header(content_type)
        .sized_body(Cursor::new(content))
        .ok()
}

pub fn generate_schema(context: Context) {
    let (schema, _errors) = juniper::introspect(
        &Schema::new(contest::ContestQueries {}, contest::ContestQueries {}),
        &context,
        juniper::IntrospectionFormat::All,
    )
    .unwrap();
    println!("{}", serde_json::to_string_pretty(&schema).unwrap());
}

pub fn run_server(host: String, port: u16, context: Context) {
    let config = rocket::Config::build(rocket::config::Environment::active().unwrap())
        .port(port)
        .address(host)
        .finalize()
        .unwrap();

    rocket::custom(config)
        .manage(Schema::new(
            contest::ContestQueries {},
            contest::ContestQueries {},
        ))
        .manage(context)
        .attach(AdHoc::on_response("Cors header", |_, res| {
            res.set_header(AccessControlAllowOrigin::Any);
        }))
        .mount(
            "/",
            rocket::routes![graphiql, options_graphql, post_graphql_handler, index, dist],
        )
        .launch();
}
<|endoftext|>


<commit_before>use crate::ast::{Enum, Field, Struct, Variant};
use syn::{Member, Type};

impl Struct<'_> {
    pub(crate) fn source_field(&self) -> Option<&Field> {
        source_field(&self.fields)
    }

    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
        backtrace_field(&self.fields)
    }
}

impl Enum<'_> {
    pub(crate) fn has_source(&self) -> bool {
        self.variants
            .iter()
            .any(|variant| variant.source_field().is_some())
    }

    pub(crate) fn has_backtrace(&self) -> bool {
        self.variants
            .iter()
            .any(|variant| variant.backtrace_field().is_some())
    }

    pub(crate) fn has_display(&self) -> bool {
        self.attrs.display.is_some()
            || self
                .variants
                .iter()
                .any(|variant| variant.attrs.display.is_some())
    }
}

impl Variant<'_> {
    pub(crate) fn source_field(&self) -> Option<&Field> {
        source_field(&self.fields)
    }

    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
        backtrace_field(&self.fields)
    }
}

fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
    for field in fields {
        if field.attrs.source.is_some() {
            return Some(&field);
        }
    }
    for field in fields {
        match &field.member {
            Member::Named(ident) if ident == "source" => return Some(&field),
            _ => {}
        }
    }
    None
}

fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
    for field in fields {
        if field.attrs.backtrace.is_some() {
            return Some(&field);
        }
    }
    for field in fields {
        if type_is_backtrace(field.ty) {
            return Some(&field);
        }
    }
    None
}

fn type_is_backtrace(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    last.ident == "Backtrace" && last.arguments.is_empty()
}
<commit_msg>Treat the #[from] field implicitly as source<commit_after>use crate::ast::{Enum, Field, Struct, Variant};
use syn::{Member, Type};

impl Struct<'_> {
    pub(crate) fn source_field(&self) -> Option<&Field> {
        source_field(&self.fields)
    }

    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
        backtrace_field(&self.fields)
    }
}

impl Enum<'_> {
    pub(crate) fn has_source(&self) -> bool {
        self.variants
            .iter()
            .any(|variant| variant.source_field().is_some())
    }

    pub(crate) fn has_backtrace(&self) -> bool {
        self.variants
            .iter()
            .any(|variant| variant.backtrace_field().is_some())
    }

    pub(crate) fn has_display(&self) -> bool {
        self.attrs.display.is_some()
            || self
                .variants
                .iter()
                .any(|variant| variant.attrs.display.is_some())
    }
}

impl Variant<'_> {
    pub(crate) fn source_field(&self) -> Option<&Field> {
        source_field(&self.fields)
    }

    pub(crate) fn backtrace_field(&self) -> Option<&Field> {
        backtrace_field(&self.fields)
    }
}

fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
    for field in fields {
        if field.attrs.from.is_some() || field.attrs.source.is_some() {
            return Some(&field);
        }
    }
    for field in fields {
        match &field.member {
            Member::Named(ident) if ident == "source" => return Some(&field),
            _ => {}
        }
    }
    None
}

fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
    for field in fields {
        if field.attrs.backtrace.is_some() {
            return Some(&field);
        }
    }
    for field in fields {
        if type_is_backtrace(field.ty) {
            return Some(&field);
        }
    }
    None
}

fn type_is_backtrace(ty: &Type) -> bool {
    let path = match ty {
        Type::Path(ty) => &ty.path,
        _ => return false,
    };

    let last = path.segments.last().unwrap();
    last.ident == "Backtrace" && last.arguments.is_empty()
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqVM, Patch, AccountCommitment};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction_and_code(transaction: &RPCTransaction, code: &str) -> Context {
    Context {
        caller: Address::from_str(&transaction.from).unwrap(),
        address: Address::from_str(&transaction.to).unwrap(),
        origin: Address::from_str(&transaction.to).unwrap(),
        value: U256::from_str(&transaction.value).unwrap(),
        code: read_hex(code).unwrap(),
        data: read_hex(&transaction.input).unwrap(),
        gas_limit: Gas::from_str(&transaction.gas).unwrap(),
        gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
    }
}

fn main() {
    let mut client = GethRPCClient::new("http://127.0.0.1:8545");

    println!("net version: {}", client.net_version());

    let block = client.get_block_by_number(format!("0x{:x}", 49439).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_block_number = format!("0x{:x}", M256::from_str(&block.number).unwrap() - M256::from(1u64));

    let block_header = from_rpc_block(&block);

    for transaction_hash in block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = client.get_transaction_by_hash(&transaction_hash);
        println!("transaction: {:?}", transaction);
        let receipt = client.get_transaction_receipt(&transaction_hash);
        println!("receipt: {:?}", receipt);
        let code = client.get_code(&transaction.to, &last_block_number);
        println!("code: {:?}", code);

        let context = from_rpc_transaction_and_code(&transaction, &code);

        let mut vm = SeqVM::new(context, block_header.clone(), Patch::empty());
        loop {
            match vm.fire() {
                Ok(()) => {
                    println!("VM exited successfully, checking results ...");
                    break;
                },
                Err(RequireError::Account(address)) => {
                    println!("Feeding VM account at 0x{:x} ...", address);
                    let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                             &last_block_number)).unwrap();
                    let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                    &last_block_number)).unwrap();
                    let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                         &last_block_number)).unwrap();
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    });
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                    let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                      &format!("0x{:x}", index),
                                                                      &last_block_number)).unwrap();
                    vm.commit_account(AccountCommitment::Storage {
                        address: address,
                        index: index,
                        value: value,
                    });
                }
                Err(err) => {
                    println!("Unhandled require: {:?}", err);
                    unimplemented!()
                },
            }
        }

        println!("\ntests after the vm has run:");
        println!("2. test gasUsed == {:?}", receipt.gasUsed);
        println!("3. logs and order is {:?}", receipt.logs);
    }

    println!("\nwhen the block is finished, test:");
    println!("1. balances of all used accounts.");
    println!("2. storage values touched.");

    // let matches = clap_app!(regression_test =>
    //     (version: "0.1")
    //     (author: "Ethereum Classic Contributors")
    //     (about: "Gaslighter - Tests the Ethereum Classic Virtual Machine in 5 different ways.")
    //     (@arg KEEP_GOING: -k --keep_going "Don't exit the program even if a test fails.")
    //     (@subcommand reg =>
    //         (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth`.\n* Wait for the chain to sync.\n* <ctrl-c>\n* Change directory into the gaslighter directory `$ cd gaslighter`\n* Run this command: `$ RUST_BACKTRACE=1 RUST_LOG=gaslighter cargo run --bin gaslighter -- -k reg -c ~/.ethereum/chaindata/`")
    //         (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r localhost:8888`.")
    //     )
    // ).get_matches();
    // let mut has_regression_test_passed = true;
    // let keep_going = if matches.is_present("KEEP_GOING") { true } else { false };
    // if let Some(ref matches) = matches.subcommand_matches("reg") {
    //     let path = matches.value_of("RPC").unwrap();
    //     has_regression_test_passed = regression(path);
    // }
    // if has_regression_test_passed {
    //     process::exit(0);
    // } else {
    //     process::exit(1);
    // }
}
<commit_msg>Problem: actual result of the VM not displayed<commit_after>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqVM, Patch, AccountCommitment};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction_and_code(transaction: &RPCTransaction, code: &str) -> Context {
    Context {
        caller: Address::from_str(&transaction.from).unwrap(),
        address: Address::from_str(&transaction.to).unwrap(),
        origin: Address::from_str(&transaction.to).unwrap(),
        value: U256::from_str(&transaction.value).unwrap(),
        code: read_hex(code).unwrap(),
        data: read_hex(&transaction.input).unwrap(),
        gas_limit: Gas::from_str(&transaction.gas).unwrap(),
        gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
    }
}

fn main() {
    let mut client = GethRPCClient::new("http://127.0.0.1:8545");

    println!("net version: {}", client.net_version());

    let block = client.get_block_by_number(format!("0x{:x}", 49439).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_block_number = format!("0x{:x}", M256::from_str(&block.number).unwrap() - M256::from(1u64));

    let block_header = from_rpc_block(&block);

    for transaction_hash in block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = client.get_transaction_by_hash(&transaction_hash);
        println!("transaction: {:?}", transaction);
        let receipt = client.get_transaction_receipt(&transaction_hash);
        println!("receipt: {:?}", receipt);
        let code = client.get_code(&transaction.to, &last_block_number);
        println!("code: {:?}", code);

        let context = from_rpc_transaction_and_code(&transaction, &code);

        let mut vm = SeqVM::new(context.clone(), block_header.clone(), Patch::empty());
        loop {
            match vm.fire() {
                Ok(()) => {
                    println!("VM exited successfully, checking results ...");
                    break;
                },
                Err(RequireError::Account(address)) => {
                    println!("Feeding VM account at 0x{:x} ...", address);
                    let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                             &last_block_number)).unwrap();
                    let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                    &last_block_number)).unwrap();
                    let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                         &last_block_number)).unwrap();
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    });
                },
                Err(RequireError::AccountStorage(address, index)) => {
                    println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                    let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                      &format!("0x{:x}", index),
                                                                      &last_block_number)).unwrap();
                    vm.commit_account(AccountCommitment::Storage {
                        address: address,
                        index: index,
                        value: value,
                    });
                }
                Err(err) => {
                    println!("Unhandled require: {:?}", err);
                    unimplemented!()
                },
            }
        }

        println!("\ntests after the vm has run:");
        println!("2. test gasUsed == {}, actual VM result: 0x{:x}", receipt.gasUsed,
                 context.gas_limit - vm.available_gas());
        println!("3. logs and order is {:?}, actual VM result: {:?}", receipt.logs, vm.logs());
    }

    println!("\nwhen the block is finished, test:");
    println!("1. balances of all used accounts.");
    println!("2. storage values touched.");

    // let matches = clap_app!(regression_test =>
    //     (version: "0.1")
    //     (author: "Ethereum Classic Contributors")
    //     (about: "Gaslighter - Tests the Ethereum Classic Virtual Machine in 5 different ways.")
    //     (@arg KEEP_GOING: -k --keep_going "Don't exit the program even if a test fails.")
    //     (@subcommand reg =>
    //         (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth`.\n* Wait for the chain to sync.\n* <ctrl-c>\n* Change directory into the gaslighter directory `$ cd gaslighter`\n* Run this command: `$ RUST_BACKTRACE=1 RUST_LOG=gaslighter cargo run --bin gaslighter -- -k reg -c ~/.ethereum/chaindata/`")
    //         (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r localhost:8888`.")
    //     )
    // ).get_matches();
    // let mut has_regression_test_passed = true;
    // let keep_going = if matches.is_present("KEEP_GOING") { true } else { false };
    // if let Some(ref matches) = matches.subcommand_matches("reg") {
    //     let path = matches.value_of("RPC").unwrap();
    //     has_regression_test_passed = regression(path);
    // }
    // if has_regression_test_passed {
    //     process::exit(0);
    // } else {
    //     process::exit(1);
    // }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use anyhow::{ensure, format_err, Context as _, Result};
use libra_config::config::RoleType;
use libra_crypto::x25519;
use libra_logger::prelude::*;
use libra_network_address::NetworkAddress;
use libra_types::{
    account_config,
    account_state::AccountState,
    account_state_blob::AccountStateWithProof,
    epoch_change::EpochChangeProof,
    ledger_info::LedgerInfoWithSignatures,
    on_chain_config::ValidatorSet,
    proof::AccumulatorConsistencyProof,
    trusted_state::{TrustedState, TrustedStateChange},
    validator_info::ValidatorInfo,
    PeerId,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, convert::TryFrom};

/// OnchainDiscovery LibraNet message types. These are sent over-the-wire.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum OnchainDiscoveryMsg {
    QueryDiscoverySetRequest(QueryDiscoverySetRequest),
    QueryDiscoverySetResponse(Box<QueryDiscoverySetResponse>),
}

/// A request for another peer's latest validator set and a validator change proof
/// to get the client up-to-date if they're behind.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct QueryDiscoverySetRequest {
    pub known_version: u64,
    // TODO(philiphayes): split sets into separate V and VFN sets. add enum to
    // select by discovery set type.
}

/// A response to a [`QueryDiscoverySetRequest`]. The server will include an
/// epoch change proof if the client is behind.
///
/// The validator set only changes when there is a new epoch. To minimize
/// wire overhead, the server will include the validator set account's
/// [`AccountStateWithProof`] _if and only if_ the server also presents a
/// non-empty epoch change proof.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct QueryDiscoverySetResponse {
    pub latest_li: LedgerInfoWithSignatures,
    pub epoch_change_proof: EpochChangeProof,
    pub accumulator_proof: AccumulatorConsistencyProof,
    pub account_state: Option<AccountStateWithProof>,
}

impl QueryDiscoverySetResponse {
    /// Verify and ratchet the given trusted state, returning the verified trusted
    /// state change and latest validator set (if present).
    ///
    /// 1. Verify and ratchet trusted state using epoch changes and latest ledger info
    /// 2. (If present) Verify validator set account state proof
    /// 3. (If present) Deserialize `ValidatorSet` from validator set resource in account blob
    pub fn verify_and_ratchet<'a>(
        &'a self,
        req_msg: &QueryDiscoverySetRequest,
        trusted_state: &'a TrustedState,
    ) -> Result<(TrustedStateChange<'a>, Option<ValidatorSet>)> {
        // TODO(philiphayes): how to deal with partial epoch change proofs?
        // should probably not return discovery set until at head?

        let has_epoch_change = !self.epoch_change_proof.ledger_info_with_sigs.is_empty();
        let has_validator_set = self.account_state.is_some();

        // enforce property: epoch change <==> some validator set in response
        ensure!(
            has_epoch_change == has_validator_set,
            "mismatch between epoch change and validator set. \
             has_epoch_change iff has_validator_set: \
             has_epoch_change: {}, has_validator_set: {}",
            has_epoch_change,
            has_validator_set,
        );

        // check response is not stale
        let ledger_version = self.latest_li.ledger_info().version();
        ensure!(
            ledger_version >= req_msg.known_version,
            "received stale ledger_info: ledger_version: {}, request known_version: {}",
            ledger_version,
            req_msg.known_version,
        );

        // try to ratchet trusted state
        let trusted_state_change = trusted_state
            .verify_and_ratchet(&self.latest_li, &self.epoch_change_proof)
            .context("failed to ratchet trusted_state")?;

        // if the response contains the validator set account, then verify
        // account_state_proof and pull out the validator set
        let opt_validator_set = self
            .account_state
            .as_ref()
            .map(|account_state| -> Result<ValidatorSet> {
                account_state
                    .verify(
                        self.latest_li.ledger_info(),
                        ledger_version,
                        account_config::validator_set_address(),
                    )
                    .context("failed to verify account state proof for validator set resource")?;

                let blob = account_state
                    .blob
                    .as_ref()
                    .ok_or_else(|| format_err!("validator set account blob cannot be missing"))?;

                let validator_set = AccountState::try_from(blob)?
                    .get_validator_set()?
                    .ok_or_else(|| format_err!("validator set resource cannot be missing"))?;
                Ok(validator_set)
            })
            .transpose()?;

        // TODO(philiphayes): do something with accumulator_proof?
        // self.query_res.accumulator_proof.

        Ok((trusted_state_change, opt_validator_set))
    }
}

/// An internal representation of the DiscoverySet.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DiscoverySetInternal(pub HashMap<PeerId, DiscoveryInfoInternal>);

impl DiscoverySetInternal {
    pub fn from_validator_set(role_filter: RoleType, validator_set: ValidatorSet) -> Self {
        Self(
            validator_set
                .into_iter()
                .filter_map(|validator_info| {
                    let peer_id = *validator_info.account_address();
                    let res_info =
                        DiscoveryInfoInternal::try_from_validator_info(role_filter, validator_info);

                    // ignore network addresses that fail to deserialize
                    res_info
                        .map_err(|err| {
                            debug!(
                                "failed to deserialize addresses from validator: {}, err: {}",
                                peer_id.short_str(),
                                err
                            )
                        })
                        .map(|info| (peer_id, info))
                        .ok()
                })
                .collect::<HashMap<_, _>>(),
        )
    }

    pub fn empty() -> Self {
        Self(HashMap::new())
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DiscoveryInfoInternal(pub x25519::PublicKey, pub Vec<NetworkAddress>);

impl DiscoveryInfoInternal {
    pub fn try_from_validator_info(
        role_filter: RoleType,
        validator_info: ValidatorInfo,
    ) -> Result<Self> {
        let info = match role_filter {
            RoleType::Validator => Self(
                validator_info
                    .config()
                    .validator_network_identity_public_key,
                vec![NetworkAddress::try_from(
                    &validator_info.config().validator_network_address,
                )?],
            ),
            RoleType::FullNode => Self(
                validator_info
                    .config()
                    .full_node_network_identity_public_key,
                vec![NetworkAddress::try_from(
                    &validator_info.config().full_node_network_address,
                )?],
            ),
        };
        Ok(info)
    }
}
<commit_msg>Removing len from network/onchain-discovery/src/types.rs<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use anyhow::{ensure, format_err, Context as _, Result};
use libra_config::config::RoleType;
use libra_crypto::x25519;
use libra_logger::prelude::*;
use libra_network_address::NetworkAddress;
use libra_types::{
    account_config,
    account_state::AccountState,
    account_state_blob::AccountStateWithProof,
    epoch_change::EpochChangeProof,
    ledger_info::LedgerInfoWithSignatures,
    on_chain_config::ValidatorSet,
    proof::AccumulatorConsistencyProof,
    trusted_state::{TrustedState, TrustedStateChange},
    validator_info::ValidatorInfo,
    PeerId,
};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, convert::TryFrom};

/// OnchainDiscovery LibraNet message types. These are sent over-the-wire.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum OnchainDiscoveryMsg {
    QueryDiscoverySetRequest(QueryDiscoverySetRequest),
    QueryDiscoverySetResponse(Box<QueryDiscoverySetResponse>),
}

/// A request for another peer's latest validator set and a validator change proof
/// to get the client up-to-date if they're behind.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct QueryDiscoverySetRequest {
    pub known_version: u64,
    // TODO(philiphayes): split sets into separate V and VFN sets. add enum to
    // select by discovery set type.
}

/// A response to a [`QueryDiscoverySetRequest`]. The server will include an
/// epoch change proof if the client is behind.
///
/// The validator set only changes when there is a new epoch. To minimize
/// wire overhead, the server will include the validator set account's
/// [`AccountStateWithProof`] _if and only if_ the server also presents a
/// non-empty epoch change proof.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct QueryDiscoverySetResponse {
    pub latest_li: LedgerInfoWithSignatures,
    pub epoch_change_proof: EpochChangeProof,
    pub accumulator_proof: AccumulatorConsistencyProof,
    pub account_state: Option<AccountStateWithProof>,
}

impl QueryDiscoverySetResponse {
    /// Verify and ratchet the given trusted state, returning the verified trusted
    /// state change and latest validator set (if present).
    ///
    /// 1. Verify and ratchet trusted state using epoch changes and latest ledger info
    /// 2. (If present) Verify validator set account state proof
    /// 3. (If present) Deserialize `ValidatorSet` from validator set resource in account blob
    pub fn verify_and_ratchet<'a>(
        &'a self,
        req_msg: &QueryDiscoverySetRequest,
        trusted_state: &'a TrustedState,
    ) -> Result<(TrustedStateChange<'a>, Option<ValidatorSet>)> {
        // TODO(philiphayes): how to deal with partial epoch change proofs?
        // should probably not return discovery set until at head?

        let has_epoch_change = !self.epoch_change_proof.ledger_info_with_sigs.is_empty();
        let has_validator_set = self.account_state.is_some();

        // enforce property: epoch change <==> some validator set in response
        ensure!(
            has_epoch_change == has_validator_set,
            "mismatch between epoch change and validator set. \
             has_epoch_change iff has_validator_set: \
             has_epoch_change: {}, has_validator_set: {}",
            has_epoch_change,
            has_validator_set,
        );

        // check response is not stale
        let ledger_version = self.latest_li.ledger_info().version();
        ensure!(
            ledger_version >= req_msg.known_version,
            "received stale ledger_info: ledger_version: {}, request known_version: {}",
            ledger_version,
            req_msg.known_version,
        );

        // try to ratchet trusted state
        let trusted_state_change = trusted_state
            .verify_and_ratchet(&self.latest_li, &self.epoch_change_proof)
            .context("failed to ratchet trusted_state")?;

        // if the response contains the validator set account, then verify
        // account_state_proof and pull out the validator set
        let opt_validator_set = self
            .account_state
            .as_ref()
            .map(|account_state| -> Result<ValidatorSet> {
                account_state
                    .verify(
                        self.latest_li.ledger_info(),
                        ledger_version,
                        account_config::validator_set_address(),
                    )
                    .context("failed to verify account state proof for validator set resource")?;

                let blob = account_state
                    .blob
                    .as_ref()
                    .ok_or_else(|| format_err!("validator set account blob cannot be missing"))?;

                let validator_set = AccountState::try_from(blob)?
                    .get_validator_set()?
                    .ok_or_else(|| format_err!("validator set resource cannot be missing"))?;
                Ok(validator_set)
            })
            .transpose()?;

        // TODO(philiphayes): do something with accumulator_proof?
        // self.query_res.accumulator_proof.

        Ok((trusted_state_change, opt_validator_set))
    }
}

/// An internal representation of the DiscoverySet.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DiscoverySetInternal(pub HashMap<PeerId, DiscoveryInfoInternal>);

impl DiscoverySetInternal {
    pub fn from_validator_set(role_filter: RoleType, validator_set: ValidatorSet) -> Self {
        Self(
            validator_set
                .into_iter()
                .filter_map(|validator_info| {
                    let peer_id = *validator_info.account_address();
                    let res_info =
                        DiscoveryInfoInternal::try_from_validator_info(role_filter, validator_info);

                    // ignore network addresses that fail to deserialize
                    res_info
                        .map_err(|err| {
                            debug!(
                                "failed to deserialize addresses from validator: {}, err: {}",
                                peer_id.short_str(),
                                err
                            )
                        })
                        .map(|info| (peer_id, info))
                        .ok()
                })
                .collect::<HashMap<_, _>>(),
        )
    }

    pub fn empty() -> Self {
        Self(HashMap::new())
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DiscoveryInfoInternal(pub x25519::PublicKey, pub Vec<NetworkAddress>);

impl DiscoveryInfoInternal {
    pub fn try_from_validator_info(
        role_filter: RoleType,
        validator_info: ValidatorInfo,
    ) -> Result<Self> {
        let info = match role_filter {
            RoleType::Validator => Self(
                validator_info
                    .config()
                    .validator_network_identity_public_key,
                vec![NetworkAddress::try_from(
                    &validator_info.config().validator_network_address,
                )?],
            ),
            RoleType::FullNode => Self(
                validator_info
                    .config()
                    .full_node_network_identity_public_key,
                vec![NetworkAddress::try_from(
                    &validator_info.config().full_node_network_address,
                )?],
            ),
        };
        Ok(info)
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

mod math_util;
#[allow(dead_code)]
mod message_relay_thread;
pub mod surface;
mod thread_message_util;
mod window;
mod window_message_dispatcher;
mod window_message_processor;
pub mod window_procedure_thread;

use std::num::NonZeroU32;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Weak;
use std::time::Duration;

use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use base::error;
use base::AsRawDescriptor;
use base::Event;
use base::EventReadResult;
use base::RawDescriptor;
#[cfg(feature = "kiwi")]
use base::Tube;
use euclid::size2;
use euclid::Size2D;
use math_util::Size2DCheckedCast;
use metrics::Metrics;
pub use surface::NoopSurface as Surface;
#[cfg(feature = "kiwi")]
use sync::Mutex;
use window_message_processor::DisplaySendToWndProc;
pub use window_procedure_thread::WindowProcedureThread;

use crate::DisplayT;
use crate::EventDevice;
use crate::GpuDisplayError;
use crate::GpuDisplayResult;
use crate::GpuDisplaySurface;
use crate::SurfaceType;
use crate::SysDisplayT;

pub(crate) type ObjectId = NonZeroU32;

pub struct VirtualDisplaySpace;
pub struct HostWindowSpace;

#[derive(Clone)]
pub struct DisplayProperties {
    pub start_hidden: bool,
    pub is_fullscreen: bool,
    pub window_width: u32,
    pub window_height: u32,
    #[cfg(feature = "kiwi")]
    pub gpu_main_display_tube: Option<Arc<Mutex<Tube>>>,
}

pub struct DisplayWin {
    wndproc_thread: WindowProcedureThread<Surface>,
    display_closed_event: Event,
    win_metrics: Option<Weak<Metrics>>,
    display_properties: DisplayProperties,
    is_surface_created: bool,
}

impl DisplayWin {
    pub fn new(
        wndproc_thread: WindowProcedureThread<Surface>,
        win_metrics: Option<Weak<Metrics>>,
        display_properties: DisplayProperties,
    ) -> Result<DisplayWin, GpuDisplayError> {
        // The display should be closed once the WndProc thread terminates.
        let display_closed_event =
            wndproc_thread
                .try_clone_thread_terminated_event()
                .map_err(|e| {
                    error!("Failed to create DisplayWin: {:?}", e);
                    GpuDisplayError::Allocate
                })?;
        Ok(Self {
            wndproc_thread,
            display_closed_event,
            win_metrics,
            display_properties,
            is_surface_created: false,
        })
    }

    /// Posts a create surface command to the WndProc thread and waits until the creation finishes
    /// to check the result.
    fn create_surface_internal(
        &mut self,
        virtual_display_size: Size2D<i32, VirtualDisplaySpace>,
    ) -> Result<()> {
        let metrics = self.win_metrics.clone();
        let display_properties = self.display_properties.clone();
        // This function should not return until surface creation finishes. Besides, we would like
        // to know if the creation succeeds. Hence, we use atomic variables so that we can wait to
        // see the result.
        let result = Arc::new(AtomicI32::new(CreateSurfaceResult::NotFinished as i32));
        let result_ready_lock = Arc::new(AtomicBool::new(false));
        let result_clone = Arc::clone(&result);
        let result_ready_lock_clone = Arc::clone(&result_ready_lock);

        // Post a message to the WndProc thread to create the surface.
        self.wndproc_thread
            .post_display_command(DisplaySendToWndProc::CreateSurface {
                function: Box::new(move |window, display_event_dispatcher| {
                    Surface::create(
                        window,
                        &virtual_display_size,
                        metrics,
                        &display_properties,
                        display_event_dispatcher,
                    )
                }),
                callback: Box::new(move |success| {
                    result_clone.store(success.into(), Ordering::SeqCst);
                    drop(result_clone);
                    result_ready_lock_clone.store(true, Ordering::SeqCst);
                }),
            })?;

        // Block until the surface creation finishes and check the result.
        // TODO(b/243184256): Use `Condvar` to avoid busy-waiting on the atomic variable.
        while !result_ready_lock.load(Ordering::SeqCst) {}

        let result = match Arc::try_unwrap(result) {
            Ok(unwrapped_result) => unwrapped_result.into_inner(),
            Err(result) => bail!(
                "Failed to unwrap surface creation result! (Current value: {})",
                result.load(Ordering::SeqCst)
            ),
        };
        if result != CreateSurfaceResult::Success as i32 {
            bail!(
                "WndProc thread failed to create surface! (Result value: {})",
                result
            );
        }
        Ok(())
    }

    fn import_event_device_internal(
        &mut self,
        event_device_id: u32,
        event_device: EventDevice,
    ) -> Result<()> {
        match ObjectId::new(event_device_id) {
            Some(event_device_id) => {
                self.wndproc_thread
                    .post_display_command(DisplaySendToWndProc::ImportEventDevice {
                        event_device_id,
                        event_device,
                    })
                    .context("Failed to send ImportEventDevice message")?;
                Ok(())
            }
            None => bail!("{} cannot be converted to ObjectId", event_device_id),
        }
    }
}

impl AsRawDescriptor for DisplayWin {
    /// Event handling is done on the GPU worker thread on other platforms. However, due to the way
    /// Windows GUI system works, we have to do that on the WndProc thread instead, and we only
    /// notify the event loop in the GPU worker thread of the display closure event.
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.display_closed_event.as_raw_descriptor()
    }
}

impl DisplayT for DisplayWin {
    fn create_surface(
        &mut self,
        parent_surface_id: Option<u32>,
        _surface_id: u32,
        virtual_display_width: u32,
        virtual_display_height: u32,
        surface_type: SurfaceType,
    ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
        if parent_surface_id.is_some() {
            return Err(GpuDisplayError::Unsupported);
        }

        if !matches!(surface_type, SurfaceType::Scanout) {
            return Err(GpuDisplayError::Unsupported);
        }

        // Gfxstream allows for attaching a window only once along the initialization, so we only
        // create the surface once. See details in b/179319775.
        if !self.is_surface_created {
            match self.create_surface_internal(
                size2(virtual_display_width, virtual_display_height).checked_cast(),
            ) {
                Ok(_) => self.is_surface_created = true,
                Err(e) => {
                    error!("Failed to create surface: {:?}", e);
                    return Err(GpuDisplayError::Allocate);
                }
            }
        }

        Ok(Box::new(SurfaceWin {
            display_closed_event: self.display_closed_event.try_clone().map_err(|e| {
                error!("Failed to clone display_closed_event: {}", e);
                GpuDisplayError::Allocate
            })?,
        }))
    }
}

impl SysDisplayT for DisplayWin {
    fn import_event_device(
        &mut self,
        event_device_id: u32,
        event_device: EventDevice,
    ) -> GpuDisplayResult<()> {
        self.import_event_device_internal(event_device_id, event_device)
            .map_err(|e| {
                GpuDisplayError::FailedEventDeviceImport(format!(
                    "Failed to import event device (ID: {}): {:?}",
                    event_device_id, e
                ))
            })
    }

    fn release_event_device(&mut self, event_device_id: u32) {
        match ObjectId::new(event_device_id) {
            Some(event_device_id) => {
                if let Err(e) = self
                    .wndproc_thread
                    .post_display_command(DisplaySendToWndProc::ReleaseEventDevice(event_device_id))
                {
                    error!(
                        "Failed to release event device (ID: {:?}): {:?}",
                        event_device_id, e
                    );
                }
            }
            None => error!(
                "Failed to release event device: {} cannot be converted to ObjectId",
                event_device_id
            ),
        }
    }
}

/// The display logic for Windows is quite different from other platforms since display events are
/// not handled on the GPU worker thread, but handled by `Surface` class that lives in the WndProc
/// thread. `SurfaceWin` will live in the GPU worker thread and provide limited functionalities.
pub(crate) struct SurfaceWin {
    display_closed_event: Event,
}

impl GpuDisplaySurface for SurfaceWin {
    /// The entire VM will be shut down when this function returns true. We don't want that happen
    /// until we know our display is expected to be closed.
    fn close_requested(&self) -> bool {
        match self
            .display_closed_event
            .read_timeout(Duration::from_secs(0))
        {
            Ok(EventReadResult::Count(_)) => true,
            Ok(EventReadResult::Timeout) => false,
            Err(e) => {
                error!("Failed to read whether display is closed: {}", e);
                false
            }
        }
    }
}

enum CreateSurfaceResult {
    NotFinished = 0,
    Success,
    Failure,
}

impl From<bool> for CreateSurfaceResult {
    fn from(success: bool) -> Self {
        if success {
            Self::Success
        } else {
            Self::Failure
        }
    }
}
<commit_msg>gpu_display: use mpsc channels to avoid busy-waiting.<commit_after>// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

mod math_util;
#[allow(dead_code)]
mod message_relay_thread;
pub mod surface;
mod thread_message_util;
mod window;
mod window_message_dispatcher;
mod window_message_processor;
pub mod window_procedure_thread;

use std::num::NonZeroU32;
use std::sync::mpsc::channel;
#[cfg(feature = "kiwi")]
use std::sync::Arc;
use std::sync::Weak;
use std::time::Duration;

use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use base::error;
use base::AsRawDescriptor;
use base::Event;
use base::EventReadResult;
use base::RawDescriptor;
#[cfg(feature = "kiwi")]
use base::Tube;
use euclid::size2;
use euclid::Size2D;
use math_util::Size2DCheckedCast;
use metrics::Metrics;
pub use surface::NoopSurface as Surface;
#[cfg(feature = "kiwi")]
use sync::Mutex;
use window_message_processor::DisplaySendToWndProc;
pub use window_procedure_thread::WindowProcedureThread;

use crate::DisplayT;
use crate::EventDevice;
use crate::GpuDisplayError;
use crate::GpuDisplayResult;
use crate::GpuDisplaySurface;
use crate::SurfaceType;
use crate::SysDisplayT;

pub(crate) type ObjectId = NonZeroU32;

pub struct VirtualDisplaySpace;
pub struct HostWindowSpace;

#[derive(Clone)]
pub struct DisplayProperties {
    pub start_hidden: bool,
    pub is_fullscreen: bool,
    pub window_width: u32,
    pub window_height: u32,
    #[cfg(feature = "kiwi")]
    pub gpu_main_display_tube: Option<Arc<Mutex<Tube>>>,
}

pub struct DisplayWin {
    wndproc_thread: WindowProcedureThread<Surface>,
    display_closed_event: Event,
    win_metrics: Option<Weak<Metrics>>,
    display_properties: DisplayProperties,
    is_surface_created: bool,
}

impl DisplayWin {
    pub fn new(
        wndproc_thread: WindowProcedureThread<Surface>,
        win_metrics: Option<Weak<Metrics>>,
        display_properties: DisplayProperties,
    ) -> Result<DisplayWin, GpuDisplayError> {
        // The display should be closed once the WndProc thread terminates.
        let display_closed_event =
            wndproc_thread
                .try_clone_thread_terminated_event()
                .map_err(|e| {
                    error!("Failed to create DisplayWin: {:?}", e);
                    GpuDisplayError::Allocate
                })?;
        Ok(Self {
            wndproc_thread,
            display_closed_event,
            win_metrics,
            display_properties,
            is_surface_created: false,
        })
    }

    /// Posts a create surface command to the WndProc thread and waits until the creation finishes
    /// to check the result.
    fn create_surface_internal(
        &mut self,
        virtual_display_size: Size2D<i32, VirtualDisplaySpace>,
    ) -> Result<()> {
        let metrics = self.win_metrics.clone();
        let display_properties = self.display_properties.clone();
        // This function should not return until surface creation finishes. Besides, we would like
        // to know if the creation succeeds. Hence, we use channels to wait to see the result.
        let (result_sender, result_receiver) = channel();

        // Post a message to the WndProc thread to create the surface.
        self.wndproc_thread
            .post_display_command(DisplaySendToWndProc::CreateSurface {
                function: Box::new(move |window, display_event_dispatcher| {
                    Surface::create(
                        window,
                        &virtual_display_size,
                        metrics,
                        &display_properties,
                        display_event_dispatcher,
                    )
                }),
                callback: Box::new(move |success| {
                    if let Err(e) = result_sender.send(success) {
                        error!("Failed to send surface creation result: {}", e);
                    }
                }),
            })?;

        // Block until the surface creation finishes and check the result.
        match result_receiver.recv() {
            Ok(true) => Ok(()),
            Ok(false) => bail!("WndProc thread failed to create surface!"),
            Err(e) => bail!("Failed to receive surface creation result: {}", e),
        }
    }

    fn import_event_device_internal(
        &mut self,
        event_device_id: u32,
        event_device: EventDevice,
    ) -> Result<()> {
        match ObjectId::new(event_device_id) {
            Some(event_device_id) => {
                self.wndproc_thread
                    .post_display_command(DisplaySendToWndProc::ImportEventDevice {
                        event_device_id,
                        event_device,
                    })
                    .context("Failed to send ImportEventDevice message")?;
                Ok(())
            }
            None => bail!("{} cannot be converted to ObjectId", event_device_id),
        }
    }
}

impl AsRawDescriptor for DisplayWin {
    /// Event handling is done on the GPU worker thread on other platforms. However, due to the way
    /// Windows GUI system works, we have to do that on the WndProc thread instead, and we only
    /// notify the event loop in the GPU worker thread of the display closure event.
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.display_closed_event.as_raw_descriptor()
    }
}

impl DisplayT for DisplayWin {
    fn create_surface(
        &mut self,
        parent_surface_id: Option<u32>,
        _surface_id: u32,
        virtual_display_width: u32,
        virtual_display_height: u32,
        surface_type: SurfaceType,
    ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> {
        if parent_surface_id.is_some() {
            return Err(GpuDisplayError::Unsupported);
        }

        if !matches!(surface_type, SurfaceType::Scanout) {
            return Err(GpuDisplayError::Unsupported);
        }

        // Gfxstream allows for attaching a window only once along the initialization, so we only
        // create the surface once. See details in b/179319775.
        if !self.is_surface_created {
            match self.create_surface_internal(
                size2(virtual_display_width, virtual_display_height).checked_cast(),
            ) {
                Ok(_) => self.is_surface_created = true,
                Err(e) => {
                    error!("Failed to create surface: {:?}", e);
                    return Err(GpuDisplayError::Allocate);
                }
            }
        }

        Ok(Box::new(SurfaceWin {
            display_closed_event: self.display_closed_event.try_clone().map_err(|e| {
                error!("Failed to clone display_closed_event: {}", e);
                GpuDisplayError::Allocate
            })?,
        }))
    }
}

impl SysDisplayT for DisplayWin {
    fn import_event_device(
        &mut self,
        event_device_id: u32,
        event_device: EventDevice,
    ) -> GpuDisplayResult<()> {
        self.import_event_device_internal(event_device_id, event_device)
            .map_err(|e| {
                GpuDisplayError::FailedEventDeviceImport(format!(
                    "Failed to import event device (ID: {}): {:?}",
                    event_device_id, e
                ))
            })
    }

    fn release_event_device(&mut self, event_device_id: u32) {
        match ObjectId::new(event_device_id) {
            Some(event_device_id) => {
                if let Err(e) = self
                    .wndproc_thread
                    .post_display_command(DisplaySendToWndProc::ReleaseEventDevice(event_device_id))
                {
                    error!(
                        "Failed to release event device (ID: {:?}): {:?}",
                        event_device_id, e
                    );
                }
            }
            None => error!(
                "Failed to release event device: {} cannot be converted to ObjectId",
                event_device_id
            ),
        }
    }
}

/// The display logic for Windows is quite different from other platforms since display events are
/// not handled on the GPU worker thread, but handled by `Surface` class that lives in the WndProc
/// thread. `SurfaceWin` will live in the GPU worker thread and provide limited functionalities.
pub(crate) struct SurfaceWin {
    display_closed_event: Event,
}

impl GpuDisplaySurface for SurfaceWin {
    /// The entire VM will be shut down when this function returns true. We don't want that happen
    /// until we know our display is expected to be closed.
    fn close_requested(&self) -> bool {
        match self
            .display_closed_event
            .read_timeout(Duration::from_secs(0))
        {
            Ok(EventReadResult::Count(_)) => true,
            Ok(EventReadResult::Timeout) => false,
            Err(e) => {
                error!("Failed to read whether display is closed: {}", e);
                false
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 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.

use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;

use base::Event;
use sync::Mutex;

use super::INTERRUPT_STATUS_CONFIG_CHANGED;
use super::INTERRUPT_STATUS_USED_RING;
use super::VIRTIO_MSI_NO_VECTOR;
use crate::irq_event::IrqEdgeEvent;
use crate::irq_event::IrqLevelEvent;
use crate::pci::MsixConfig;

pub trait SignalableInterrupt: Clone {
    /// Writes to the irqfd to VMM to deliver virtual interrupt to the guest.
    fn signal(&self, vector: u16, interrupt_status_mask: u32);

    /// Notify the driver that buffers have been placed in the used queue.
    fn signal_used_queue(&self, vector: u16) {
        self.signal(vector, INTERRUPT_STATUS_USED_RING)
    }

    /// Notify the driver that the device configuration has changed.
    fn signal_config_changed(&self);

    /// Get the event to signal resampling is needed if it exists.
    fn get_resample_evt(&self) -> Option<&Event>;

    /// Reads the status and writes to the interrupt event. Doesn't read the resample event, it
    /// assumes the resample has been requested.
    fn do_interrupt_resample(&self);
}

struct TransportPci {
    irq_evt_lvl: IrqLevelEvent,
    msix_config: Option<Arc<Mutex<MsixConfig>>>,
    config_msix_vector: u16,
}

enum Transport {
    Pci { pci: TransportPci },
    Mmio { irq_evt_edge: IrqEdgeEvent },
}

#[derive(Clone)]
pub struct Interrupt {
    interrupt_status: Arc<AtomicUsize>,
    transport: Arc<Transport>,
}

impl SignalableInterrupt for Interrupt {
    /// Virtqueue Interrupts From The Device
    ///
    /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
    /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        // Don't need to set ISR for MSI-X interrupts
        if let Transport::Pci { pci } = self.transport.as_ref() {
            if let Some(msix_config) = &pci.msix_config {
                let mut msix_config = msix_config.lock();
                if msix_config.enabled() {
                    if vector != VIRTIO_MSI_NO_VECTOR {
                        msix_config.trigger(vector);
                    }
                    return;
                }
            }
        }

        // Set bit in ISR and inject the interrupt if it was not already pending.
        // Don't need to inject the interrupt if the guest hasn't processed it.
        if self
            .interrupt_status
            .fetch_or(interrupt_status_mask as usize, Ordering::SeqCst)
            == 0
        {
            // Write to irqfd to inject PCI INTx or MMIO interrupt
            match self.transport.as_ref() {
                Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
                Transport::Mmio { irq_evt_edge } => irq_evt_edge.trigger().unwrap(),
            }
        }
    }

    fn signal_config_changed(&self) {
        let vector = match self.transport.as_ref() {
            Transport::Pci { pci } => pci.config_msix_vector,
            _ => VIRTIO_MSI_NO_VECTOR,
        };
        self.signal(vector, INTERRUPT_STATUS_CONFIG_CHANGED)
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        match self.transport.as_ref() {
            Transport::Pci { pci } => Some(pci.irq_evt_lvl.get_resample()),
            _ => None,
        }
    }

    fn do_interrupt_resample(&self) {
        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
            match self.transport.as_ref() {
                Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
                _ => panic!("do_interrupt_resample() not supported"),
            }
        }
    }
}

impl Interrupt {
    pub fn new(
        irq_evt_lvl: IrqLevelEvent,
        msix_config: Option<Arc<Mutex<MsixConfig>>>,
        config_msix_vector: u16,
    ) -> Interrupt {
        Interrupt {
            interrupt_status: Arc::new(AtomicUsize::new(0)),
            transport: Arc::new(Transport::Pci {
                pci: TransportPci {
                    irq_evt_lvl,
                    msix_config,
                    config_msix_vector,
                },
            }),
        }
    }

    pub fn new_mmio(irq_evt_edge: IrqEdgeEvent) -> Interrupt {
        Interrupt {
            interrupt_status: Arc::new(AtomicUsize::new(0)),
            transport: Arc::new(Transport::Mmio { irq_evt_edge }),
        }
    }

    /// Get a reference to the interrupt event.
    pub fn get_interrupt_evt(&self) -> &Event {
        match self.transport.as_ref() {
            Transport::Pci { pci } => pci.irq_evt_lvl.get_trigger(),
            Transport::Mmio { irq_evt_edge } => irq_evt_edge.get_trigger(),
        }
    }

    /// Handle interrupt resampling event, reading the value from the event and doing the resample.
    pub fn interrupt_resample(&self) {
        match self.transport.as_ref() {
            Transport::Pci { pci } => {
                pci.irq_evt_lvl.clear_resample();
                self.do_interrupt_resample();
            }
            _ => panic!("interrupt_resample() not supported"),
        }
    }

    /// Get a reference to the msix configuration
    pub fn get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
        match self.transport.as_ref() {
            Transport::Pci { pci } => &pci.msix_config,
            _ => &None,
        }
    }

    /// Reads the current value of the interrupt status.
    pub fn read_interrupt_status(&self) -> u8 {
        self.interrupt_status.load(Ordering::SeqCst) as u8
    }

    /// Reads the current value of the interrupt status and resets it to 0.
    pub fn read_and_reset_interrupt_status(&self) -> u8 {
        self.interrupt_status.swap(0, Ordering::SeqCst) as u8
    }

    /// Clear the bits set in `mask` in the interrupt status.
    pub fn clear_interrupt_status_bits(&self, mask: u8) {
        self.interrupt_status
            .fetch_and(!(mask as usize), Ordering::SeqCst);
    }
}
<commit_msg>devices: interrupt: merge Interrupt Arcs into one<commit_after>// Copyright 2019 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.

use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;

use base::Event;
use sync::Mutex;

use super::INTERRUPT_STATUS_CONFIG_CHANGED;
use super::INTERRUPT_STATUS_USED_RING;
use super::VIRTIO_MSI_NO_VECTOR;
use crate::irq_event::IrqEdgeEvent;
use crate::irq_event::IrqLevelEvent;
use crate::pci::MsixConfig;

pub trait SignalableInterrupt: Clone {
    /// Writes to the irqfd to VMM to deliver virtual interrupt to the guest.
    fn signal(&self, vector: u16, interrupt_status_mask: u32);

    /// Notify the driver that buffers have been placed in the used queue.
    fn signal_used_queue(&self, vector: u16) {
        self.signal(vector, INTERRUPT_STATUS_USED_RING)
    }

    /// Notify the driver that the device configuration has changed.
    fn signal_config_changed(&self);

    /// Get the event to signal resampling is needed if it exists.
    fn get_resample_evt(&self) -> Option<&Event>;

    /// Reads the status and writes to the interrupt event. Doesn't read the resample event, it
    /// assumes the resample has been requested.
    fn do_interrupt_resample(&self);
}

struct TransportPci {
    irq_evt_lvl: IrqLevelEvent,
    msix_config: Option<Arc<Mutex<MsixConfig>>>,
    config_msix_vector: u16,
}

enum Transport {
    Pci { pci: TransportPci },
    Mmio { irq_evt_edge: IrqEdgeEvent },
}

struct InterruptInner {
    interrupt_status: AtomicUsize,
    transport: Transport,
}

#[derive(Clone)]
pub struct Interrupt {
    inner: Arc<InterruptInner>,
}

impl SignalableInterrupt for Interrupt {
    /// Virtqueue Interrupts From The Device
    ///
    /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
    /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
    fn signal(&self, vector: u16, interrupt_status_mask: u32) {
        // Don't need to set ISR for MSI-X interrupts
        if let Transport::Pci { pci } = &self.inner.as_ref().transport {
            if let Some(msix_config) = &pci.msix_config {
                let mut msix_config = msix_config.lock();
                if msix_config.enabled() {
                    if vector != VIRTIO_MSI_NO_VECTOR {
                        msix_config.trigger(vector);
                    }
                    return;
                }
            }
        }

        // Set bit in ISR and inject the interrupt if it was not already pending.
        // Don't need to inject the interrupt if the guest hasn't processed it.
        if self
            .inner
            .as_ref()
            .interrupt_status
            .fetch_or(interrupt_status_mask as usize, Ordering::SeqCst)
            == 0
        {
            // Write to irqfd to inject PCI INTx or MMIO interrupt
            match &self.inner.as_ref().transport {
                Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
                Transport::Mmio { irq_evt_edge } => irq_evt_edge.trigger().unwrap(),
            }
        }
    }

    fn signal_config_changed(&self) {
        let vector = match &self.inner.as_ref().transport {
            Transport::Pci { pci } => pci.config_msix_vector,
            _ => VIRTIO_MSI_NO_VECTOR,
        };
        self.signal(vector, INTERRUPT_STATUS_CONFIG_CHANGED)
    }

    fn get_resample_evt(&self) -> Option<&Event> {
        match &self.inner.as_ref().transport {
            Transport::Pci { pci } => Some(pci.irq_evt_lvl.get_resample()),
            _ => None,
        }
    }

    fn do_interrupt_resample(&self) {
        if self.inner.interrupt_status.load(Ordering::SeqCst) != 0 {
            match &self.inner.as_ref().transport {
                Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
                _ => panic!("do_interrupt_resample() not supported"),
            }
        }
    }
}

impl Interrupt {
    pub fn new(
        irq_evt_lvl: IrqLevelEvent,
        msix_config: Option<Arc<Mutex<MsixConfig>>>,
        config_msix_vector: u16,
    ) -> Interrupt {
        Interrupt {
            inner: Arc::new(InterruptInner {
                interrupt_status: AtomicUsize::new(0),
                transport: Transport::Pci {
                    pci: TransportPci {
                        irq_evt_lvl,
                        msix_config,
                        config_msix_vector,
                    },
                },
            }),
        }
    }

    pub fn new_mmio(irq_evt_edge: IrqEdgeEvent) -> Interrupt {
        Interrupt {
            inner: Arc::new(InterruptInner {
                interrupt_status: AtomicUsize::new(0),
                transport: Transport::Mmio { irq_evt_edge },
            }),
        }
    }

    /// Get a reference to the interrupt event.
    pub fn get_interrupt_evt(&self) -> &Event {
        match &self.inner.as_ref().transport {
            Transport::Pci { pci } => pci.irq_evt_lvl.get_trigger(),
            Transport::Mmio { irq_evt_edge } => irq_evt_edge.get_trigger(),
        }
    }

    /// Handle interrupt resampling event, reading the value from the event and doing the resample.
    pub fn interrupt_resample(&self) {
        match &self.inner.as_ref().transport {
            Transport::Pci { pci } => {
                pci.irq_evt_lvl.clear_resample();
                self.do_interrupt_resample();
            }
            _ => panic!("interrupt_resample() not supported"),
        }
    }

    /// Get a reference to the msix configuration
    pub fn get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
        match &self.inner.as_ref().transport {
            Transport::Pci { pci } => &pci.msix_config,
            _ => &None,
        }
    }

    /// Reads the current value of the interrupt status.
    pub fn read_interrupt_status(&self) -> u8 {
        self.inner.interrupt_status.load(Ordering::SeqCst) as u8
    }

    /// Reads the current value of the interrupt status and resets it to 0.
    pub fn read_and_reset_interrupt_status(&self) -> u8 {
        self.inner.interrupt_status.swap(0, Ordering::SeqCst) as u8
    }

    /// Clear the bits set in `mask` in the interrupt status.
    pub fn clear_interrupt_status_bits(&self, mask: u8) {
        self.inner
            .interrupt_status
            .fetch_and(!(mask as usize), Ordering::SeqCst);
    }
}
<|endoftext|>