1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-22 15:06:54 -05:00
denoland-deno/core/examples/http_bench_json_ops.rs
Bartek Iwańczuk 6984b63f2f
refactor: rewrite ops to use ResourceTable2 (#8512)
This commit migrates all ops to use new resource table
and "AsyncRefCell".

Old implementation of resource table was completely 
removed and all code referencing it was updated to use
new system.
2020-12-16 17:14:12 +01:00

261 lines
6.4 KiB
Rust

// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
#[macro_use]
extern crate log;
use deno_core::error::bad_resource_id;
use deno_core::error::AnyError;
use deno_core::AsyncRefCell;
use deno_core::BufVec;
use deno_core::CancelHandle;
use deno_core::CancelTryFuture;
use deno_core::JsRuntime;
use deno_core::OpState;
use deno_core::RcRef;
use deno_core::Resource;
use deno_core::ZeroCopyBuf;
use serde_json::Value;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::env;
use std::io::Error;
use std::net::SocketAddr;
use std::rc::Rc;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
struct Logger;
impl log::Log for Logger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= log::max_level()
}
fn log(&self, record: &log::Record) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
// Note: a `tokio::net::TcpListener` doesn't need to be wrapped in a cell,
// because it only supports one op (`accept`) which does not require a mutable
// reference to the listener.
struct TcpListener {
inner: tokio::net::TcpListener,
cancel: CancelHandle,
}
impl TcpListener {
async fn accept(self: Rc<Self>) -> Result<TcpStream, Error> {
let cancel = RcRef::map(&self, |r| &r.cancel);
let stream = self.inner.accept().try_or_cancel(cancel).await?.0.into();
Ok(stream)
}
}
impl Resource for TcpListener {
fn close(self: Rc<Self>) {
self.cancel.cancel();
}
}
impl TryFrom<std::net::TcpListener> for TcpListener {
type Error = Error;
fn try_from(
std_listener: std::net::TcpListener,
) -> Result<Self, Self::Error> {
tokio::net::TcpListener::try_from(std_listener).map(|tokio_listener| Self {
inner: tokio_listener,
cancel: Default::default(),
})
}
}
struct TcpStream {
rd: AsyncRefCell<tokio::net::tcp::OwnedReadHalf>,
wr: AsyncRefCell<tokio::net::tcp::OwnedWriteHalf>,
// When a `TcpStream` resource is closed, all pending 'read' ops are
// canceled, while 'write' ops are allowed to complete. Therefore only
// 'read' futures are attached to this cancel handle.
cancel: CancelHandle,
}
impl TcpStream {
async fn read(self: Rc<Self>, buf: &mut [u8]) -> Result<usize, Error> {
let mut rd = RcRef::map(&self, |r| &r.rd).borrow_mut().await;
let cancel = RcRef::map(self, |r| &r.cancel);
rd.read(buf).try_or_cancel(cancel).await
}
async fn write(self: Rc<Self>, buf: &[u8]) -> Result<usize, Error> {
let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await;
wr.write(buf).await
}
}
impl Resource for TcpStream {
fn close(self: Rc<Self>) {
self.cancel.cancel()
}
}
impl From<tokio::net::TcpStream> for TcpStream {
fn from(s: tokio::net::TcpStream) -> Self {
let (rd, wr) = s.into_split();
Self {
rd: rd.into(),
wr: wr.into(),
cancel: Default::default(),
}
}
}
fn create_js_runtime() -> JsRuntime {
let mut runtime = JsRuntime::new(Default::default());
runtime.register_op("listen", deno_core::json_op_sync(op_listen));
runtime.register_op("close", deno_core::json_op_sync(op_close));
runtime.register_op("accept", deno_core::json_op_async(op_accept));
runtime.register_op("read", deno_core::json_op_async(op_read));
runtime.register_op("write", deno_core::json_op_async(op_write));
runtime
}
fn op_listen(
state: &mut OpState,
_args: Value,
_bufs: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
debug!("listen");
let addr = "127.0.0.1:4544".parse::<SocketAddr>().unwrap();
let std_listener = std::net::TcpListener::bind(&addr)?;
std_listener.set_nonblocking(true)?;
let listener = TcpListener::try_from(std_listener)?;
let rid = state.resource_table.add(listener);
Ok(serde_json::json!({ "rid": rid }))
}
fn op_close(
state: &mut OpState,
args: Value,
_buf: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let rid: u32 = args
.get("rid")
.unwrap()
.as_u64()
.unwrap()
.try_into()
.unwrap();
debug!("close rid={}", rid);
state
.resource_table
.close(rid)
.map(|_| serde_json::json!(()))
.ok_or_else(bad_resource_id)
}
async fn op_accept(
state: Rc<RefCell<OpState>>,
args: Value,
_bufs: BufVec,
) -> Result<Value, AnyError> {
let rid: u32 = args
.get("rid")
.unwrap()
.as_u64()
.unwrap()
.try_into()
.unwrap();
debug!("accept rid={}", rid);
let listener = state
.borrow()
.resource_table
.get::<TcpListener>(rid)
.ok_or_else(bad_resource_id)?;
let stream = listener.accept().await?;
let rid = state.borrow_mut().resource_table.add(stream);
Ok(serde_json::json!({ "rid": rid }))
}
async fn op_read(
state: Rc<RefCell<OpState>>,
args: Value,
mut bufs: BufVec,
) -> Result<Value, AnyError> {
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
let rid: u32 = args
.get("rid")
.unwrap()
.as_u64()
.unwrap()
.try_into()
.unwrap();
debug!("read rid={}", rid);
let stream = state
.borrow()
.resource_table
.get::<TcpStream>(rid)
.ok_or_else(bad_resource_id)?;
let nread = stream.read(&mut bufs[0]).await?;
Ok(serde_json::json!({ "nread": nread }))
}
async fn op_write(
state: Rc<RefCell<OpState>>,
args: Value,
bufs: BufVec,
) -> Result<Value, AnyError> {
assert_eq!(bufs.len(), 1, "Invalid number of arguments");
let rid: u32 = args
.get("rid")
.unwrap()
.as_u64()
.unwrap()
.try_into()
.unwrap();
debug!("write rid={}", rid);
let stream = state
.borrow()
.resource_table
.get::<TcpStream>(rid)
.ok_or_else(bad_resource_id)?;
let nwritten = stream.write(&bufs[0]).await?;
Ok(serde_json::json!({ "nwritten": nwritten }))
}
fn main() {
log::set_logger(&Logger).unwrap();
log::set_max_level(
env::args()
.find(|a| a == "-D")
.map(|_| log::LevelFilter::Debug)
.unwrap_or(log::LevelFilter::Warn),
);
// NOTE: `--help` arg will display V8 help and exit
deno_core::v8_set_flags(env::args().collect());
let mut js_runtime = create_js_runtime();
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let future = async move {
js_runtime
.execute(
"http_bench_json_ops.js",
include_str!("http_bench_json_ops.js"),
)
.unwrap();
js_runtime.run_event_loop().await
};
runtime.block_on(future).unwrap();
}