<commit_before>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::{debug, info};
use oak_abi::proto::oak::application::{
    node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
    WebAssemblyConfiguration,
};
use prost::Message;
use std::{collections::HashMap, path::PathBuf, process::Command};
use tonic::transport::Certificate;

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    // Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
    // should end with `target` so that it gets automatically ignored by our `.gitignore`.
    let target_dir = PathBuf::from("oak_tests/target");

    Command::new("cargo")
        .args(&[
            "build",
            &format!(
                "--target-dir={}",
                target_dir.to_str().expect("invalid target dir")
            ),
            "--target=wasm32-unknown-unknown",
            &format!("--manifest-path={}", cargo_path),
        ])
        .env_remove("RUSTFLAGS")
        .spawn()?
        .wait()?;

    let mut module_path = target_dir;
    module_path.push("wasm32-unknown-unknown/debug");
    module_path.push(module_name);

    info!("compiled Wasm module path: {:?}", module_path);

    std::fs::read(module_path)
}

const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main";
const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml";
const MODULE_WASM_SUFFIX: &str = ".wasm";

/// Convenience helper to build and run a single-Node Application with the given module name, using
/// the default name "oak_main" for its entrypoint.
pub fn run_single_module_default(
    module_config_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME)
}

/// Convenience helper to build and run a single-Node application with the given module name, using
/// the provided entrypoint name.
pub fn run_single_module(
    module_config_name: &str,
    entrypoint_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    let wasm: HashMap<String, Vec<u8>> = [(
        module_config_name.to_owned(),
        compile_rust_wasm(
            DEFAULT_MODULE_MANIFEST,
            &(module_config_name.to_owned() + MODULE_WASM_SUFFIX),
        )
        .expect("failed to build wasm module"),
    )]
    .iter()
    .cloned()
    .collect();

    let application_configuration = ApplicationConfiguration {
        wasm_modules: wasm,
        initial_node_configuration: Some(NodeConfiguration {
            name: "test".to_string(),
            config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                wasm_module_name: module_config_name.to_string(),
                wasm_entrypoint_name: entrypoint_name.to_string(),
            })),
        }),
    };

    oak_runtime::configure_and_run(
        application_configuration,
        oak_runtime::RuntimeConfiguration::default(),
        oak_runtime::GrpcConfiguration {
            grpc_server_tls_identity: None,
            oidc_client_info: None,
            // Some of the tests require a gRPC client, so we populate the required certificate with
            // an invalid value here, even though it will still fail when instantiating the actual
            // gRPC client.
            grpc_client_root_tls_certificate: Some(Certificate::from_pem("invalid-cert")),
        },
    )
}

/// Inject a gRPC request into an Oak Application under test and return the corresponding
/// response (or error).
pub fn grpc_request<R, Q>(
    proxy: &oak_runtime::RuntimeProxy,
    handle: oak_abi::Handle,
    method_name: &str,
    req: &R,
) -> oak::grpc::Result<Q>
where
    R: prost::Message,
    Q: prost::Message + Default,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req =
        oak_abi::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![],
    };

    grpc_req
        .encode(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (req_write_handle, req_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");
    proxy
        .channel_write(req_write_handle, req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (rsp_write_handle, rsp_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![req_read_handle, rsp_write_handle],
    };

    // Send the notification message (with attached handles) into the Node under test.
    proxy
        .channel_write(handle, notify_msg)
        .expect("could not write message");
    proxy
        .channel_close(req_write_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(req_read_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(rsp_write_handle)
        .expect("failed to close channel");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match proxy.channel_read(rsp_read_handle) {
            Ok(Some(r)) => r,
            Ok(None) => {
                debug!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        proxy
            .channel_close(rsp_read_handle)
            .expect("failed to close channel");
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let rsp = oak_abi::proto::oak::encap::GrpcResponse::decode(rsp.data.as_slice())
            .expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        let status = rsp.status.unwrap_or_default();
        if status.code != oak::grpc::Code::Ok as i32 {
            return Err(status);
        }
        let rsp =
            Q::decode(rsp.rsp_msg.as_slice()).expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}
<commit_msg>sdk: separate config building from starting runtime<commit_after>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::{debug, info};
use oak_abi::proto::oak::application::{
    node_configuration::ConfigType, ApplicationConfiguration, NodeConfiguration,
    WebAssemblyConfiguration,
};
use prost::Message;
use std::{collections::HashMap, path::PathBuf, process::Command};
use tonic::transport::Certificate;

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    // Use a separate target dir for Wasm build artifacts. The precise name is not relevant, but it
    // should end with `target` so that it gets automatically ignored by our `.gitignore`.
    let target_dir = PathBuf::from("oak_tests/target");

    Command::new("cargo")
        .args(&[
            "build",
            &format!(
                "--target-dir={}",
                target_dir.to_str().expect("invalid target dir")
            ),
            "--target=wasm32-unknown-unknown",
            &format!("--manifest-path={}", cargo_path),
        ])
        .env_remove("RUSTFLAGS")
        .spawn()?
        .wait()?;

    let mut module_path = target_dir;
    module_path.push("wasm32-unknown-unknown/debug");
    module_path.push(module_name);

    info!("compiled Wasm module path: {:?}", module_path);

    std::fs::read(module_path)
}

/// Default entrypoint name for the module under test.
const DEFAULT_ENTRYPOINT_NAME: &str = "oak_main";

const DEFAULT_MODULE_MANIFEST: &str = "Cargo.toml";
const MODULE_WASM_SUFFIX: &str = ".wasm";

/// Convenience helper to build and run a single-Node Application with the given module name, using
/// the default name "oak_main" for its entrypoint.
pub fn run_single_module_default(
    module_config_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    run_single_module(module_config_name, DEFAULT_ENTRYPOINT_NAME)
}

/// Convenience helper to build and run a single-Node application with the given module name, using
/// the provided entrypoint name.
pub fn run_single_module(
    module_config_name: &str,
    entrypoint_name: &str,
) -> Result<(oak_runtime::RuntimeProxy, oak_abi::Handle), oak::OakStatus> {
    let (app_config, runtime_config, grpc_config) =
        runtime_configs(module_config_name, entrypoint_name);
    oak_runtime::configure_and_run(app_config, runtime_config, grpc_config)
}

/// Build the set of configurations needed to launch a test Runtime instance that
/// runs a single-Node application with the given module name and entrypoint.
pub fn runtime_configs(
    module_config_name: &str,
    entrypoint_name: &str,
) -> (
    ApplicationConfiguration,
    oak_runtime::RuntimeConfiguration,
    oak_runtime::GrpcConfiguration,
) {
    let wasm: HashMap<String, Vec<u8>> = [(
        module_config_name.to_owned(),
        compile_rust_wasm(
            DEFAULT_MODULE_MANIFEST,
            &(module_config_name.to_owned() + MODULE_WASM_SUFFIX),
        )
        .expect("failed to build wasm module"),
    )]
    .iter()
    .cloned()
    .collect();

    (
        ApplicationConfiguration {
            wasm_modules: wasm,
            initial_node_configuration: Some(NodeConfiguration {
                name: "test".to_string(),
                config_type: Some(ConfigType::WasmConfig(WebAssemblyConfiguration {
                    wasm_module_name: module_config_name.to_string(),
                    wasm_entrypoint_name: entrypoint_name.to_string(),
                })),
            }),
        },
        oak_runtime::RuntimeConfiguration::default(),
        oak_runtime::GrpcConfiguration {
            grpc_server_tls_identity: None,
            oidc_client_info: None,
            // Some of the tests require a gRPC client, so we populate the required certificate with
            // an invalid value here, even though it will still fail when instantiating the actual
            // gRPC client.
            grpc_client_root_tls_certificate: Some(Certificate::from_pem("invalid-cert")),
        },
    )
}

/// Inject a gRPC request into an Oak Application under test and return the corresponding
/// response (or error).
pub fn grpc_request<R, Q>(
    proxy: &oak_runtime::RuntimeProxy,
    handle: oak_abi::Handle,
    method_name: &str,
    req: &R,
) -> oak::grpc::Result<Q>
where
    R: prost::Message,
    Q: prost::Message + Default,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req =
        oak_abi::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![],
    };

    grpc_req
        .encode(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (req_write_handle, req_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");
    proxy
        .channel_write(req_write_handle, req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    //
    // In most cases we do not care about labels, so we use the least privileged label for this
    // channel.
    let (rsp_write_handle, rsp_read_handle) = proxy
        .channel_create(&oak_abi::label::Label::public_untrusted())
        .expect("could not create channel");

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::NodeMessage {
        data: vec![],
        handles: vec![req_read_handle, rsp_write_handle],
    };

    // Send the notification message (with attached handles) into the Node under test.
    proxy
        .channel_write(handle, notify_msg)
        .expect("could not write message");
    proxy
        .channel_close(req_write_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(req_read_handle)
        .expect("failed to close channel");
    proxy
        .channel_close(rsp_write_handle)
        .expect("failed to close channel");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match proxy.channel_read(rsp_read_handle) {
            Ok(Some(r)) => r,
            Ok(None) => {
                debug!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        proxy
            .channel_close(rsp_read_handle)
            .expect("failed to close channel");
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let rsp = oak_abi::proto::oak::encap::GrpcResponse::decode(rsp.data.as_slice())
            .expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        let status = rsp.status.unwrap_or_default();
        if status.code != oak::grpc::Code::Ok as i32 {
            return Err(status);
        }
        let rsp =
            Q::decode(rsp.rsp_msg.as_slice()).expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}
<|endoftext|>


<commit_before>
extern crate rand;
use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
use file_stream_iter::FileStream;
use std::io::Error;
use tftp_specific::PacketType;
use std::str::FromStr;
use std::net::AddrParseError;
use std::error;

pub struct EndPoint{
    local_connection : SocketAddrV4,
    remote_connection : SocketAddrV4
}

impl EndPoint{

    pub fn new(remote : &str) -> Result<EndPoint, AddrParseError>{

        let ipv4 = try!(get_ip_from(remote));
        Ok(EndPoint{
            local_connection : SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 6900),
            remote_connection : SocketAddrV4::new(ipv4, 9990)
        })
    }

    fn start_listen(){

    }

    pub fn recv(&mut self) -> Result<(), Error> {
        // Define the local connection information
        //let ip = Ipv4Addr::new(127, 0, 0, 1);
        //TODO we want to use port 69 here
        //let connection = SocketAddrV4::new(ip, 6900);

        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Read from the socket
        let mut buf = [0u8; 516]; //UDP packet is 516 bytes
        let (amt, src) = try!(socket.recv_from(&mut buf));
        match buf[1]{
            3 =>{
                let block_num : u16 = 0u16 | (buf[2] as u16) << 8 |  buf[3] as u16;
                let block_size = amt - 4;
                println!("{:?}", block_size);
                if block_size < 512{
                    println!("recvr recvd: {:?}", &buf[0 .. amt]);
                    println!("Last block of the file received");
                }
                //send ACK

                let low = block_num & 0x00FF;
                let high = (block_num & 0xFF00) >> 8;

                socket.send_to(&[0,PacketType::ACK as u8, high as u8, low as u8], src);
            },
            _ => {}
        }

        Ok(())
    }

    pub fn send(&mut self) -> Result<(), Error> {
        // Define the local connection (to send the data from)
        //let ip = Ipv4Addr::new(127, 0, 0, 1);
        //TODO generate port # using rand
        //let connection = SocketAddrV4::new(ip, 9992);

        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Define the remote connection (to send the data to)
        //let connection2 = SocketAddrV4::new(ip, 6900);

        // Send data via the socket
        let mut fs = FileStream::new("/home/abhi/code/rust/fei/target/debug/foo.txt".to_string()).unwrap();
        let (buf, num_bytes_read) = fs.next().unwrap();
        let mut v = Vec::with_capacity(num_bytes_read + 4);
        v.push(0);
        v.push(3);
        v.push(0xFF);
        let mut cnt = 0xFF;
        v.push(cnt);
        v.extend_from_slice(&buf[0..num_bytes_read]);
        try!(socket.send_to(v.as_slice(), self.remote_connection));

        let mut buf  = [0; 10];
        socket.recv_from(&mut buf);

        println!("sender recvd {:?}", buf);
        Ok(())
    }

    fn send_file(&mut self, filename : &str) {

    }

    fn create_rrq_wrq_packet(&self, p_type: PacketType, file_name: &str, mode : &'static str)->Vec<u8>{
        let mut v = Vec::with_capacity(2+file_name.len()+1+mode.len()+1);
        v.push(0);
        v.push(p_type as u8);
        v.extend(file_name.as_bytes());
        v.push(0);//zero terminator
        v.extend(mode.as_bytes());
        v.push(0);//zero terminator
        v
    }

    fn get(&mut self, files : &[&str], mode : &'static str){
        let socket = try!(UdpSocket::bind(self.local_connection));
        let mut buf  = [0; 516];
        for file in files{
            //start with a RRQ
            let packet = self.create_rrq_wrq_packet(PacketType::RRQ, file, mode);
            try!(socket.send_to(packet.as_slice(), self.remote_connection));

            let (amt, src) = try!(socket.recv_from(&mut buf));
            match buf[1]{
                3 =>{
                    let block_num : u16 = 0u16 | (buf[2] as u16) << 8 |  buf[3] as u16;
                    let block_size = amt - 4;
                    println!("{:?}", block_size);
                    if block_size < 512{
                        println!("recvr recvd: {:?}", &buf[0 .. amt]);
                        println!("Last block of the file received");
                    }
                    //send ACK

                    let low = block_num & 0x00FF;
                    let high = (block_num & 0xFF00) >> 8;

                    socket.send_to(&[0,PacketType::ACK as u8, high as u8, low as u8], src);
                },
                _ => {}
            }

        }
    }
}

fn get_ip_from(ip : &str) -> Result<Ipv4Addr, AddrParseError>{
    Ipv4Addr::from_str(ip)
}

<commit_msg>Fix return type<commit_after>
extern crate rand;
use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
use file_stream_iter::FileStream;
use std::io::Error;
use tftp_specific::PacketType;
use std::str::FromStr;
use std::net::AddrParseError;
use std::error;

pub struct EndPoint{
    local_connection : SocketAddrV4,
    remote_connection : SocketAddrV4
}

impl EndPoint{

    pub fn new(remote : &str) -> Result<EndPoint, AddrParseError>{

        let ipv4 = try!(get_ip_from(remote));
        Ok(EndPoint{
            local_connection : SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 6900),
            remote_connection : SocketAddrV4::new(ipv4, 9990)
        })
    }

    fn start_listen(){

    }

    pub fn recv(&mut self) -> Result<(), Error> {
        // Define the local connection information
        //let ip = Ipv4Addr::new(127, 0, 0, 1);
        //TODO we want to use port 69 here
        //let connection = SocketAddrV4::new(ip, 6900);

        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Read from the socket
        let mut buf = [0u8; 516]; //UDP packet is 516 bytes
        let (amt, src) = try!(socket.recv_from(&mut buf));
        match buf[1]{
            3 =>{
                let block_num : u16 = 0u16 | (buf[2] as u16) << 8 |  buf[3] as u16;
                let block_size = amt - 4;
                println!("{:?}", block_size);
                if block_size < 512{
                    println!("recvr recvd: {:?}", &buf[0 .. amt]);
                    println!("Last block of the file received");
                }
                //send ACK

                let low = block_num & 0x00FF;
                let high = (block_num & 0xFF00) >> 8;

                socket.send_to(&[0,PacketType::ACK as u8, high as u8, low as u8], src);
            },
            _ => {}
        }

        Ok(())
    }

    pub fn send(&mut self) -> Result<(), Error> {
        // Define the local connection (to send the data from)
        //let ip = Ipv4Addr::new(127, 0, 0, 1);
        //TODO generate port # using rand
        //let connection = SocketAddrV4::new(ip, 9992);

        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Define the remote connection (to send the data to)
        //let connection2 = SocketAddrV4::new(ip, 6900);

        // Send data via the socket
        let mut fs = FileStream::new("/home/abhi/code/rust/fei/target/debug/foo.txt".to_string()).unwrap();
        let (buf, num_bytes_read) = fs.next().unwrap();
        let mut v = Vec::with_capacity(num_bytes_read + 4);
        v.push(0);
        v.push(3);
        v.push(0xFF);
        let mut cnt = 0xFF;
        v.push(cnt);
        v.extend_from_slice(&buf[0..num_bytes_read]);
        try!(socket.send_to(v.as_slice(), self.remote_connection));

        let mut buf  = [0; 10];
        socket.recv_from(&mut buf);

        println!("sender recvd {:?}", buf);
        Ok(())
    }

    fn send_file(&mut self, filename : &str) {

    }

    fn create_rrq_wrq_packet(&self, p_type: PacketType, file_name: &str, mode : &'static str)->Vec<u8>{
        let mut v = Vec::with_capacity(2+file_name.len()+1+mode.len()+1);
        v.push(0);
        v.push(p_type as u8);
        v.extend(file_name.as_bytes());
        v.push(0);//zero terminator
        v.extend(mode.as_bytes());
        v.push(0);//zero terminator
        v
    }

    fn get(&mut self, files : &[&str], mode : &'static str) -> Result<(), Error>{
        let socket = try!(UdpSocket::bind(self.local_connection));
        let mut buf  = [0; 516];
        for file in files{
            //start with a RRQ
            let packet = self.create_rrq_wrq_packet(PacketType::RRQ, file, mode);
            try!(socket.send_to(packet.as_slice(), self.remote_connection));

            //get the first block of the requested file
            let (amt, src) = try!(socket.recv_from(&mut buf));
            match buf[1]{
                3 =>{
                    let block_num : u16 = 0u16 | (buf[2] as u16) << 8 |  buf[3] as u16;
                    let block_size = amt - 4;
                    println!("{:?}", block_size);
                    if block_size < 512{
                        println!("recvr recvd: {:?}", &buf[0 .. amt]);
                        println!("Last block of the file received");
                    }
                    //send ACK

                    let low = block_num & 0x00FF;
                    let high = (block_num & 0xFF00) >> 8;

                    socket.send_to(&[0,PacketType::ACK as u8, high as u8, low as u8], src);
                },
                _ => {}
            }

        }

        Ok(())
    }
}

fn get_ip_from(ip : &str) -> Result<Ipv4Addr, AddrParseError>{
    Ipv4Addr::from_str(ip)
}

<|endoftext|>


<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use crate::{
    message::Message,
    proto::oak::introspection_events::{event::EventDetails, ChannelDestroyed},
    Runtime,
};
use log::{debug, error};
use oak_abi::OakStatus;
use std::{
    collections::{HashMap, VecDeque},
    sync::{
        atomic::{AtomicU64, Ordering::SeqCst},
        Arc, Mutex, RwLock, RwLockReadGuard, Weak,
    },
    thread::{Thread, ThreadId},
};

type Messages = VecDeque<Message>;

/// Trait that gives an identifier for a data structure that is suitable for
/// use with Graphviz/Dot.
pub trait DotIdentifier {
    fn dot_id(&self) -> String;
}

/// A `HashMap` of waiting threads, keyed by `ThreadId`.
///
/// Implemented as a `HashMap` to prevent build up of stale `Weak<Thread>`s.
///
/// That is: If a thread waiting/blocked on a channel is woken by a different channel, its
/// `Weak<Thread>` will remain in the first channel's waiting_thread member. If a thread keeps
/// waiting on this first channel, and keeps being woken by other channels, it will keep re-adding
/// itself. We use a `HashMap` and insert at the current `ThreadId` so that we replace any stale
/// `Weak<Thread>`s which will have gone out of scope. (`wait_on_channels` drops the underlying arc
/// as soon as it is resumed.)
type WaitingThreads = Mutex<HashMap<ThreadId, Weak<Thread>>>;

/// The internal implementation of a channel representation backed by a `VecDeque<Message>`.
///
/// Channels are reference counted using `Arc<Channel>`, which are always in the form of a
/// [`ChannelHalf`] object that references one end of the [`Channel`] (read or write) and which
/// is included in the `reader_count` or `writer_count`.
pub struct Channel {
    // An internal identifier for this channel. Purely for disambiguation in debugging output.
    id: ChannelId,

    pub messages: RwLock<Messages>,

    // Counts of the numbers of `ChannelHalf` objects that refer to this channel.
    writer_count: AtomicU64,
    reader_count: AtomicU64,

    /// A HashMap of `ThreadId`s to `Weak<Thread>`s. This allows threads to insert a weak reference
    /// to themselves to be woken when a new message is available. Weak references are used so that
    /// if the thread is woken by a different channel, it can deallocate the underlying `Arc`
    /// instead of removing itself from all the `Channel`s it subscribed to.
    /// Threads can be woken up spuriously without issue.
    waiting_threads: WaitingThreads,

    /// The `Label` associated with this channel.
    ///
    /// This is set at channel creation time and does not change after that.
    ///
    /// See https://github.com/project-oak/oak/blob/main/docs/concepts.md#labels
    pub label: oak_abi::label::Label,

    /// The name for the channel.
    ///
    /// The name does not have to be unique and can be empty. In logs it is
    /// combined with the id to form a unique debug_ig to identify channels.
    name: String,

    /// Weak reference to the Runtime used for sending introspection events.
    runtime_weak: Weak<Runtime>,
}

impl std::fmt::Debug for Channel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Channel {{ id={}, #readers={}, #writers={}, name={:?}, label={:?} }}",
            self.id,
            self.reader_count.load(SeqCst),
            self.writer_count.load(SeqCst),
            self.name,
            self.label,
        )
    }
}

/// A reference to one half of a `Channel`
pub struct ChannelHalf {
    channel: Arc<Channel>,
    pub direction: ChannelHalfDirection,
}

impl ChannelHalf {
    /// Constructor for [`ChannelHalf`] keeps the underlying `Channel`'s reader/writer count
    /// up-to-date.
    pub fn new(channel: Arc<Channel>, direction: ChannelHalfDirection) -> Self {
        match direction {
            ChannelHalfDirection::Write => channel.inc_writer_count(),
            ChannelHalfDirection::Read => channel.inc_reader_count(),
        };
        ChannelHalf { channel, direction }
    }

    /// Get the ID of the underlying channel.  For debugging/introspection
    /// purposes.
    pub fn get_channel_id(&self) -> u64 {
        self.channel.id
    }

    /// Returns a unique debug_id used to identify the channel in debug output.
    pub fn get_channel_debug_id(&self) -> String {
        self.channel.get_debug_id()
    }

    /// Get read-only access to the channel's messages.  For debugging/introspection
    /// purposes.
    pub fn get_messages(&self) -> RwLockReadGuard<Messages> {
        self.channel.messages.read().unwrap()
    }

    /// Visit all channel halves that are reachable via this `ChannelHalf`, starting with
    /// `self`. The `visitor` function should return a boolean indicating whether the provided half
    /// needs to be further explored.  For debugging/introspection purposes.
    pub fn visit_halves<F>(&self, visitor: &mut F)
    where
        F: FnMut(&ChannelHalf) -> bool,
    {
        if visitor(self) {
            let mut to_visit = Vec::new();
            {
                let messages = self.get_messages();
                for msg in messages.iter() {
                    for half in &msg.channels {
                        to_visit.push(half.clone())
                    }
                }
            }
            // Now the lock is not held, let us go and make our visit.
            for half in to_visit {
                half.visit_halves(visitor);
            }
        }
    }

    /// Wake any threads waiting on the underlying channel.
    pub fn wake_waiters(&self) {
        self.channel.wake_waiters();
    }
}

/// Manual implementation of the [`Clone`] trait to keep the counts for the underlying `Channel` in
/// sync.
impl Clone for ChannelHalf {
    fn clone(&self) -> Self {
        ChannelHalf::new(self.channel.clone(), self.direction)
    }
}

/// Manual implementation of the [`Drop`] trait to keep the counts for the underlying `Channel` in
/// sync.
impl Drop for ChannelHalf {
    fn drop(&mut self) {
        match self.direction {
            ChannelHalfDirection::Write => self.channel.dec_writer_count(),
            ChannelHalfDirection::Read => self.channel.dec_reader_count(),
        };
        if self.direction == ChannelHalfDirection::Write && !self.channel.has_writers() {
            // This was the last writer to the channel: wake any waiters so they
            // can be aware that the channel is orphaned.
            debug!(
                "last writer for channel {} gone, wake waiters",
                self.channel.id
            );
            self.wake_waiters();
        }
    }
}

impl std::fmt::Debug for ChannelHalf {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Channel {} ('{}') {}",
            self.channel.id,
            self.channel.name,
            match self.direction {
                ChannelHalfDirection::Write => "WRITE",
                ChannelHalfDirection::Read => "READ",
            },
        )
    }
}

impl DotIdentifier for ChannelHalf {
    fn dot_id(&self) -> String {
        self.channel.id.dot_id()
    }
}

/// Perform an operation on a [`Channel`] associated with a [`ChannelId`].
fn with_channel<U, F: FnOnce(Arc<Channel>) -> Result<U, OakStatus>>(
    half: &ChannelHalf,
    f: F,
) -> Result<U, OakStatus> {
    f(half.channel.clone())
}

/// Perform an operation on a [`Channel`] associated with a reader [`ChannelHalf`].
pub fn with_reader_channel<U, F: FnOnce(Arc<Channel>) -> Result<U, OakStatus>>(
    half: &ChannelHalf,
    f: F,
) -> Result<U, OakStatus> {
    if half.direction != ChannelHalfDirection::Read {
        return Err(OakStatus::ErrBadHandle);
    }
    with_channel(half, f)
}

/// Perform an operation on a [`Channel`] associated with a writer [`ChannelHalf`].
pub fn with_writer_channel<U, F: FnOnce(Arc<Channel>) -> Result<U, OakStatus>>(
    half: &ChannelHalf,
    f: F,
) -> Result<U, OakStatus> {
    if half.direction != ChannelHalfDirection::Write {
        return Err(OakStatus::ErrBadHandle);
    }
    with_channel(half, f)
}

/// The direction of a [`ChannelHalf`].
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub enum ChannelHalfDirection {
    Read,
    Write,
}

/// An internal identifier to track a [`Channel`].
type ChannelId = u64;

impl DotIdentifier for ChannelId {
    fn dot_id(&self) -> String {
        format!("channel{}", self)
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        debug!("dropping Channel object {:?}", self);
        match self.runtime_weak.upgrade() {
            Some(runtime) => {
                runtime.introspection_event(EventDetails::ChannelDestroyed(ChannelDestroyed {
                    channel_id: self.id,
                }));
            }
            None => {
                error!("Couldn't send ChannelDestroyed event, since the runtime has been dropped. {:?}", self);
            }
        }

        // There should be no waiters for this channel (a waiting Node would have
        // to have a `Handle` to wait on, which would be a reference that pins this
        // channel to existence) so no need to `wake_waiters()`.
        // Deliberately clear the HashMap under the lock to avoid a TSAN report.
        self.waiting_threads.lock().unwrap().clear();
    }
}

impl Channel {
    pub fn new(
        id: ChannelId,
        name: &str,
        label: &oak_abi::label::Label,
        runtime_weak: Weak<Runtime>,
    ) -> Arc<Channel> {
        debug!("create new Channel object with ID {}", id);
        Arc::new(Channel {
            id,
            messages: RwLock::new(Messages::new()),
            writer_count: AtomicU64::new(0),
            reader_count: AtomicU64::new(0),
            waiting_threads: Mutex::new(HashMap::new()),
            label: label.clone(),
            name: name.to_string(),
            runtime_weak,
        })
    }

    /// Returns a unique debug_id used to identify the channel in the debug
    /// output, consisting of the provided [`ChannelId`], and the channel name.
    fn get_debug_id(&self) -> String {
        format!("{}({})", &self.name, self.id)
    }

    /// Determine whether there are any readers of the channel.
    pub fn has_readers(&self) -> bool {
        self.reader_count.load(SeqCst) > 0
    }

    /// Determine whether there are any writers of the channel.
    pub fn has_writers(&self) -> bool {
        self.writer_count.load(SeqCst) > 0
    }

    /// Decrement the [`Channel`] writer counter.
    fn dec_writer_count(&self) {
        if self.writer_count.fetch_sub(1, SeqCst) == 0 {
            panic!("remove_reader: Writer count was already 0, something is very wrong!")
        }
    }

    /// Decrement the [`Channel`] reader counter.
    fn dec_reader_count(&self) {
        if self.reader_count.fetch_sub(1, SeqCst) == 0 {
            panic!("remove_reader: Reader count was already 0, something is very wrong!")
        }
    }

    /// Increment the [`Channel`] writer counter.
    fn inc_writer_count(&self) -> u64 {
        self.writer_count.fetch_add(1, SeqCst)
    }

    /// Increment the [`Channel`] reader counter.
    fn inc_reader_count(&self) -> u64 {
        self.reader_count.fetch_add(1, SeqCst)
    }

    /// Add the given [`Thread`] reference into the collection of [`Thread`]s waiting on this
    /// [`Channel`]'s readability.  Threads waiting on the [`Channel`] will be woken when
    /// data is available, or if the [`Channel`] becomes orphaned (no writers left).
    pub fn add_waiter(&self, thread: &Arc<Thread>) {
        self.waiting_threads
            .lock()
            .unwrap()
            .insert(thread.id(), Arc::downgrade(thread));
    }

    /// Wake any [`Thread`]s that are waiting on the [`Channel`].
    pub fn wake_waiters(&self) {
        // Unpark (wake up) all waiting threads that still have live references. The first thread
        // woken can immediately read the message, and others might find `messages` is empty before
        // they are even woken. This should not be an issue (being woken does not guarantee a
        // message is available), but it could potentially result in some particular thread always
        // getting first chance to read the message.
        //
        // If a thread is woken and finds no message it will take the `waiting_threads` lock and
        // add itself again. Note that since that lock is currently held, the woken thread will add
        // itself to waiting_threads *after* we call clear below as we release the lock implicilty
        // on leaving this function.
        let mut waiting_threads = self.waiting_threads.lock().unwrap();
        for thread in waiting_threads.values() {
            if let Some(thread) = thread.upgrade() {
                thread.unpark();
            }
        }
        waiting_threads.clear();
    }
}
<commit_msg>runtime: include label in channel half info (#1878)<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use crate::{
    message::Message,
    proto::oak::introspection_events::{event::EventDetails, ChannelDestroyed},
    Runtime,
};
use log::{debug, error};
use oak_abi::OakStatus;
use std::{
    collections::{HashMap, VecDeque},
    sync::{
        atomic::{AtomicU64, Ordering::SeqCst},
        Arc, Mutex, RwLock, RwLockReadGuard, Weak,
    },
    thread::{Thread, ThreadId},
};

type Messages = VecDeque<Message>;

/// Trait that gives an identifier for a data structure that is suitable for
/// use with Graphviz/Dot.
pub trait DotIdentifier {
    fn dot_id(&self) -> String;
}

/// A `HashMap` of waiting threads, keyed by `ThreadId`.
///
/// Implemented as a `HashMap` to prevent build up of stale `Weak<Thread>`s.
///
/// That is: If a thread waiting/blocked on a channel is woken by a different channel, its
/// `Weak<Thread>` will remain in the first channel's waiting_thread member. If a thread keeps
/// waiting on this first channel, and keeps being woken by other channels, it will keep re-adding
/// itself. We use a `HashMap` and insert at the current `ThreadId` so that we replace any stale
/// `Weak<Thread>`s which will have gone out of scope. (`wait_on_channels` drops the underlying arc
/// as soon as it is resumed.)
type WaitingThreads = Mutex<HashMap<ThreadId, Weak<Thread>>>;

/// The internal implementation of a channel representation backed by a `VecDeque<Message>`.
///
/// Channels are reference counted using `Arc<Channel>`, which are always in the form of a
/// [`ChannelHalf`] object that references one end of the [`Channel`] (read or write) and which
/// is included in the `reader_count` or `writer_count`.
pub struct Channel {
    // An internal identifier for this channel. Purely for disambiguation in debugging output.
    id: ChannelId,

    pub messages: RwLock<Messages>,

    // Counts of the numbers of `ChannelHalf` objects that refer to this channel.
    writer_count: AtomicU64,
    reader_count: AtomicU64,

    /// A HashMap of `ThreadId`s to `Weak<Thread>`s. This allows threads to insert a weak reference
    /// to themselves to be woken when a new message is available. Weak references are used so that
    /// if the thread is woken by a different channel, it can deallocate the underlying `Arc`
    /// instead of removing itself from all the `Channel`s it subscribed to.
    /// Threads can be woken up spuriously without issue.
    waiting_threads: WaitingThreads,

    /// The `Label` associated with this channel.
    ///
    /// This is set at channel creation time and does not change after that.
    ///
    /// See https://github.com/project-oak/oak/blob/main/docs/concepts.md#labels
    pub label: oak_abi::label::Label,

    /// The name for the channel.
    ///
    /// The name does not have to be unique and can be empty. In logs it is
    /// combined with the id to form a unique debug_ig to identify channels.
    name: String,

    /// Weak reference to the Runtime used for sending introspection events.
    runtime_weak: Weak<Runtime>,
}

impl std::fmt::Debug for Channel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Channel {{ id={}, #readers={}, #writers={}, name={:?}, label={:?} }}",
            self.id,
            self.reader_count.load(SeqCst),
            self.writer_count.load(SeqCst),
            self.name,
            self.label,
        )
    }
}

/// A reference to one half of a `Channel`
pub struct ChannelHalf {
    channel: Arc<Channel>,
    pub direction: ChannelHalfDirection,
}

impl ChannelHalf {
    /// Constructor for [`ChannelHalf`] keeps the underlying `Channel`'s reader/writer count
    /// up-to-date.
    pub fn new(channel: Arc<Channel>, direction: ChannelHalfDirection) -> Self {
        match direction {
            ChannelHalfDirection::Write => channel.inc_writer_count(),
            ChannelHalfDirection::Read => channel.inc_reader_count(),
        };
        ChannelHalf { channel, direction }
    }

    /// Get the ID of the underlying channel.  For debugging/introspection
    /// purposes.
    pub fn get_channel_id(&self) -> u64 {
        self.channel.id
    }

    /// Returns a unique debug_id used to identify the channel in debug output.
    pub fn get_channel_debug_id(&self) -> String {
        self.channel.get_debug_id()
    }

    /// Get read-only access to the channel's messages.  For debugging/introspection
    /// purposes.
    pub fn get_messages(&self) -> RwLockReadGuard<Messages> {
        self.channel.messages.read().unwrap()
    }

    /// Visit all channel halves that are reachable via this `ChannelHalf`, starting with
    /// `self`. The `visitor` function should return a boolean indicating whether the provided half
    /// needs to be further explored.  For debugging/introspection purposes.
    pub fn visit_halves<F>(&self, visitor: &mut F)
    where
        F: FnMut(&ChannelHalf) -> bool,
    {
        if visitor(self) {
            let mut to_visit = Vec::new();
            {
                let messages = self.get_messages();
                for msg in messages.iter() {
                    for half in &msg.channels {
                        to_visit.push(half.clone())
                    }
                }
            }
            // Now the lock is not held, let us go and make our visit.
            for half in to_visit {
                half.visit_halves(visitor);
            }
        }
    }

    /// Wake any threads waiting on the underlying channel.
    pub fn wake_waiters(&self) {
        self.channel.wake_waiters();
    }
}

/// Manual implementation of the [`Clone`] trait to keep the counts for the underlying `Channel` in
/// sync.
impl Clone for ChannelHalf {
    fn clone(&self) -> Self {
        ChannelHalf::new(self.channel.clone(), self.direction)
    }
}

/// Manual implementation of the [`Drop`] trait to keep the counts for the underlying `Channel` in
/// sync.
impl Drop for ChannelHalf {
    fn drop(&mut self) {
        match self.direction {
            ChannelHalfDirection::Write => self.channel.dec_writer_count(),
            ChannelHalfDirection::Read => self.channel.dec_reader_count(),
        };
        if self.direction == ChannelHalfDirection::Write && !self.channel.has_writers() {
            // This was the last writer to the channel: wake any waiters so they
            // can be aware that the channel is orphaned.
            debug!(
                "last writer for channel {} gone, wake waiters",
                self.channel.id
            );
            self.wake_waiters();
        }
    }
}

impl std::fmt::Debug for ChannelHalf {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Channel {} ('{}', label={:?}) {}",
            self.channel.id,
            self.channel.name,
            self.channel.label,
            match self.direction {
                ChannelHalfDirection::Write => "WRITE",
                ChannelHalfDirection::Read => "READ",
            },
        )
    }
}

impl DotIdentifier for ChannelHalf {
    fn dot_id(&self) -> String {
        self.channel.id.dot_id()
    }
}

/// Perform an operation on a [`Channel`] associated with a [`ChannelId`].
fn with_channel<U, F: FnOnce(Arc<Channel>) -> Result<U, OakStatus>>(
    half: &ChannelHalf,
    f: F,
) -> Result<U, OakStatus> {
    f(half.channel.clone())
}

/// Perform an operation on a [`Channel`] associated with a reader [`ChannelHalf`].
pub fn with_reader_channel<U, F: FnOnce(Arc<Channel>) -> Result<U, OakStatus>>(
    half: &ChannelHalf,
    f: F,
) -> Result<U, OakStatus> {
    if half.direction != ChannelHalfDirection::Read {
        return Err(OakStatus::ErrBadHandle);
    }
    with_channel(half, f)
}

/// Perform an operation on a [`Channel`] associated with a writer [`ChannelHalf`].
pub fn with_writer_channel<U, F: FnOnce(Arc<Channel>) -> Result<U, OakStatus>>(
    half: &ChannelHalf,
    f: F,
) -> Result<U, OakStatus> {
    if half.direction != ChannelHalfDirection::Write {
        return Err(OakStatus::ErrBadHandle);
    }
    with_channel(half, f)
}

/// The direction of a [`ChannelHalf`].
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub enum ChannelHalfDirection {
    Read,
    Write,
}

/// An internal identifier to track a [`Channel`].
type ChannelId = u64;

impl DotIdentifier for ChannelId {
    fn dot_id(&self) -> String {
        format!("channel{}", self)
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        debug!("dropping Channel object {:?}", self);
        match self.runtime_weak.upgrade() {
            Some(runtime) => {
                runtime.introspection_event(EventDetails::ChannelDestroyed(ChannelDestroyed {
                    channel_id: self.id,
                }));
            }
            None => {
                error!("Couldn't send ChannelDestroyed event, since the runtime has been dropped. {:?}", self);
            }
        }

        // There should be no waiters for this channel (a waiting Node would have
        // to have a `Handle` to wait on, which would be a reference that pins this
        // channel to existence) so no need to `wake_waiters()`.
        // Deliberately clear the HashMap under the lock to avoid a TSAN report.
        self.waiting_threads.lock().unwrap().clear();
    }
}

impl Channel {
    pub fn new(
        id: ChannelId,
        name: &str,
        label: &oak_abi::label::Label,
        runtime_weak: Weak<Runtime>,
    ) -> Arc<Channel> {
        debug!("create new Channel object with ID {}", id);
        Arc::new(Channel {
            id,
            messages: RwLock::new(Messages::new()),
            writer_count: AtomicU64::new(0),
            reader_count: AtomicU64::new(0),
            waiting_threads: Mutex::new(HashMap::new()),
            label: label.clone(),
            name: name.to_string(),
            runtime_weak,
        })
    }

    /// Returns a unique debug_id used to identify the channel in the debug
    /// output, consisting of the provided [`ChannelId`], and the channel name.
    fn get_debug_id(&self) -> String {
        format!("{}({})", &self.name, self.id)
    }

    /// Determine whether there are any readers of the channel.
    pub fn has_readers(&self) -> bool {
        self.reader_count.load(SeqCst) > 0
    }

    /// Determine whether there are any writers of the channel.
    pub fn has_writers(&self) -> bool {
        self.writer_count.load(SeqCst) > 0
    }

    /// Decrement the [`Channel`] writer counter.
    fn dec_writer_count(&self) {
        if self.writer_count.fetch_sub(1, SeqCst) == 0 {
            panic!("remove_reader: Writer count was already 0, something is very wrong!")
        }
    }

    /// Decrement the [`Channel`] reader counter.
    fn dec_reader_count(&self) {
        if self.reader_count.fetch_sub(1, SeqCst) == 0 {
            panic!("remove_reader: Reader count was already 0, something is very wrong!")
        }
    }

    /// Increment the [`Channel`] writer counter.
    fn inc_writer_count(&self) -> u64 {
        self.writer_count.fetch_add(1, SeqCst)
    }

    /// Increment the [`Channel`] reader counter.
    fn inc_reader_count(&self) -> u64 {
        self.reader_count.fetch_add(1, SeqCst)
    }

    /// Add the given [`Thread`] reference into the collection of [`Thread`]s waiting on this
    /// [`Channel`]'s readability.  Threads waiting on the [`Channel`] will be woken when
    /// data is available, or if the [`Channel`] becomes orphaned (no writers left).
    pub fn add_waiter(&self, thread: &Arc<Thread>) {
        self.waiting_threads
            .lock()
            .unwrap()
            .insert(thread.id(), Arc::downgrade(thread));
    }

    /// Wake any [`Thread`]s that are waiting on the [`Channel`].
    pub fn wake_waiters(&self) {
        // Unpark (wake up) all waiting threads that still have live references. The first thread
        // woken can immediately read the message, and others might find `messages` is empty before
        // they are even woken. This should not be an issue (being woken does not guarantee a
        // message is available), but it could potentially result in some particular thread always
        // getting first chance to read the message.
        //
        // If a thread is woken and finds no message it will take the `waiting_threads` lock and
        // add itself again. Note that since that lock is currently held, the woken thread will add
        // itself to waiting_threads *after* we call clear below as we release the lock implicilty
        // on leaving this function.
        let mut waiting_threads = self.waiting_threads.lock().unwrap();
        for thread in waiting_threads.values() {
            if let Some(thread) = thread.upgrade() {
                thread.unpark();
            }
        }
        waiting_threads.clear();
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use typed_arena::Arena;

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ID(usize);

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Ty<'ty> {
  Deferred(ID),
  Func(&'ty Ty<'ty>, &'ty Ty<'ty>),
  Bool,
}

pub struct Check<'ty> {
  arena: &'ty Arena<Ty<'ty>>,
  next_id: usize,
  solved: HashMap<ID, &'ty Ty<'ty>>,
}

impl<'ty> Check<'ty> {
  pub fn new(arena: &'ty Arena<Ty<'ty>>) -> Self {
    Check{
      arena: arena,
      next_id: 0,
      solved: HashMap::new(),
    }
  }

  pub fn fresh(&mut self) -> &'ty Ty<'ty> {
    let ty = self.arena.alloc(Ty::Deferred(ID(self.next_id)));
    self.next_id += 1;
    ty
  }

  fn solve(&mut self, id: ID, ty: &'ty Ty<'ty>) {
    self.solved.insert(id, ty);
  }

  pub fn purge(&self, ty: &'ty Ty<'ty>) -> &'ty Ty<'ty> {
    match ty {
      &Ty::Deferred(id) =>
        match self.solved.get(&id) {
          Some(uy) => self.purge(uy),
          None => ty,
        },
      _ => ty,
    }
  }

  pub fn unify(&mut self, ty: &'ty Ty<'ty>, uy: &'ty Ty<'ty>) -> Result<(), ()> {
    match (self.purge(ty), self.purge(uy)) {
      (&Ty::Deferred(ty_id), &Ty::Deferred(uy_id))
        if ty_id == uy_id =>
        Ok(()),
      (&Ty::Deferred(ty_id), uy_p) => {
        self.solve(ty_id, uy_p);
        Ok(())
      },
      (ty_p, &Ty::Deferred(uy_id)) => {
        self.solve(uy_id, ty_p);
        Ok(())
      },
      (&Ty::Func(ty_a, ty_b), &Ty::Func(uy_a, uy_b)) => {
        try!(self.unify(ty_a, uy_a));
        try!(self.unify(ty_b, uy_b));
        Ok(())
      },
      (&Ty::Bool, &Ty::Bool) =>
        Ok(()),
      _ =>
        Err(()),
    }
  }
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  fn test_unify() {
    let arena = Arena::new();
    let mut check = Check::new(&arena);

    let ty1 = arena.alloc(Ty::Bool);
    let ty2 = arena.alloc(Ty::Func(ty1, ty1));
    let ty3 = check.fresh();
    let ty4 = check.fresh();
    let ty5 = check.fresh();
    let ty6 = check.fresh();

    assert_eq!(check.unify(ty1, ty1), Ok(()));

    assert_eq!(check.unify(ty2, ty2), Ok(()));

    assert_eq!(check.unify(ty1, ty2), Err(()));

    assert_eq!(check.unify(ty3, ty3), Ok(()));
    assert_eq!(check.unify(ty3, ty4), Ok(()));
    assert_eq!(check.unify(ty4, ty1), Ok(()));
    assert_eq!(check.purge(ty3),      ty1);
    assert_eq!(check.unify(ty5, ty1), Ok(()));
    assert_eq!(check.unify(ty5, ty3), Ok(()));
    assert_eq!(check.unify(ty6, ty2), Ok(()));
    assert_eq!(check.unify(ty5, ty6), Err(()));
  }
}
<commit_msg>Add type inference and crappy test<commit_after>use std::collections::HashMap;
use syntax::{Expr, ExprF};
use typed_arena::Arena;

pub type Error = &'static str;

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ID(usize);

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Ty<'ty> {
  Deferred(ID),
  Func(&'ty Ty<'ty>, &'ty Ty<'ty>),
  Bool,
  Int,
  Str,
}

pub static TY_BOOL: Ty<'static> = Ty::Bool;
pub static TY_INT:  Ty<'static> = Ty::Int;
pub static TY_STR:  Ty<'static> = Ty::Str;

pub struct Check<'ty> {
  arena: &'ty Arena<Ty<'ty>>,
  next_id: usize,
  solved: HashMap<ID, &'ty Ty<'ty>>,
}

impl<'ty> Check<'ty> {
  pub fn new(arena: &'ty Arena<Ty<'ty>>) -> Self {
    Check{
      arena: arena,
      next_id: 0,
      solved: HashMap::new(),
    }
  }

  pub fn fresh(&mut self) -> &'ty Ty<'ty> {
    let ty = self.arena.alloc(Ty::Deferred(ID(self.next_id)));
    self.next_id += 1;
    ty
  }

  fn solve(&mut self, id: ID, ty: &'ty Ty<'ty>) {
    self.solved.insert(id, ty);
  }

  pub fn purge(&self, ty: &'ty Ty<'ty>) -> &'ty Ty<'ty> {
    match ty {
      &Ty::Deferred(id) =>
        match self.solved.get(&id) {
          Some(uy) => self.purge(uy),
          None => ty,
        },
      _ => ty,
    }
  }

  pub fn unify(&mut self, ty: &'ty Ty<'ty>, uy: &'ty Ty<'ty>) -> Result<(), Error> {
    match (self.purge(ty), self.purge(uy)) {
      (&Ty::Deferred(ty_id), &Ty::Deferred(uy_id))
        if ty_id == uy_id =>
        Ok(()),
      (&Ty::Deferred(ty_id), uy_p) => {
        self.solve(ty_id, uy_p);
        Ok(())
      },
      (ty_p, &Ty::Deferred(uy_id)) => {
        self.solve(uy_id, ty_p);
        Ok(())
      },
      (&Ty::Func(ty_a, ty_b), &Ty::Func(uy_a, uy_b)) => {
        try!(self.unify(ty_a, uy_a));
        try!(self.unify(ty_b, uy_b));
        Ok(())
      },
      (&Ty::Bool, &Ty::Bool) =>
        Ok(()),
      (&Ty::Int, &Ty::Int) =>
        Ok(()),
      (&Ty::Str, &Ty::Str) =>
        Ok(()),
      (a, b) => {
        println!("{:?} /= {:?}", a, b);
        Err("cannot unify types")
      },
    }
  }

  pub fn infer<T>(
    &mut self,
    env: &HashMap<&str, &'ty Ty<'ty>>,
    expr: &Expr<T>,
  ) -> Result<&'ty Ty<'ty>, Error> {
    match expr.1 {
      ExprF::Lit(_) =>
        // FIXME: Non-Boolean literals.
        Ok(&TY_BOOL),
      ExprF::Var(name) =>
        match env.get(&name) {
          Some(ty) => Ok(ty),
          None => {
            println!("{:?}", name);
            Err("cannot find variable")
          },
        },
      ExprF::Abs(param, body) => {
        let param_ty = self.fresh();

        let mut body_env = env.clone();
        body_env.insert(param, param_ty);
        let result_ty = try!(self.infer(&body_env, body));

        let func_ty = self.arena.alloc(Ty::Func(param_ty, result_ty));
        Ok(func_ty)
      },
      ExprF::App(callee, argument) => {
        let callee_ty = try!(self.infer(env, callee));
        let argument_ty = try!(self.infer(env, argument));

        let result_ty = self.fresh();
        let func_ty = self.arena.alloc(Ty::Func(argument_ty, result_ty));
        try!(self.unify(callee_ty, func_ty));

        Ok(result_ty)
      },
    }
  }
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  fn test_unify() {
    let arena = Arena::new();
    let mut check = Check::new(&arena);

    let ty1 = &TY_BOOL;
    let ty2 = arena.alloc(Ty::Func(ty1, ty1));
    let ty3 = check.fresh();
    let ty4 = check.fresh();
    let ty5 = check.fresh();
    let ty6 = check.fresh();

    assert_eq!(check.unify(ty1, ty1), Ok(()));

    assert_eq!(check.unify(ty2, ty2), Ok(()));

    assert_eq!(check.unify(ty1, ty2), Err("cannot unify types"));

    assert_eq!(check.unify(ty3, ty3), Ok(()));
    assert_eq!(check.unify(ty3, ty4), Ok(()));
    assert_eq!(check.unify(ty4, ty1), Ok(()));
    assert_eq!(check.purge(ty3),      ty1);
    assert_eq!(check.unify(ty5, ty1), Ok(()));
    assert_eq!(check.unify(ty5, ty3), Ok(()));
    assert_eq!(check.unify(ty6, ty2), Ok(()));
    assert_eq!(check.unify(ty5, ty6), Err("cannot unify types"));
  }

  #[test]
  fn test_infer() {
    let expr_arena = Arena::new();
    let ty_arena = Arena::new();

    let mut check = Check::new(&ty_arena);

    let mut env = HashMap::new();
    env.insert("vari", &TY_INT);
    env.insert("even", ty_arena.alloc(Ty::Func(&TY_INT, &TY_BOOL)));

    let expr = Expr(
      (),
      ExprF::App(
        expr_arena.alloc(Expr((), ExprF::Var("even"))),
        expr_arena.alloc(Expr((), ExprF::Var("vari"))),
      ),
    );
    let ty = check.infer(&env, &expr);
    assert_eq!(ty.map(|t| check.purge(t)), Ok(&TY_BOOL));
  }
}
<|endoftext|>


<commit_before>extern crate rustyline;
extern crate weld;
extern crate libc;

#[macro_use]
extern crate lazy_static;

extern crate clap;
use clap::{Arg, App};

use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
use std::fmt;
use std::ffi::{CStr, CString};
use std::collections::HashMap;
use libc::c_char;

use weld::*;
use weld::common::*;

const PROMPT: &'static str = ">>> ";

enum ReplCommands {
    LoadFile,
    GetConf,
    SetConf,
}

impl fmt::Display for ReplCommands {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ReplCommands::LoadFile => write!(f, "load"),
            ReplCommands::GetConf => write!(f, "getconf"),
            ReplCommands::SetConf => write!(f, "setconf"),
        }
    }
}

lazy_static! {
    static ref RESERVED_WORDS: HashMap<String, ReplCommands> = {
        let mut m = HashMap::new();
        m.insert(ReplCommands::LoadFile.to_string(), ReplCommands::LoadFile);
        m.insert(ReplCommands::SetConf.to_string(), ReplCommands::SetConf);
        m.insert(ReplCommands::GetConf.to_string(), ReplCommands::GetConf);
        m
    };
}

/// Process the `SetConf` command.
///
/// The argument is a key/value pair. The command sets the key/value pair for the REPL's
/// configuration.
fn process_setconf(conf: *mut WeldConf, key: String, value: String) {
    let key = CString::new(key).unwrap();
    let value = CString::new(value).unwrap();
    unsafe {
        weld_conf_set(conf, key.as_ptr(), value.as_ptr());
    }
}

/// Process the `GetConf` command.
///
/// The argument is a key in the configuration. The command returns the value of the key or `None`
/// if no value is set.
fn process_getconf(conf: *mut WeldConf, key: String) -> Option<String> {
    let key = CString::new(key).unwrap();
    unsafe {
        let val = weld_conf_get(conf, key.as_ptr());
        if val.is_null() {
            None
        } else {
            let val = CStr::from_ptr(val);
            let val = val.to_str();
            if let Ok(s) = val {
                Some(s.to_string())
            } else {
                None
            }
        }
    }
}

/// Processes the LoadFile command.
///
/// The argument is a filename containing a Weld program. Returns the string
/// representation of the program or an error with an error message.
fn process_loadfile(arg: String) -> Result<String, String> {
    if arg.len() == 0 {
        return Err("Error: expected argument for command 'load'".to_string());
    }
    let path = Path::new(&arg);
    let path_display = path.display();
    let mut file;
    match File::open(&path) {
        Err(why) => {
            return Err(format!("Error: couldn't open {}: {}",
                               path_display,
                               why.description()));
        }
        Ok(res) => {
            file = res;
        }
    }

    let mut contents = String::new();
    match file.read_to_string(&mut contents) {
        Err(why) => {
            return Err(format!("Error: couldn't read {}: {}",
                               path_display,
                               why.description()));
        }
        _ => {}
    }
    Ok(contents.trim().to_string())
}

/// Reads a line of input, returning the read line or `None` if an error occurred
/// or the user exits.
fn read_input(rl: &mut Editor<()>, prompt: &str, history: bool) -> Option<String> {
    let raw_readline = rl.readline(prompt);
    match raw_readline {
        Ok(raw_readline) => {
            if history {
                rl.add_history_entry(&raw_readline);
            }
            let trimmed = raw_readline.trim();
            Some(trimmed.to_string())
        }
        Err(ReadlineError::Interrupted) => {
            println!("Exiting!");
            None
        }
        Err(ReadlineError::Eof) => {
            println!("Exiting!");
            None
        }
        Err(err) => {
            println!("Error: {:?}", err);
            None
        }
    }
}

/// Handles a single string command. Returns a string if the command
/// contains code or `None` if the command is fully processed.
fn handle_string<'a>(command: &'a str, conf: *mut WeldConf) -> Option<String> {
    let mut tokens = command.splitn(2, " ");
    let command = tokens.next().unwrap();
    let arg = tokens.next().unwrap_or("");
    if RESERVED_WORDS.contains_key(command) {
        let command = RESERVED_WORDS.get(command).unwrap();
        match *command {
            ReplCommands::LoadFile => {
                match process_loadfile(arg.to_string()) {
                    Err(s) => {
                        println!("{}", s);
                        None
                    }
                    Ok(code) => {
                        Some(code)
                    }
                }
            }
            ReplCommands::SetConf => {
                let mut setconf_args = arg.splitn(2, " ");
                let key = setconf_args.next().unwrap_or("");
                let value = setconf_args.next().unwrap_or("");
                process_setconf(conf, key.to_string(), value.to_string());
                None
            }
            ReplCommands::GetConf => {
                let mut setconf_args = arg.splitn(2, " ");
                let key = setconf_args.next().unwrap_or("");
                let value = process_getconf(conf, key.to_string());
                if let Some(s) = value {
                    println!("{}={}", key, s);
                } else {
                    println!("{}=<unset>", key);
                }
                None
            }
        }
    } else {
        Some(command.to_string())
    }
}

fn main() {
    let matches = App::new("Weld REPL")
        .version("0.1.0")
        .author("Weld authors <weld-group@cs.stanford.edu")
        .about("A REPL for Weld")
        .arg(Arg::with_name("loglevel")
             .short("l")
             .long("loglevel")
             .value_name("LEVEL")
             .help("Log level for the Weld compiler")
             .takes_value(true))
        .get_matches();

    let log_level_str = matches.value_of("loglevel").unwrap_or("debug").to_lowercase();
    let (log_level, log_str) = match log_level_str.as_str() {
        "none" =>   (WeldLogLevel::Off,         "none"),
        "error" =>  (WeldLogLevel::Error,       "\x1b[0;31merror\x1b[0m"),
        "warn" =>   (WeldLogLevel::Warn,        "\x1b[0;33mwarn\x1b[0m"),
        "info" =>   (WeldLogLevel::Info,        "\x1b[0;33minfo\x1b[0m"),
        "debug" =>  (WeldLogLevel::Debug,       "\x1b[0;32mdebug\x1b[0m"),
        "trace" =>  (WeldLogLevel::Trace,       "\x1b[0;32mtrace\x1b[0m"),
        ref s => {
            println!("Unrecognized log level {}", s);
            std::process::exit(1);
        }
    };
    weld_set_log_level(log_level);
    println!("Log Level set to '{}'", log_str);

    // This is the conf we use for compilation.
    let conf = weld_conf_new();

    let home_path = env::home_dir().unwrap_or(PathBuf::new());
    let history_file_path = home_path.join(".weld_history");
    let history_file_path = history_file_path.to_str().unwrap_or(".weld_history");

    let mut rl = Editor::<()>::new();
    if let Err(_) = rl.load_history(&history_file_path) {}

    loop {
        // Check if the input was valid.
        let input = read_input(&mut rl, PROMPT, true);
        if input.is_none() {
            break;
        }
        let input = input.unwrap();
        if input == "" {
            continue;
        }

        // Handle repl commands.
        let code = handle_string(&input, conf);
        if code.is_none() {
            continue;
        }
        let code = code.unwrap();

        // Process the code.
        unsafe {
            let code = CString::new(code).unwrap();
            let err = weld_error_new();
            let module = weld_module_compile(code.into_raw() as *const c_char, conf, err);
            if weld_error_code(err) != WeldRuntimeErrno::Success {
                println!("REPL: Compile error: {}",
                    CStr::from_ptr(weld_error_message(err)).to_str().unwrap());
            } else {
                println!("REPL: Program compiled successfully to LLVM");
                weld_module_free(module);
            }
            weld_error_free(err);
        }
    }

    unsafe {
        weld_conf_free(conf);
    }

    rl.save_history(&history_file_path).unwrap();
}
<commit_msg>Hotfix: bug in repl that didn't allow spaces<commit_after>extern crate rustyline;
extern crate weld;
extern crate libc;

#[macro_use]
extern crate lazy_static;

extern crate clap;
use clap::{Arg, App};

use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
use std::fmt;
use std::ffi::{CStr, CString};
use std::collections::HashMap;
use libc::c_char;

use weld::*;
use weld::common::*;

const PROMPT: &'static str = ">>> ";

enum ReplCommands {
    LoadFile,
    GetConf,
    SetConf,
}

impl fmt::Display for ReplCommands {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ReplCommands::LoadFile => write!(f, "load"),
            ReplCommands::GetConf => write!(f, "getconf"),
            ReplCommands::SetConf => write!(f, "setconf"),
        }
    }
}

lazy_static! {
    static ref RESERVED_WORDS: HashMap<String, ReplCommands> = {
        let mut m = HashMap::new();
        m.insert(ReplCommands::LoadFile.to_string(), ReplCommands::LoadFile);
        m.insert(ReplCommands::SetConf.to_string(), ReplCommands::SetConf);
        m.insert(ReplCommands::GetConf.to_string(), ReplCommands::GetConf);
        m
    };
}

/// Process the `SetConf` command.
///
/// The argument is a key/value pair. The command sets the key/value pair for the REPL's
/// configuration.
fn process_setconf(conf: *mut WeldConf, key: String, value: String) {
    let key = CString::new(key).unwrap();
    let value = CString::new(value).unwrap();
    unsafe {
        weld_conf_set(conf, key.as_ptr(), value.as_ptr());
    }
}

/// Process the `GetConf` command.
///
/// The argument is a key in the configuration. The command returns the value of the key or `None`
/// if no value is set.
fn process_getconf(conf: *mut WeldConf, key: String) -> Option<String> {
    let key = CString::new(key).unwrap();
    unsafe {
        let val = weld_conf_get(conf, key.as_ptr());
        if val.is_null() {
            None
        } else {
            let val = CStr::from_ptr(val);
            let val = val.to_str();
            if let Ok(s) = val {
                Some(s.to_string())
            } else {
                None
            }
        }
    }
}

/// Processes the LoadFile command.
///
/// The argument is a filename containing a Weld program. Returns the string
/// representation of the program or an error with an error message.
fn process_loadfile(arg: String) -> Result<String, String> {
    if arg.len() == 0 {
        return Err("Error: expected argument for command 'load'".to_string());
    }
    let path = Path::new(&arg);
    let path_display = path.display();
    let mut file;
    match File::open(&path) {
        Err(why) => {
            return Err(format!("Error: couldn't open {}: {}",
                               path_display,
                               why.description()));
        }
        Ok(res) => {
            file = res;
        }
    }

    let mut contents = String::new();
    match file.read_to_string(&mut contents) {
        Err(why) => {
            return Err(format!("Error: couldn't read {}: {}",
                               path_display,
                               why.description()));
        }
        _ => {}
    }
    Ok(contents.trim().to_string())
}

/// Reads a line of input, returning the read line or `None` if an error occurred
/// or the user exits.
fn read_input(rl: &mut Editor<()>, prompt: &str, history: bool) -> Option<String> {
    let raw_readline = rl.readline(prompt);
    match raw_readline {
        Ok(raw_readline) => {
            if history {
                rl.add_history_entry(&raw_readline);
            }
            let trimmed = raw_readline.trim();
            Some(trimmed.to_string())
        }
        Err(ReadlineError::Interrupted) => {
            println!("Exiting!");
            None
        }
        Err(ReadlineError::Eof) => {
            println!("Exiting!");
            None
        }
        Err(err) => {
            println!("Error: {:?}", err);
            None
        }
    }
}

/// Handles a single string command. Returns a string if the command
/// contains code or `None` if the command is fully processed.
fn handle_string<'a>(command: &'a str, conf: *mut WeldConf) -> Option<String> {
    let mut tokens = command.splitn(2, " ");
    let repl_command = tokens.next().unwrap();
    let arg = tokens.next().unwrap_or("");
    if RESERVED_WORDS.contains_key(repl_command) {
        let command = RESERVED_WORDS.get(repl_command).unwrap();
        match *command {
            ReplCommands::LoadFile => {
                match process_loadfile(arg.to_string()) {
                    Err(s) => {
                        println!("{}", s);
                        None
                    }
                    Ok(code) => {
                        Some(code)
                    }
                }
            }
            ReplCommands::SetConf => {
                let mut setconf_args = arg.splitn(2, " ");
                let key = setconf_args.next().unwrap_or("");
                let value = setconf_args.next().unwrap_or("");
                process_setconf(conf, key.to_string(), value.to_string());
                None
            }
            ReplCommands::GetConf => {
                let mut setconf_args = arg.splitn(2, " ");
                let key = setconf_args.next().unwrap_or("");
                let value = process_getconf(conf, key.to_string());
                if let Some(s) = value {
                    println!("{}={}", key, s);
                } else {
                    println!("{}=<unset>", key);
                }
                None
            }
        }
    } else {
        Some(command.to_string())
    }
}

fn main() {
    let matches = App::new("Weld REPL")
        .version("0.1.0")
        .author("Weld authors <weld-group@cs.stanford.edu")
        .about("A REPL for Weld")
        .arg(Arg::with_name("loglevel")
             .short("l")
             .long("loglevel")
             .value_name("LEVEL")
             .help("Log level for the Weld compiler")
             .takes_value(true))
        .get_matches();

    let log_level_str = matches.value_of("loglevel").unwrap_or("debug").to_lowercase();
    let (log_level, log_str) = match log_level_str.as_str() {
        "none" =>   (WeldLogLevel::Off,         "none"),
        "error" =>  (WeldLogLevel::Error,       "\x1b[0;31merror\x1b[0m"),
        "warn" =>   (WeldLogLevel::Warn,        "\x1b[0;33mwarn\x1b[0m"),
        "info" =>   (WeldLogLevel::Info,        "\x1b[0;33minfo\x1b[0m"),
        "debug" =>  (WeldLogLevel::Debug,       "\x1b[0;32mdebug\x1b[0m"),
        "trace" =>  (WeldLogLevel::Trace,       "\x1b[0;32mtrace\x1b[0m"),
        ref s => {
            println!("Unrecognized log level {}", s);
            std::process::exit(1);
        }
    };
    weld_set_log_level(log_level);
    println!("Log Level set to '{}'", log_str);

    // This is the conf we use for compilation.
    let conf = weld_conf_new();

    let home_path = env::home_dir().unwrap_or(PathBuf::new());
    let history_file_path = home_path.join(".weld_history");
    let history_file_path = history_file_path.to_str().unwrap_or(".weld_history");

    let mut rl = Editor::<()>::new();
    if let Err(_) = rl.load_history(&history_file_path) {}

    loop {
        // Check if the input was valid.
        let input = read_input(&mut rl, PROMPT, true);
        if input.is_none() {
            break;
        }
        let input = input.unwrap();
        if input == "" {
            continue;
        }

        // Handle repl commands.
        let code = handle_string(&input, conf);
        if code.is_none() {
            continue;
        }
        let code = code.unwrap();

        // Process the code.
        unsafe {
            let code = CString::new(code).unwrap();
            let err = weld_error_new();
            let module = weld_module_compile(code.into_raw() as *const c_char, conf, err);
            if weld_error_code(err) != WeldRuntimeErrno::Success {
                println!("REPL: Compile error: {}",
                    CStr::from_ptr(weld_error_message(err)).to_str().unwrap());
            } else {
                println!("REPL: Program compiled successfully to LLVM");
                weld_module_free(module);
            }
            weld_error_free(err);
        }
    }

    unsafe {
        weld_conf_free(conf);
    }

    rl.save_history(&history_file_path).unwrap();
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::env;

fn main() {
    let mut path = match env::var("WELD_HOME") {
        Ok(val) => val,
        Err(_) => ".".to_string(),
    };

    if path.chars().last().unwrap() != '/' {
        path = path + &"/";
    }

    let cpp_path = path + &"weld_rt/cpp";

    Command::new("make")
        .arg("clean")
        .arg("-C")
        .arg(cpp_path.clone())
        .status()
        .unwrap();

    Command::new("make")
        .arg("-C")
        .arg(cpp_path.clone())
        .status()
        .unwrap();

    println!("{}", format!("cargo:rustc-link-search=native={}", cpp_path));

    let target = env::var("TARGET").unwrap();
    if target == "x86_64-apple-darwin" {
        let libs = vec!["z", "c++"];
        for lib in libs {
            println!("cargo:rustc-link-lib={}", lib);
        }
    } else if target == "x86_64-unknown-linux-gnu" {
        let libs = vec!["z", "stdc++"];
        for lib in libs {
            println!("cargo:rustc-link-lib={}", lib);
        }
    }
}
<commit_msg>Fix build.rs not exiting if make commands fail<commit_after>use std::process::Command;
use std::env;
use std::process;

fn main() {
    let mut path = match env::var("WELD_HOME") {
        Ok(val) => val,
        Err(_) => ".".to_string(),
    };

    if path.chars().last().unwrap() != '/' {
        path = path + &"/";
    }

    let cpp_path = path + &"weld_rt/cpp";

    let status = Command::new("make")
        .arg("clean")
        .arg("-C")
        .arg(cpp_path.clone())
        .status()
        .unwrap();
    if !status.success() {
        process::exit(status.code().unwrap_or(1));
    }

    let status = Command::new("make")
        .arg("-C")
        .arg(cpp_path.clone())
        .status()
        .unwrap();
    if !status.success() {
        process::exit(status.code().unwrap_or(1));
    }

    println!("{}", format!("cargo:rustc-link-search=native={}", cpp_path));

    let target = env::var("TARGET").unwrap();
    if target == "x86_64-apple-darwin" {
        let libs = vec!["z", "c++"];
        for lib in libs {
            println!("cargo:rustc-link-lib={}", lib);
        }
    } else if target == "x86_64-unknown-linux-gnu" {
        let libs = vec!["z", "stdc++"];
        for lib in libs {
            println!("cargo:rustc-link-lib={}", lib);
        }
    }
}
<|endoftext|>


<commit_before>extern crate musicbrainz;
extern crate uuid;

use uuid::Uuid;
use std::collections::HashMap;
use musicbrainz::*;

#[test]
fn test_search_artist() {
    let m = MusicBrainz::new();
    let generated = artist::Artist::new(
        Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60")
            .expect("failed to parse artist ID as Uuid"),
        String::from("deadmau5"),
        String::from("male"),
        enums::PersonType::Other,
        vec![
            String::from("electronic"), String::from("house"),
            String::from("seen live"), String::from("electro"),
            String::from("progressive house"), String::from("dance and electronica")
        ],
        Vec::new()
    );

    let mut query = HashMap::new();
    query.insert("query", "deadmau5");

    let correct = m.search_artist(&mut query).remove(0);

    assert_eq!(correct, generated);
}

#[test]
fn test_search_album() {
    let m = MusicBrainz::new();
    let generated = release_group::ReleaseGroup::new(
        String::from("For Lack of a Better Name"),
        String::new(),
        Uuid::parse_str("638a007a-8423-40d6-a97c-f819d320d33c")
            .expect("failed to parse release group ID as Uuid"),
        Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60")
            .expect("failed to parse artist ID as Uuid"),
        enums::AlbumMainType::Album,
        Vec::new()
    );

    let mut query = HashMap::new();
    query.insert("query", "for lack of a better name");

    let correct = m.search_album(&mut query).remove(0);

    assert_eq!(correct, generated);
}
<commit_msg>Add artist lookup test<commit_after>extern crate musicbrainz;
extern crate uuid;

use uuid::Uuid;
use std::collections::HashMap;
use musicbrainz::*;

#[test]
fn test_search_artist() {
    let m = MusicBrainz::new();
    let generated = artist::Artist::new(
        Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60")
            .expect("failed to parse artist ID as Uuid"),
        String::from("deadmau5"),
        String::from("male"),
        enums::PersonType::Other,
        vec![
            String::from("electronic"), String::from("house"),
            String::from("seen live"), String::from("electro"),
            String::from("progressive house"), String::from("dance and electronica")
        ],
        Vec::new()
    );

    let mut query = HashMap::new();
    query.insert("query", "deadmau5");

    let correct = m.search_artist(&mut query).remove(0);

    assert_eq!(correct, generated);
}

#[test]
fn test_search_album() {
    let m = MusicBrainz::new();
    let generated = release_group::ReleaseGroup::new(
        String::from("For Lack of a Better Name"),
        String::new(),
        Uuid::parse_str("638a007a-8423-40d6-a97c-f819d320d33c")
            .expect("failed to parse release group ID as Uuid"),
        Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60")
            .expect("failed to parse artist ID as Uuid"),
        enums::AlbumMainType::Album,
        Vec::new()
    );

    let mut query = HashMap::new();
    query.insert("query", "for lack of a better name");

    let correct = m.search_album(&mut query).remove(0);

    assert_eq!(correct, generated);
}

#[test]
fn test_lookup_artist() {
    let m = MusicBrainz::new();
    let generated = artist::Artist::new(
        Uuid::parse_str("4a00ec9d-c635-463a-8cd4-eb61725f0c60")
            .expect("failed to parse artist ID as Uuid"),
        String::from("deadmau5"),
        String::from("Male"),
        enums::PersonType::Person,
        Vec::new(),
        Vec::new()
    );

    let mut query = HashMap::new();
    let correct = m.lookup_artist(generated.id, &mut query).unwrap();

    assert_eq!(correct, generated);
}
<|endoftext|>


<commit_before>extern crate lifeguard;

#[cfg(test)]
mod tests {
  use lifeguard::{Pool, RcRecycled, Recycled};

  #[test]
  fn test_deref() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_rc_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_mut() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_deref_mut_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_rc_from("cat");
      rstring.push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_mut() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.as_mut().push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_mut_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_rc_from("cat");
      rstring.as_mut().push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_ref() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", rstring.as_ref());
  }

  #[test]
  fn test_as_ref_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_rc_from("cat");
      assert_eq!("cat", rstring.as_ref());
  }

  #[test]
  fn test_recycle() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_recycle_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_rc_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_detach() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _string : String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

  #[test]
  fn test_detach_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _string : String = str_pool.new_rc().detach();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

  #[test]
  fn test_attach() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let string: String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
        let _rstring: Recycled<String> = str_pool.attach(string);
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_attach_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let string: String = str_pool.new_rc().detach();
        assert_eq!(0, str_pool.size());
        let _rstring: RcRecycled<String> = str_pool.attach_rc(string);
      }
      assert_eq!(1, str_pool.size());
  }
}
<commit_msg>Added test for Pool size cap. Closes #12.<commit_after>extern crate lifeguard;

#[cfg(test)]
mod tests {
  use lifeguard::{Pool, RcRecycled, Recycled};

  #[test]
  fn test_deref() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_rc_from("cat");
      assert_eq!("cat", *rstring);
  }

  #[test]
  fn test_deref_mut() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_deref_mut_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_rc_from("cat");
      rstring.push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_mut() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_from("cat");
      rstring.as_mut().push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_mut_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let mut rstring = str_pool.new_rc_from("cat");
      rstring.as_mut().push_str("s love eating mice");
      assert_eq!("cats love eating mice", *rstring);
  }

  #[test]
  fn test_as_ref() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_from("cat");
      assert_eq!("cat", rstring.as_ref());
  }

  #[test]
  fn test_as_ref_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      let rstring = str_pool.new_rc_from("cat");
      assert_eq!("cat", rstring.as_ref());
  }

  #[test]
  fn test_recycle() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_recycle_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_rc_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_size_cap() {
      let str_pool : Pool<String> = Pool::with_size_and_max(1, 1);
      {
        assert_eq!(1, str_pool.size());
        let _rstring = str_pool.new_from("dog");
        let _rstring2 = str_pool.new_from("cat");
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_detach() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _string : String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

  #[test]
  fn test_detach_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let _string : String = str_pool.new_rc().detach();
        assert_eq!(0, str_pool.size());
      }
      assert_eq!(0, str_pool.size());
  }

  #[test]
  fn test_attach() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let string: String = str_pool.new().detach();
        assert_eq!(0, str_pool.size());
        let _rstring: Recycled<String> = str_pool.attach(string);
      }
      assert_eq!(1, str_pool.size());
  }

  #[test]
  fn test_attach_rc() {
      let str_pool : Pool<String> = Pool::with_size(1);
      {
        assert_eq!(1, str_pool.size());
        let string: String = str_pool.new_rc().detach();
        assert_eq!(0, str_pool.size());
        let _rstring: RcRecycled<String> = str_pool.attach_rc(string);
      }
      assert_eq!(1, str_pool.size());
  }
}
<|endoftext|>