summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/http_bench.js26
-rw-r--r--core/http_bench.rs4
-rw-r--r--core/isolate.rs307
-rw-r--r--core/lib.rs2
-rwxr-xr-x[l---------]core/libdeno.rs189
-rw-r--r--core/shared_queue.js40
-rw-r--r--core/shared_queue.rs59
-rw-r--r--core/shared_queue_test.js2
-rw-r--r--core/test_util.rs51
9 files changed, 505 insertions, 175 deletions
diff --git a/core/http_bench.js b/core/http_bench.js
index e5598c06f..d7cffcb75 100644
--- a/core/http_bench.js
+++ b/core/http_bench.js
@@ -37,21 +37,12 @@ const scratchBytes = new Uint8Array(
);
assert(scratchBytes.byteLength === 4 * 4);
-// Toggle what method we send with. false = legacy.
-// AFAICT This has no effect on performance.
-const sendWithShared = true;
-
function send(promiseId, opId, arg, zeroCopy = null) {
scratch32[0] = promiseId;
scratch32[1] = opId;
scratch32[2] = arg;
scratch32[3] = -1;
- if (sendWithShared) {
- Deno._sharedQueue.push(scratchBytes);
- libdeno.send(null, zeroCopy);
- } else {
- libdeno.send(scratchBytes, zeroCopy);
- }
+ return DenoCore.dispatch(scratchBytes, zeroCopy);
}
/** Returns Promise<number> */
@@ -74,19 +65,10 @@ function recordFromBuf(buf) {
};
}
-function recv() {
- const buf = Deno._sharedQueue.shift();
- if (!buf) {
- return null;
- }
- return recordFromBuf(buf);
-}
-
/** Returns i32 number */
function sendSync(opId, arg) {
- send(0, opId, arg);
- const record = recv();
- assert(recv() == null);
+ const buf = send(0, opId, arg);
+ const record = recordFromBuf(buf);
return record.result;
}
@@ -141,7 +123,7 @@ async function serve(rid) {
}
async function main() {
- Deno._setAsyncHandler(handleAsyncMsgFromRust);
+ DenoCore.setAsyncHandler(handleAsyncMsgFromRust);
libdeno.print("http_bench.js start\n");
diff --git a/core/http_bench.rs b/core/http_bench.rs
index ed3792d6a..eb7acc379 100644
--- a/core/http_bench.rs
+++ b/core/http_bench.rs
@@ -167,9 +167,7 @@ fn main() {
let js_source = include_str!("http_bench.js");
let main_future = lazy(move || {
- let isolate = deno_core::Isolate::new(HttpBench());
-
- isolate.shared_init();
+ let mut isolate = deno_core::Isolate::new(HttpBench());
// TODO currently isolate.execute() must be run inside tokio, hence the
// lazy(). It would be nice to not have that contraint. Probably requires
diff --git a/core/isolate.rs b/core/isolate.rs
index ef2da2a68..99e88f553 100644
--- a/core/isolate.rs
+++ b/core/isolate.rs
@@ -71,6 +71,7 @@ pub trait Behavior {
pub struct Isolate<B: Behavior> {
libdeno_isolate: *const libdeno::isolate,
behavior: B,
+ needs_init: bool,
shared: SharedQueue,
pending_ops: Vec<PendingOp>,
polled_recently: bool,
@@ -94,6 +95,7 @@ impl<B: Behavior> Isolate<B> {
let shared = SharedQueue::new(RECOMMENDED_SIZE);
+ let needs_init = true;
let config = libdeno::deno_config {
will_snapshot: 0,
load_snapshot: match behavior.startup_snapshot() {
@@ -109,14 +111,20 @@ impl<B: Behavior> Isolate<B> {
libdeno_isolate,
behavior,
shared,
+ needs_init,
pending_ops: Vec::new(),
polled_recently: false,
}
}
/// Executes a bit of built-in JavaScript to provide Deno._sharedQueue.
- pub fn shared_init(&self) {
- js_check(self.execute("shared_queue.js", include_str!("shared_queue.js")));
+ pub fn shared_init(&mut self) {
+ if self.needs_init {
+ self.needs_init = false;
+ js_check(
+ self.execute("shared_queue.js", include_str!("shared_queue.js")),
+ );
+ }
}
extern "C" fn pre_dispatch(
@@ -151,11 +159,11 @@ impl<B: Behavior> Isolate<B> {
if is_sync {
let res_record = op.wait().unwrap();
- let push_success = isolate.shared.push(res_record);
- assert!(push_success);
- // TODO check that if JSError thrown during respond(), that it will be
+ // For sync messages, we always return the response via libdeno.send's
+ // return value.
+ // TODO(ry) check that if JSError thrown during respond(), that it will be
// picked up.
- let _ = isolate.respond();
+ let _ = isolate.respond(Some(&res_record));
} else {
isolate.pending_ops.push(PendingOp {
op,
@@ -184,10 +192,11 @@ impl<B: Behavior> Isolate<B> {
}
pub fn execute(
- &self,
+ &mut self,
js_filename: &str,
js_source: &str,
) -> Result<(), JSError> {
+ self.shared_init();
let filename = CString::new(js_filename).unwrap();
let source = CString::new(js_source).unwrap();
unsafe {
@@ -223,8 +232,11 @@ impl<B: Behavior> Isolate<B> {
}
}
- fn respond(&mut self) -> Result<(), JSError> {
- let buf = deno_buf::empty();
+ fn respond(&mut self, maybe_buf: Option<&[u8]>) -> Result<(), JSError> {
+ let buf = match maybe_buf {
+ None => deno_buf::empty(),
+ Some(r) => deno_buf::from(r),
+ };
unsafe {
libdeno::deno_respond(self.libdeno_isolate, self.as_raw_ptr(), buf)
}
@@ -290,7 +302,8 @@ impl<B: Behavior> Isolate<B> {
Ok(())
}
- pub fn mod_evaluate(&self, id: deno_mod) -> Result<(), JSError> {
+ pub fn mod_evaluate(&mut self, id: deno_mod) -> Result<(), JSError> {
+ self.shared_init();
unsafe {
libdeno::deno_mod_evaluate(self.libdeno_isolate, self.as_raw_ptr(), id)
};
@@ -350,8 +363,11 @@ impl<B: Behavior> Future for Isolate<B> {
self.polled_recently = true;
assert_eq!(self.shared.size(), 0);
+ let mut overflow_response: Option<Buf> = None;
+
let mut i = 0;
while i < self.pending_ops.len() {
+ assert!(overflow_response.is_none());
let pending = &mut self.pending_ops[i];
match pending.poll() {
Err(()) => panic!("unexpected error"),
@@ -360,22 +376,35 @@ impl<B: Behavior> Future for Isolate<B> {
}
Ok(Async::Ready(buf)) => {
let completed = self.pending_ops.remove(i);
- completed_count += 1;
if completed.zero_copy_id > 0 {
self.zero_copy_release(completed.zero_copy_id);
}
- self.shared.push(buf);
+ let successful_push = self.shared.push(&buf);
+ if !successful_push {
+ // If we couldn't push the response to the shared queue, because
+ // there wasn't enough size, we will return the buffer via the
+ // legacy route, using the argument of deno_respond.
+ overflow_response = Some(buf);
+ break;
+ }
+
+ completed_count += 1;
}
}
}
if completed_count > 0 {
- self.respond()?;
+ self.respond(None)?;
// The other side should have shifted off all the messages.
assert_eq!(self.shared.size(), 0);
}
+
+ if overflow_response.is_some() {
+ let buf = overflow_response.take().unwrap();
+ self.respond(Some(&buf))?;
+ }
}
self.check_promise_errors();
@@ -401,12 +430,111 @@ pub fn js_check(r: Result<(), JSError>) {
#[cfg(test)]
mod tests {
use super::*;
- use crate::test_util::*;
+ use std::collections::HashMap;
+
+ pub enum TestBehaviorMode {
+ AsyncImmediate,
+ OverflowReqSync,
+ OverflowResSync,
+ OverflowReqAsync,
+ OverflowResAsync,
+ }
+
+ pub struct TestBehavior {
+ pub dispatch_count: usize,
+ pub resolve_count: usize,
+ pub mod_map: HashMap<String, deno_mod>,
+ mode: TestBehaviorMode,
+ }
+
+ impl TestBehavior {
+ pub fn setup(mode: TestBehaviorMode) -> Isolate<Self> {
+ let mut isolate = Isolate::new(TestBehavior {
+ dispatch_count: 0,
+ resolve_count: 0,
+ mode,
+ mod_map: HashMap::new(),
+ });
+ js_check(isolate.execute(
+ "setup.js",
+ r#"
+ function assert(cond) {
+ if (!cond) {
+ throw Error("assert");
+ }
+ }
+ "#,
+ ));
+ assert_eq!(isolate.behavior.dispatch_count, 0);
+ isolate
+ }
+
+ pub fn register(&mut self, name: &str, id: deno_mod) {
+ self.mod_map.insert(name.to_string(), id);
+ }
+ }
+
+ impl Behavior for TestBehavior {
+ fn startup_snapshot(&mut self) -> Option<deno_buf> {
+ None
+ }
+
+ fn resolve(&mut self, specifier: &str, _referrer: deno_mod) -> deno_mod {
+ self.resolve_count += 1;
+ match self.mod_map.get(specifier) {
+ Some(id) => *id,
+ None => 0,
+ }
+ }
+
+ fn dispatch(
+ &mut self,
+ control: &[u8],
+ _zero_copy_buf: deno_buf,
+ ) -> (bool, Box<Op>) {
+ self.dispatch_count += 1;
+ match self.mode {
+ TestBehaviorMode::AsyncImmediate => {
+ assert_eq!(control.len(), 1);
+ assert_eq!(control[0], 42);
+ let buf = vec![43u8].into_boxed_slice();
+ (false, Box::new(futures::future::ok(buf)))
+ }
+ TestBehaviorMode::OverflowReqSync => {
+ assert_eq!(control.len(), 100 * 1024 * 1024);
+ let buf = vec![43u8].into_boxed_slice();
+ (true, Box::new(futures::future::ok(buf)))
+ }
+ TestBehaviorMode::OverflowResSync => {
+ assert_eq!(control.len(), 1);
+ assert_eq!(control[0], 42);
+ let mut vec = Vec::<u8>::new();
+ vec.resize(100 * 1024 * 1024, 0);
+ vec[0] = 99;
+ let buf = vec.into_boxed_slice();
+ (true, Box::new(futures::future::ok(buf)))
+ }
+ TestBehaviorMode::OverflowReqAsync => {
+ assert_eq!(control.len(), 100 * 1024 * 1024);
+ let buf = vec![43u8].into_boxed_slice();
+ (false, Box::new(futures::future::ok(buf)))
+ }
+ TestBehaviorMode::OverflowResAsync => {
+ assert_eq!(control.len(), 1);
+ assert_eq!(control[0], 42);
+ let mut vec = Vec::<u8>::new();
+ vec.resize(100 * 1024 * 1024, 0);
+ vec[0] = 4;
+ let buf = vec.into_boxed_slice();
+ (false, Box::new(futures::future::ok(buf)))
+ }
+ }
+ }
+ }
#[test]
fn test_dispatch() {
- let behavior = TestBehavior::new();
- let isolate = Isolate::new(behavior);
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::AsyncImmediate);
js_check(isolate.execute(
"filename.js",
r#"
@@ -423,8 +551,7 @@ mod tests {
#[test]
fn test_mods() {
- let behavior = TestBehavior::new();
- let mut isolate = Isolate::new(behavior);
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::AsyncImmediate);
let mod_a = isolate
.mod_new(
true,
@@ -464,33 +591,25 @@ mod tests {
#[test]
fn test_poll_async_immediate_ops() {
- let behavior = TestBehavior::new();
- let mut isolate = Isolate::new(behavior);
-
- isolate.shared_init();
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::AsyncImmediate);
js_check(isolate.execute(
- "setup.js",
+ "setup2.js",
r#"
let nrecv = 0;
- Deno._setAsyncHandler((buf) => {
+ DenoCore.setAsyncHandler((buf) => {
nrecv++;
});
- function assertEq(actual, expected) {
- if (expected != actual) {
- throw Error(`actual ${actual} expected ${expected} `);
- }
- }
"#,
));
assert_eq!(isolate.behavior.dispatch_count, 0);
js_check(isolate.execute(
"check1.js",
r#"
- assertEq(nrecv, 0);
+ assert(nrecv == 0);
let control = new Uint8Array([42]);
libdeno.send(control);
- assertEq(nrecv, 0);
+ assert(nrecv == 0);
"#,
));
assert_eq!(isolate.behavior.dispatch_count, 1);
@@ -499,14 +618,14 @@ mod tests {
js_check(isolate.execute(
"check2.js",
r#"
- assertEq(nrecv, 1);
+ assert(nrecv == 1);
libdeno.send(control);
- assertEq(nrecv, 1);
+ assert(nrecv == 1);
"#,
));
assert_eq!(isolate.behavior.dispatch_count, 2);
assert_eq!(Ok(Async::Ready(())), isolate.poll());
- js_check(isolate.execute("check3.js", "assertEq(nrecv, 2)"));
+ js_check(isolate.execute("check3.js", "assert(nrecv == 2)"));
assert_eq!(isolate.behavior.dispatch_count, 2);
// We are idle, so the next poll should be the last.
assert_eq!(Ok(Async::Ready(())), isolate.poll());
@@ -514,25 +633,17 @@ mod tests {
#[test]
fn test_shared() {
- let behavior = TestBehavior::new();
- let mut isolate = Isolate::new(behavior);
-
- isolate.shared_init();
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::AsyncImmediate);
js_check(isolate.execute(
- "setup.js",
+ "setup2.js",
r#"
let nrecv = 0;
- Deno._setAsyncHandler((buf) => {
+ DenoCore.setAsyncHandler((buf) => {
assert(buf.byteLength === 1);
assert(buf[0] === 43);
nrecv++;
});
- function assert(cond) {
- if (!cond) {
- throw Error("assert");
- }
- }
"#,
));
assert_eq!(isolate.behavior.dispatch_count, 0);
@@ -541,11 +652,11 @@ mod tests {
"send1.js",
r#"
let control = new Uint8Array([42]);
- Deno._sharedQueue.push(control);
+ DenoCore.shared.push(control);
libdeno.send();
assert(nrecv === 0);
- Deno._sharedQueue.push(control);
+ DenoCore.shared.push(control);
libdeno.send();
assert(nrecv === 0);
"#,
@@ -556,4 +667,106 @@ mod tests {
js_check(isolate.execute("send1.js", "assert(nrecv === 2);"));
}
+ #[test]
+ fn overflow_req_sync() {
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::OverflowReqSync);
+ js_check(isolate.execute(
+ "overflow_req_sync.js",
+ r#"
+ let asyncRecv = 0;
+ DenoCore.setAsyncHandler((buf) => { asyncRecv++ });
+ // Large message that will overflow the shared space.
+ let control = new Uint8Array(100 * 1024 * 1024);
+ let response = DenoCore.dispatch(control);
+ assert(response instanceof Uint8Array);
+ assert(response.length == 1);
+ assert(response[0] == 43);
+ assert(asyncRecv == 0);
+ "#,
+ ));
+ assert_eq!(isolate.behavior.dispatch_count, 1);
+ }
+
+ #[test]
+ fn overflow_res_sync() {
+ // TODO(ry) This test is quite slow due to memcpy-ing 100MB into JS. We
+ // should optimize this.
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::OverflowResSync);
+ js_check(isolate.execute(
+ "overflow_res_sync.js",
+ r#"
+ let asyncRecv = 0;
+ DenoCore.setAsyncHandler((buf) => { asyncRecv++ });
+ // Large message that will overflow the shared space.
+ let control = new Uint8Array([42]);
+ let response = DenoCore.dispatch(control);
+ assert(response instanceof Uint8Array);
+ assert(response.length == 100 * 1024 * 1024);
+ assert(response[0] == 99);
+ assert(asyncRecv == 0);
+ "#,
+ ));
+ assert_eq!(isolate.behavior.dispatch_count, 1);
+ }
+
+ #[test]
+ fn overflow_req_async() {
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::OverflowReqAsync);
+ js_check(isolate.execute(
+ "overflow_req_async.js",
+ r#"
+ let asyncRecv = 0;
+ DenoCore.setAsyncHandler((buf) => {
+ assert(buf.byteLength === 1);
+ assert(buf[0] === 43);
+ asyncRecv++;
+ });
+ // Large message that will overflow the shared space.
+ let control = new Uint8Array(100 * 1024 * 1024);
+ let response = DenoCore.dispatch(control);
+ // Async messages always have null response.
+ assert(response == null);
+ assert(asyncRecv == 0);
+ "#,
+ ));
+ assert_eq!(isolate.behavior.dispatch_count, 1);
+ assert_eq!(Ok(Async::Ready(())), isolate.poll());
+ js_check(isolate.execute("check.js", "assert(asyncRecv == 1);"));
+ }
+
+ #[test]
+ fn overflow_res_async() {
+ // TODO(ry) This test is quite slow due to memcpy-ing 100MB into JS. We
+ // should optimize this.
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::OverflowResAsync);
+ js_check(isolate.execute(
+ "overflow_res_async.js",
+ r#"
+ let asyncRecv = 0;
+ DenoCore.setAsyncHandler((buf) => {
+ assert(buf.byteLength === 100 * 1024 * 1024);
+ assert(buf[0] === 4);
+ asyncRecv++;
+ });
+ // Large message that will overflow the shared space.
+ let control = new Uint8Array([42]);
+ let response = DenoCore.dispatch(control);
+ assert(response == null);
+ assert(asyncRecv == 0);
+ "#,
+ ));
+ assert_eq!(isolate.behavior.dispatch_count, 1);
+ assert_eq!(Ok(Async::Ready(())), isolate.poll());
+ js_check(isolate.execute("check.js", "assert(asyncRecv == 1);"));
+ }
+
+ #[test]
+ fn test_js() {
+ let mut isolate = TestBehavior::setup(TestBehaviorMode::AsyncImmediate);
+ js_check(
+ isolate
+ .execute("shared_queue_test.js", include_str!("shared_queue_test.js")),
+ );
+ assert_eq!(Ok(Async::Ready(())), isolate.poll());
+ }
}
diff --git a/core/lib.rs b/core/lib.rs
index d044f60b5..90c4448f3 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -9,8 +9,6 @@ mod isolate;
mod js_errors;
mod libdeno;
mod shared_queue;
-#[cfg(test)]
-mod test_util;
pub use crate::flags::v8_set_flags;
pub use crate::isolate::*;
diff --git a/core/libdeno.rs b/core/libdeno.rs
index 32688906e..7b6f07a9b 120000..100755
--- a/core/libdeno.rs
+++ b/core/libdeno.rs
@@ -1 +1,188 @@
-../src/libdeno.rs \ No newline at end of file
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+
+use libc::c_char;
+use libc::c_int;
+use libc::c_void;
+use libc::size_t;
+use std::ops::{Deref, DerefMut};
+use std::ptr::null;
+
+// TODO(F001): change this definition to `extern { pub type isolate; }`
+// After RFC 1861 is stablized. See https://github.com/rust-lang/rust/issues/43467.
+#[repr(C)]
+pub struct isolate {
+ _unused: [u8; 0],
+}
+
+/// If "alloc_ptr" is not null, this type represents a buffer which is created
+/// in C side, and then passed to Rust side by `deno_recv_cb`. Finally it should
+/// be moved back to C side by `deno_respond`. If it is not passed to
+/// `deno_respond` in the end, it will be leaked.
+///
+/// If "alloc_ptr" is null, this type represents a borrowed slice.
+#[repr(C)]
+pub struct deno_buf {
+ alloc_ptr: *const u8,
+ alloc_len: usize,
+ data_ptr: *const u8,
+ data_len: usize,
+ pub zero_copy_id: usize,
+}
+
+/// `deno_buf` can not clone, and there is no interior mutability.
+/// This type satisfies Send bound.
+unsafe impl Send for deno_buf {}
+
+impl deno_buf {
+ #[inline]
+ pub fn empty() -> Self {
+ Self {
+ alloc_ptr: null(),
+ alloc_len: 0,
+ data_ptr: null(),
+ data_len: 0,
+ zero_copy_id: 0,
+ }
+ }
+
+ #[inline]
+ pub unsafe fn from_raw_parts(ptr: *const u8, len: usize) -> Self {
+ Self {
+ alloc_ptr: null(),
+ alloc_len: 0,
+ data_ptr: ptr,
+ data_len: len,
+ zero_copy_id: 0,
+ }
+ }
+}
+
+/// Converts Rust &Buf to libdeno `deno_buf`.
+impl<'a> From<&'a [u8]> for deno_buf {
+ #[inline]
+ fn from(x: &'a [u8]) -> Self {
+ Self {
+ alloc_ptr: null(),
+ alloc_len: 0,
+ data_ptr: x.as_ref().as_ptr(),
+ data_len: x.len(),
+ zero_copy_id: 0,
+ }
+ }
+}
+
+impl Deref for deno_buf {
+ type Target = [u8];
+ #[inline]
+ fn deref(&self) -> &[u8] {
+ unsafe { std::slice::from_raw_parts(self.data_ptr, self.data_len) }
+ }
+}
+
+impl DerefMut for deno_buf {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ if self.alloc_ptr.is_null() {
+ panic!("Can't modify the buf");
+ }
+ std::slice::from_raw_parts_mut(self.data_ptr as *mut u8, self.data_len)
+ }
+ }
+}
+
+impl AsRef<[u8]> for deno_buf {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ &*self
+ }
+}
+
+impl AsMut<[u8]> for deno_buf {
+ #[inline]
+ fn as_mut(&mut self) -> &mut [u8] {
+ if self.alloc_ptr.is_null() {
+ panic!("Can't modify the buf");
+ }
+ &mut *self
+ }
+}
+
+#[allow(non_camel_case_types)]
+type deno_recv_cb = unsafe extern "C" fn(
+ user_data: *mut c_void,
+ control_buf: deno_buf, // deprecated
+ zero_copy_buf: deno_buf,
+);
+
+#[allow(non_camel_case_types)]
+pub type deno_mod = i32;
+
+#[allow(non_camel_case_types)]
+type deno_resolve_cb = unsafe extern "C" fn(
+ user_data: *mut c_void,
+ specifier: *const c_char,
+ referrer: deno_mod,
+) -> deno_mod;
+
+#[repr(C)]
+pub struct deno_config {
+ pub will_snapshot: c_int,
+ pub load_snapshot: deno_buf,
+ pub shared: deno_buf,
+ pub recv_cb: deno_recv_cb,
+}
+
+extern "C" {
+ pub fn deno_init();
+ pub fn deno_v8_version() -> *const c_char;
+ pub fn deno_set_v8_flags(argc: *mut c_int, argv: *mut *mut c_char);
+ pub fn deno_new(config: deno_config) -> *const isolate;
+ pub fn deno_delete(i: *const isolate);
+ pub fn deno_last_exception(i: *const isolate) -> *const c_char;
+ pub fn deno_check_promise_errors(i: *const isolate);
+ pub fn deno_lock(i: *const isolate);
+ pub fn deno_unlock(i: *const isolate);
+ pub fn deno_respond(
+ i: *const isolate,
+ user_data: *const c_void,
+ buf: deno_buf,
+ );
+ pub fn deno_zero_copy_release(i: *const isolate, zero_copy_id: usize);
+ pub fn deno_execute(
+ i: *const isolate,
+ user_data: *const c_void,
+ js_filename: *const c_char,
+ js_source: *const c_char,
+ );
+
+ // Modules
+
+ pub fn deno_mod_new(
+ i: *const isolate,
+ main: bool,
+ name: *const c_char,
+ source: *const c_char,
+ ) -> deno_mod;
+
+ pub fn deno_mod_imports_len(i: *const isolate, id: deno_mod) -> size_t;
+
+ pub fn deno_mod_imports_get(
+ i: *const isolate,
+ id: deno_mod,
+ index: size_t,
+ ) -> *const c_char;
+
+ pub fn deno_mod_instantiate(
+ i: *const isolate,
+ user_data: *const c_void,
+ id: deno_mod,
+ resolve_cb: deno_resolve_cb,
+ );
+
+ pub fn deno_mod_evaluate(
+ i: *const isolate,
+ user_data: *const c_void,
+ id: deno_mod,
+ );
+}
diff --git a/core/shared_queue.js b/core/shared_queue.js
index 756f86e72..6c9a0326f 100644
--- a/core/shared_queue.js
+++ b/core/shared_queue.js
@@ -11,10 +11,6 @@
let sharedBytes = null;
let shared32 = null;
- if (!window["Deno"]) {
- window["Deno"] = {};
- }
-
function assert(cond) {
if (!cond) {
throw Error("assert");
@@ -105,10 +101,13 @@
asyncHandler = cb;
}
- function handleAsyncMsgFromRust() {
- let buf;
- while ((buf = shift()) != null) {
+ function handleAsyncMsgFromRust(buf) {
+ if (buf) {
asyncHandler(buf);
+ } else {
+ while ((buf = shift()) != null) {
+ asyncHandler(buf);
+ }
}
}
@@ -122,13 +121,26 @@
libdeno.recv(handleAsyncMsgFromRust);
}
- window.Deno._setAsyncHandler = setAsyncHandler;
- window.Deno._sharedQueue = {
- head,
- numRecords,
- size,
- push,
- shift
+ function dispatch(control, zeroCopy = null) {
+ // First try to push control to shared.
+ const success = push(control);
+ // If successful, don't use first argument of libdeno.send.
+ const arg0 = success ? null : control;
+ return libdeno.send(arg0, zeroCopy);
+ }
+
+ assert(!window["DenoCore"]);
+ window["DenoCore"] = {
+ setAsyncHandler,
+ dispatch,
+ shared: {
+ head,
+ numRecords,
+ size,
+ push,
+ reset,
+ shift
+ }
};
init(libdeno.shared);
diff --git a/core/shared_queue.rs b/core/shared_queue.rs
index 1a8692b5f..9070253b9 100644
--- a/core/shared_queue.rs
+++ b/core/shared_queue.rs
@@ -1,5 +1,4 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
-use crate::isolate::Buf;
use crate::libdeno::deno_buf;
const MAX_RECORDS: usize = 100;
@@ -43,14 +42,22 @@ impl SharedQueue {
s[INDEX_HEAD] = HEAD_INIT as u32;
}
- fn as_u32_slice<'a>(&'a self) -> &'a [u32] {
- let p = self.bytes.as_ptr() as *const u32;
- unsafe { std::slice::from_raw_parts(p, self.bytes.len() / 4) }
+ fn as_u32_slice(&self) -> &[u32] {
+ let p = self.bytes.as_ptr();
+ // Assert pointer is 32 bit aligned before casting.
+ assert_eq!((p as usize) % std::mem::align_of::<u32>(), 0);
+ #[allow(clippy::cast_ptr_alignment)]
+ let p32 = p as *const u32;
+ unsafe { std::slice::from_raw_parts(p32, self.bytes.len() / 4) }
}
- fn as_u32_slice_mut<'a>(&'a mut self) -> &'a mut [u32] {
- let p = self.bytes.as_mut_ptr() as *mut u32;
- unsafe { std::slice::from_raw_parts_mut(p, self.bytes.len() / 4) }
+ fn as_u32_slice_mut(&mut self) -> &mut [u32] {
+ let p = self.bytes.as_mut_ptr();
+ // Assert pointer is 32 bit aligned before casting.
+ assert_eq!((p as usize) % std::mem::align_of::<u32>(), 0);
+ #[allow(clippy::cast_ptr_alignment)]
+ let p32 = p as *mut u32;
+ unsafe { std::slice::from_raw_parts_mut(p32, self.bytes.len() / 4) }
}
pub fn size(&self) -> usize {
@@ -96,7 +103,7 @@ impl SharedQueue {
}
/// Returns none if empty.
- pub fn shift<'a>(&'a mut self) -> Option<&'a [u8]> {
+ pub fn shift(&mut self) -> Option<&[u8]> {
let u32_slice = self.as_u32_slice();
let i = u32_slice[INDEX_NUM_SHIFTED_OFF] as usize;
if self.size() == 0 {
@@ -117,7 +124,7 @@ impl SharedQueue {
Some(&self.bytes[off..end])
}
- pub fn push(&mut self, record: Buf) -> bool {
+ pub fn push(&mut self, record: &[u8]) -> bool {
let off = self.head();
let end = off + record.len();
let index = self.num_records();
@@ -127,7 +134,7 @@ impl SharedQueue {
}
self.set_end(index, end);
assert_eq!(end - off, record.len());
- self.bytes[off..end].copy_from_slice(&record);
+ self.bytes[off..end].copy_from_slice(record);
let u32_slice = self.as_u32_slice_mut();
u32_slice[INDEX_NUM_RECORDS] += 1;
u32_slice[INDEX_HEAD] = end as u32;
@@ -138,11 +145,7 @@ impl SharedQueue {
#[cfg(test)]
mod tests {
use super::*;
- use crate::isolate::js_check;
- use crate::isolate::Isolate;
- use crate::test_util::*;
- use futures::Async;
- use futures::Future;
+ use crate::isolate::Buf;
#[test]
fn basic() {
@@ -153,14 +156,14 @@ mod tests {
let r = vec![1u8, 2, 3, 4, 5].into_boxed_slice();
let len = r.len() + h;
- assert!(q.push(r));
+ assert!(q.push(&r));
assert_eq!(q.head(), len);
let r = vec![6, 7].into_boxed_slice();
- assert!(q.push(r));
+ assert!(q.push(&r));
let r = vec![8, 9, 10, 11].into_boxed_slice();
- assert!(q.push(r));
+ assert!(q.push(&r));
assert_eq!(q.num_records(), 3);
assert_eq!(q.size(), 3);
@@ -195,31 +198,19 @@ mod tests {
#[test]
fn overflow() {
let mut q = SharedQueue::new(RECOMMENDED_SIZE);
- assert!(q.push(alloc_buf(RECOMMENDED_SIZE - 1)));
+ assert!(q.push(&alloc_buf(RECOMMENDED_SIZE - 1)));
assert_eq!(q.size(), 1);
- assert!(!q.push(alloc_buf(2)));
+ assert!(!q.push(&alloc_buf(2)));
assert_eq!(q.size(), 1);
- assert!(q.push(alloc_buf(1)));
+ assert!(q.push(&alloc_buf(1)));
assert_eq!(q.size(), 2);
assert_eq!(q.shift().unwrap().len(), RECOMMENDED_SIZE - 1);
assert_eq!(q.size(), 1);
- assert!(!q.push(alloc_buf(1)));
+ assert!(!q.push(&alloc_buf(1)));
assert_eq!(q.shift().unwrap().len(), 1);
assert_eq!(q.size(), 0);
}
-
- #[test]
- fn test_js() {
- let behavior = TestBehavior::new();
- let mut isolate = Isolate::new(behavior);
- isolate.shared_init();
- js_check(
- isolate
- .execute("shared_queue_test.js", include_str!("shared_queue_test.js")),
- );
- assert_eq!(Ok(Async::Ready(())), isolate.poll());
- }
}
diff --git a/core/shared_queue_test.js b/core/shared_queue_test.js
index e1e4c9987..817bb3bcd 100644
--- a/core/shared_queue_test.js
+++ b/core/shared_queue_test.js
@@ -7,7 +7,7 @@ function assert(cond) {
}
function main() {
- const q = Deno._sharedQueue;
+ const q = DenoCore.shared;
let h = q.head();
assert(h > 0);
diff --git a/core/test_util.rs b/core/test_util.rs
deleted file mode 100644
index 80025c2be..000000000
--- a/core/test_util.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use crate::isolate::Behavior;
-use crate::isolate::Op;
-use crate::libdeno::deno_buf;
-use crate::libdeno::deno_mod;
-use std::collections::HashMap;
-
-pub struct TestBehavior {
- pub dispatch_count: usize,
- pub resolve_count: usize,
- pub mod_map: HashMap<String, deno_mod>,
-}
-
-impl TestBehavior {
- pub fn new() -> Self {
- Self {
- dispatch_count: 0,
- resolve_count: 0,
- mod_map: HashMap::new(),
- }
- }
-
- pub fn register(&mut self, name: &str, id: deno_mod) {
- self.mod_map.insert(name.to_string(), id);
- }
-}
-
-impl Behavior for TestBehavior {
- fn startup_snapshot(&mut self) -> Option<deno_buf> {
- None
- }
-
- fn dispatch(
- &mut self,
- control: &[u8],
- _zero_copy_buf: deno_buf,
- ) -> (bool, Box<Op>) {
- assert_eq!(control.len(), 1);
- assert_eq!(control[0], 42);
- self.dispatch_count += 1;
- let buf = vec![43u8].into_boxed_slice();
- (false, Box::new(futures::future::ok(buf)))
- }
-
- fn resolve(&mut self, specifier: &str, _referrer: deno_mod) -> deno_mod {
- self.resolve_count += 1;
- match self.mod_map.get(specifier) {
- Some(id) => *id,
- None => 0,
- }
- }
-}