Refactor Worker and ThreadSafeState (#3242)

* Split ThreadSafeState into State and GlobalState. State is a "local"
  state belonging to "Worker" while "GlobalState" is state shared by
  whole program.
* Update "Worker" and ops to use "GlobalState" where applicable
* Move and refactor "WorkerChannels" resource
This commit is contained in:
Bartek Iwańczuk 2019-11-04 16:38:52 +01:00 committed by Ry Dahl
parent 429439d198
commit 0049d4e50c
16 changed files with 557 additions and 414 deletions

View file

@ -2,7 +2,6 @@
use crate::compilers::CompiledModule; use crate::compilers::CompiledModule;
use crate::compilers::CompiledModuleFuture; use crate::compilers::CompiledModuleFuture;
use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFile;
use crate::state::ThreadSafeState;
use std::str; use std::str;
pub struct JsCompiler {} pub struct JsCompiler {}
@ -10,7 +9,6 @@ pub struct JsCompiler {}
impl JsCompiler { impl JsCompiler {
pub fn compile_async( pub fn compile_async(
self: &Self, self: &Self,
_state: ThreadSafeState,
source_file: &SourceFile, source_file: &SourceFile,
) -> Box<CompiledModuleFuture> { ) -> Box<CompiledModuleFuture> {
let module = CompiledModule { let module = CompiledModule {

View file

@ -2,7 +2,6 @@
use crate::compilers::CompiledModule; use crate::compilers::CompiledModule;
use crate::compilers::CompiledModuleFuture; use crate::compilers::CompiledModuleFuture;
use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFile;
use crate::state::ThreadSafeState;
use deno::ErrBox; use deno::ErrBox;
use regex::Regex; use regex::Regex;
use std::str; use std::str;
@ -15,7 +14,6 @@ pub struct JsonCompiler {}
impl JsonCompiler { impl JsonCompiler {
pub fn compile_async( pub fn compile_async(
self: &Self, self: &Self,
_state: ThreadSafeState,
source_file: &SourceFile, source_file: &SourceFile,
) -> Box<CompiledModuleFuture> { ) -> Box<CompiledModuleFuture> {
let maybe_json_value: serde_json::Result<serde_json::Value> = let maybe_json_value: serde_json::Result<serde_json::Value> =

View file

@ -5,8 +5,8 @@ use crate::diagnostics::Diagnostic;
use crate::disk_cache::DiskCache; use crate::disk_cache::DiskCache;
use crate::file_fetcher::SourceFile; use crate::file_fetcher::SourceFile;
use crate::file_fetcher::SourceFileFetcher; use crate::file_fetcher::SourceFileFetcher;
use crate::global_state::ThreadSafeGlobalState;
use crate::msg; use crate::msg;
use crate::resources;
use crate::source_maps::SourceMapGetter; use crate::source_maps::SourceMapGetter;
use crate::startup_data; use crate::startup_data;
use crate::state::*; use crate::state::*;
@ -16,7 +16,7 @@ use deno::Buf;
use deno::ErrBox; use deno::ErrBox;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use futures::Future; use futures::Future;
use futures::Stream; use futures::IntoFuture;
use regex::Regex; use regex::Regex;
use std::collections::HashSet; use std::collections::HashSet;
use std::fs; use std::fs;
@ -222,16 +222,20 @@ impl TsCompiler {
} }
/// Create a new V8 worker with snapshot of TS compiler and setup compiler's runtime. /// Create a new V8 worker with snapshot of TS compiler and setup compiler's runtime.
fn setup_worker(state: ThreadSafeState) -> Worker { fn setup_worker(global_state: ThreadSafeGlobalState) -> Worker {
let worker_state = ThreadSafeState::new(global_state.clone(), None, true)
.expect("Unable to create worker state");
// Count how many times we start the compiler worker. // Count how many times we start the compiler worker.
state.metrics.compiler_starts.fetch_add(1, Ordering::SeqCst); global_state
.metrics
.compiler_starts
.fetch_add(1, Ordering::SeqCst);
let mut worker = Worker::new( let mut worker = Worker::new(
"TS".to_string(), "TS".to_string(),
startup_data::compiler_isolate_init(), startup_data::compiler_isolate_init(),
// TODO(ry) Maybe we should use a separate state for the compiler. worker_state,
// as was done previously.
state.clone(),
); );
worker.execute("denoMain()").unwrap(); worker.execute("denoMain()").unwrap();
worker.execute("workerMain()").unwrap(); worker.execute("workerMain()").unwrap();
@ -241,7 +245,7 @@ impl TsCompiler {
pub fn bundle_async( pub fn bundle_async(
self: &Self, self: &Self,
state: ThreadSafeState, global_state: ThreadSafeGlobalState,
module_name: String, module_name: String,
out_file: String, out_file: String,
) -> impl Future<Item = (), Error = ErrBox> { ) -> impl Future<Item = (), Error = ErrBox> {
@ -253,12 +257,11 @@ impl TsCompiler {
let root_names = vec![module_name.clone()]; let root_names = vec![module_name.clone()];
let req_msg = req(root_names, self.config.clone(), Some(out_file)); let req_msg = req(root_names, self.config.clone(), Some(out_file));
let worker = TsCompiler::setup_worker(state.clone()); let worker = TsCompiler::setup_worker(global_state.clone());
let resource = worker.state.resource.clone(); let worker_ = worker.clone();
let compiler_rid = resource.rid; let first_msg_fut = worker
let first_msg_fut = .post_message(req_msg)
resources::post_message_to_worker(compiler_rid, req_msg) .into_future()
.expect("Bad compiler rid")
.then(move |_| worker) .then(move |_| worker)
.then(move |result| { .then(move |result| {
if let Err(err) = result { if let Err(err) = result {
@ -267,10 +270,7 @@ impl TsCompiler {
std::process::exit(1); std::process::exit(1);
} }
debug!("Sent message to worker"); debug!("Sent message to worker");
let stream_future = worker_.get_message()
resources::get_message_stream_from_worker(compiler_rid)
.into_future();
stream_future.map(|(f, _rest)| f).map_err(|(f, _rest)| f)
}); });
first_msg_fut.map_err(|_| panic!("not handled")).and_then( first_msg_fut.map_err(|_| panic!("not handled")).and_then(
@ -312,7 +312,7 @@ impl TsCompiler {
/// If compilation is required then new V8 worker is spawned with fresh TS compiler. /// If compilation is required then new V8 worker is spawned with fresh TS compiler.
pub fn compile_async( pub fn compile_async(
self: &Self, self: &Self,
state: ThreadSafeState, global_state: ThreadSafeGlobalState,
source_file: &SourceFile, source_file: &SourceFile,
) -> Box<CompiledModuleFuture> { ) -> Box<CompiledModuleFuture> {
if self.has_compiled(&source_file.url) { if self.has_compiled(&source_file.url) {
@ -359,15 +359,16 @@ impl TsCompiler {
let root_names = vec![module_url.to_string()]; let root_names = vec![module_url.to_string()];
let req_msg = req(root_names, self.config.clone(), None); let req_msg = req(root_names, self.config.clone(), None);
let worker = TsCompiler::setup_worker(state.clone()); let worker = TsCompiler::setup_worker(global_state.clone());
let compiling_job = state.progress.add("Compile", &module_url.to_string()); let worker_ = worker.clone();
let state_ = state.clone(); let compiling_job = global_state
.progress
.add("Compile", &module_url.to_string());
let global_state_ = global_state.clone();
let resource = worker.state.resource.clone(); let first_msg_fut = worker
let compiler_rid = resource.rid; .post_message(req_msg)
let first_msg_fut = .into_future()
resources::post_message_to_worker(compiler_rid, req_msg)
.expect("Bad compiler rid")
.then(move |_| worker) .then(move |_| worker)
.then(move |result| { .then(move |result| {
if let Err(err) = result { if let Err(err) = result {
@ -376,10 +377,7 @@ impl TsCompiler {
std::process::exit(1); std::process::exit(1);
} }
debug!("Sent message to worker"); debug!("Sent message to worker");
let stream_future = worker_.get_message()
resources::get_message_stream_from_worker(compiler_rid)
.into_future();
stream_future.map(|(f, _rest)| f).map_err(|(f, _rest)| f)
}); });
let fut = first_msg_fut let fut = first_msg_fut
@ -400,7 +398,7 @@ impl TsCompiler {
.and_then(move |_| { .and_then(move |_| {
// if we are this far it means compilation was successful and we can // if we are this far it means compilation was successful and we can
// load compiled filed from disk // load compiled filed from disk
state_ global_state_
.ts_compiler .ts_compiler
.get_compiled_module(&source_file_.url) .get_compiled_module(&source_file_.url)
.map_err(|e| { .map_err(|e| {
@ -663,7 +661,7 @@ mod tests {
source_code: include_bytes!("../tests/002_hello.ts").to_vec(), source_code: include_bytes!("../tests/002_hello.ts").to_vec(),
}; };
let mock_state = ThreadSafeState::mock(vec![ let mock_state = ThreadSafeGlobalState::mock(vec![
String::from("deno"), String::from("deno"),
String::from("hello.js"), String::from("hello.js"),
]); ]);
@ -696,7 +694,7 @@ mod tests {
.unwrap() .unwrap()
.to_string(); .to_string();
let state = ThreadSafeState::mock(vec![ let state = ThreadSafeGlobalState::mock(vec![
String::from("deno"), String::from("deno"),
p.to_string_lossy().into(), p.to_string_lossy().into(),
String::from("$deno$/bundle.js"), String::from("$deno$/bundle.js"),

247
cli/global_state.rs Normal file
View file

@ -0,0 +1,247 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::compilers::CompiledModule;
use crate::compilers::JsCompiler;
use crate::compilers::JsonCompiler;
use crate::compilers::TsCompiler;
use crate::deno_dir;
use crate::deno_error::permission_denied;
use crate::file_fetcher::SourceFileFetcher;
use crate::flags;
use crate::lockfile::Lockfile;
use crate::metrics::Metrics;
use crate::msg;
use crate::permissions::DenoPermissions;
use crate::progress::Progress;
use deno::ErrBox;
use deno::ModuleSpecifier;
use futures::Future;
use std;
use std::env;
use std::ops::Deref;
use std::str;
use std::sync::Arc;
use std::sync::Mutex;
/// Holds state of the program and can be accessed by V8 isolate.
pub struct ThreadSafeGlobalState(Arc<GlobalState>);
/// This structure represents state of single "deno" program.
///
/// It is shared by all created workers (thus V8 isolates).
#[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub struct GlobalState {
/// Vector of CLI arguments - these are user script arguments, all Deno specific flags are removed.
pub argv: Vec<String>,
/// Flags parsed from `argv` contents.
pub flags: flags::DenoFlags,
/// Entry script parsed from CLI arguments.
pub main_module: Option<ModuleSpecifier>,
/// Permissions parsed from `flags`.
pub permissions: DenoPermissions,
pub dir: deno_dir::DenoDir,
pub metrics: Metrics,
pub progress: Progress,
pub file_fetcher: SourceFileFetcher,
pub js_compiler: JsCompiler,
pub json_compiler: JsonCompiler,
pub ts_compiler: TsCompiler,
pub lockfile: Option<Mutex<Lockfile>>,
}
impl Clone for ThreadSafeGlobalState {
fn clone(&self) -> Self {
ThreadSafeGlobalState(self.0.clone())
}
}
impl Deref for ThreadSafeGlobalState {
type Target = Arc<GlobalState>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ThreadSafeGlobalState {
pub fn new(
flags: flags::DenoFlags,
argv_rest: Vec<String>,
progress: Progress,
) -> Result<Self, ErrBox> {
let custom_root = env::var("DENO_DIR").map(String::into).ok();
let dir = deno_dir::DenoDir::new(custom_root)?;
let file_fetcher = SourceFileFetcher::new(
dir.deps_cache.clone(),
progress.clone(),
!flags.reload,
flags.cache_blacklist.clone(),
flags.no_fetch,
)?;
let ts_compiler = TsCompiler::new(
file_fetcher.clone(),
dir.gen_cache.clone(),
!flags.reload,
flags.config_path.clone(),
)?;
let main_module: Option<ModuleSpecifier> = if argv_rest.len() <= 1 {
None
} else {
let root_specifier = argv_rest[1].clone();
Some(ModuleSpecifier::resolve_url_or_path(&root_specifier)?)
};
// Note: reads lazily from disk on first call to lockfile.check()
let lockfile = if let Some(filename) = &flags.lock {
Some(Mutex::new(Lockfile::new(filename.to_string())))
} else {
None
};
let state = GlobalState {
main_module,
dir,
argv: argv_rest,
permissions: DenoPermissions::from_flags(&flags),
flags,
metrics: Metrics::default(),
progress,
file_fetcher,
ts_compiler,
js_compiler: JsCompiler {},
json_compiler: JsonCompiler {},
lockfile,
};
Ok(ThreadSafeGlobalState(Arc::new(state)))
}
pub fn fetch_compiled_module(
self: &Self,
module_specifier: &ModuleSpecifier,
) -> impl Future<Item = CompiledModule, Error = ErrBox> {
let state1 = self.clone();
let state2 = self.clone();
self
.file_fetcher
.fetch_source_file_async(&module_specifier)
.and_then(move |out| match out.media_type {
msg::MediaType::Unknown => state1.js_compiler.compile_async(&out),
msg::MediaType::Json => state1.json_compiler.compile_async(&out),
msg::MediaType::TypeScript
| msg::MediaType::TSX
| msg::MediaType::JSX => {
state1.ts_compiler.compile_async(state1.clone(), &out)
}
msg::MediaType::JavaScript => {
if state1.ts_compiler.compile_js {
state1.ts_compiler.compile_async(state1.clone(), &out)
} else {
state1.js_compiler.compile_async(&out)
}
}
})
.and_then(move |compiled_module| {
if let Some(ref lockfile) = state2.lockfile {
let mut g = lockfile.lock().unwrap();
if state2.flags.lock_write {
g.insert(&compiled_module);
} else if !g.check(&compiled_module)? {
eprintln!(
"Subresource integrety check failed --lock={}\n{}",
g.filename, compiled_module.name
);
std::process::exit(10);
}
}
Ok(compiled_module)
})
}
#[inline]
pub fn check_read(&self, filename: &str) -> Result<(), ErrBox> {
self.permissions.check_read(filename)
}
#[inline]
pub fn check_write(&self, filename: &str) -> Result<(), ErrBox> {
self.permissions.check_write(filename)
}
#[inline]
pub fn check_env(&self) -> Result<(), ErrBox> {
self.permissions.check_env()
}
#[inline]
pub fn check_net(&self, hostname: &str, port: u16) -> Result<(), ErrBox> {
self.permissions.check_net(hostname, port)
}
#[inline]
pub fn check_net_url(&self, url: &url::Url) -> Result<(), ErrBox> {
self.permissions.check_net_url(url)
}
#[inline]
pub fn check_run(&self) -> Result<(), ErrBox> {
self.permissions.check_run()
}
pub fn check_dyn_import(
self: &Self,
module_specifier: &ModuleSpecifier,
) -> Result<(), ErrBox> {
let u = module_specifier.as_url();
match u.scheme() {
"http" | "https" => {
self.check_net_url(u)?;
Ok(())
}
"file" => {
let filename = u
.to_file_path()
.unwrap()
.into_os_string()
.into_string()
.unwrap();
self.check_read(&filename)?;
Ok(())
}
_ => Err(permission_denied()),
}
}
#[cfg(test)]
pub fn mock(argv: Vec<String>) -> ThreadSafeGlobalState {
ThreadSafeGlobalState::new(
flags::DenoFlags::default(),
argv,
Progress::new(),
)
.unwrap()
}
}
#[test]
fn thread_safe() {
fn f<S: Send + Sync>(_: S) {}
f(ThreadSafeGlobalState::mock(vec![
String::from("./deno"),
String::from("hello.js"),
]));
}
#[test]
fn import_map_given_for_repl() {
let _result = ThreadSafeGlobalState::new(
flags::DenoFlags {
import_map_path: Some("import_map.json".to_string()),
..flags::DenoFlags::default()
},
vec![String::from("./deno")],
Progress::new(),
);
}

View file

@ -65,7 +65,6 @@ export function postMessage(data: any): void {
export async function getMessage(): Promise<any> { export async function getMessage(): Promise<any> {
log("getMessage"); log("getMessage");
const res = await sendAsync(dispatch.OP_WORKER_GET_MESSAGE); const res = await sendAsync(dispatch.OP_WORKER_GET_MESSAGE);
if (res.data != null) { if (res.data != null) {
return decodeMessage(new Uint8Array(res.data)); return decodeMessage(new Uint8Array(res.data));
} else { } else {

View file

@ -15,6 +15,7 @@ extern crate nix;
extern crate rand; extern crate rand;
extern crate serde; extern crate serde;
extern crate serde_derive; extern crate serde_derive;
extern crate tokio;
extern crate url; extern crate url;
mod checksum; mod checksum;
@ -28,12 +29,14 @@ mod file_fetcher;
pub mod flags; pub mod flags;
pub mod fmt_errors; pub mod fmt_errors;
mod fs; mod fs;
mod global_state;
mod global_timer; mod global_timer;
mod http_body; mod http_body;
mod http_util; mod http_util;
mod import_map; mod import_map;
mod js; mod js;
mod lockfile; mod lockfile;
mod metrics;
pub mod msg; pub mod msg;
pub mod ops; pub mod ops;
pub mod permissions; pub mod permissions;
@ -55,6 +58,7 @@ pub mod worker;
use crate::deno_error::js_check; use crate::deno_error::js_check;
use crate::deno_error::print_err_and_exit; use crate::deno_error::print_err_and_exit;
use crate::global_state::ThreadSafeGlobalState;
use crate::progress::Progress; use crate::progress::Progress;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use crate::worker::Worker; use crate::worker::Worker;
@ -97,11 +101,13 @@ impl log::Log for Logger {
fn create_worker_and_state( fn create_worker_and_state(
flags: DenoFlags, flags: DenoFlags,
argv: Vec<String>, argv: Vec<String>,
) -> (Worker, ThreadSafeState) { ) -> (Worker, ThreadSafeGlobalState) {
use crate::shell::Shell; use crate::shell::Shell;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
let shell = Arc::new(Mutex::new(Shell::new())); let shell = Arc::new(Mutex::new(Shell::new()));
let progress = Progress::new(); let progress = Progress::new();
progress.set_callback(move |_done, _completed, _total, status, msg| { progress.set_callback(move |_done, _completed, _total, status, msg| {
if !status.is_empty() { if !status.is_empty() {
@ -109,17 +115,23 @@ fn create_worker_and_state(
s.status(status, msg).expect("shell problem"); s.status(status, msg).expect("shell problem");
} }
}); });
// TODO(kevinkassimo): maybe make include_deno_namespace also configurable?
let state = ThreadSafeState::new(flags, argv, progress, true) let global_state = ThreadSafeGlobalState::new(flags, argv, progress)
.map_err(deno_error::print_err_and_exit) .map_err(deno_error::print_err_and_exit)
.unwrap(); .unwrap();
let worker = Worker::new(
"main".to_string(),
startup_data::deno_isolate_init(),
state.clone(),
);
(worker, state) let state = ThreadSafeState::new(
global_state.clone(),
global_state.main_module.clone(),
true,
)
.map_err(deno_error::print_err_and_exit)
.unwrap();
let worker =
Worker::new("main".to_string(), startup_data::deno_isolate_init(), state);
(worker, global_state)
} }
fn types_command() { fn types_command() {
@ -128,7 +140,7 @@ fn types_command() {
} }
fn print_cache_info(worker: Worker) { fn print_cache_info(worker: Worker) {
let state = worker.state; let state = &worker.state.global_state;
println!( println!(
"{} {:?}", "{} {:?}",
@ -151,10 +163,11 @@ pub fn print_file_info(
worker: Worker, worker: Worker,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
) -> impl Future<Item = Worker, Error = ()> { ) -> impl Future<Item = Worker, Error = ()> {
let global_state_ = worker.state.global_state.clone();
let state_ = worker.state.clone(); let state_ = worker.state.clone();
let module_specifier_ = module_specifier.clone(); let module_specifier_ = module_specifier.clone();
state_ global_state_
.file_fetcher .file_fetcher
.fetch_source_file_async(&module_specifier) .fetch_source_file_async(&module_specifier)
.map_err(|err| println!("{}", err)) .map_err(|err| println!("{}", err))
@ -171,7 +184,7 @@ pub fn print_file_info(
msg::enum_name_media_type(out.media_type) msg::enum_name_media_type(out.media_type)
); );
state_ global_state_
.clone() .clone()
.fetch_compiled_module(&module_specifier_) .fetch_compiled_module(&module_specifier_)
.map_err(|e| { .map_err(|e| {
@ -182,9 +195,9 @@ pub fn print_file_info(
.and_then(move |compiled| { .and_then(move |compiled| {
if out.media_type == msg::MediaType::TypeScript if out.media_type == msg::MediaType::TypeScript
|| (out.media_type == msg::MediaType::JavaScript || (out.media_type == msg::MediaType::JavaScript
&& state_.ts_compiler.compile_js) && global_state_.ts_compiler.compile_js)
{ {
let compiled_source_file = state_ let compiled_source_file = global_state_
.ts_compiler .ts_compiler
.get_compiled_source_file(&out.url) .get_compiled_source_file(&out.url)
.unwrap(); .unwrap();
@ -196,7 +209,7 @@ pub fn print_file_info(
); );
} }
if let Ok(source_map) = state_ if let Ok(source_map) = global_state_
.clone() .clone()
.ts_compiler .ts_compiler
.get_source_map_file(&module_specifier_) .get_source_map_file(&module_specifier_)
@ -209,7 +222,7 @@ pub fn print_file_info(
} }
if let Some(deps) = if let Some(deps) =
worker.state.modules.lock().unwrap().deps(&compiled.name) state_.modules.lock().unwrap().deps(&compiled.name)
{ {
println!("{}{}", colors::bold("deps:\n".to_string()), deps.name); println!("{}{}", colors::bold("deps:\n".to_string()), deps.name);
if let Some(ref depsdeps) = deps.deps { if let Some(ref depsdeps) = deps.deps {
@ -236,7 +249,7 @@ fn info_command(flags: DenoFlags, argv: Vec<String>) {
return print_cache_info(worker); return print_cache_info(worker);
} }
let main_module = state.main_module().unwrap(); let main_module = state.main_module.as_ref().unwrap().clone();
let main_future = lazy(move || { let main_future = lazy(move || {
// Setup runtime. // Setup runtime.
js_check(worker.execute("denoMain()")); js_check(worker.execute("denoMain()"));
@ -259,7 +272,7 @@ fn info_command(flags: DenoFlags, argv: Vec<String>) {
fn fetch_command(flags: DenoFlags, argv: Vec<String>) { fn fetch_command(flags: DenoFlags, argv: Vec<String>) {
let (mut worker, state) = create_worker_and_state(flags, argv.clone()); let (mut worker, state) = create_worker_and_state(flags, argv.clone());
let main_module = state.main_module().unwrap(); let main_module = state.main_module.as_ref().unwrap().clone();
let main_future = lazy(move || { let main_future = lazy(move || {
// Setup runtime. // Setup runtime.
js_check(worker.execute("denoMain()")); js_check(worker.execute("denoMain()"));
@ -307,7 +320,7 @@ fn eval_command(flags: DenoFlags, argv: Vec<String>) {
fn bundle_command(flags: DenoFlags, argv: Vec<String>) { fn bundle_command(flags: DenoFlags, argv: Vec<String>) {
let (worker, state) = create_worker_and_state(flags, argv); let (worker, state) = create_worker_and_state(flags, argv);
let main_module = state.main_module().unwrap(); let main_module = state.main_module.as_ref().unwrap().clone();
assert!(state.argv.len() >= 3); assert!(state.argv.len() >= 3);
let out_file = state.argv[2].clone(); let out_file = state.argv[2].clone();
debug!(">>>>> bundle_async START"); debug!(">>>>> bundle_async START");
@ -353,7 +366,7 @@ fn run_script(flags: DenoFlags, argv: Vec<String>) {
let use_current_thread = flags.current_thread; let use_current_thread = flags.current_thread;
let (mut worker, state) = create_worker_and_state(flags, argv); let (mut worker, state) = create_worker_and_state(flags, argv);
let main_module = state.main_module().unwrap(); let main_module = state.main_module.as_ref().unwrap().clone();
// Normal situation of executing a module. // Normal situation of executing a module.
let main_future = lazy(move || { let main_future = lazy(move || {
// Setup runtime. // Setup runtime.

12
cli/metrics.rs Normal file
View file

@ -0,0 +1,12 @@
use std::sync::atomic::AtomicUsize;
#[derive(Default)]
pub struct Metrics {
pub ops_dispatched: AtomicUsize,
pub ops_completed: AtomicUsize,
pub bytes_sent_control: AtomicUsize,
pub bytes_sent_data: AtomicUsize,
pub bytes_received: AtomicUsize,
pub resolve_count: AtomicUsize,
pub compiler_starts: AtomicUsize,
}

View file

@ -4,6 +4,7 @@ use crate::futures::future::join_all;
use crate::futures::Future; use crate::futures::Future;
use crate::ops::json_op; use crate::ops::json_op;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use deno::Loader;
use deno::*; use deno::*;
pub fn init(i: &mut Isolate, s: &ThreadSafeState) { pub fn init(i: &mut Isolate, s: &ThreadSafeState) {
@ -36,7 +37,7 @@ fn op_cache(
let module_specifier = ModuleSpecifier::resolve_url(&args.module_id) let module_specifier = ModuleSpecifier::resolve_url(&args.module_id)
.expect("Should be valid module specifier"); .expect("Should be valid module specifier");
state.ts_compiler.cache_compiler_output( state.global_state.ts_compiler.cache_compiler_output(
&module_specifier, &module_specifier,
&args.extension, &args.extension,
&args.contents, &args.contents,
@ -67,6 +68,7 @@ fn op_fetch_source_files(
let resolved_specifier = let resolved_specifier =
state.resolve(specifier, &args.referrer, false, is_dyn_import)?; state.resolve(specifier, &args.referrer, false, is_dyn_import)?;
let fut = state let fut = state
.global_state
.file_fetcher .file_fetcher
.fetch_source_file_async(&resolved_specifier); .fetch_source_file_async(&resolved_specifier);
futures.push(fut); futures.push(fut);

View file

@ -30,7 +30,7 @@ fn op_format_error(
_zero_copy: Option<PinnedBuf>, _zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> { ) -> Result<JsonOp, ErrBox> {
let args: FormatErrorArgs = serde_json::from_value(args)?; let args: FormatErrorArgs = serde_json::from_value(args)?;
let error = JSError::from_json(&args.error, &state.ts_compiler); let error = JSError::from_json(&args.error, &state.global_state.ts_compiler);
Ok(JsonOp::Sync(json!({ Ok(JsonOp::Sync(json!({
"error": error.to_string(), "error": error.to_string(),
@ -57,7 +57,7 @@ fn op_apply_source_map(
args.line.into(), args.line.into(),
args.column.into(), args.column.into(),
&mut mappings_map, &mut mappings_map,
&state.ts_compiler, &state.global_state.ts_compiler,
); );
Ok(JsonOp::Sync(json!({ Ok(JsonOp::Sync(json!({

View file

@ -7,7 +7,6 @@ use crate::state::ThreadSafeState;
use crate::version; use crate::version;
use atty; use atty;
use deno::*; use deno::*;
use log;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use sys_info; use sys_info;
@ -40,16 +39,15 @@ fn op_start(
_args: Value, _args: Value,
_zero_copy: Option<PinnedBuf>, _zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> { ) -> Result<JsonOp, ErrBox> {
let gs = &state.global_state;
Ok(JsonOp::Sync(json!({ Ok(JsonOp::Sync(json!({
"cwd": deno_fs::normalize_path(&env::current_dir().unwrap()), "cwd": deno_fs::normalize_path(&env::current_dir().unwrap()),
"pid": std::process::id(), "pid": std::process::id(),
"argv": state.argv, "argv": gs.argv,
"mainModule": state.main_module().map(|x| x.as_str().to_string()), "mainModule": gs.main_module.as_ref().map(|x| x.to_string()),
"debugFlag": state "debugFlag": gs.flags.log_level.map_or(false, |l| l == log::Level::Debug),
.flags "versionFlag": gs.flags.version,
.log_level
.map_or(false, |l| l == log::Level::Debug),
"versionFlag": state.flags.version,
"v8Version": version::v8(), "v8Version": version::v8(),
"denoVersion": version::DENO, "denoVersion": version::DENO,
"tsVersion": version::TYPESCRIPT, "tsVersion": version::TYPESCRIPT,

View file

@ -44,7 +44,8 @@ fn op_repl_start(
let args: ReplStartArgs = serde_json::from_value(args)?; let args: ReplStartArgs = serde_json::from_value(args)?;
debug!("op_repl_start {}", args.history_file); debug!("op_repl_start {}", args.history_file);
let history_path = repl::history_path(&state.dir, &args.history_file); let history_path =
repl::history_path(&state.global_state.dir, &args.history_file);
let repl = repl::Repl::new(history_path); let repl = repl::Repl::new(history_path);
let resource = ReplResource(Arc::new(Mutex::new(repl))); let resource = ReplResource(Arc::new(Mutex::new(repl)));
let mut table = resources::lock_resource_table(); let mut table = resources::lock_resource_table();

View file

@ -4,7 +4,6 @@ use crate::deno_error::js_check;
use crate::deno_error::DenoError; use crate::deno_error::DenoError;
use crate::deno_error::ErrorKind; use crate::deno_error::ErrorKind;
use crate::ops::json_op; use crate::ops::json_op;
use crate::resources;
use crate::startup_data; use crate::startup_data;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use crate::worker::Worker; use crate::worker::Worker;
@ -12,6 +11,7 @@ use deno::*;
use futures; use futures;
use futures::Async; use futures::Async;
use futures::Future; use futures::Future;
use futures::IntoFuture;
use futures::Sink; use futures::Sink;
use futures::Stream; use futures::Stream;
use std; use std;
@ -48,18 +48,17 @@ pub fn init(i: &mut Isolate, s: &ThreadSafeState) {
} }
struct GetMessageFuture { struct GetMessageFuture {
pub state: ThreadSafeState, state: ThreadSafeState,
} }
impl Future for GetMessageFuture { impl Future for GetMessageFuture {
type Item = Option<Buf>; type Item = Option<Buf>;
type Error = (); type Error = ErrBox;
fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> { fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
let mut wc = self.state.worker_channels.lock().unwrap(); let mut channels = self.state.worker_channels.lock().unwrap();
wc.1 let receiver = &mut channels.receiver;
.poll() receiver.poll().map_err(ErrBox::from)
.map_err(|err| panic!("worker_channel recv err {:?}", err))
} }
} }
@ -93,12 +92,10 @@ fn op_worker_post_message(
data: Option<PinnedBuf>, data: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> { ) -> Result<JsonOp, ErrBox> {
let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice(); let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice();
let mut channels = state.worker_channels.lock().unwrap();
let tx = { let sender = &mut channels.sender;
let wc = state.worker_channels.lock().unwrap(); sender
wc.0.clone() .send(d)
};
tx.send(d)
.wait() .wait()
.map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?; .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?;
@ -132,28 +129,23 @@ fn op_create_worker(
let parent_state = state.clone(); let parent_state = state.clone();
// TODO(bartlomieju): Isn't this wrong?
let mut module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?; let mut module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;
let mut child_argv = parent_state.argv.clone();
if !has_source_code { if !has_source_code {
if let Some(module) = state.main_module() { if let Some(referrer) = parent_state.main_module.as_ref() {
module_specifier = let referrer = referrer.clone().to_string();
ModuleSpecifier::resolve_import(specifier, &module.to_string())?; module_specifier = ModuleSpecifier::resolve_import(specifier, &referrer)?;
child_argv[1] = module_specifier.to_string();
} }
} }
let child_state = ThreadSafeState::new( let child_state = ThreadSafeState::new(
parent_state.flags.clone(), state.global_state.clone(),
child_argv, Some(module_specifier.clone()),
parent_state.progress.clone(),
include_deno_namespace, include_deno_namespace,
)?; )?;
let rid = child_state.resource.rid; let rid = child_state.rid;
let name = format!("USER-WORKER-{}", specifier); let name = format!("USER-WORKER-{}", specifier);
let deno_main_call = format!("denoMain({})", include_deno_namespace); let deno_main_call = format!("denoMain({})", include_deno_namespace);
let mut worker = let mut worker =
Worker::new(name, startup_data::deno_isolate_init(), child_state); Worker::new(name, startup_data::deno_isolate_init(), child_state);
js_check(worker.execute(&deno_main_call)); js_check(worker.execute(&deno_main_call));
@ -201,9 +193,8 @@ fn op_host_get_worker_closed(
worker.clone() worker.clone()
}; };
let op = Box::new( let op =
shared_worker_future.then(move |_result| futures::future::ok(json!({}))), shared_worker_future.then(move |_result| futures::future::ok(json!({})));
);
Ok(JsonOp::Async(Box::new(op))) Ok(JsonOp::Async(Box::new(op)))
} }
@ -222,7 +213,7 @@ fn op_host_get_message(
let args: HostGetMessageArgs = serde_json::from_value(args)?; let args: HostGetMessageArgs = serde_json::from_value(args)?;
let rid = args.rid as u32; let rid = args.rid as u32;
let op = resources::get_message_from_worker(rid) let op = Worker::get_message_from_resource(rid)
.map_err(move |_| -> ErrBox { unimplemented!() }) .map_err(move |_| -> ErrBox { unimplemented!() })
.and_then(move |maybe_buf| { .and_then(move |maybe_buf| {
futures::future::ok(json!({ futures::future::ok(json!({
@ -250,7 +241,9 @@ fn op_host_post_message(
let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice(); let d = Vec::from(data.unwrap().as_ref()).into_boxed_slice();
resources::post_message_to_worker(rid, d)? // TODO: rename to post_message_to_child(rid, d)
Worker::post_message_to_resource(rid, d)
.into_future()
.wait() .wait()
.map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?; .map_err(|e| DenoError::new(ErrorKind::Other, e.to_string()))?;

View file

@ -52,7 +52,7 @@ impl fmt::Display for PermissionAccessorState {
} }
} }
#[derive(Debug)] #[derive(Clone, Debug)]
pub struct PermissionAccessor { pub struct PermissionAccessor {
state: Arc<AtomicUsize>, state: Arc<AtomicUsize>,
} }
@ -110,7 +110,7 @@ impl Default for PermissionAccessor {
} }
} }
#[derive(Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct DenoPermissions { pub struct DenoPermissions {
// Keep in sync with cli/js/permissions.ts // Keep in sync with cli/js/permissions.ts
pub allow_read: PermissionAccessor, pub allow_read: PermissionAccessor,

View file

@ -11,9 +11,6 @@
use crate::deno_error; use crate::deno_error;
use crate::deno_error::bad_resource; use crate::deno_error::bad_resource;
use crate::http_body::HttpBody; use crate::http_body::HttpBody;
use crate::state::WorkerChannels;
use deno::Buf;
use deno::ErrBox; use deno::ErrBox;
pub use deno::Resource as CoreResource; pub use deno::Resource as CoreResource;
pub use deno::ResourceId; pub use deno::ResourceId;
@ -22,8 +19,6 @@ use deno::ResourceTable;
use futures; use futures;
use futures::Future; use futures::Future;
use futures::Poll; use futures::Poll;
use futures::Sink;
use futures::Stream;
use reqwest::r#async::Decoder as ReqwestDecoder; use reqwest::r#async::Decoder as ReqwestDecoder;
use std; use std;
use std::io::{Error, Read, Seek, SeekFrom, Write}; use std::io::{Error, Read, Seek, SeekFrom, Write};
@ -34,7 +29,6 @@ use std::sync::MutexGuard;
use tokio; use tokio;
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tokio_process; use tokio_process;
use tokio_rustls::client::TlsStream as ClientTlsStream; use tokio_rustls::client::TlsStream as ClientTlsStream;
use tokio_rustls::server::TlsStream as ServerTlsStream; use tokio_rustls::server::TlsStream as ServerTlsStream;
@ -101,7 +95,6 @@ enum CliResource {
ChildStdin(tokio_process::ChildStdin), ChildStdin(tokio_process::ChildStdin),
ChildStdout(tokio_process::ChildStdout), ChildStdout(tokio_process::ChildStdout),
ChildStderr(tokio_process::ChildStderr), ChildStderr(tokio_process::ChildStderr),
Worker(WorkerChannels),
} }
impl CoreResource for CliResource { impl CoreResource for CliResource {
@ -133,7 +126,6 @@ impl CoreResource for CliResource {
CliResource::ChildStdin(_) => "childStdin", CliResource::ChildStdin(_) => "childStdin",
CliResource::ChildStdout(_) => "childStdout", CliResource::ChildStdout(_) => "childStdout",
CliResource::ChildStderr(_) => "childStderr", CliResource::ChildStderr(_) => "childStderr",
CliResource::Worker(_) => "worker",
} }
} }
} }
@ -372,78 +364,6 @@ pub fn add_reqwest_body(body: ReqwestDecoder) -> Resource {
Resource { rid } Resource { rid }
} }
pub fn add_worker(wc: WorkerChannels) -> Resource {
let mut table = lock_resource_table();
let rid = table.add(Box::new(CliResource::Worker(wc)));
Resource { rid }
}
/// Post message to worker as a host or privilged overlord
pub fn post_message_to_worker(
rid: ResourceId,
buf: Buf,
) -> Result<futures::sink::Send<mpsc::Sender<Buf>>, ErrBox> {
let mut table = lock_resource_table();
let repr = table.get_mut::<CliResource>(rid).ok_or_else(bad_resource)?;
match repr {
CliResource::Worker(ref mut wc) => {
let sender = wc.0.clone();
Ok(sender.send(buf))
}
_ => Err(bad_resource()),
}
}
pub struct WorkerReceiver {
rid: ResourceId,
}
// Invert the dumbness that tokio_process causes by making Child itself a future.
impl Future for WorkerReceiver {
type Item = Option<Buf>;
type Error = ErrBox;
fn poll(&mut self) -> Poll<Option<Buf>, ErrBox> {
let mut table = lock_resource_table();
let repr = table
.get_mut::<CliResource>(self.rid)
.ok_or_else(bad_resource)?;
match repr {
CliResource::Worker(ref mut wc) => wc.1.poll().map_err(ErrBox::from),
_ => Err(bad_resource()),
}
}
}
pub fn get_message_from_worker(rid: ResourceId) -> WorkerReceiver {
WorkerReceiver { rid }
}
pub struct WorkerReceiverStream {
rid: ResourceId,
}
// Invert the dumbness that tokio_process causes by making Child itself a future.
impl Stream for WorkerReceiverStream {
type Item = Buf;
type Error = ErrBox;
fn poll(&mut self) -> Poll<Option<Buf>, ErrBox> {
let mut table = lock_resource_table();
let repr = table
.get_mut::<CliResource>(self.rid)
.ok_or_else(bad_resource)?;
match repr {
CliResource::Worker(ref mut wc) => wc.1.poll().map_err(ErrBox::from),
_ => Err(bad_resource()),
}
}
}
pub fn get_message_stream_from_worker(rid: ResourceId) -> WorkerReceiverStream {
WorkerReceiverStream { rid }
}
pub struct ChildResources { pub struct ChildResources {
pub child_rid: Option<ResourceId>, pub child_rid: Option<ResourceId>,
pub stdin_rid: Option<ResourceId>, pub stdin_rid: Option<ResourceId>,

View file

@ -1,22 +1,15 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::compilers::CompiledModule;
use crate::compilers::JsCompiler;
use crate::compilers::JsonCompiler;
use crate::compilers::TsCompiler;
use crate::deno_dir;
use crate::deno_error::permission_denied; use crate::deno_error::permission_denied;
use crate::file_fetcher::SourceFileFetcher; use crate::global_state::ThreadSafeGlobalState;
use crate::flags;
use crate::global_timer::GlobalTimer; use crate::global_timer::GlobalTimer;
use crate::import_map::ImportMap; use crate::import_map::ImportMap;
use crate::lockfile::Lockfile; use crate::metrics::Metrics;
use crate::msg;
use crate::ops::JsonOp; use crate::ops::JsonOp;
use crate::permissions::DenoPermissions; use crate::permissions::DenoPermissions;
use crate::progress::Progress;
use crate::resources; use crate::resources;
use crate::resources::ResourceId; use crate::resources::ResourceId;
use crate::worker::Worker; use crate::worker::Worker;
use crate::worker::WorkerChannels;
use deno::Buf; use deno::Buf;
use deno::CoreOp; use deno::CoreOp;
use deno::ErrBox; use deno::ErrBox;
@ -31,31 +24,18 @@ use rand::SeedableRng;
use serde_json::Value; use serde_json::Value;
use std; use std;
use std::collections::HashMap; use std::collections::HashMap;
use std::env;
use std::ops::Deref; use std::ops::Deref;
use std::str; use std::str;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::Ordering;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::Instant; use std::time::Instant;
use tokio::sync::mpsc as async_mpsc; use tokio::sync::mpsc as async_mpsc;
pub type WorkerSender = async_mpsc::Sender<Buf>; // TODO: hold references to concrete Workers instead of shared futures of
pub type WorkerReceiver = async_mpsc::Receiver<Buf>; // those workers?
pub type WorkerChannels = (WorkerSender, WorkerReceiver);
pub type UserWorkerTable = HashMap<ResourceId, Shared<Worker>>; pub type UserWorkerTable = HashMap<ResourceId, Shared<Worker>>;
#[derive(Default)]
pub struct Metrics {
pub ops_dispatched: AtomicUsize,
pub ops_completed: AtomicUsize,
pub bytes_sent_control: AtomicUsize,
pub bytes_sent_data: AtomicUsize,
pub bytes_received: AtomicUsize,
pub resolve_count: AtomicUsize,
pub compiler_starts: AtomicUsize,
}
/// Isolate cannot be passed between threads but ThreadSafeState can. /// Isolate cannot be passed between threads but ThreadSafeState can.
/// ThreadSafeState satisfies Send and Sync. So any state that needs to be /// ThreadSafeState satisfies Send and Sync. So any state that needs to be
/// accessed outside the main V8 thread should be inside ThreadSafeState. /// accessed outside the main V8 thread should be inside ThreadSafeState.
@ -63,34 +43,22 @@ pub struct ThreadSafeState(Arc<State>);
#[cfg_attr(feature = "cargo-clippy", allow(stutter))] #[cfg_attr(feature = "cargo-clippy", allow(stutter))]
pub struct State { pub struct State {
pub global_state: ThreadSafeGlobalState,
pub modules: Arc<Mutex<deno::Modules>>, pub modules: Arc<Mutex<deno::Modules>>,
pub main_module: Option<ModuleSpecifier>,
pub dir: deno_dir::DenoDir,
pub argv: Vec<String>,
pub permissions: DenoPermissions, pub permissions: DenoPermissions,
pub flags: flags::DenoFlags, pub main_module: Option<ModuleSpecifier>,
pub worker_channels: Mutex<WorkerChannels>,
/// When flags contains a `.import_map_path` option, the content of the /// When flags contains a `.import_map_path` option, the content of the
/// import map file will be resolved and set. /// import map file will be resolved and set.
pub import_map: Option<ImportMap>, pub import_map: Option<ImportMap>,
pub metrics: Metrics, pub metrics: Metrics,
pub worker_channels: Mutex<WorkerChannels>,
pub global_timer: Mutex<GlobalTimer>, pub global_timer: Mutex<GlobalTimer>,
pub workers: Mutex<UserWorkerTable>, pub workers: Mutex<UserWorkerTable>,
pub start_time: Instant, pub start_time: Instant,
/// A reference to this worker's resource. /// A reference to this worker's resource.
pub resource: resources::Resource, pub rid: ResourceId,
/// Reference to global progress bar.
pub progress: Progress,
pub seeded_rng: Option<Mutex<StdRng>>, pub seeded_rng: Option<Mutex<StdRng>>,
pub file_fetcher: SourceFileFetcher,
pub js_compiler: JsCompiler,
pub json_compiler: JsonCompiler,
pub ts_compiler: TsCompiler,
pub include_deno_namespace: bool, pub include_deno_namespace: bool,
pub lockfile: Option<Mutex<Lockfile>>,
} }
impl Clone for ThreadSafeState { impl Clone for ThreadSafeState {
@ -195,158 +163,74 @@ impl Loader for ThreadSafeState {
) -> Box<deno::SourceCodeInfoFuture> { ) -> Box<deno::SourceCodeInfoFuture> {
self.metrics.resolve_count.fetch_add(1, Ordering::SeqCst); self.metrics.resolve_count.fetch_add(1, Ordering::SeqCst);
let module_url_specified = module_specifier.to_string(); let module_url_specified = module_specifier.to_string();
Box::new(self.fetch_compiled_module(module_specifier).map( let fut = self
|compiled_module| deno::SourceCodeInfo { .global_state
.fetch_compiled_module(module_specifier)
.map(|compiled_module| deno::SourceCodeInfo {
// Real module name, might be different from initial specifier // Real module name, might be different from initial specifier
// due to redirections. // due to redirections.
code: compiled_module.code, code: compiled_module.code,
module_url_specified, module_url_specified,
module_url_found: compiled_module.name, module_url_found: compiled_module.name,
}, });
))
Box::new(fut)
} }
} }
impl ThreadSafeState { impl ThreadSafeState {
pub fn new( pub fn new(
flags: flags::DenoFlags, global_state: ThreadSafeGlobalState,
argv_rest: Vec<String>, main_module: Option<ModuleSpecifier>,
progress: Progress,
include_deno_namespace: bool, include_deno_namespace: bool,
) -> Result<Self, ErrBox> { ) -> Result<Self, ErrBox> {
let custom_root = env::var("DENO_DIR").map(String::into).ok();
let (worker_in_tx, worker_in_rx) = async_mpsc::channel::<Buf>(1); let (worker_in_tx, worker_in_rx) = async_mpsc::channel::<Buf>(1);
let (worker_out_tx, worker_out_rx) = async_mpsc::channel::<Buf>(1); let (worker_out_tx, worker_out_rx) = async_mpsc::channel::<Buf>(1);
let internal_channels = (worker_out_tx, worker_in_rx); let internal_channels = WorkerChannels {
let external_channels = (worker_in_tx, worker_out_rx); sender: worker_out_tx,
let resource = resources::add_worker(external_channels); receiver: worker_in_rx,
};
let dir = deno_dir::DenoDir::new(custom_root)?; let external_channels = WorkerChannels {
sender: worker_in_tx,
let file_fetcher = SourceFileFetcher::new( receiver: worker_out_rx,
dir.deps_cache.clone(),
progress.clone(),
!flags.reload,
flags.cache_blacklist.clone(),
flags.no_fetch,
)?;
let ts_compiler = TsCompiler::new(
file_fetcher.clone(),
dir.gen_cache.clone(),
!flags.reload,
flags.config_path.clone(),
)?;
let main_module: Option<ModuleSpecifier> = if argv_rest.len() <= 1 {
None
} else {
let root_specifier = argv_rest[1].clone();
Some(ModuleSpecifier::resolve_url_or_path(&root_specifier)?)
}; };
let import_map: Option<ImportMap> = match &flags.import_map_path { let mut table = resources::lock_resource_table();
let rid = table.add(Box::new(external_channels));
let import_map: Option<ImportMap> =
match global_state.flags.import_map_path.as_ref() {
None => None, None => None,
Some(file_path) => Some(ImportMap::load(file_path)?), Some(file_path) => Some(ImportMap::load(file_path)?),
}; };
let mut seeded_rng = None; let seeded_rng = match global_state.flags.seed {
if let Some(seed) = flags.seed { Some(seed) => Some(Mutex::new(StdRng::seed_from_u64(seed))),
seeded_rng = Some(Mutex::new(StdRng::seed_from_u64(seed))); None => None,
}; };
let modules = Arc::new(Mutex::new(deno::Modules::new())); let modules = Arc::new(Mutex::new(deno::Modules::new()));
let permissions = global_state.permissions.clone();
// Note: reads lazily from disk on first call to lockfile.check()
let lockfile = if let Some(filename) = &flags.lock {
Some(Mutex::new(Lockfile::new(filename.to_string())))
} else {
None
};
let state = State { let state = State {
main_module, global_state,
modules, modules,
dir, main_module,
argv: argv_rest, permissions,
permissions: DenoPermissions::from_flags(&flags),
flags,
import_map, import_map,
metrics: Metrics::default(),
worker_channels: Mutex::new(internal_channels), worker_channels: Mutex::new(internal_channels),
metrics: Metrics::default(),
global_timer: Mutex::new(GlobalTimer::new()), global_timer: Mutex::new(GlobalTimer::new()),
workers: Mutex::new(UserWorkerTable::new()), workers: Mutex::new(UserWorkerTable::new()),
start_time: Instant::now(), start_time: Instant::now(),
resource, rid,
progress,
seeded_rng, seeded_rng,
file_fetcher,
ts_compiler,
js_compiler: JsCompiler {},
json_compiler: JsonCompiler {},
include_deno_namespace, include_deno_namespace,
lockfile,
}; };
Ok(ThreadSafeState(Arc::new(state))) Ok(ThreadSafeState(Arc::new(state)))
} }
pub fn fetch_compiled_module(
self: &Self,
module_specifier: &ModuleSpecifier,
) -> impl Future<Item = CompiledModule, Error = ErrBox> {
let state1 = self.clone();
let state2 = self.clone();
self
.file_fetcher
.fetch_source_file_async(&module_specifier)
.and_then(move |out| match out.media_type {
msg::MediaType::Unknown => {
state1.js_compiler.compile_async(state1.clone(), &out)
}
msg::MediaType::Json => {
state1.json_compiler.compile_async(state1.clone(), &out)
}
msg::MediaType::TypeScript
| msg::MediaType::TSX
| msg::MediaType::JSX => {
state1.ts_compiler.compile_async(state1.clone(), &out)
}
msg::MediaType::JavaScript => {
if state1.ts_compiler.compile_js {
state1.ts_compiler.compile_async(state1.clone(), &out)
} else {
state1.js_compiler.compile_async(state1.clone(), &out)
}
}
})
.and_then(move |compiled_module| {
if let Some(ref lockfile) = state2.lockfile {
let mut g = lockfile.lock().unwrap();
if state2.flags.lock_write {
g.insert(&compiled_module);
} else if !g.check(&compiled_module)? {
eprintln!(
"Subresource integrety check failed --lock={}\n{}",
g.filename, compiled_module.name
);
std::process::exit(10);
}
}
Ok(compiled_module)
})
}
/// Read main module from argv
pub fn main_module(&self) -> Option<ModuleSpecifier> {
match &self.main_module {
Some(module_specifier) => Some(module_specifier.clone()),
None => None,
}
}
#[inline] #[inline]
pub fn check_read(&self, filename: &str) -> Result<(), ErrBox> { pub fn check_read(&self, filename: &str) -> Result<(), ErrBox> {
self.permissions.check_read(filename) self.permissions.check_read(filename)
@ -403,10 +287,17 @@ impl ThreadSafeState {
#[cfg(test)] #[cfg(test)]
pub fn mock(argv: Vec<String>) -> ThreadSafeState { pub fn mock(argv: Vec<String>) -> ThreadSafeState {
let module_specifier = if argv.is_empty() {
None
} else {
let module_specifier = ModuleSpecifier::resolve_url_or_path(&argv[0])
.expect("Invalid entry module");
Some(module_specifier)
};
ThreadSafeState::new( ThreadSafeState::new(
flags::DenoFlags::default(), ThreadSafeGlobalState::mock(argv),
argv, module_specifier,
Progress::new(),
true, true,
) )
.unwrap() .unwrap()
@ -445,16 +336,3 @@ fn thread_safe() {
String::from("hello.js"), String::from("hello.js"),
])); ]));
} }
#[test]
fn import_map_given_for_repl() {
let _result = ThreadSafeState::new(
flags::DenoFlags {
import_map_path: Some("import_map.json".to_string()),
..flags::DenoFlags::default()
},
vec![String::from("./deno")],
Progress::new(),
true,
);
}

View file

@ -1,33 +1,57 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use crate::deno_error::bad_resource;
use crate::fmt_errors::JSError; use crate::fmt_errors::JSError;
use crate::ops; use crate::ops;
use crate::resources;
use crate::resources::CoreResource;
use crate::resources::ResourceId;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use deno; use deno;
use deno::Buf;
use deno::ErrBox; use deno::ErrBox;
use deno::ModuleSpecifier; use deno::ModuleSpecifier;
use deno::RecursiveLoad; use deno::RecursiveLoad;
use deno::StartupData; use deno::StartupData;
use futures::Async; use futures::Async;
use futures::Future; use futures::Future;
use futures::Poll;
use futures::Sink;
use futures::Stream;
use std::env; use std::env;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use tokio::sync::mpsc;
use url::Url; use url::Url;
/// Wraps mpsc channels into a generic resource so they can be referenced
/// from ops and used to facilitate parent-child communication
/// for workers.
pub struct WorkerChannels {
pub sender: mpsc::Sender<Buf>,
pub receiver: mpsc::Receiver<Buf>,
}
impl CoreResource for WorkerChannels {
fn inspect_repr(&self) -> &str {
"worker"
}
}
/// Wraps deno::Isolate to provide source maps, ops for the CLI, and /// Wraps deno::Isolate to provide source maps, ops for the CLI, and
/// high-level module loading /// high-level module loading.
#[derive(Clone)] #[derive(Clone)]
pub struct Worker { pub struct Worker {
pub name: String,
isolate: Arc<Mutex<deno::Isolate>>, isolate: Arc<Mutex<deno::Isolate>>,
pub state: ThreadSafeState, pub state: ThreadSafeState,
} }
impl Worker { impl Worker {
pub fn new( pub fn new(
_name: String, name: String,
startup_data: StartupData, startup_data: StartupData,
state: ThreadSafeState, state: ThreadSafeState,
) -> Worker { ) -> Self {
let isolate = Arc::new(Mutex::new(deno::Isolate::new(startup_data, false))); let isolate = Arc::new(Mutex::new(deno::Isolate::new(startup_data, false)));
{ {
let mut i = isolate.lock().unwrap(); let mut i = isolate.lock().unwrap();
@ -61,12 +85,16 @@ impl Worker {
Box::new(load_stream) Box::new(load_stream)
}); });
let state_ = state.clone(); let global_state_ = state.global_state.clone();
i.set_js_error_create(move |v8_exception| { i.set_js_error_create(move |v8_exception| {
JSError::from_v8_exception(v8_exception, &state_.ts_compiler) JSError::from_v8_exception(v8_exception, &global_state_.ts_compiler)
}) })
} }
Self { isolate, state } Self {
name,
isolate,
state,
}
} }
/// Same as execute2() but the filename defaults to "$CWD/__anonymous__". /// Same as execute2() but the filename defaults to "$CWD/__anonymous__".
@ -106,7 +134,7 @@ impl Worker {
) )
.get_future(isolate); .get_future(isolate);
recursive_load.and_then(move |id| -> Result<(), ErrBox> { recursive_load.and_then(move |id| -> Result<(), ErrBox> {
worker.state.progress.done(); worker.state.global_state.progress.done();
if is_prefetch { if is_prefetch {
Ok(()) Ok(())
} else { } else {
@ -115,6 +143,37 @@ impl Worker {
} }
}) })
} }
/// Post message to worker as a host or privileged overlord
pub fn post_message(self: &Self, buf: Buf) -> Result<Async<()>, ErrBox> {
Worker::post_message_to_resource(self.state.rid, buf)
}
pub fn post_message_to_resource(
rid: resources::ResourceId,
buf: Buf,
) -> Result<Async<()>, ErrBox> {
debug!("post message to resource {}", rid);
let mut table = resources::lock_resource_table();
let worker = table
.get_mut::<WorkerChannels>(rid)
.ok_or_else(bad_resource)?;
let sender = &mut worker.sender;
sender
.send(buf)
.poll()
.map(|_| Async::Ready(()))
.map_err(ErrBox::from)
}
pub fn get_message(self: &Self) -> WorkerReceiver {
Worker::get_message_from_resource(self.state.rid)
}
pub fn get_message_from_resource(rid: ResourceId) -> WorkerReceiver {
debug!("get message from resource {}", rid);
WorkerReceiver { rid }
}
} }
impl Future for Worker { impl Future for Worker {
@ -127,16 +186,39 @@ impl Future for Worker {
} }
} }
/// This structure wraps worker's resource id to implement future
/// that will return message received from worker or None
/// if worker's channel has been closed.
pub struct WorkerReceiver {
rid: ResourceId,
}
impl Future for WorkerReceiver {
type Item = Option<Buf>;
type Error = ErrBox;
fn poll(&mut self) -> Poll<Option<Buf>, ErrBox> {
let mut table = resources::lock_resource_table();
let worker = table
.get_mut::<WorkerChannels>(self.rid)
.ok_or_else(bad_resource)?;
let receiver = &mut worker.receiver;
receiver.poll().map_err(ErrBox::from)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::flags; use crate::flags;
use crate::flags::DenoFlags;
use crate::global_state::ThreadSafeGlobalState;
use crate::progress::Progress; use crate::progress::Progress;
use crate::resources;
use crate::startup_data; use crate::startup_data;
use crate::state::ThreadSafeState; use crate::state::ThreadSafeState;
use crate::tokio_util; use crate::tokio_util;
use futures::future::lazy; use futures::future::lazy;
use futures::IntoFuture;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
#[test] #[test]
@ -149,13 +231,15 @@ mod tests {
let module_specifier = let module_specifier =
ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap();
let argv = vec![String::from("./deno"), module_specifier.to_string()]; let argv = vec![String::from("./deno"), module_specifier.to_string()];
let state = ThreadSafeState::new( let global_state = ThreadSafeGlobalState::new(
flags::DenoFlags::default(), flags::DenoFlags::default(),
argv, argv,
Progress::new(), Progress::new(),
true,
) )
.unwrap(); .unwrap();
let state =
ThreadSafeState::new(global_state, Some(module_specifier.clone()), true)
.unwrap();
let state_ = state.clone(); let state_ = state.clone();
tokio_util::run(lazy(move || { tokio_util::run(lazy(move || {
let mut worker = let mut worker =
@ -186,12 +270,11 @@ mod tests {
let module_specifier = let module_specifier =
ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap(); ModuleSpecifier::resolve_url_or_path(&p.to_string_lossy()).unwrap();
let argv = vec![String::from("deno"), module_specifier.to_string()]; let argv = vec![String::from("deno"), module_specifier.to_string()];
let state = ThreadSafeState::new( let global_state =
flags::DenoFlags::default(), ThreadSafeGlobalState::new(DenoFlags::default(), argv, Progress::new())
argv, .unwrap();
Progress::new(), let state =
true, ThreadSafeState::new(global_state, Some(module_specifier.clone()), true)
)
.unwrap(); .unwrap();
let state_ = state.clone(); let state_ = state.clone();
tokio_util::run(lazy(move || { tokio_util::run(lazy(move || {
@ -227,8 +310,15 @@ mod tests {
let argv = vec![String::from("deno"), module_specifier.to_string()]; let argv = vec![String::from("deno"), module_specifier.to_string()];
let mut flags = flags::DenoFlags::default(); let mut flags = flags::DenoFlags::default();
flags.reload = true; flags.reload = true;
let state = let global_state =
ThreadSafeState::new(flags, argv, Progress::new(), true).unwrap(); ThreadSafeGlobalState::new(flags, argv, Progress::new()).unwrap();
let state = ThreadSafeState::new(
global_state.clone(),
Some(module_specifier.clone()),
true,
)
.unwrap();
let global_state_ = global_state.clone();
let state_ = state.clone(); let state_ = state.clone();
tokio_util::run(lazy(move || { tokio_util::run(lazy(move || {
let mut worker = Worker::new( let mut worker = Worker::new(
@ -247,10 +337,12 @@ mod tests {
}) })
})); }));
let metrics = &state_.metrics; assert_eq!(state_.metrics.resolve_count.load(Ordering::SeqCst), 3);
assert_eq!(metrics.resolve_count.load(Ordering::SeqCst), 3);
// Check that we've only invoked the compiler once. // Check that we've only invoked the compiler once.
assert_eq!(metrics.compiler_starts.load(Ordering::SeqCst), 1); assert_eq!(
global_state_.metrics.compiler_starts.load(Ordering::SeqCst),
1
);
drop(http_server_guard); drop(http_server_guard);
} }
@ -285,8 +377,9 @@ mod tests {
"#; "#;
worker.execute(source).unwrap(); worker.execute(source).unwrap();
let resource = worker.state.resource.clone(); let worker_ = worker.clone();
let resource_ = resource.clone(); let rid = worker.state.rid;
let resource_ = resources::Resource { rid };
tokio::spawn(lazy(move || { tokio::spawn(lazy(move || {
worker.then(move |r| -> Result<(), ()> { worker.then(move |r| -> Result<(), ()> {
@ -298,14 +391,10 @@ mod tests {
let msg = json!("hi").to_string().into_boxed_str().into_boxed_bytes(); let msg = json!("hi").to_string().into_boxed_str().into_boxed_bytes();
let r = resources::post_message_to_worker(resource.rid, msg) let r = worker_.post_message(msg).into_future().wait();
.expect("Bad resource")
.wait();
assert!(r.is_ok()); assert!(r.is_ok());
let maybe_msg = resources::get_message_from_worker(resource.rid) let maybe_msg = worker_.get_message().wait().unwrap();
.wait()
.unwrap();
assert!(maybe_msg.is_some()); assert!(maybe_msg.is_some());
// Check if message received is [1, 2, 3] in json // Check if message received is [1, 2, 3] in json
assert_eq!(*maybe_msg.unwrap(), *b"[1,2,3]"); assert_eq!(*maybe_msg.unwrap(), *b"[1,2,3]");
@ -314,9 +403,7 @@ mod tests {
.to_string() .to_string()
.into_boxed_str() .into_boxed_str()
.into_boxed_bytes(); .into_boxed_bytes();
let r = resources::post_message_to_worker(resource.rid, msg) let r = worker_.post_message(msg).into_future().wait();
.expect("Bad resource")
.wait();
assert!(r.is_ok()); assert!(r.is_ok());
}) })
} }
@ -329,8 +416,9 @@ mod tests {
.execute("onmessage = () => { delete window.onmessage; }") .execute("onmessage = () => { delete window.onmessage; }")
.unwrap(); .unwrap();
let resource = worker.state.resource.clone(); let rid = worker.state.rid;
let rid = resource.rid; let resource = resources::Resource { rid };
let worker_ = worker.clone();
let worker_future = worker let worker_future = worker
.then(move |r| -> Result<(), ()> { .then(move |r| -> Result<(), ()> {
@ -345,9 +433,7 @@ mod tests {
tokio::spawn(lazy(move || worker_future_.then(|_| Ok(())))); tokio::spawn(lazy(move || worker_future_.then(|_| Ok(()))));
let msg = json!("hi").to_string().into_boxed_str().into_boxed_bytes(); let msg = json!("hi").to_string().into_boxed_str().into_boxed_bytes();
let r = resources::post_message_to_worker(rid, msg) let r = worker_.post_message(msg).into_future().wait();
.expect("Bad resource")
.wait();
assert!(r.is_ok()); assert!(r.is_ok());
debug!("rid {:?}", rid); debug!("rid {:?}", rid);