Skip to content
Permalink
Browse files

core: Behavior shouldn't be generic

We always pass around Box<[u8]>, and adding this generic is an
unnecessary complication.
  • Loading branch information...
ry committed Mar 14, 2019
1 parent 630ead1 commit dbb3a20e92b352218c7470a4fda43eedfaea33fa
Showing with 110 additions and 47 deletions.
  1. +1 −2 BUILD.gn
  2. +24 −8 core/BUILD.gn
  3. +55 −10 core/http_bench.rs
  4. +30 −27 core/isolate.rs
@@ -13,8 +13,7 @@ group("default") {
":deno",
":hyper_hello",
":test_rs",
"core:deno_core_http_bench",
"core:deno_core_test",
"core:default",
"libdeno:test_cc",
]
}
@@ -1,5 +1,14 @@
import("//build_extra/rust/rust.gni")

group("default") {
testonly = true
deps = [
":deno_core_http_bench",
":deno_core_http_bench_test",
":deno_core_test",
]
}

# deno_core does not depend on flatbuffers nor tokio.
main_extern = [
"$rust_build:futures",
@@ -24,14 +33,21 @@ rust_test("deno_core_test") {
]
}

http_bench_extern = [
"$rust_build:futures",
"$rust_build:lazy_static",
"$rust_build:libc",
"$rust_build:log",
"$rust_build:tokio",
":deno_core"
]

rust_executable("deno_core_http_bench") {
source_root = "http_bench.rs"
extern = [
"$rust_build:futures",
"$rust_build:lazy_static",
"$rust_build:libc",
"$rust_build:log",
"$rust_build:tokio",
":deno_core"
]
extern = http_bench_extern
}

rust_test("deno_core_http_bench_test") {
source_root = "http_bench.rs"
extern = http_bench_extern
}
@@ -35,14 +35,58 @@ const INDEX_END: usize = 1;
const NUM_RECORDS: usize = 128;
const RECORD_SIZE: usize = 4;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Record {
pub promise_id: i32,
pub op_id: i32,
pub arg: i32,
pub result: i32,
}

impl Into<Buf> for Record {
fn into(self) -> Buf {
let buf32 = vec![self.promise_id, self.op_id, self.arg, self.result]
.into_boxed_slice();
let ptr = Box::into_raw(buf32) as *mut [u8; 16];
unsafe { Box::from_raw(ptr) }
}
}

impl From<Buf> for Record {
fn from(buf: Buf) -> Record {
assert_eq!(buf.len(), 4 * 4);
//let byte_len = buf.len();
let ptr = Box::into_raw(buf) as *mut [i32; 4];
let ints: Box<[i32]> = unsafe { Box::from_raw(ptr) };
assert_eq!(ints.len(), 4);
Record {
promise_id: ints[0],
op_id: ints[1],
arg: ints[2],
result: ints[3],
}
}
}

#[test]
fn test_record_from() {
let r = Record {
promise_id: 1,
op_id: 2,
arg: 3,
result: 4,
};
let expected = r.clone();
let buf: Buf = r.into();
// TODO How do only do this on little endian?
assert_eq!(
buf,
vec![1u8, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0].into_boxed_slice()
);
let actual = Record::from(buf);
assert_eq!(actual, expected);
}

pub type HttpBenchOp = dyn Future<Item = i32, Error = std::io::Error> + Send;

struct HttpBench {
@@ -64,7 +108,7 @@ fn idx(i: usize, off: usize) -> usize {
2 + i * RECORD_SIZE + off
}

impl Behavior<Record> for HttpBench {
impl Behavior for HttpBench {
fn startup_snapshot(&mut self) -> Option<deno_buf> {
None
}
@@ -80,11 +124,8 @@ impl Behavior<Record> for HttpBench {
unimplemented!()
}

fn recv(
&mut self,
record: Record,
zero_copy_buf: deno_buf,
) -> (bool, Box<Op<Record>>) {
fn recv(&mut self, control: Buf, zero_copy_buf: deno_buf) -> (bool, Box<Op>) {
let record = Record::from(control);
let is_sync = record.promise_id == 0;
let http_bench_op = match record.op_id {
OP_LISTEN => {
@@ -125,6 +166,9 @@ impl Behavior<Record> for HttpBench {
eprintln!("unexpected err {}", err);
record_b.result = -1;
Ok(record_b)
}).then(|result| -> Result<Buf, ()> {
let record = result.unwrap();
Ok(record.into())
}),
);
(is_sync, op)
@@ -135,7 +179,8 @@ impl Behavior<Record> for HttpBench {
self.shared32[INDEX_END] = 0;
}

fn records_push(&mut self, record: Record) -> bool {
fn records_push(&mut self, buf: Buf) -> bool {
let record = Record::from(buf);
debug!("push {:?}", record);
let i = self.shared32[INDEX_END] as usize;
if i >= NUM_RECORDS {
@@ -149,7 +194,7 @@ impl Behavior<Record> for HttpBench {
true
}

fn records_shift(&mut self) -> Option<Record> {
fn records_shift(&mut self) -> Option<Buf> {
let i = self.shared32[INDEX_START] as usize;
if i == self.shared32[INDEX_END] as usize {
return None;
@@ -161,7 +206,7 @@ impl Behavior<Record> for HttpBench {
result: self.shared32[idx(i, 3)],
};
self.shared32[INDEX_START] += 1;
Some(record)
Some(record.into())
}
}

@@ -11,19 +11,20 @@ use std::ffi::CStr;
use std::ffi::CString;
use std::sync::{Once, ONCE_INIT};

pub type Op<R> = dyn Future<Item = R, Error = ()> + Send;
pub type Buf = Box<[u8]>;
pub type Op = dyn Future<Item = Buf, Error = ()> + Send;

struct PendingOp<R> {
op: Box<Op<R>>,
struct PendingOp {
op: Box<Op>,
polled_recently: bool,
zero_copy_id: usize, // non-zero if associated zero-copy buffer.
}

impl<R> Future for PendingOp<R> {
type Item = R;
impl Future for PendingOp {
type Item = Buf;
type Error = ();

fn poll(&mut self) -> Poll<R, ()> {
fn poll(&mut self) -> Poll<Buf, ()> {
// Do not call poll on ops we've already polled this turn.
if self.polled_recently {
Ok(Async::NotReady)
@@ -32,15 +33,15 @@ impl<R> Future for PendingOp<R> {
let op = &mut self.op;
op.poll().map_err(|()| {
// Ops should not error. If an op experiences an error it needs to
// encode that error into the record R, so it can be returned to JS.
// encode that error into a buf, so it can be returned to JS.
panic!("ops should not error")
})
}
}
}

/// Defines the behavior of an Isolate.
pub trait Behavior<R> {
pub trait Behavior {
/// Called exactly once when an Isolate is created to retrieve the startup
/// snapshot.
fn startup_snapshot(&mut self) -> Option<deno_buf>;
@@ -54,7 +55,7 @@ pub trait Behavior<R> {

/// Called whenever libdeno.send() is called in JavaScript. zero_copy_buf
/// corresponds to the second argument of libdeno.send().
fn recv(&mut self, record: R, zero_copy_buf: deno_buf) -> (bool, Box<Op<R>>);
fn recv(&mut self, record: Buf, zero_copy_buf: deno_buf) -> (bool, Box<Op>);

// TODO(ry) Remove records_reset().
// TODO(ry) Abstract records_* and startup_shared() methods into standalone
@@ -65,10 +66,10 @@ pub trait Behavior<R> {
fn records_reset(&mut self);

/// Returns false if not enough room.
fn records_push(&mut self, record: R) -> bool;
fn records_push(&mut self, record: Buf) -> bool;

/// Returns none if empty.
fn records_shift(&mut self) -> Option<R>;
fn records_shift(&mut self) -> Option<Buf>;
}

/// A single execution context of JavaScript. Corresponds roughly to the "Web
@@ -79,24 +80,24 @@ pub trait Behavior<R> {
/// Ops are created in JavaScript by calling libdeno.send(), and in Rust by
/// implementing Behavior::recv. An Op corresponds exactly to a Promise in
/// JavaScript.
pub struct Isolate<R, B: Behavior<R>> {
pub struct Isolate<B: Behavior> {
libdeno_isolate: *const libdeno::isolate,
behavior: B,
pending_ops: Vec<PendingOp<R>>,
pending_ops: Vec<PendingOp>,
polled_recently: bool,
}

unsafe impl<R, B: Behavior<R>> Send for Isolate<R, B> {}
unsafe impl<B: Behavior> Send for Isolate<B> {}

impl<R, B: Behavior<R>> Drop for Isolate<R, B> {
impl<B: Behavior> Drop for Isolate<B> {
fn drop(&mut self) {
unsafe { libdeno::deno_delete(self.libdeno_isolate) }
}
}

static DENO_INIT: Once = ONCE_INIT;

impl<R, B: Behavior<R>> Isolate<R, B> {
impl<B: Behavior> Isolate<B> {
pub fn new(mut behavior: B) -> Self {
DENO_INIT.call_once(|| {
unsafe { libdeno::deno_init() };
@@ -129,7 +130,7 @@ impl<R, B: Behavior<R>> Isolate<R, B> {
control_buf: deno_buf,
zero_copy_buf: deno_buf,
) {
let isolate = unsafe { Isolate::<R, B>::from_raw_ptr(user_data) };
let isolate = unsafe { Isolate::<B>::from_raw_ptr(user_data) };
assert_eq!(control_buf.len(), 0);
let zero_copy_id = zero_copy_buf.zero_copy_id;

@@ -295,7 +296,7 @@ impl<R, B: Behavior<R>> Isolate<R, B> {
specifier_ptr: *const libc::c_char,
referrer: deno_mod,
) -> deno_mod {
let isolate = unsafe { Isolate::<R, B>::from_raw_ptr(user_data) };
let isolate = unsafe { Isolate::<B>::from_raw_ptr(user_data) };
let specifier_c: &CStr = unsafe { CStr::from_ptr(specifier_ptr) };
let specifier: &str = specifier_c.to_str().unwrap();
isolate.behavior.resolve(specifier, referrer)
@@ -319,7 +320,7 @@ impl Drop for LockerScope {
}
}

impl<R, B: Behavior<R>> Future for Isolate<R, B> {
impl<B: Behavior> Future for Isolate<B> {
type Item = ();
type Error = JSError;

@@ -347,7 +348,7 @@ impl<R, B: Behavior<R>> Future for Isolate<R, B> {
while i != self.pending_ops.len() {
let pending = &mut self.pending_ops[i];
match pending.poll() {
Err(()) => panic!("unexpectd error"),
Err(()) => panic!("unexpected error"),
Ok(Async::NotReady) => {
i += 1;
}
@@ -422,7 +423,7 @@ mod tests {
}
}

impl Behavior<()> for TestBehavior {
impl Behavior for TestBehavior {
fn startup_snapshot(&mut self) -> Option<deno_buf> {
None
}
@@ -433,11 +434,12 @@ mod tests {

fn recv(
&mut self,
_record: (),
_record: Buf,
_zero_copy_buf: deno_buf,
) -> (bool, Box<Op<()>>) {
) -> (bool, Box<Op>) {
self.recv_count += 1;
(false, Box::new(futures::future::ok(())))
let buf = vec![42u8].into_boxed_slice();
(false, Box::new(futures::future::ok(buf)))
}

fn resolve(&mut self, specifier: &str, _referrer: deno_mod) -> deno_mod {
@@ -452,14 +454,15 @@ mod tests {
self.reset_count += 1;
}

fn records_push(&mut self, _record: ()) -> bool {
fn records_push(&mut self, _record: Buf) -> bool {
self.push_count += 1;
true
}

fn records_shift(&mut self) -> Option<()> {
fn records_shift(&mut self) -> Option<Buf> {
self.shift_count += 1;
Some(())
let buf = Box::new([42]);
Some(buf)
}
}

0 comments on commit dbb3a20

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.