Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4cfc4e89b983
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2ae30b5a955e
Choose a head ref
  • 4 commits
  • 5 files changed
  • 1 contributor

Commits on Oct 6, 2016

  1. Rust: implement sending for all RPC types.

    whitequark committed Oct 6, 2016
    Copy the full SHA
    5a63006 View commit details
  2. language: export TTuple.

    whitequark committed Oct 6, 2016
    Copy the full SHA
    516c6fd View commit details
  3. Rust: implement receiving for all RPC types.

    whitequark committed Oct 6, 2016
    Copy the full SHA
    3362887 View commit details
  4. Copy the full SHA
    2ae30b5 View commit details
Showing with 241 additions and 40 deletions.
  1. +2 −1 artiq/language/types.py
  2. +14 −0 artiq/runtime.rs/src/proto.rs
  3. +200 −23 artiq/runtime.rs/src/rpc.rs
  4. +25 −5 artiq/runtime.rs/src/session.rs
  5. +0 −11 artiq/runtime.rs/src/session_proto.rs
3 changes: 2 additions & 1 deletion artiq/language/types.py
Original file line number Diff line number Diff line change
@@ -6,14 +6,15 @@
from artiq.compiler import types, builtins

__all__ = ["TNone", "TBool", "TInt32", "TInt64", "TFloat",
"TStr", "TList", "TRange32", "TRange64", "TVar"]
"TStr", "TTuple", "TList", "TRange32", "TRange64", "TVar"]

TNone = builtins.TNone()
TBool = builtins.TBool()
TInt32 = builtins.TInt(types.TValue(32))
TInt64 = builtins.TInt(types.TValue(64))
TFloat = builtins.TFloat()
TStr = builtins.TStr()
TTuple = types.TTuple
TList = builtins.TList
TRange32 = builtins.TRange(builtins.TInt(types.TValue(32)))
TRange64 = builtins.TRange(builtins.TInt(types.TValue(64)))
14 changes: 14 additions & 0 deletions artiq/runtime.rs/src/proto.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

use std::io::{self, Read, Write};
use std::vec::Vec;
use std::string::String;
use byteorder::{ByteOrder, NetworkEndian};

// FIXME: replace these with byteorder core io traits once those are in
@@ -63,3 +64,16 @@ pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> {
try!(write_u32(writer, value.len() as u32));
writer.write_all(value)
}

pub fn read_string(reader: &mut Read) -> io::Result<String> {
let mut bytes = try!(read_bytes(reader));
let len = bytes.len() - 1; // length without trailing \0
bytes.resize(len, 0); // FIXME: don't send \0 in the first place
String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}

pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> {
try!(write_u32(writer, (value.len() + 1) as u32));
try!(writer.write_all(value.as_bytes()));
write_u8(writer, 0)
}
223 changes: 200 additions & 23 deletions artiq/runtime.rs/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,162 @@
use std::slice;
use std::io::{self, Read, Write};
use proto::{write_u8, write_bytes};
use proto::*;
use self::tag::{Tag, TagIterator, split_tag};

fn recv_value(reader: &mut Read, tag: Tag, data: &mut *const ()) -> io::Result<()> {
unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (),
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
macro_rules! consume_value {
($ty:ty, |$ptr:ident| $map:expr) => ({
let ptr = (*data) as *mut $ty;
*data = ptr.offset(1) as *mut ();
(|$ptr: *mut $ty| $map)(ptr)
})
}

match tag {
Tag::None => Ok(()),
_ => unreachable!()
Tag::Bool =>
consume_value!(u8, |ptr| {
*ptr = try!(read_u8(reader)); Ok(())
}),
Tag::Int32 =>
consume_value!(u32, |ptr| {
*ptr = try!(read_u32(reader)); Ok(())
}),
Tag::Int64 | Tag::Float64 =>
consume_value!(u64, |ptr| {
*ptr = try!(read_u64(reader)); Ok(())
}),
Tag::String => {
consume_value!(*mut u8, |ptr| {
let length = try!(read_u32(reader));
// NB: the received string includes a trailing \0
*ptr = try!(alloc(length as usize)) as *mut u8;
try!(reader.read_exact(slice::from_raw_parts_mut(*ptr, length as usize)));
Ok(())
})
}
Tag::Tuple(it, arity) => {
let mut it = it.clone();
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
try!(recv_value(reader, tag, data, alloc))
}
Ok(())
}
Tag::List(it) | Tag::Array(it) => {
struct List { length: u32, elements: *mut () };
consume_value!(List, |ptr| {
(*ptr).length = try!(read_u32(reader));

let tag = it.clone().next().expect("truncated tag");
(*ptr).elements = try!(alloc(tag.size() * (*ptr).length as usize));

let mut data = (*ptr).elements;
for _ in 0..(*ptr).length as usize {
try!(recv_value(reader, tag, &mut data, alloc));
}
Ok(())
})
}
Tag::Range(it) => {
let tag = it.clone().next().expect("truncated tag");
try!(recv_value(reader, tag, data, alloc));
try!(recv_value(reader, tag, data, alloc));
try!(recv_value(reader, tag, data, alloc));
Ok(())
}
Tag::Keyword(_) => unreachable!(),
Tag::Object => unreachable!()
}
}

pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *const ()) -> io::Result<()> {
pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *mut (),
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
let mut it = TagIterator::new(tag_bytes);
trace!("recv ...->{}", it);

let tag = it.next().expect("truncated tag");
let mut data = data;
try!(recv_value(reader, it.next().expect("RPC without a return value"), &mut data));
try!(unsafe { recv_value(reader, tag, &mut data, alloc) });

Ok(())
}

fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> {
pub unsafe fn from_c_str<'a>(ptr: *const u8) -> &'a str {
use core::{str, slice};
extern { fn strlen(ptr: *const u8) -> usize; }
str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, strlen(ptr)))
}

unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> {
macro_rules! consume_value {
($ty:ty, |$ptr:ident| $map:expr) => ({
let ptr = (*data) as *const $ty;
*data = ptr.offset(1) as *const ();
(|$ptr: *const $ty| $map)(ptr)
})
}

try!(write_u8(writer, tag.as_u8()));
match tag {
Tag::None => write_u8(writer, b'n'),
_ => unreachable!()
Tag::None => Ok(()),
Tag::Bool =>
consume_value!(u8, |ptr|
write_u8(writer, *ptr)),
Tag::Int32 =>
consume_value!(u32, |ptr|
write_u32(writer, *ptr)),
Tag::Int64 | Tag::Float64 =>
consume_value!(u64, |ptr|
write_u64(writer, *ptr)),
Tag::String =>
consume_value!(*const u8, |ptr|
write_string(writer, from_c_str(*ptr))),
Tag::Tuple(it, arity) => {
let mut it = it.clone();
try!(write_u8(writer, arity));
for _ in 0..arity {
let tag = it.next().expect("truncated tag");
try!(send_value(writer, tag, data))
}
Ok(())
}
Tag::List(it) | Tag::Array(it) => {
struct List { length: u32, elements: *const () };
consume_value!(List, |ptr| {
try!(write_u32(writer, (*ptr).length));
let tag = it.clone().next().expect("truncated tag");
let mut data = (*ptr).elements;
for _ in 0..(*ptr).length as usize {
try!(send_value(writer, tag, &mut data));
}
Ok(())
})
}
Tag::Range(it) => {
let tag = it.clone().next().expect("truncated tag");
try!(send_value(writer, tag, data));
try!(send_value(writer, tag, data));
try!(send_value(writer, tag, data));
Ok(())
}
Tag::Keyword(it) => {
struct Keyword { name: *const u8, contents: () };
consume_value!(Keyword, |ptr| {
try!(write_string(writer, from_c_str((*ptr).name)));
let tag = it.clone().next().expect("truncated tag");
let mut data = &(*ptr).contents as *const ();
send_value(writer, tag, &mut data)
})
// Tag::Keyword never appears in composite types, so we don't have
// to accurately advance data.
}
Tag::Object => {
struct Object { id: u32 };
consume_value!(*const Object, |ptr|
write_u32(writer, (**ptr).id))
}
}
}

@@ -37,7 +171,7 @@ pub fn send_args(writer: &mut Write, tag_bytes: &[u8],
for index in 0.. {
if let Some(arg_tag) = args_it.next() {
let mut data = unsafe { *data.offset(index) };
try!(send_value(writer, arg_tag, &mut data));
try!(unsafe { send_value(writer, arg_tag, &mut data) });
} else {
break
}
@@ -61,7 +195,7 @@ mod tag {
(arg_tags_bytes, return_tag_bytes)
}

#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub enum Tag<'a> {
None,
Bool,
@@ -77,6 +211,52 @@ mod tag {
Object
}

impl<'a> Tag<'a> {
pub fn as_u8(self) -> u8 {
match self {
Tag::None => b'n',
Tag::Bool => b'b',
Tag::Int32 => b'i',
Tag::Int64 => b'I',
Tag::Float64 => b'f',
Tag::String => b's',
Tag::Tuple(_, _) => b't',
Tag::List(_) => b'l',
Tag::Array(_) => b'a',
Tag::Range(_) => b'r',
Tag::Keyword(_) => b'k',
Tag::Object => b'O',
}
}

pub fn size(self) -> usize {
match self {
Tag::None => 0,
Tag::Bool => 1,
Tag::Int32 => 4,
Tag::Int64 => 8,
Tag::Float64 => 8,
Tag::String => 4,
Tag::Tuple(it, arity) => {
let mut size = 0;
for _ in 0..arity {
let tag = it.clone().next().expect("truncated tag");
size += tag.size();
}
size
}
Tag::List(_) => 8,
Tag::Array(_) => 8,
Tag::Range(it) => {
let tag = it.clone().next().expect("truncated tag");
tag.size() * 3
}
Tag::Keyword(_) => unreachable!(),
Tag::Object => unreachable!(),
}
}
}

#[derive(Debug, Clone, Copy)]
pub struct TagIterator<'a> {
data: &'a [u8]
@@ -104,23 +284,23 @@ mod tag {
b't' => {
let count = self.data[0];
self.data = &self.data[1..];
Tag::Tuple(self.skip(count), count)
Tag::Tuple(self.sub(count), count)
}
b'l' => Tag::List(self.skip(1)),
b'a' => Tag::Array(self.skip(1)),
b'r' => Tag::Range(self.skip(1)),
b'k' => Tag::Keyword(self.skip(1)),
b'l' => Tag::List(self.sub(1)),
b'a' => Tag::Array(self.sub(1)),
b'r' => Tag::Range(self.sub(1)),
b'k' => Tag::Keyword(self.sub(1)),
b'O' => Tag::Object,
_ => unreachable!()
})
}

fn skip(&mut self, count: u8) -> TagIterator<'a> {
let origin = self.clone();
fn sub(&mut self, count: u8) -> TagIterator<'a> {
let data = self.data;
for _ in 0..count {
self.next().expect("truncated tag");
}
origin
TagIterator { data: &data[..(data.len() - self.data.len())] }
}
}

@@ -148,12 +328,9 @@ mod tag {
try!(write!(f, "Float64")),
Tag::String =>
try!(write!(f, "String")),
Tag::Tuple(it, cnt) => {
Tag::Tuple(it, _) => {
try!(write!(f, "Tuple("));
for i in 0..cnt {
try!(it.fmt(f));
if i != cnt - 1 { try!(write!(f, ", ")) }
}
try!(it.fmt(f));
try!(write!(f, ")"))
}
Tag::List(it) => {
30 changes: 25 additions & 5 deletions artiq/runtime.rs/src/session.rs
Original file line number Diff line number Diff line change
@@ -55,15 +55,17 @@ enum KernelState {
struct Session<'a> {
congress: &'a mut Congress,
kernel_state: KernelState,
watchdog_set: clock::WatchdogSet
watchdog_set: clock::WatchdogSet,
log_buffer: String
}

impl<'a> Session<'a> {
fn new(congress: &mut Congress) -> Session {
Session {
congress: congress,
kernel_state: KernelState::Absent,
watchdog_set: clock::WatchdogSet::new()
watchdog_set: clock::WatchdogSet::new(),
log_buffer: String::new()
}
}

@@ -240,7 +242,17 @@ fn process_host_message(waiter: Waiter,
match reply {
kern::RpcRecvRequest { slot } => {
let mut data = io::Cursor::new(data);
rpc::recv_return(&mut data, &tag, slot)
rpc::recv_return(&mut data, &tag, slot, &|size| {
try!(kern_send(waiter, kern::RpcRecvReply {
alloc_size: size, exception: None
}));
kern_recv(waiter, |reply| {
match reply {
kern::RpcRecvRequest { slot } => Ok(slot),
_ => unreachable!()
}
})
})
}
other =>
unexpected!("unexpected reply from kernel CPU: {:?}", other)
@@ -276,8 +288,16 @@ fn process_kern_message(waiter: Waiter,
trace!("comm<-kern {:?}", request);
match request {
kern::Log(log) => {
info!(target: "kernel", "{}", log);
kern_acknowledge()
session.log_buffer += log;
try!(kern_acknowledge());

if &log[log.len() - 1..] == "\n" {
for line in session.log_buffer.lines() {
info!(target: "kernel", "{}", line);
}
session.log_buffer.clear()
}
Ok(())
}

kern::NowInitRequest =>
Loading