diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/Cargo.toml | 1 | ||||
-rw-r--r-- | core/examples/http_bench.rs | 144 | ||||
-rw-r--r-- | core/lib.rs | 4 | ||||
-rw-r--r-- | core/resources.rs | 84 |
4 files changed, 149 insertions, 84 deletions
diff --git a/core/Cargo.toml b/core/Cargo.toml index 241e8a2ac..cbc19edbc 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,6 +14,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] +downcast-rs = "1.1.0" futures = "0.1.29" lazy_static = "1.4.0" libc = "0.2.62" diff --git a/core/examples/http_bench.rs b/core/examples/http_bench.rs index c019d8a11..a6cc6d548 100644 --- a/core/examples/http_bench.rs +++ b/core/examples/http_bench.rs @@ -14,12 +14,10 @@ extern crate lazy_static; use deno::*; use futures::future::lazy; -use std::collections::HashMap; use std::env; use std::net::SocketAddr; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; use std::sync::Mutex; +use std::sync::MutexGuard; use tokio::prelude::*; static LOGGER: Logger = Logger; @@ -184,44 +182,38 @@ fn main() { } } -enum Repr { - TcpListener(tokio::net::TcpListener), - TcpStream(tokio::net::TcpStream), -} +struct TcpListener(tokio::net::TcpListener); + +impl Resource for TcpListener {} + +struct TcpStream(tokio::net::TcpStream); + +impl Resource for TcpStream {} -type ResourceTable = HashMap<i32, Repr>; lazy_static! { - static ref RESOURCE_TABLE: Mutex<ResourceTable> = Mutex::new(HashMap::new()); - static ref NEXT_RID: AtomicUsize = AtomicUsize::new(3); + static ref RESOURCE_TABLE: Mutex<ResourceTable> = + Mutex::new(ResourceTable::default()); } -fn new_rid() -> i32 { - let rid = NEXT_RID.fetch_add(1, Ordering::SeqCst); - rid as i32 +fn lock_resource_table<'a>() -> MutexGuard<'a, ResourceTable> { + RESOURCE_TABLE.lock().unwrap() } fn op_accept(record: Record, _zero_copy_buf: Option<PinnedBuf>) -> Box<HttpOp> { - let listener_rid = record.arg; - debug!("accept {}", listener_rid); - Box::new( - futures::future::poll_fn(move || { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&listener_rid); - match maybe_repr { - Some(Repr::TcpListener(ref mut listener)) => listener.poll_accept(), - _ => panic!("bad rid {}", listener_rid), - } - }) - .and_then(move |(stream, addr)| { - debug!("accept success {}", addr); - let rid = new_rid(); - - let mut guard = RESOURCE_TABLE.lock().unwrap(); - guard.insert(rid, Repr::TcpStream(stream)); - - Ok(rid as i32) - }), - ) + let rid = record.arg as u32; + debug!("accept {}", rid); + let fut = futures::future::poll_fn(move || { + let mut table = lock_resource_table(); + let listener = table.get_mut::<TcpListener>(rid)?; + listener.0.poll_accept() + }) + .and_then(move |(stream, addr)| { + debug!("accept success {}", addr); + let mut table = lock_resource_table(); + let rid = table.add(Box::new(TcpStream(stream))); + Ok(rid as i32) + }); + Box::new(fut) } fn op_listen( @@ -229,70 +221,54 @@ fn op_listen( _zero_copy_buf: Option<PinnedBuf>, ) -> Box<HttpOp> { debug!("listen"); - Box::new(lazy(move || { - let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap(); - let listener = tokio::net::TcpListener::bind(&addr).unwrap(); - let rid = new_rid(); - - let mut guard = RESOURCE_TABLE.lock().unwrap(); - guard.insert(rid, Repr::TcpListener(listener)); - futures::future::ok(rid) - })) + let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap(); + let listener = tokio::net::TcpListener::bind(&addr).unwrap(); + let mut table = lock_resource_table(); + let rid = table.add(Box::new(TcpListener(listener))); + Box::new(futures::future::ok(rid as i32)) } fn op_close(record: Record, _zero_copy_buf: Option<PinnedBuf>) -> Box<HttpOp> { debug!("close"); - let rid = record.arg; - Box::new(lazy(move || { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let r = table.remove(&rid); - let result = if r.is_some() { 0 } else { -1 }; - futures::future::ok(result) - })) + let rid = record.arg as u32; + let mut table = lock_resource_table(); + let fut = match table.close(rid) { + Ok(_) => futures::future::ok(0), + Err(e) => futures::future::err(e), + }; + Box::new(fut) } fn op_read(record: Record, zero_copy_buf: Option<PinnedBuf>) -> Box<HttpOp> { - let rid = record.arg; + let rid = record.arg as u32; debug!("read rid={}", rid); let mut zero_copy_buf = zero_copy_buf.unwrap(); - Box::new( - futures::future::poll_fn(move || { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&rid); - match maybe_repr { - Some(Repr::TcpStream(ref mut stream)) => { - stream.poll_read(&mut zero_copy_buf) - } - _ => panic!("bad rid"), - } - }) - .and_then(move |nread| { - debug!("read success {}", nread); - Ok(nread as i32) - }), - ) + let fut = futures::future::poll_fn(move || { + let mut table = lock_resource_table(); + let stream = table.get_mut::<TcpStream>(rid)?; + stream.0.poll_read(&mut zero_copy_buf) + }) + .and_then(move |nread| { + debug!("read success {}", nread); + Ok(nread as i32) + }); + Box::new(fut) } fn op_write(record: Record, zero_copy_buf: Option<PinnedBuf>) -> Box<HttpOp> { - let rid = record.arg; + let rid = record.arg as u32; debug!("write rid={}", rid); let zero_copy_buf = zero_copy_buf.unwrap(); - Box::new( - futures::future::poll_fn(move || { - let mut table = RESOURCE_TABLE.lock().unwrap(); - let maybe_repr = table.get_mut(&rid); - match maybe_repr { - Some(Repr::TcpStream(ref mut stream)) => { - stream.poll_write(&zero_copy_buf) - } - _ => panic!("bad rid"), - } - }) - .and_then(move |nwritten| { - debug!("write success {}", nwritten); - Ok(nwritten as i32) - }), - ) + let fut = futures::future::poll_fn(move || { + let mut table = lock_resource_table(); + let stream = table.get_mut::<TcpStream>(rid)?; + stream.0.poll_write(&zero_copy_buf) + }) + .and_then(move |nwritten| { + debug!("write success {}", nwritten); + Ok(nwritten as i32) + }); + Box::new(fut) } fn js_check(r: Result<(), ErrBox>) { diff --git a/core/lib.rs b/core/lib.rs index 42a692f1a..31f717769 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -3,6 +3,8 @@ extern crate log; extern crate futures; extern crate libc; +#[macro_use] +extern crate downcast_rs; mod any_error; mod flags; @@ -12,6 +14,7 @@ mod libdeno; mod module_specifier; mod modules; mod ops; +mod resources; mod shared_queue; pub use crate::any_error::*; @@ -24,6 +27,7 @@ pub use crate::libdeno::PinnedBuf; pub use crate::module_specifier::*; pub use crate::modules::*; pub use crate::ops::*; +pub use crate::resources::*; pub fn v8_version() -> &'static str { use std::ffi::CStr; diff --git a/core/resources.rs b/core/resources.rs new file mode 100644 index 000000000..c3d8b7107 --- /dev/null +++ b/core/resources.rs @@ -0,0 +1,84 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. + +// Think of Resources as File Descriptors. They are integers that are allocated by +// the privileged side of Deno to refer to various rust objects that need to be +// referenced between multiple ops. For example, network sockets are resources. +// Resources may or may not correspond to a real operating system file +// descriptor (hence the different name). + +use downcast_rs::Downcast; +use std; +use std::any::Any; +use std::collections::HashMap; +use std::io::Error; +use std::io::ErrorKind; + +/// ResourceId is Deno's version of a file descriptor. ResourceId is also referred +/// to as rid in the code base. +pub type ResourceId = u32; + +/// These store Deno's file descriptors. These are not necessarily the operating +/// system ones. +type ResourceMap = HashMap<ResourceId, Box<dyn Resource>>; + +#[derive(Default)] +pub struct ResourceTable { + map: ResourceMap, + next_id: u32, +} + +impl ResourceTable { + pub fn get<T: Resource>(&self, rid: ResourceId) -> Result<&T, Error> { + let resource = self.map.get(&rid).ok_or_else(bad_resource)?; + let resource = &resource.downcast_ref::<T>().ok_or_else(bad_resource)?; + Ok(resource) + } + + pub fn get_mut<T: Resource>( + &mut self, + rid: ResourceId, + ) -> Result<&mut T, Error> { + let resource = self.map.get_mut(&rid).ok_or_else(bad_resource)?; + let resource = resource.downcast_mut::<T>().ok_or_else(bad_resource)?; + Ok(resource) + } + + // TODO: resource id allocation should probably be randomized for security. + fn next_rid(&mut self) -> ResourceId { + let next_rid = self.next_id; + self.next_id += 1; + next_rid as ResourceId + } + + pub fn add(&mut self, resource: Box<dyn Resource>) -> ResourceId { + let rid = self.next_rid(); + let r = self.map.insert(rid, resource); + assert!(r.is_none()); + rid + } + + // close(2) is done by dropping the value. Therefore we just need to remove + // the resource from the RESOURCE_TABLE. + pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> { + let repr = self.map.remove(&rid).ok_or_else(bad_resource)?; + // Give resource a chance to cleanup (notify tasks, etc.) + repr.close(); + Ok(()) + } +} + +/// Abstract type representing resource in Deno. +pub trait Resource: Downcast + Any + Send { + /// Method that allows to cleanup resource. + fn close(&self) {} + + fn inspect_repr(&self) -> &str { + unimplemented!(); + } +} +impl_downcast!(Resource); + +// TODO: probably bad error kind +pub fn bad_resource() -> Error { + Error::new(ErrorKind::NotFound, "bad resource id") +} |