<commit_before>/*
 * VM model
 *
 * TODO: describe locking policy
 */

use std::sync::{Arc, Mutex, atomic};
use std::rc::Rc;
use std::mem;
use rlibc::*;
use hypervisor_framework::*;
use util::bitmap::*;

extern "C" {
    fn valloc(size: usize) -> *mut ::std::os::raw::c_void;
}

/**
 * VM allocated memory region
 *
 * Describes single contigious region of host memory allocated to VM
 * A region can be mapped to guest physical memory, several mappings of the same region can exist
 * (see memory_mapping)
 */
pub struct memory_region {
    pub data: hv_uvaddr_t,  // Host base address
    pub size: usize,        // Region size in bytes
}

impl memory_region {

    /**
     * Read bytes from guest region into caller buffer
     */
    pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let toread = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy(buf.as_mut_ptr(), (self.data as *const u8).offset(offset as isize), toread);
        }

        toread
    }

    /**
     * Write bytes from caller buffer to guest memory region
     */
    pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let towrite = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy((self.data as *mut u8).offset(offset as isize), buf.as_ptr(), towrite);
        }

        towrite
    }
}

/**
 * Guest memory mapping
 * Describes a mapping of memory_region to specific guest physical address
 */
pub struct memory_mapping
{
    pub region: Arc<memory_region>,     // Mapped memory region
    pub base: hv_gpaddr_t,              // Guest base physical address
    pub flags: hv_memory_flags_t,       // Mapping flags (RWX)
}

/**
 * IO handler trait
 * Instances of this trait register as guest PIO handlers for specific io regions
 */
pub trait io_handler
{
    /**
     * Read from IO port
     */
    fn io_read(&self, addr: u16, size: u8) -> IoOperandType;

    /**
     * Write to IO port
     */
    fn io_write(&self, vm: &mut Vm, addr: u16, data: IoOperandType);
}

/**
 * Guest IO address space region
 * Usually registered by emulated devices to handle guest IO requests
 */
pub struct io_region
{
    base: u16,              // IO port base
    size: u8,               // IO size (1, 2, 4)
    ops: Rc<io_handler>,    // Instance of io_handler for this region
}

/**
 * Interrupt controller trait
 *
 * Instances of this trait provide generic VM with an interface to assert IRQ lines and
 * acknowledge delivered interrupts.
 */
pub trait interrupt_controller
{
    /**
     * Assert given IRQ line.
     * \param irq   IRQ line to assert
     */
    fn assert_irq(&self, irq: u8);

    /**
     * Notify interrupt controller that interrupt vector has been injected in guest
     * \param vec   Interrupt vector that was previously raise with raise_external_interrupt
     */
    fn ack(&self, vec: u8);
}

/**
 * VM internal state for owning process
 *
 * HV framework internally creates a single global VM context for running process.
 * This means that there is a single shared VM state per single process.
 *
 * TODO: drop trait to clean up and call hv_vm_destroy
 * TODO: a better lookup for memory mappings
 */
struct vm {
    /* HV vcpu id */
    vcpu: hv_vcpuid_t,

    /* Interrupt state */
    pic: Option<Rc<interrupt_controller>>,
    pending_ext_ints: Bitmap,

    /* Mapped memory regions */
    memory: Vec<memory_mapping>,

    /* Registred PIO regions */
    io: Vec<io_region>,
}

/*
 * Unsafe heap pointer to global VM state
 *
 * HV framework internally creates a single global VM context for process context which means
 * that dynamic instanses of this struct don't really make sense.
 *
 * Use get_vm() to safely unwrap this pointer.
 *
 * TODO: We are single threaded now, but remeber to add proper sync in case of multiple threads
 */
static mut VM: Option<*mut vm> = Option::None;
static RUNNING: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn get_vm() -> &'static mut vm
{
    unsafe {
        mem::transmute(VM.unwrap())
    }
}

fn get_pic() -> Rc<interrupt_controller>
{
    get_vm().pic.clone().unwrap()
}

pub fn create()
{
    unsafe {
        let res = hv_vm_create(HV_VM_DEFAULT);
        assert!(res == HV_SUCCESS);

        let vm = vm {
                    vcpu: vcpu_create(),
                    pic: Option::None,
                    pending_ext_ints: Bitmap::new(256),
                    memory: Vec::new(),
                    io: Vec::new()
        };

        VM = Option::Some(mem::transmute(Box::new(vm)));
    }
}

pub fn register_interrupt_controller(pic: Rc<interrupt_controller>)
{
    get_vm().pic = Option::Some(pic);
}

pub fn assert_irq(vec: u8)
{
    get_pic().assert_irq(vec);
}

pub fn has_pending_interrupts() -> bool
{
    get_vm().pending_ext_ints.has_any_set()
}

pub fn raise_external_interrupt(vec: u8)
{
    get_vm().pending_ext_ints.set(vec as usize);
}

pub fn cancel_all_external_interrupts()
{
    get_vm().pending_ext_ints.clear_all();
}

pub fn next_external_interrupt() -> Option<u8>
{
    match get_vm().pending_ext_ints.bsf() {
        Some(vec) => {
            /* ACK interrupt */
            get_vm().pending_ext_ints.clear(vec);
            get_pic().ack(vec as u8);
            return Option::Some(vec as u8);
        }

        None => Option::None,
    }
}

pub fn interrupt_guest()
{
    unsafe {
        let mut vcpus: [hv_vcpuid_t; 1] = [vcpu(); 1];
        let res = hv_vcpu_interrupt(vcpus.as_mut_ptr(), vcpus.len() as u32);
        if res != 0 {
            panic!("hv_vcpu_interrupt failed with {:x}", res);
        }
    }
}

pub fn get_guest_exec_time() -> u64
{
    unsafe {
        let mut time: u64 = 0;
        let res = hv_vcpu_get_exec_time(vcpu(), &mut time as *mut u64);
        if res != 0 {
            panic!("hv_vcpu_get_exec_time failed with {:x}", res);
        }

        time
    }
}

fn alloc_pages(size: usize) -> hv_uvaddr_t
{
    unsafe {
        let va = valloc(size);
        assert!(!va.is_null());
        return va;
    }
}

pub fn vcpu() -> hv_vcpuid_t
{
    //VM.unwrap().vcpu
    get_vm().vcpu
}

pub fn alloc_memory_region(size: usize) -> Arc<memory_region>
{
    let va = alloc_pages(size);
    Arc::new(memory_region { size: size, data: va })
}

pub fn map_memory_region(base: hv_gpaddr_t, flags: hv_memory_flags_t, region: Arc<memory_region>)
{
    unsafe {
        let res = hv_vm_map(region.data, base, region.size, flags);
        assert!(res == HV_SUCCESS);
    }

    get_vm().memory.push(memory_mapping { region: region, base: base, flags: flags });
}

pub fn find_memory_mapping(addr: hv_gpaddr_t) -> Option<&'static memory_mapping>
{
    for i in &get_vm().memory {
        if addr >= i.base && addr < i.base + i.region.size as u64 {
            return Some(i);
        }
    }

    return None;
}

pub fn read_guest_memory(addr: hv_gpaddr_t, buf: &mut [u8]) -> usize
{
    let mapping = match find_memory_mapping(addr) {
        Some(mapping) => mapping,
        None => return 0,
    };

    assert!(addr >= mapping.base);
    mapping.region.read_bytes((addr - mapping.base) as usize, buf)
}

pub fn vcpu_create() -> hv_vcpuid_t
{
    unsafe {
        let mut vcpu: hv_vcpuid_t = 0;
        let res = hv_vcpu_create(&mut vcpu, HV_VCPU_DEFAULT);
        assert!(res == HV_SUCCESS);
        vcpu
    }
}

pub fn run() -> hv_return_t
{
    let res: hv_return_t;

    RUNNING.store(true, atomic::Ordering::Release);
    unsafe {
        res = hv_vcpu_run(vcpu())
    }
    RUNNING.store(false, atomic::Ordering::Release);

    return res;
}

pub fn is_running() -> bool
{
    RUNNING.load(atomic::Ordering::Acquire)
}

pub fn register_io_region(handler: Rc<io_handler>, base: u16, len: u8)
{
    // TODO: check if range intersects
    get_vm().io.push(io_region {
        ops: handler,
        base: base,
        size: len
    });
}

#[derive(Clone, Copy)]
pub enum IoOperandType {
    byte(u8),
    word(u16),
    dword(u32),
}

#[allow(dead_code)]
impl IoOperandType {
    pub fn unwrap_byte(&self) -> u8 {
        match self {
            &IoOperandType::byte(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_word(&self) -> u16 {
        match self {
            &IoOperandType::word(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_dword(&self) -> u32 {
        match self {
            &IoOperandType::dword(v) => v,
            _ => panic!(),
        }
    }

    pub fn make_unhandled(size: u8) -> IoOperandType {
        match size {
            1 => IoOperandType::byte(0xFF),
            2 => IoOperandType::word(0xFFFF),
            4 => IoOperandType::dword(0xFFFFFFFF),
            _ => panic!(),
        }
    }
}

pub fn handle_io_read(port: u16, size: u8) -> IoOperandType
{
    for i in &get_vm().io {
        if port == i.base {
            return i.ops.io_read(port, size);
        }
    }

    panic!("Unhandled IO read from port {:x}", port);
}

pub fn handle_io_write(port: u16, data: IoOperandType)
{
    for i in &get_vm().io {
        if port == i.base {
            i.ops.io_write(port, data);
            return;

        }
    }

    panic!("Unhandled IO write to port {:x}", port);
}
<commit_msg>Build fix<commit_after>/*
 * VM model
 *
 * TODO: describe locking policy
 */

use std::sync::{Arc, Mutex, atomic};
use std::rc::Rc;
use std::mem;
use rlibc::*;
use hypervisor_framework::*;
use util::bitmap::*;

extern "C" {
    fn valloc(size: usize) -> *mut ::std::os::raw::c_void;
}

/**
 * VM allocated memory region
 *
 * Describes single contigious region of host memory allocated to VM
 * A region can be mapped to guest physical memory, several mappings of the same region can exist
 * (see memory_mapping)
 */
pub struct memory_region {
    pub data: hv_uvaddr_t,  // Host base address
    pub size: usize,        // Region size in bytes
}

impl memory_region {

    /**
     * Read bytes from guest region into caller buffer
     */
    pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let toread = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy(buf.as_mut_ptr(), (self.data as *const u8).offset(offset as isize), toread);
        }

        toread
    }

    /**
     * Write bytes from caller buffer to guest memory region
     */
    pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> usize {
        if offset >= self.size {
            return 0;
        }

        let towrite = if offset + buf.len() > self.size {
            self.size - offset
        } else {
            buf.len()
        };

        unsafe {
            memcpy((self.data as *mut u8).offset(offset as isize), buf.as_ptr(), towrite);
        }

        towrite
    }
}

/**
 * Guest memory mapping
 * Describes a mapping of memory_region to specific guest physical address
 */
pub struct memory_mapping
{
    pub region: Arc<memory_region>,     // Mapped memory region
    pub base: hv_gpaddr_t,              // Guest base physical address
    pub flags: hv_memory_flags_t,       // Mapping flags (RWX)
}

/**
 * IO handler trait
 * Instances of this trait register as guest PIO handlers for specific io regions
 */
pub trait io_handler
{
    /**
     * Read from IO port
     */
    fn io_read(&self, addr: u16, size: u8) -> IoOperandType;

    /**
     * Write to IO port
     */
    fn io_write(&self, addr: u16, data: IoOperandType);
}

/**
 * Guest IO address space region
 * Usually registered by emulated devices to handle guest IO requests
 */
pub struct io_region
{
    base: u16,              // IO port base
    size: u8,               // IO size (1, 2, 4)
    ops: Rc<io_handler>,    // Instance of io_handler for this region
}

/**
 * Interrupt controller trait
 *
 * Instances of this trait provide generic VM with an interface to assert IRQ lines and
 * acknowledge delivered interrupts.
 */
pub trait interrupt_controller
{
    /**
     * Assert given IRQ line.
     * \param irq   IRQ line to assert
     */
    fn assert_irq(&self, irq: u8);

    /**
     * Notify interrupt controller that interrupt vector has been injected in guest
     * \param vec   Interrupt vector that was previously raise with raise_external_interrupt
     */
    fn ack(&self, vec: u8);
}

/**
 * VM internal state for owning process
 *
 * HV framework internally creates a single global VM context for running process.
 * This means that there is a single shared VM state per single process.
 *
 * TODO: drop trait to clean up and call hv_vm_destroy
 * TODO: a better lookup for memory mappings
 */
struct vm {
    /* HV vcpu id */
    vcpu: hv_vcpuid_t,

    /* Interrupt state */
    pic: Option<Rc<interrupt_controller>>,
    pending_ext_ints: Bitmap,

    /* Mapped memory regions */
    memory: Vec<memory_mapping>,

    /* Registred PIO regions */
    io: Vec<io_region>,
}

/*
 * Unsafe heap pointer to global VM state
 *
 * HV framework internally creates a single global VM context for process context which means
 * that dynamic instanses of this struct don't really make sense.
 *
 * Use get_vm() to safely unwrap this pointer.
 *
 * TODO: We are single threaded now, but remeber to add proper sync in case of multiple threads
 */
static mut VM: Option<*mut vm> = Option::None;
static RUNNING: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;

fn get_vm() -> &'static mut vm
{
    unsafe {
        mem::transmute(VM.unwrap())
    }
}

fn get_pic() -> Rc<interrupt_controller>
{
    get_vm().pic.clone().unwrap()
}

pub fn create()
{
    unsafe {
        let res = hv_vm_create(HV_VM_DEFAULT);
        assert!(res == HV_SUCCESS);

        let vm = vm {
                    vcpu: vcpu_create(),
                    pic: Option::None,
                    pending_ext_ints: Bitmap::new(256),
                    memory: Vec::new(),
                    io: Vec::new()
        };

        VM = Option::Some(mem::transmute(Box::new(vm)));
    }
}

pub fn register_interrupt_controller(pic: Rc<interrupt_controller>)
{
    get_vm().pic = Option::Some(pic);
}

pub fn assert_irq(vec: u8)
{
    get_pic().assert_irq(vec);
}

pub fn has_pending_interrupts() -> bool
{
    get_vm().pending_ext_ints.has_any_set()
}

pub fn raise_external_interrupt(vec: u8)
{
    get_vm().pending_ext_ints.set(vec as usize);
}

pub fn cancel_all_external_interrupts()
{
    get_vm().pending_ext_ints.clear_all();
}

pub fn next_external_interrupt() -> Option<u8>
{
    match get_vm().pending_ext_ints.bsf() {
        Some(vec) => {
            /* ACK interrupt */
            get_vm().pending_ext_ints.clear(vec);
            get_pic().ack(vec as u8);
            return Option::Some(vec as u8);
        }

        None => Option::None,
    }
}

pub fn interrupt_guest()
{
    unsafe {
        let mut vcpus: [hv_vcpuid_t; 1] = [vcpu(); 1];
        let res = hv_vcpu_interrupt(vcpus.as_mut_ptr(), vcpus.len() as u32);
        if res != 0 {
            panic!("hv_vcpu_interrupt failed with {:x}", res);
        }
    }
}

pub fn get_guest_exec_time() -> u64
{
    unsafe {
        let mut time: u64 = 0;
        let res = hv_vcpu_get_exec_time(vcpu(), &mut time as *mut u64);
        if res != 0 {
            panic!("hv_vcpu_get_exec_time failed with {:x}", res);
        }

        time
    }
}

fn alloc_pages(size: usize) -> hv_uvaddr_t
{
    unsafe {
        let va = valloc(size);
        assert!(!va.is_null());
        return va;
    }
}

pub fn vcpu() -> hv_vcpuid_t
{
    //VM.unwrap().vcpu
    get_vm().vcpu
}

pub fn alloc_memory_region(size: usize) -> Arc<memory_region>
{
    let va = alloc_pages(size);
    Arc::new(memory_region { size: size, data: va })
}

pub fn map_memory_region(base: hv_gpaddr_t, flags: hv_memory_flags_t, region: Arc<memory_region>)
{
    unsafe {
        let res = hv_vm_map(region.data, base, region.size, flags);
        assert!(res == HV_SUCCESS);
    }

    get_vm().memory.push(memory_mapping { region: region, base: base, flags: flags });
}

pub fn find_memory_mapping(addr: hv_gpaddr_t) -> Option<&'static memory_mapping>
{
    for i in &get_vm().memory {
        if addr >= i.base && addr < i.base + i.region.size as u64 {
            return Some(i);
        }
    }

    return None;
}

pub fn read_guest_memory(addr: hv_gpaddr_t, buf: &mut [u8]) -> usize
{
    let mapping = match find_memory_mapping(addr) {
        Some(mapping) => mapping,
        None => return 0,
    };

    assert!(addr >= mapping.base);
    mapping.region.read_bytes((addr - mapping.base) as usize, buf)
}

pub fn vcpu_create() -> hv_vcpuid_t
{
    unsafe {
        let mut vcpu: hv_vcpuid_t = 0;
        let res = hv_vcpu_create(&mut vcpu, HV_VCPU_DEFAULT);
        assert!(res == HV_SUCCESS);
        vcpu
    }
}

pub fn run() -> hv_return_t
{
    let res: hv_return_t;

    RUNNING.store(true, atomic::Ordering::Release);
    unsafe {
        res = hv_vcpu_run(vcpu())
    }
    RUNNING.store(false, atomic::Ordering::Release);

    return res;
}

pub fn is_running() -> bool
{
    RUNNING.load(atomic::Ordering::Acquire)
}

pub fn register_io_region(handler: Rc<io_handler>, base: u16, len: u8)
{
    // TODO: check if range intersects
    get_vm().io.push(io_region {
        ops: handler,
        base: base,
        size: len
    });
}

#[derive(Clone, Copy)]
pub enum IoOperandType {
    byte(u8),
    word(u16),
    dword(u32),
}

#[allow(dead_code)]
impl IoOperandType {
    pub fn unwrap_byte(&self) -> u8 {
        match self {
            &IoOperandType::byte(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_word(&self) -> u16 {
        match self {
            &IoOperandType::word(v) => v,
            _ => panic!(),
        }
    }

    pub fn unwrap_dword(&self) -> u32 {
        match self {
            &IoOperandType::dword(v) => v,
            _ => panic!(),
        }
    }

    pub fn make_unhandled(size: u8) -> IoOperandType {
        match size {
            1 => IoOperandType::byte(0xFF),
            2 => IoOperandType::word(0xFFFF),
            4 => IoOperandType::dword(0xFFFFFFFF),
            _ => panic!(),
        }
    }
}

pub fn handle_io_read(port: u16, size: u8) -> IoOperandType
{
    for i in &get_vm().io {
        if port == i.base {
            return i.ops.io_read(port, size);
        }
    }

    panic!("Unhandled IO read from port {:x}", port);
}

pub fn handle_io_write(port: u16, data: IoOperandType)
{
    for i in &get_vm().io {
        if port == i.base {
            i.ops.io_write(port, data);
            return;

        }
    }

    panic!("Unhandled IO write to port {:x}", port);
}
<|endoftext|>


<commit_before>//! The module exposes an API for defining data prioritization strategies.
//!
//! Types that implement the `DataPrioritizer` trait can be used to provide new data for an
//! `HttpConnection` to send to its peer. Neither the `HttpConnection` nor the `DataPrioritizer`
//! have control over exactly *when* the data is sent. This is left up to the particular client
//! implementations to trigger.

use http::{HttpResult};
use http::connection::{
    DataChunk,
};

/// A trait that types that want to provide data to an HTTP/2 connection need to implement.
pub trait DataPrioritizer {
    /// Returns the next `DataChunk` that should be sent on the HTTP/2 connection. `None` indicates
    /// that currently there was no data that could be sent at that point.
    fn get_next_chunk(&mut self) -> HttpResult<Option<DataChunk>>;
}
<commit_msg>Implement a simple data prioritization strategy<commit_after>//! The module exposes an API for defining data prioritization strategies.
//!
//! Types that implement the `DataPrioritizer` trait can be used to provide new data for an
//! `HttpConnection` to send to its peer. Neither the `HttpConnection` nor the `DataPrioritizer`
//! have control over exactly *when* the data is sent. This is left up to the particular client
//! implementations to trigger.

use http::{HttpResult, HttpError};
use http::connection::{
    DataChunk,
    EndStream,
};
use http::session::{
    SessionState,
    StreamDataChunk, StreamDataError,
    Stream,
};

/// A trait that types that want to provide data to an HTTP/2 connection need to implement.
pub trait DataPrioritizer {
    /// Returns the next `DataChunk` that should be sent on the HTTP/2 connection. `None` indicates
    /// that currently there was no data that could be sent at that point.
    fn get_next_chunk(&mut self) -> HttpResult<Option<DataChunk>>;
}

/// An implementation of the `DataPrioritizer` trait that is based on finding the first stream from
/// the given `SessionState` instance that can send data and returning this chunk.
///
/// For all means and purposes, the order of data chunks that the prioritizer returns is undefined
/// and should not be relied on.
pub struct SimplePrioritizer<'a, 'b, State> where State: SessionState + 'a {
    /// The session state from which the streams' data will be taken
    state: &'a mut State,
    /// The buffer into which the prioritizer can place the stream data chunk
    buf: &'b mut [u8],
}

impl<'a, 'b, State> SimplePrioritizer<'a, 'b, State> where State: SessionState +'a {
    /// Creates a new `SimplePrioritizer` that will use the given state to find stream data that
    /// should be sent and use the given buffer to hold the data of the returned chunk.
    pub fn new(state: &'a mut State, buf: &'b mut [u8]) -> SimplePrioritizer<'a, 'b, State> {
        SimplePrioritizer {
            state: state,
            buf: buf,
        }
    }
}

impl<'a, 'b, State> DataPrioritizer for SimplePrioritizer<'a, 'b, State>
        where State: SessionState +'a {
    fn get_next_chunk(&mut self) -> HttpResult<Option<DataChunk>> {
        // Returns the data of the first stream that has data to be written.
        for stream in self.state.iter().filter(|s| !s.is_closed_local()) {
            let res = stream.get_data_chunk(self.buf);
            match res {
                Ok(StreamDataChunk::Last(total)) => {
                    return Ok(Some(DataChunk::new_borrowed(
                                &self.buf[..total], stream.id(), EndStream::Yes)));
                },
                Ok(StreamDataChunk::Chunk(total)) => {
                    return Ok(Some(DataChunk::new_borrowed(
                                &self.buf[..total], stream.id(), EndStream::No)));
                },
                Ok(StreamDataChunk::Unavailable) => {
                    // Stream is still open, but currently has no data that could be sent.
                    // Pass...
                },
                Err(StreamDataError::Closed) => {
                    // Transition the stream state to be locally closed, so we don't attempt to
                    // write any more data on this stream.
                    stream.close_local();
                    // Find a stream with data to actually write to...
                },
                Err(StreamDataError::Other(e)) => {
                    // Any other error is fatal!
                    return Err(HttpError::Other(e));
                },
            };
        }
        // Nothing can be sent if we reach here -- no streams have data that can be sent.
        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::{DataPrioritizer, SimplePrioritizer};
    use http::session::{DefaultSessionState, SessionState, Stream};

    use http::tests::common::TestStream;

    #[test]
    fn test_simple_prioritizer() {
        fn prepare_state() -> DefaultSessionState<TestStream> {
            DefaultSessionState::new()
        }

        {
            // No streams in the session
            let mut buf = [0; 5];
            let mut state = prepare_state();
            let mut prioritizer = SimplePrioritizer::new(&mut state, &mut buf);

            let chunk = prioritizer.get_next_chunk().unwrap();

            assert!(chunk.is_none());
        }
        {
            // One stream, one chunk
            let mut buf = [0; 5];
            let mut state = prepare_state();
            let mut stream = TestStream::new(1);
            stream.set_outgoing(vec![1, 2, 3]);
            state.insert_stream(stream);
            let mut prioritizer = SimplePrioritizer::new(&mut state, &mut buf);

            {
                let chunk = prioritizer.get_next_chunk().unwrap().unwrap();
                assert_eq!(chunk.data, vec![1, 2, 3]);
            }

            // Now we have no more data?
            assert!(prioritizer.get_next_chunk().unwrap().is_none());
        }
        {
            // One stream, two chunks
            let mut buf = [0; 2];
            let mut state = prepare_state();
            let mut stream = TestStream::new(1);
            stream.set_outgoing(vec![1, 2, 3]);
            state.insert_stream(stream);
            let mut prioritizer = SimplePrioritizer::new(&mut state, &mut buf);

            {
                let chunk = prioritizer.get_next_chunk().unwrap().unwrap();
                assert_eq!(chunk.data, vec![1, 2]);
            }
            {
                let chunk = prioritizer.get_next_chunk().unwrap().unwrap();
                assert_eq!(chunk.data, vec![3]);
            }

            // Now we have no more data?
            assert!(prioritizer.get_next_chunk().unwrap().is_none());
        }
        {
            // Multiple streams
            let mut buf = [0; 10];
            let mut state = prepare_state();
            for id in 0..3 {
                let mut stream = TestStream::new(id);
                stream.set_outgoing(vec![1, 2, 3]);
                state.insert_stream(stream);
            }

            // In total, we get 3 frames; we don't know anything about the order of the streams,
            // though.
            for _ in 0..3 {
                {
                    let mut prioritizer = SimplePrioritizer::new(&mut state, &mut buf);
                    let chunk = prioritizer.get_next_chunk().unwrap().unwrap();
                    assert_eq!(chunk.data, vec![1, 2, 3]);
                }
                // Zero out the buffer to make sure we don't get false results due to the previous
                // data being the same
                for b in buf.iter_mut() { *b = 0; }
            }

            // Now we have no more data?
            let mut prioritizer = SimplePrioritizer::new(&mut state, &mut buf);
            assert!(prioritizer.get_next_chunk().unwrap().is_none());
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate failure;
extern crate log;
extern crate pb_rs;

use clap::{App, Arg};
use pb_rs::types::FileDescriptor;
use pb_rs::ConfigBuilder;
use std::path::{Path, PathBuf};

fn run() -> Result<(), ::failure::Error> {
    let matches = App::new(crate_name!())
        .about(crate_description!())
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .arg(
            Arg::with_name("OUTPUT")
                .required(false)
                .long("output")
                .short("o")
                .takes_value(true)
                .help("Generated file name, defaults to INPUT with 'rs' extension, cannot be used with --output_directory")
                .validator(|x| extension_matches(x, "rs")),
        ).arg(
            Arg::with_name("OUTPUT_DIR")
                .required(false)
                .long("output_directory")
                .short("d")
                .takes_value(true)
                .help("Output directory of generated code, cannot be used with --output"),
        ).arg(
            Arg::with_name("INCLUDE_PATH")
                .required(false)
                .long("include")
                .short("I")
                .takes_value(true)
                .help("Path to search for imported protobufs"),
        ).arg(
            Arg::with_name("SINGLE_MOD")
                .required(false)
                .long("single-mod")
                .short("s")
                .help("Omit generation of modules for each package when there is only one package"),
        ).arg(
            Arg::with_name("NO_OUTPUT")
                .required(false)
                .long("no-output")
                .short("n")
                .help(
                    "Show enums and messages in this .proto file, including those imported. \
                     No code generated",
                ),
        ).arg(
            Arg::with_name("INPUT")
                .multiple(true)
                .help("The .proto files used to generate quick-protobuf code")
                .validator(|x| extension_matches(x, "proto")),
        ).arg(
            Arg::with_name("CYCLE")
                .long("error-cycle")
                .short("e")
                .required(false)
                .help("Error out if recursive messages do not have optional fields"),
        ).arg(
            Arg::with_name("HEADERS")
                .long("no-headers")
                .short("H")
                .required(false)
                .help("Do not add module comments and module attributes in generated file"),
        ).arg(
            Arg::with_name("CUSTOMSTRUCTDERIVE")
                .long("custom_struct_derive")
                .short("C")
                .required(false)
                .help("The comma separated values to add to #[derive(...)] for every struct"),
        .get_matches();

    let in_files = path_vec(values_t!(matches, "INPUT", String));
    let include_paths = path_vec(values_t!(matches, "INCLUDE_PATH", String));
    let out_file = matches.value_of("OUTPUT").map(|o| PathBuf::from(o));
    let out_dir = matches.value_of("OUTPUT_DIR").map(|o| PathBuf::from(o));
    let custom_struct_derive: Vec<String> = matches.value_of("CUSTOMSTRUCTDERIVE").map(|o| o.split(","));

    let compiler = ConfigBuilder::new(
        &in_files,
        out_file.as_ref(),
        out_dir.as_ref(),
        &include_paths,
    )?
    .single_module(matches.is_present("SINGLE_MOD"))
    .no_output(matches.is_present("NO_OUTPUT"))
    .error_cycle(matches.is_present("CYCLE"))
    .headers(matches.is_present("HEADERS"));
    .custom_struct_derive(custom_struct_derive)

    FileDescriptor::run(&compiler.build()).map_err(|e| e.into())
}

fn extension_matches<P: AsRef<Path>>(path: P, expected: &str) -> std::result::Result<(), String> {
    match path.as_ref().extension() {
        Some(x) if x == expected => Ok(()),
        Some(x) => Err(format!(
            "Expected path with extension '{}', not: '{}'",
            expected,
            x.to_string_lossy()
        )),
        None => Err(format!("Expected path with extension '{}'", expected)),
    }
}

fn path_vec(maybe_vec: std::result::Result<Vec<String>, clap::Error>) -> Vec<PathBuf> {
    maybe_vec
        .unwrap_or_else(|_| Vec::new())
        .iter()
        .map(|s| s.into())
        .collect()
}

fn main() {
    env_logger::init();
    ::std::process::exit({
        if let Err(e) = run() {
            eprintln!("pb-rs fatal error");
            for e in e.iter_chain() {
                eprintln!("  - {}", e);
            }
            1
        } else {
            0
        }
    });
}
<commit_msg>Fixed compile error<commit_after>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate failure;
extern crate log;
extern crate pb_rs;

use clap::{App, Arg};
use pb_rs::types::FileDescriptor;
use pb_rs::ConfigBuilder;
use std::path::{Path, PathBuf};

fn run() -> Result<(), ::failure::Error> {
    let matches = App::new(crate_name!())
        .about(crate_description!())
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .arg(
            Arg::with_name("OUTPUT")
                .required(false)
                .long("output")
                .short("o")
                .takes_value(true)
                .help("Generated file name, defaults to INPUT with 'rs' extension, cannot be used with --output_directory")
                .validator(|x| extension_matches(x, "rs")),
        ).arg(
            Arg::with_name("OUTPUT_DIR")
                .required(false)
                .long("output_directory")
                .short("d")
                .takes_value(true)
                .help("Output directory of generated code, cannot be used with --output"),
        ).arg(
            Arg::with_name("INCLUDE_PATH")
                .required(false)
                .long("include")
                .short("I")
                .takes_value(true)
                .help("Path to search for imported protobufs"),
        ).arg(
            Arg::with_name("SINGLE_MOD")
                .required(false)
                .long("single-mod")
                .short("s")
                .help("Omit generation of modules for each package when there is only one package"),
        ).arg(
            Arg::with_name("NO_OUTPUT")
                .required(false)
                .long("no-output")
                .short("n")
                .help(
                    "Show enums and messages in this .proto file, including those imported. \
                     No code generated",
                ),
        ).arg(
            Arg::with_name("INPUT")
                .multiple(true)
                .help("The .proto files used to generate quick-protobuf code")
                .validator(|x| extension_matches(x, "proto")),
        ).arg(
            Arg::with_name("CYCLE")
                .long("error-cycle")
                .short("e")
                .required(false)
                .help("Error out if recursive messages do not have optional fields"),
        ).arg(
            Arg::with_name("HEADERS")
                .long("no-headers")
                .short("H")
                .required(false)
                .help("Do not add module comments and module attributes in generated file"),
        ).arg(
            Arg::with_name("CUSTOMSTRUCTDERIVE")
                .long("custom_struct_derive")
                .short("C")
                .required(false)
                .help("The comma separated values to add to #[derive(...)] for every struct"),
        ).get_matches();

    let in_files = path_vec(values_t!(matches, "INPUT", String));
    let include_paths = path_vec(values_t!(matches, "INCLUDE_PATH", String));
    let out_file = matches.value_of("OUTPUT").map(|o| PathBuf::from(o));
    let out_dir = matches.value_of("OUTPUT_DIR").map(|o| PathBuf::from(o));
    let custom_struct_derive: Vec<String> = matches.value_of("CUSTOMSTRUCTDERIVE").unwrap_or("")
        .split(",").map(|s| s.to_string()).collect();

    let compiler = ConfigBuilder::new(
        &in_files,
        out_file.as_ref(),
        out_dir.as_ref(),
        &include_paths,
    )?
    .single_module(matches.is_present("SINGLE_MOD"))
    .no_output(matches.is_present("NO_OUTPUT"))
    .error_cycle(matches.is_present("CYCLE"))
    .headers(matches.is_present("HEADERS"))
    .custom_struct_derive(custom_struct_derive);

    FileDescriptor::run(&compiler.build()).map_err(|e| e.into())
}

fn extension_matches<P: AsRef<Path>>(path: P, expected: &str) -> std::result::Result<(), String> {
    match path.as_ref().extension() {
        Some(x) if x == expected => Ok(()),
        Some(x) => Err(format!(
            "Expected path with extension '{}', not: '{}'",
            expected,
            x.to_string_lossy()
        )),
        None => Err(format!("Expected path with extension '{}'", expected)),
    }
}

fn path_vec(maybe_vec: std::result::Result<Vec<String>, clap::Error>) -> Vec<PathBuf> {
    maybe_vec
        .unwrap_or_else(|_| Vec::new())
        .iter()
        .map(|s| s.into())
        .collect()
}

fn main() {
    env_logger::init();
    ::std::process::exit({
        if let Err(e) = run() {
            eprintln!("pb-rs fatal error");
            for e in e.iter_chain() {
                eprintln!("  - {}", e);
            }
            1
        } else {
            0
        }
    });
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate failure;
extern crate log;
extern crate pb_rs;

use clap::{App, Arg};
use pb_rs::types::FileDescriptor;
use pb_rs::ConfigBuilder;
use std::path::{Path, PathBuf};

fn run() -> Result<(), ::failure::Error> {
    let matches = App::new(crate_name!())
        .about(crate_description!())
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .arg(
            Arg::with_name("OUTPUT")
                .required(false)
                .long("output")
                .short("o")
                .takes_value(true)
                .help("Generated file name, defaults to INPUT with 'rs' extension, cannot be used with --output_directory")
                .validator(|x| extension_matches(x, "rs")),
        ).arg(
            Arg::with_name("OUTPUT_DIR")
                .required(false)
                .long("output_directory")
                .short("d")
                .takes_value(true)
                .help("Output directory of generated code, cannot be used with --output"),
        ).arg(
            Arg::with_name("INCLUDE_PATH")
                .required(false)
                .long("include")
                .short("I")
                .takes_value(true)
                .help("Path to search for imported protobufs"),
        ).arg(
            Arg::with_name("SINGLE_MOD")
                .required(false)
                .long("single-mod")
                .short("s")
                .help("Omit generation of modules for each package when there is only one package"),
        ).arg(
            Arg::with_name("NO_OUTPUT")
                .required(false)
                .long("no-output")
                .short("n")
                .help(
                    "Show enums and messages in this .proto file, including those imported. \
                     No code generated",
                ),
        ).arg(
            Arg::with_name("INPUT")
                .multiple(true)
                .help("The .proto files used to generate quick-protobuf code")
                .validator(|x| extension_matches(x, "proto")),
        ).arg(
            Arg::with_name("CYCLE")
                .long("error-cycle")
                .short("e")
                .required(false)
                .help("Error out if recursive messages do not have optional fields"),
        ).arg(
            Arg::with_name("HEADERS")
                .long("no-headers")
                .short("H")
                .required(false)
                .help("Do not add module comments and module attributes in generated file"),
        ).arg(
            Arg::with_name("CUSTOM_STRUCT_DERIVE")
                .long("custom_struct_derive")
                .short("C")
                .required(false)
                .help("The comma separated values to add to #[derive(...)] for every struct"),
        ).arg(
            Arg::with_name("DONT_USE_COW")
                .required(false)
                .long("dont_use_cow")
                .short("D")
                .help("Don't use Cow for String and Byte types"),
        ).get_matches();

    let in_files = path_vec(values_t!(matches, "INPUT", String));
    let include_paths = path_vec(values_t!(matches, "INCLUDE_PATH", String));
    let out_file = matches.value_of("OUTPUT").map(|o| PathBuf::from(o));
    let out_dir = matches.value_of("OUTPUT_DIR").map(|o| PathBuf::from(o));
    let custom_struct_derive: Vec<String> = matches.value_of("CUSTOM_STRUCT_DERIVE").unwrap_or("")
        .split(",").map(|s| s.to_string()).collect();

    let compiler = ConfigBuilder::new(
        &in_files,
        out_file.as_ref(),
        out_dir.as_ref(),
        &include_paths,
    )?
    .single_module(matches.is_present("SINGLE_MOD"))
    .no_output(matches.is_present("NO_OUTPUT"))
    .error_cycle(matches.is_present("CYCLE"))
    .headers(matches.is_present("HEADERS"))
    .dont_use_cow(matches.is_present("DONT_USE_COW"))
    .custom_struct_derive(custom_struct_derive);

    FileDescriptor::run(&compiler.build()).map_err(|e| e.into())
}

fn extension_matches<P: AsRef<Path>>(path: P, expected: &str) -> std::result::Result<(), String> {
    match path.as_ref().extension() {
        Some(x) if x == expected => Ok(()),
        Some(x) => Err(format!(
            "Expected path with extension '{}', not: '{}'",
            expected,
            x.to_string_lossy()
        )),
        None => Err(format!("Expected path with extension '{}'", expected)),
    }
}

fn path_vec(maybe_vec: std::result::Result<Vec<String>, clap::Error>) -> Vec<PathBuf> {
    maybe_vec
        .unwrap_or_else(|_| Vec::new())
        .iter()
        .map(|s| s.into())
        .collect()
}

fn main() {
    env_logger::init();
    ::std::process::exit({
        if let Err(e) = run() {
            eprintln!("pb-rs fatal error");
            for e in e.iter_chain() {
                eprintln!("  - {}", e);
            }
            1
        } else {
            0
        }
    });
}
<commit_msg>Now use NO_HEADERS to best describe not to use them.<commit_after>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate failure;
extern crate log;
extern crate pb_rs;

use clap::{App, Arg};
use pb_rs::types::FileDescriptor;
use pb_rs::ConfigBuilder;
use std::path::{Path, PathBuf};

fn run() -> Result<(), ::failure::Error> {
    let matches = App::new(crate_name!())
        .about(crate_description!())
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .arg(
            Arg::with_name("OUTPUT")
                .required(false)
                .long("output")
                .short("o")
                .takes_value(true)
                .help("Generated file name, defaults to INPUT with 'rs' extension, cannot be used with --output_directory")
                .validator(|x| extension_matches(x, "rs")),
        ).arg(
            Arg::with_name("OUTPUT_DIR")
                .required(false)
                .long("output_directory")
                .short("d")
                .takes_value(true)
                .help("Output directory of generated code, cannot be used with --output"),
        ).arg(
            Arg::with_name("INCLUDE_PATH")
                .required(false)
                .long("include")
                .short("I")
                .takes_value(true)
                .help("Path to search for imported protobufs"),
        ).arg(
            Arg::with_name("SINGLE_MOD")
                .required(false)
                .long("single-mod")
                .short("s")
                .help("Omit generation of modules for each package when there is only one package"),
        ).arg(
            Arg::with_name("NO_OUTPUT")
                .required(false)
                .long("no-output")
                .short("n")
                .help(
                    "Show enums and messages in this .proto file, including those imported. \
                     No code generated",
                ),
        ).arg(
            Arg::with_name("INPUT")
                .multiple(true)
                .help("The .proto files used to generate quick-protobuf code")
                .validator(|x| extension_matches(x, "proto")),
        ).arg(
            Arg::with_name("CYCLE")
                .long("error-cycle")
                .short("e")
                .required(false)
                .help("Error out if recursive messages do not have optional fields"),
        ).arg(
            Arg::with_name("NO_HEADERS")
                .long("no-headers")
                .short("H")
                .required(false)
                .help("Do not add module comments and module attributes in generated file"),
        ).arg(
            Arg::with_name("CUSTOM_STRUCT_DERIVE")
                .long("custom_struct_derive")
                .short("C")
                .required(false)
                .help("The comma separated values to add to #[derive(...)] for every struct"),
        ).arg(
            Arg::with_name("DONT_USE_COW")
                .required(false)
                .long("dont_use_cow")
                .short("D")
                .help("Don't use Cow for String and Byte types"),
        ).get_matches();

    let in_files = path_vec(values_t!(matches, "INPUT", String));
    let include_paths = path_vec(values_t!(matches, "INCLUDE_PATH", String));
    let out_file = matches.value_of("OUTPUT").map(|o| PathBuf::from(o));
    let out_dir = matches.value_of("OUTPUT_DIR").map(|o| PathBuf::from(o));
    let custom_struct_derive: Vec<String> = matches.value_of("CUSTOM_STRUCT_DERIVE").unwrap_or("")
        .split(",").map(|s| s.to_string()).collect();

    let compiler = ConfigBuilder::new(
        &in_files,
        out_file.as_ref(),
        out_dir.as_ref(),
        &include_paths,
    )?
    .single_module(matches.is_present("SINGLE_MOD"))
    .no_output(matches.is_present("NO_OUTPUT"))
    .error_cycle(matches.is_present("CYCLE"))
    .headers(!matches.is_present("NO_HEADERS"))
    .dont_use_cow(matches.is_present("DONT_USE_COW"))
    .custom_struct_derive(custom_struct_derive);

    FileDescriptor::run(&compiler.build()).map_err(|e| e.into())
}

fn extension_matches<P: AsRef<Path>>(path: P, expected: &str) -> std::result::Result<(), String> {
    match path.as_ref().extension() {
        Some(x) if x == expected => Ok(()),
        Some(x) => Err(format!(
            "Expected path with extension '{}', not: '{}'",
            expected,
            x.to_string_lossy()
        )),
        None => Err(format!("Expected path with extension '{}'", expected)),
    }
}

fn path_vec(maybe_vec: std::result::Result<Vec<String>, clap::Error>) -> Vec<PathBuf> {
    maybe_vec
        .unwrap_or_else(|_| Vec::new())
        .iter()
        .map(|s| s.into())
        .collect()
}

fn main() {
    env_logger::init();
    ::std::process::exit({
        if let Err(e) = run() {
            eprintln!("pb-rs fatal error");
            for e in e.iter_chain() {
                eprintln!("  - {}", e);
            }
            1
        } else {
            0
        }
    });
}
<|endoftext|>


<commit_before>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(unused_imports))]

use std::env;
use std::thread;
use std::process::{self, Stdio};
use std::sync::mpsc;

use client;
use query::{Query, Response};

/// Manages the renderer process and all communication with it
#[cfg(not(any(feature = "test", test)))]
pub struct RendererProcess {
    process: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

#[cfg(not(any(feature = "test", test)))]
impl RendererProcess {
    /// Spawn the renderer process and also a thread for communicating with that process
    pub fn new() -> Self {
        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let (response_tx, response_rx) = mpsc::channel();
        let handle = thread::spawn(move || {
            client::run(renderer_stdout, response_tx);
        });

        Self {
            process: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Sends a query and automatically decides whether or not to wait for a response.
    ///
    /// If a query does not require a response, this function will return immediately after
    /// sending the query
    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        client::send_query(match self.process.stdin {
            Some(ref mut stdin) => stdin,
            None => unreachable!("bug: renderer process was not opened with stdin"),
        }, &query).unwrap_or_else(|_| {
            // Something went wrong while sending the query, check if the renderer process
            // panicked (exited with an error)
            match self.process.try_wait() {
                Ok(Some(status)) => {
                    if status.success() {
                        // The window/renderer process was closed normally
                        process::exit(0);
                    }
                    else {
                        // Something went wrong, likely the other thread panicked
                        process::exit(1);
                    }
                },
                Ok(None) => panic!("bug: failed to send query even though renderer process was still running"),
                Err(_) => panic!("bug: unable to check the exit status of the renderer process"),
            }
        });

        // Requests need responses
        if let Query::Request(_) = query {
            Some(self.wait_for_response())
        }
        else {
            None
        }
    }

    fn wait_for_response(&mut self) -> Response {
        match self.response_channel.recv() {
            Ok(response) => response,
            // The client thread has exited, that means that the renderer process has exited
            // and the window has closed
            Err(_) => self.exit_process(), // Quit
        }
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        if let Some(handle) = self.thread_handle.take() {
            // First check if the other thread panicked before it quit
            match handle.join() {
                Ok(_) => match self.process.try_wait() {
                    Ok(Some(status)) => {
                        if status.success() {
                            // The window/renderer process was closed normally
                            process::exit(0);
                        }
                        else {
                            // Something went wrong, likely the other thread panicked
                            process::exit(1);
                        }
                    },
                    Ok(None) => match self.process.wait() {
                        Ok(status) => {
                            if status.success() {
                                process::exit(0);
                            }
                            else {
                                process::exit(1);
                            }
                        },
                        Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
                    },
                    Err(_) => panic!("bug: unable to check the exit status of the renderer process after client thread quit"),
                },
                // If this returns an error, the other thread panicked
                Err(_) => process::exit(1),
            }
        }
        else {
            unreachable!("bug: the thread handle was used but the process did not end");
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for RendererProcess {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }

        // Now that the thread has completed, the process likely has as well (or is about to).
        // Check its exit status to see if we need to quit with an error
        match self.process.wait() {
            Ok(status) => if !status.success() {
                process::exit(1);
            },
            Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
        }
    }
}

/// A special "renderer process" specifically for tests. Simulates the renderer process by
/// providing all of the same functionality and reusing internal parts of the
#[cfg(any(feature = "test", test))]
pub struct RendererProcess {
    app: ::app::TurtleApp,
    events: (mpsc::Sender<::event::Event>, mpsc::Receiver<::event::Event>),
    drawing: (mpsc::Sender<::query::DrawingCommand>, mpsc::Receiver<::query::DrawingCommand>),
}

#[cfg(any(feature = "test", test))]
impl RendererProcess {
    pub fn new() -> Self {
        Self {
            app: ::app::TurtleApp::new(),
            events: mpsc::channel(),
            drawing: mpsc::channel(),
        }
    }

    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        ::server::handle_query_for_test_use_only(query, &mut self.app, &self.events.1, &self.drawing.0)
            .expect("test bug: a query failed to be successful")
    }
}
<commit_msg>Reduced the size of exit_process and made it more readable using combinators<commit_after>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(unused_imports))]

use std::env;
use std::thread;
use std::process::{self, Stdio};
use std::sync::mpsc;

use client;
use query::{Query, Response};

/// Manages the renderer process and all communication with it
#[cfg(not(any(feature = "test", test)))]
pub struct RendererProcess {
    process: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

#[cfg(not(any(feature = "test", test)))]
impl RendererProcess {
    /// Spawn the renderer process and also a thread for communicating with that process
    pub fn new() -> Self {
        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let (response_tx, response_rx) = mpsc::channel();
        let handle = thread::spawn(move || {
            client::run(renderer_stdout, response_tx);
        });

        Self {
            process: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Sends a query and automatically decides whether or not to wait for a response.
    ///
    /// If a query does not require a response, this function will return immediately after
    /// sending the query
    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        client::send_query(match self.process.stdin {
            Some(ref mut stdin) => stdin,
            None => unreachable!("bug: renderer process was not opened with stdin"),
        }, &query).unwrap_or_else(|_| {
            // Something went wrong while sending the query, check if the renderer process
            // panicked (exited with an error)
            match self.process.try_wait() {
                Ok(Some(status)) => {
                    if status.success() {
                        // The window/renderer process was closed normally
                        process::exit(0);
                    }
                    else {
                        // Something went wrong, likely the other thread panicked
                        process::exit(1);
                    }
                },
                Ok(None) => panic!("bug: failed to send query even though renderer process was still running"),
                Err(_) => panic!("bug: unable to check the exit status of the renderer process"),
            }
        });

        // Requests need responses
        if let Query::Request(_) = query {
            Some(self.wait_for_response())
        }
        else {
            None
        }
    }

    fn wait_for_response(&mut self) -> Response {
        match self.response_channel.recv() {
            Ok(response) => response,
            // The client thread has exited, that means that the renderer process has exited
            // and the window has closed
            Err(_) => self.exit_process(), // Quit
        }
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        let status = self.thread_handle.take().ok_or_else(|| {
            unreachable!("bug: the thread handle was used but the process did not end");
        }).and_then(|handle| {
            // First check if the other thread panicked before it quit
            handle.join().map_err(|_| ())
        }).and_then(|_| {
            // Then check if the renderer process ended normally
            self.process.wait()
                .map_err(|_| unreachable!("bug: renderer process never ran even though we exited"))
                .and_then(|status| if status.success() { Ok(()) } else { Err(()) })
        });

        match status {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(1),
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for RendererProcess {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }

        // Now that the thread has completed, the process likely has as well (or is about to).
        // Check its exit status to see if we need to quit with an error
        match self.process.wait() {
            Ok(status) => if !status.success() {
                process::exit(1);
            },
            Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
        }
    }
}

/// A special "renderer process" specifically for tests. Simulates the renderer process by
/// providing all of the same functionality and reusing internal parts of the
#[cfg(any(feature = "test", test))]
pub struct RendererProcess {
    app: ::app::TurtleApp,
    events: (mpsc::Sender<::event::Event>, mpsc::Receiver<::event::Event>),
    drawing: (mpsc::Sender<::query::DrawingCommand>, mpsc::Receiver<::query::DrawingCommand>),
}

#[cfg(any(feature = "test", test))]
impl RendererProcess {
    pub fn new() -> Self {
        Self {
            app: ::app::TurtleApp::new(),
            events: mpsc::channel(),
            drawing: mpsc::channel(),
        }
    }

    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        ::server::handle_query_for_test_use_only(query, &mut self.app, &self.events.1, &self.drawing.0)
            .expect("test bug: a query failed to be successful")
    }
}
<|endoftext|>


<commit_before>use crate::config::Config;
use crate::errors::RunError;
use crate::traces::{CoverageStat, TraceMap};
use coveralls_api::*;
use log::{info, warn, trace};
use std::collections::HashMap;
use std::path::Path;
use std::fs;

fn get_git_info(manifest_path: &Path) -> Result<GitInfo, String> {
    let dir_path = manifest_path
        .parent()
        .ok_or_else(|| format!("failed to get parent for path: {}", manifest_path.display()))?;
    let repo = git2::Repository::discover(dir_path).map_err(|err| {
        format!(
            "failed to open git repository: {}: {}",
            dir_path.display(),
            err
        )
    })?;

    let head = repo
        .head()
        .map_err(|err| format!("failed to get repository head: {}", err))?;
    let branch = git2::Branch::wrap(head);
    let branch_name = branch
        .name()
        .map_err(|err| format!("failed to get branch name: {}", err))?;
    let get_string = |data: Option<&str>| match data {
        Some(str) => Ok(str.to_string()),
        None => Err("string is not valid utf-8".to_string()),
    };
    let branch_name = get_string(branch_name)?;
    let commit = repo
        .head()
        .unwrap()
        .peel_to_commit()
        .map_err(|err| format!("failed to get commit: {}", err))?;

    let author = commit.author();
    let committer = commit.committer();
    Ok(GitInfo {
        head: Head {
            id: commit.id().to_string(),
            author_name: get_string(author.name())?,
            author_email: get_string(author.email())?,
            committer_name: get_string(committer.name())?,
            committer_email: get_string(committer.email())?,
            message: get_string(commit.message())?,
        },
        branch: branch_name,
        remotes: Vec::new(),
    })
}

fn get_identity(ci_tool: &Option<CiService>, key: &str) -> Identity {
    match ci_tool {
        Some(CiService::Travis) => {
            let service = Service {
                name: CiService::Travis,
                job_id: Some(key.to_string()),
                number: None,
                build_url: None,
                branch: None,
                pull_request: None,
            };
            Identity::ServiceToken(key.to_string(), service)
        },
        Some(ref service) => {
            let service = match Service::from_ci(service.clone()) {
                Some(s) => s,
                None => Service {
                    name: service.clone(),
                    job_id: Some(key.to_string()),
                    number: None,
                    build_url: None,
                    branch: None,
                    pull_request: None,
                }
            };
            Identity::ServiceToken(key.to_string(), service)
        },
        _ => Identity::best_match_with_token(key.to_string()),
    }
}

pub fn export(coverage_data: &TraceMap, config: &Config) -> Result<(), RunError> {
    if let Some(ref key) = config.coveralls {
        let id = get_identity(&config.ci_tool, key);

        let mut report = CoverallsReport::new(id);
        for file in &coverage_data.files() {
            let rel_path = config.strip_project_path(file);
            let mut lines: HashMap<usize, usize> = HashMap::new();
            let fcov = coverage_data.get_child_traces(file);

            for c in &fcov {
                match c.stats {
                    CoverageStat::Line(hits) => {
                        lines.insert(c.line as usize, hits as usize);
                    }
                    _ => {
                        info!("Support for coverage statistic not implemented or supported for coveralls.io");
                    }
                }
            }
            if let Ok(source) = Source::new(&rel_path, file, &lines, &None, false) {
                report.add_source(source);
            }
        }

        match get_git_info(&config.manifest) {
            Ok(git_info) => {
                report.set_detailed_git_info(git_info);
                info!("Git info collected");
            }
            Err(err) => warn!("Failed to collect git info: {}", err),
        }

        let res = match config.report_uri {
            Some(ref uri) => {
                info!("Sending report to endpoint: {}", uri);
                report.send_to_endpoint(uri)
            }
            None => {
                info!("Sending coverage data to coveralls.io");
                report.send_to_coveralls()
            }
        };
        if config.debug {
            if let Ok(text) = serde_json::to_string(&report) {
                info!("Attempting to write coveralls report to coveralls.json");
                let _ = fs::write("coveralls.json", text);
            } else {
                warn!("Failed to serialise coverage report");
            }
        }
        match res {
            Ok(s) => {
                trace!("Coveralls response {:?}", s);
                Ok(())
            },
            Err(e) => Err(RunError::CovReport(format!("Coveralls send failed. {}", e))),
        }
    } else {
        Err(RunError::CovReport(
            "No coveralls key specified.".to_string(),
        ))
    }
}
<commit_msg>Remove repotoken from travis free<commit_after>use crate::config::Config;
use crate::errors::RunError;
use crate::traces::{CoverageStat, TraceMap};
use coveralls_api::*;
use log::{info, warn, trace};
use std::collections::HashMap;
use std::path::Path;
use std::fs;

fn get_git_info(manifest_path: &Path) -> Result<GitInfo, String> {
    let dir_path = manifest_path
        .parent()
        .ok_or_else(|| format!("failed to get parent for path: {}", manifest_path.display()))?;
    let repo = git2::Repository::discover(dir_path).map_err(|err| {
        format!(
            "failed to open git repository: {}: {}",
            dir_path.display(),
            err
        )
    })?;

    let head = repo
        .head()
        .map_err(|err| format!("failed to get repository head: {}", err))?;
    let branch = git2::Branch::wrap(head);
    let branch_name = branch
        .name()
        .map_err(|err| format!("failed to get branch name: {}", err))?;
    let get_string = |data: Option<&str>| match data {
        Some(str) => Ok(str.to_string()),
        None => Err("string is not valid utf-8".to_string()),
    };
    let branch_name = get_string(branch_name)?;
    let commit = repo
        .head()
        .unwrap()
        .peel_to_commit()
        .map_err(|err| format!("failed to get commit: {}", err))?;

    let author = commit.author();
    let committer = commit.committer();
    Ok(GitInfo {
        head: Head {
            id: commit.id().to_string(),
            author_name: get_string(author.name())?,
            author_email: get_string(author.email())?,
            committer_name: get_string(committer.name())?,
            committer_email: get_string(committer.email())?,
            message: get_string(commit.message())?,
        },
        branch: branch_name,
        remotes: Vec::new(),
    })
}

fn get_identity(ci_tool: &Option<CiService>, key: &str) -> Identity {
    match ci_tool {
        Some(CiService::Travis) => {
            let service = Service {
                name: CiService::Travis,
                job_id: Some(key.to_string()),
                number: None,
                build_url: None,
                branch: None,
                pull_request: None,
            };
            Identity::ServiceToken(String::new(), service)
        },
        Some(ref service) => {
            let service = match Service::from_ci(service.clone()) {
                Some(s) => s,
                None => Service {
                    name: service.clone(),
                    job_id: Some(key.to_string()),
                    number: None,
                    build_url: None,
                    branch: None,
                    pull_request: None,
                }
            };
            Identity::ServiceToken(key.to_string(), service)
        },
        _ => Identity::best_match_with_token(key.to_string()),
    }
}

pub fn export(coverage_data: &TraceMap, config: &Config) -> Result<(), RunError> {
    if let Some(ref key) = config.coveralls {
        let id = get_identity(&config.ci_tool, key);

        let mut report = CoverallsReport::new(id);
        for file in &coverage_data.files() {
            let rel_path = config.strip_project_path(file);
            let mut lines: HashMap<usize, usize> = HashMap::new();
            let fcov = coverage_data.get_child_traces(file);

            for c in &fcov {
                match c.stats {
                    CoverageStat::Line(hits) => {
                        lines.insert(c.line as usize, hits as usize);
                    }
                    _ => {
                        info!("Support for coverage statistic not implemented or supported for coveralls.io");
                    }
                }
            }
            if let Ok(source) = Source::new(&rel_path, file, &lines, &None, false) {
                report.add_source(source);
            }
        }

        match get_git_info(&config.manifest) {
            Ok(git_info) => {
                report.set_detailed_git_info(git_info);
                info!("Git info collected");
            }
            Err(err) => warn!("Failed to collect git info: {}", err),
        }

        let res = match config.report_uri {
            Some(ref uri) => {
                info!("Sending report to endpoint: {}", uri);
                report.send_to_endpoint(uri)
            }
            None => {
                info!("Sending coverage data to coveralls.io");
                report.send_to_coveralls()
            }
        };
        if config.debug {
            if let Ok(text) = serde_json::to_string(&report) {
                info!("Attempting to write coveralls report to coveralls.json");
                let _ = fs::write("coveralls.json", text);
            } else {
                warn!("Failed to serialise coverage report");
            }
        }
        match res {
            Ok(s) => {
                trace!("Coveralls response {:?}", s);
                Ok(())
            },
            Err(e) => Err(RunError::CovReport(format!("Coveralls send failed. {}", e))),
        }
    } else {
        Err(RunError::CovReport(
            "No coveralls key specified.".to_string(),
        ))
    }
}
<|endoftext|>