diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2018-08-09 12:17:08 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-09 12:17:08 -0700 |
commit | fb87cb38eca7a2d490c3e70738407dc6538e80d3 (patch) | |
tree | 29e81258ca714594f0b6b325ddd41bb5f8c54257 /src | |
parent | 0e96125260f2d78718e57cac95c7dc672bb24e57 (diff) |
First pass at setTimeout with Tokio (#434)
Diffstat (limited to 'src')
-rw-r--r-- | src/binding.rs | 1 | ||||
-rw-r--r-- | src/handlers.h | 4 | ||||
-rw-r--r-- | src/handlers.rs | 168 | ||||
-rw-r--r-- | src/main.rs | 11 | ||||
-rw-r--r-- | src/msg.fbs | 2 | ||||
-rw-r--r-- | src/reply.cc | 17 |
6 files changed, 194 insertions, 9 deletions
diff --git a/src/binding.rs b/src/binding.rs index a846401bc..2be5f7206 100644 --- a/src/binding.rs +++ b/src/binding.rs @@ -29,6 +29,7 @@ extern "C" { pub fn deno_last_exception(d: *const DenoC) -> *const c_char; pub fn deno_get_data(d: *const DenoC) -> *const c_void; pub fn deno_set_response(d: *const DenoC, buf: deno_buf); + pub fn deno_send(d: *const DenoC, buf: deno_buf); pub fn deno_execute( d: *const DenoC, js_filename: *const c_char, diff --git a/src/handlers.h b/src/handlers.h index 067819529..decbe59da 100644 --- a/src/handlers.h +++ b/src/handlers.h @@ -10,5 +10,9 @@ void handle_code_fetch(Deno* d, uint32_t cmd_id, const char* module_specifier, const char* containing_file); void handle_code_cache(Deno* d, uint32_t cmd_id, const char* filename, const char* source_code, const char* output_code); + +void handle_timer_start(Deno* d, uint32_t cmd_id, uint32_t timer_id, + bool interval, uint32_t delay); +void handle_timer_clear(Deno* d, uint32_t cmd_id, uint32_t timer_id); } // extern "C" #endif // HANDLERS_H_ diff --git a/src/handlers.rs b/src/handlers.rs index 517e146a1..480d7b6b8 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,7 +1,10 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. +use binding; use binding::{deno_buf, deno_set_response, DenoC}; use flatbuffers; use from_c; +use futures; +use futures::sync::oneshot; use libc::c_char; use msg_generated::deno as msg; use std::ffi::CStr; @@ -24,6 +27,7 @@ pub fn deno_handle_msg_from_js(d: *const DenoC, buf: deno_buf) { } */ +// TODO(ry) Use Deno instead of DenoC as first arg. fn reply_error(d: *const DenoC, cmd_id: u32, msg: &String) { let mut builder = flatbuffers::FlatBufferBuilder::new(); // println!("reply_error{}", msg); @@ -35,16 +39,14 @@ fn reply_error(d: *const DenoC, cmd_id: u32, msg: &String) { set_response_base(d, &mut builder, &args) } -fn set_response_base( - d: *const DenoC, +fn create_msg( builder: &mut flatbuffers::FlatBufferBuilder, args: &msg::BaseArgs, -) { +) -> deno_buf { let base = msg::CreateBase(builder, &args); builder.finish(base); let data = builder.get_active_buf_slice(); - // println!("buf slice {} {} {} {} {}", data[0], data[1], data[2], data[3], data[4]); - let buf = deno_buf { + deno_buf { // TODO(ry) // The deno_buf / ImportBuf / ExportBuf semantics should be such that we do not need to yield // ownership. Temporarally there is a hack in ImportBuf that when alloc_ptr is null, it will @@ -53,12 +55,29 @@ fn set_response_base( alloc_len: 0, data_ptr: data.as_ptr() as *mut u8, data_len: data.len(), - }; - // println!("data_ptr {:p}", data_ptr); - // println!("data_len {}", data.len()); + } +} + +// TODO(ry) Use Deno instead of DenoC as first arg. +fn set_response_base( + d: *const DenoC, + builder: &mut flatbuffers::FlatBufferBuilder, + args: &msg::BaseArgs, +) { + let buf = create_msg(builder, args); unsafe { deno_set_response(d, buf) } } +// TODO(ry) Use Deno instead of DenoC as first arg. +fn send_base( + d: *const DenoC, + builder: &mut flatbuffers::FlatBufferBuilder, + args: &msg::BaseArgs, +) { + let buf = create_msg(builder, args); + unsafe { binding::deno_send(d, buf) } +} + // https://github.com/denoland/deno/blob/golang/os.go#L100-L154 #[no_mangle] pub extern "C" fn handle_code_fetch( @@ -131,3 +150,136 @@ pub extern "C" fn handle_code_cache( } // null response indicates success. } + +fn set_timeout<F>( + cb: F, + delay: u32, +) -> ( + impl Future<Item = (), Error = ()>, + futures::sync::oneshot::Sender<()>, +) +where + F: FnOnce() -> (), +{ + let (cancel_tx, cancel_rx) = oneshot::channel::<()>(); + let when = Instant::now() + Duration::from_millis(delay.into()); + let delay_task = Delay::new(when) + .map_err(|e| panic!("timer failed; err={:?}", e)) + .and_then(|_| { + cb(); + Ok(()) + }) + .select(cancel_rx) + .map(|_| ()) + .map_err(|_| ()); + + (delay_task, cancel_tx) +} + +fn set_interval<F>( + cb: F, + delay: u32, +) -> ( + impl Future<Item = (), Error = ()>, + futures::sync::oneshot::Sender<()>, +) +where + F: Fn() -> (), +{ + let (cancel_tx, cancel_rx) = oneshot::channel::<()>(); + let delay = Duration::from_millis(delay.into()); + let interval_task = future::lazy(move || { + Interval::new(Instant::now() + delay, delay) + .for_each(move |_| { + cb(); + future::ok(()) + }) + .into_future() + .map_err(|_| panic!()) + }).select(cancel_rx) + .map(|_| ()) + .map_err(|_| ()); + + (interval_task, cancel_tx) +} + +// TODO(ry) Use Deno instead of DenoC as first arg. +fn send_timer_ready(d: *const DenoC, timer_id: u32, done: bool) { + let mut builder = flatbuffers::FlatBufferBuilder::new(); + let msg = msg::CreateTimerReady( + &mut builder, + &msg::TimerReadyArgs { + id: timer_id, + done, + ..Default::default() + }, + ); + builder.finish(msg); + send_base( + d, + &mut builder, + &msg::BaseArgs { + msg: Some(msg.union()), + msg_type: msg::Any::TimerReady, + ..Default::default() + }, + ); +} + +// TODO(ry) Use Deno instead of DenoC as first arg. +fn remove_timer(d: *const DenoC, timer_id: u32) { + let deno = from_c(d); + deno.timers.remove(&timer_id); +} + +use std::time::{Duration, Instant}; +use tokio::prelude::*; +use tokio::timer::{Delay, Interval}; +// Prototype: https://github.com/ry/deno/blob/golang/timers.go#L25-L39 +#[no_mangle] +pub extern "C" fn handle_timer_start( + d: *const DenoC, + cmd_id: u32, + timer_id: u32, + interval: bool, + delay: u32, +) { + assert!(cmd_id == 0); + debug!("handle_timer_start"); + let deno = from_c(d); + + if interval { + let (interval_task, cancel_interval) = set_interval( + move || { + send_timer_ready(d, timer_id, false); + }, + delay, + ); + + deno.timers.insert(timer_id, cancel_interval); + deno.rt.spawn(interval_task); + } else { + let (delay_task, cancel_delay) = set_timeout( + move || { + remove_timer(d, timer_id); + send_timer_ready(d, timer_id, true); + }, + delay, + ); + + deno.timers.insert(timer_id, cancel_delay); + deno.rt.spawn(delay_task); + } +} + +// Prototype: https://github.com/ry/deno/blob/golang/timers.go#L40-L43 +#[no_mangle] +pub extern "C" fn handle_timer_clear( + d: *const DenoC, + cmd_id: u32, + timer_id: u32, +) { + assert!(cmd_id == 0); + debug!("handle_timer_clear"); + remove_timer(d, timer_id); +} diff --git a/src/main.rs b/src/main.rs index ae1ee37b1..db923a31d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,11 @@ extern crate flatbuffers; +extern crate futures; extern crate libc; extern crate msg_rs as msg_generated; extern crate sha1; extern crate tempfile; +extern crate tokio; +extern crate tokio_current_thread; extern crate url; #[macro_use] extern crate log; @@ -14,6 +17,7 @@ pub mod handlers; use libc::c_int; use libc::c_void; +use std::collections::HashMap; use std::env; use std::ffi::CStr; use std::ffi::CString; @@ -91,6 +95,8 @@ type DenoException<'a> = &'a str; pub struct Deno { ptr: *const binding::DenoC, dir: deno_dir::DenoDir, + rt: tokio::runtime::current_thread::Runtime, + timers: HashMap<u32, futures::sync::oneshot::Sender<()>>, } static DENO_INIT: std::sync::Once = std::sync::ONCE_INIT; @@ -104,6 +110,8 @@ impl Deno { let deno_box = Box::new(Deno { ptr: 0 as *const binding::DenoC, dir: deno_dir::DenoDir::new(None).unwrap(), + rt: tokio::runtime::current_thread::Runtime::new().unwrap(), + timers: HashMap::new(), }); let deno: &'a mut Deno = Box::leak(deno_box); let external_ptr = deno as *mut _ as *const c_void; @@ -204,4 +212,7 @@ fn main() { error!("{}", err); std::process::exit(1); }); + + // Start the Tokio event loop + d.rt.run().expect("err"); } diff --git a/src/msg.fbs b/src/msg.fbs index cb1cf57a9..46cea1e29 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -61,7 +61,7 @@ table Exit { table TimerStart { id: uint; interval: bool; - delay: int; + delay: uint; } table TimerReady { diff --git a/src/reply.cc b/src/reply.cc index a6558890d..dada4e168 100644 --- a/src/reply.cc +++ b/src/reply.cc @@ -71,6 +71,23 @@ void deno_handle_msg_from_js(Deno* d, deno_buf buf) { break; } + case deno::Any_TimerStart: { + auto msg = base->msg_as_TimerStart(); + handle_timer_start(d, cmd_id, msg->id(), msg->interval(), msg->delay()); + break; + } + + case deno::Any_TimerReady: { + CHECK(false && "Privileged side should not receive TimerReady message."); + break; + } + + case deno::Any_TimerClear: { + auto msg = base->msg_as_TimerClear(); + handle_timer_clear(d, cmd_id, msg->id()); + break; + } + case deno::Any_Exit: { auto msg = base->msg_as_Exit(); uint32_t code = msg->code(); |