mirror of
https://github.com/denoland/deno.git
synced 2025-01-03 04:48:52 -05:00
refactor: use a single Mutex in ProcState for module graph (#12489)
This commit factors out 4 different fields from "ProcState", that are behind "Arc<Mutex<>>" into a single struct behind a single mutex.
This commit is contained in:
parent
d77a4f1d43
commit
f83c756aa0
2 changed files with 123 additions and 99 deletions
|
@ -548,7 +548,7 @@ async fn cache_command(
|
|||
Permissions::allow_all(),
|
||||
)
|
||||
.await?;
|
||||
if let Some(graph_error) = ps.maybe_graph_error.lock().take() {
|
||||
if let Some(graph_error) = ps.take_graph_error() {
|
||||
return Err(graph_error.into());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,31 +52,35 @@ use std::sync::Arc;
|
|||
#[derive(Clone)]
|
||||
pub struct ProcState(Arc<Inner>);
|
||||
|
||||
#[derive(Default)]
|
||||
struct GraphData {
|
||||
modules: HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>,
|
||||
// because the graph detects resolution issues early, but is build and dropped
|
||||
// during the `prepare_module_load` method, we need to extract out the module
|
||||
// resolution map so that those errors can be surfaced at the appropriate time
|
||||
resolution_map:
|
||||
HashMap<ModuleSpecifier, HashMap<String, deno_graph::Resolved>>,
|
||||
// in some cases we want to provide the span where the resolution error
|
||||
// occurred but need to surface it on load, but on load we don't know who the
|
||||
// referrer and span was, so we need to cache those
|
||||
resolved_map: HashMap<ModuleSpecifier, deno_graph::Span>,
|
||||
// deno_graph detects all sorts of issues at build time (prepare_module_load)
|
||||
// but if they are errors at that stage, the don't cause the correct behaviors
|
||||
// so we cache the error and then surface it when appropriate (e.g. load)
|
||||
maybe_graph_error: Option<deno_graph::ModuleGraphError>,
|
||||
}
|
||||
|
||||
pub struct Inner {
|
||||
/// Flags parsed from `argv` contents.
|
||||
pub flags: flags::Flags,
|
||||
pub dir: deno_dir::DenoDir,
|
||||
pub coverage_dir: Option<String>,
|
||||
pub file_fetcher: FileFetcher,
|
||||
modules: Arc<Mutex<HashMap<ModuleSpecifier, Result<ModuleSource, AnyError>>>>,
|
||||
graph_data: Arc<Mutex<GraphData>>,
|
||||
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
pub maybe_config_file: Option<ConfigFile>,
|
||||
pub maybe_import_map: Option<ImportMap>,
|
||||
pub maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||
// deno_graph detects all sorts of issues at build time (prepare_module_load)
|
||||
// but if they are errors at that stage, the don't cause the correct behaviors
|
||||
// so we cache the error and then surface it when appropriate (e.g. load)
|
||||
pub(crate) maybe_graph_error:
|
||||
Arc<Mutex<Option<deno_graph::ModuleGraphError>>>,
|
||||
// because the graph detects resolution issues early, but is build and dropped
|
||||
// during the `prepare_module_load` method, we need to extract out the module
|
||||
// resolution map so that those errors can be surfaced at the appropriate time
|
||||
resolution_map:
|
||||
Arc<Mutex<HashMap<ModuleSpecifier, HashMap<String, deno_graph::Resolved>>>>,
|
||||
// in some cases we want to provide the span where the resolution error
|
||||
// occurred but need to surface it on load, but on load we don't know who the
|
||||
// referrer and span was, so we need to cache those
|
||||
resolved_map: Arc<Mutex<HashMap<ModuleSpecifier, deno_graph::Span>>>,
|
||||
pub root_cert_store: Option<RootCertStore>,
|
||||
pub blob_store: BlobStore,
|
||||
pub broadcast_channel: InMemoryBroadcastChannel,
|
||||
|
@ -233,14 +237,11 @@ impl ProcState {
|
|||
coverage_dir,
|
||||
flags,
|
||||
file_fetcher,
|
||||
modules: Default::default(),
|
||||
graph_data: Default::default(),
|
||||
lockfile,
|
||||
maybe_config_file,
|
||||
maybe_import_map,
|
||||
maybe_inspector_server,
|
||||
maybe_graph_error: Default::default(),
|
||||
resolution_map: Default::default(),
|
||||
resolved_map: Default::default(),
|
||||
root_cert_store: Some(root_cert_store.clone()),
|
||||
blob_store,
|
||||
broadcast_channel,
|
||||
|
@ -249,6 +250,12 @@ impl ProcState {
|
|||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn take_graph_error(
|
||||
&self,
|
||||
) -> Option<deno_graph::ModuleGraphError> {
|
||||
self.graph_data.lock().maybe_graph_error.take()
|
||||
}
|
||||
|
||||
/// Return any imports that should be brought into the scope of the module
|
||||
/// graph.
|
||||
fn get_maybe_imports(&self) -> Option<Vec<(ModuleSpecifier, Vec<String>)>> {
|
||||
|
@ -325,8 +332,8 @@ impl ProcState {
|
|||
// Determine any modules that have already been emitted this session and
|
||||
// should be skipped.
|
||||
let reload_exclusions: HashSet<ModuleSpecifier> = {
|
||||
let modules = self.modules.lock();
|
||||
modules.keys().cloned().collect()
|
||||
let graph_data = self.graph_data.lock();
|
||||
graph_data.modules.keys().cloned().collect()
|
||||
};
|
||||
|
||||
let config_type = if self.flags.no_check {
|
||||
|
@ -409,26 +416,23 @@ impl ProcState {
|
|||
}
|
||||
}
|
||||
|
||||
// we iterate over the graph, looking for any modules that were emitted, or
|
||||
// should be loaded as their un-emitted source and add them to the in memory
|
||||
// cache of modules for loading by deno_core.
|
||||
{
|
||||
let mut modules = self.modules.lock();
|
||||
modules.extend(emit::to_module_sources(graph.as_ref(), &cache));
|
||||
}
|
||||
let mut graph_data = self.graph_data.lock();
|
||||
// we iterate over the graph, looking for any modules that were emitted, or
|
||||
// should be loaded as their un-emitted source and add them to the in memory
|
||||
// cache of modules for loading by deno_core.
|
||||
graph_data
|
||||
.modules
|
||||
.extend(emit::to_module_sources(graph.as_ref(), &cache));
|
||||
|
||||
// since we can't store the graph in proc state, because proc state needs to
|
||||
// be thread safe because of the need to provide source map resolution and
|
||||
// the graph needs to not be thread safe (due to wasmbind_gen constraints),
|
||||
// we have no choice but to extract out other meta data from the graph to
|
||||
// provide the correct loading behaviors for CLI
|
||||
{
|
||||
let mut resolution_map = self.resolution_map.lock();
|
||||
resolution_map.extend(graph.resolution_map());
|
||||
}
|
||||
{
|
||||
let mut self_maybe_graph_error = self.maybe_graph_error.lock();
|
||||
*self_maybe_graph_error = maybe_graph_error;
|
||||
// since we can't store the graph in proc state, because proc state needs to
|
||||
// be thread safe because of the need to provide source map resolution and
|
||||
// the graph needs to not be thread safe (due to wasmbind_gen constraints),
|
||||
// we have no choice but to extract out other meta data from the graph to
|
||||
// provide the correct loading behaviors for CLI
|
||||
graph_data.resolution_map.extend(graph.resolution_map());
|
||||
|
||||
graph_data.maybe_graph_error = maybe_graph_error;
|
||||
}
|
||||
|
||||
// any updates to the lockfile should be updated now
|
||||
|
@ -445,17 +449,22 @@ impl ProcState {
|
|||
specifier: &str,
|
||||
referrer: &str,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
let resolution_map = self.resolution_map.lock();
|
||||
if let Some((_, Some(map))) = deno_core::resolve_url_or_path(referrer)
|
||||
.ok()
|
||||
.map(|s| (s.clone(), resolution_map.get(&s)))
|
||||
{
|
||||
if let Some(resolved) = map.get(specifier) {
|
||||
if let Ok(s) = deno_core::resolve_url_or_path(referrer) {
|
||||
let maybe_resolved = {
|
||||
let graph_data = self.graph_data.lock();
|
||||
let resolved_specifier = graph_data
|
||||
.resolution_map
|
||||
.get(&s)
|
||||
.and_then(|map| map.get(specifier));
|
||||
resolved_specifier.cloned()
|
||||
};
|
||||
|
||||
if let Some(resolved) = maybe_resolved {
|
||||
match resolved {
|
||||
Some(Ok((specifier, span))) => {
|
||||
let mut resolved_map = self.resolved_map.lock();
|
||||
resolved_map.insert(specifier.clone(), span.clone());
|
||||
return Ok(specifier.clone());
|
||||
let mut graph_data = self.graph_data.lock();
|
||||
graph_data.resolved_map.insert(specifier.clone(), span);
|
||||
return Ok(specifier);
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
return Err(custom_error(
|
||||
|
@ -467,6 +476,7 @@ impl ProcState {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(bartlomieju): hacky way to provide compatibility with repl
|
||||
let referrer = if referrer.is_empty() && self.flags.repl {
|
||||
deno_core::DUMMY_SPECIFIER
|
||||
|
@ -497,60 +507,74 @@ impl ProcState {
|
|||
.unwrap_or_else(|| "<none>".to_string()),
|
||||
is_dynamic
|
||||
);
|
||||
let modules = self.modules.lock();
|
||||
modules
|
||||
.get(&specifier)
|
||||
.map(|r| match r {
|
||||
Ok(module_source) => Ok(module_source.clone()),
|
||||
Err(err) => {
|
||||
// this is the "pending" error we will return
|
||||
let err = if let Some(error_class) = get_custom_error_class(err) {
|
||||
if error_class == "NotFound" && maybe_referrer.is_some() && !is_dynamic {
|
||||
let resolved_map = self.resolved_map.lock();
|
||||
// in situations where we were to try to load a module that wasn't
|
||||
// emitted and we can't run the original source code (it isn't)
|
||||
// JavaScript, we will load a blank module instead. This is
|
||||
// usually caused by people exporting type only exports and not
|
||||
// type checking.
|
||||
if let Some(span) = resolved_map.get(&specifier) {
|
||||
log::warn!("{}: Cannot load module \"{}\".\n at {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", colors::yellow("warning"), specifier, span);
|
||||
return Ok(ModuleSource {
|
||||
code: "".to_string(),
|
||||
module_url_found: specifier.to_string(),
|
||||
module_url_specified: specifier.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
custom_error(error_class, err.to_string())
|
||||
} else {
|
||||
anyhow!(err.to_string())
|
||||
};
|
||||
// if there is a pending graph error though we haven't returned, we
|
||||
// will return that one
|
||||
let mut maybe_graph_error = self.maybe_graph_error.lock();
|
||||
if let Some(graph_error) = maybe_graph_error.take() {
|
||||
log::debug!("returning cached graph error");
|
||||
let resolved_map = self.resolved_map.lock();
|
||||
if let Some(span) = resolved_map.get(&specifier) {
|
||||
if !span.specifier.as_str().contains("$deno") {
|
||||
return Err(custom_error(get_module_graph_error_class(&graph_error), format!("{}\n at {}", graph_error, span)));
|
||||
}
|
||||
}
|
||||
Err(graph_error.into())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
|
||||
{
|
||||
let graph_data = self.graph_data.lock();
|
||||
if let Some(module_result) = graph_data.modules.get(&specifier) {
|
||||
if let Ok(module_source) = module_result {
|
||||
return Ok(module_source.clone());
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
} else {
|
||||
if maybe_referrer.is_some() && !is_dynamic {
|
||||
let resolved_map = self.resolved_map.lock();
|
||||
if let Some(span) = resolved_map.get(&specifier) {
|
||||
return Err(custom_error("NotFound", format!("Cannot load module \"{}\".\n at {}", specifier, span)));
|
||||
if let Some(span) = graph_data.resolved_map.get(&specifier) {
|
||||
return Err(custom_error(
|
||||
"NotFound",
|
||||
format!("Cannot load module \"{}\".\n at {}", specifier, span),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(custom_error("NotFound", format!("Cannot load module \"{}\".", specifier)))
|
||||
})
|
||||
return Err(custom_error(
|
||||
"NotFound",
|
||||
format!("Cannot load module \"{}\".", specifier),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// If we're this far it means that there was an error for this module load.
|
||||
let mut graph_data = self.graph_data.lock();
|
||||
let err = graph_data
|
||||
.modules
|
||||
.get(&specifier)
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap_err();
|
||||
// this is the "pending" error we will return
|
||||
let err = if let Some(error_class) = get_custom_error_class(err) {
|
||||
if error_class == "NotFound" && maybe_referrer.is_some() && !is_dynamic {
|
||||
// in situations where we were to try to load a module that wasn't
|
||||
// emitted and we can't run the original source code (it isn't)
|
||||
// JavaScript, we will load a blank module instead. This is
|
||||
// usually caused by people exporting type only exports and not
|
||||
// type checking.
|
||||
if let Some(span) = graph_data.resolved_map.get(&specifier) {
|
||||
log::warn!("{}: Cannot load module \"{}\".\n at {}\n If the source module contains only types, use `import type` and `export type` to import it instead.", colors::yellow("warning"), specifier, span);
|
||||
return Ok(ModuleSource {
|
||||
code: "".to_string(),
|
||||
module_url_found: specifier.to_string(),
|
||||
module_url_specified: specifier.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
custom_error(error_class, err.to_string())
|
||||
} else {
|
||||
anyhow!(err.to_string())
|
||||
};
|
||||
// if there is a pending graph error though we haven't returned, we
|
||||
// will return that one
|
||||
if let Some(graph_error) = graph_data.maybe_graph_error.take() {
|
||||
log::debug!("returning cached graph error");
|
||||
if let Some(span) = graph_data.resolved_map.get(&specifier) {
|
||||
if !span.specifier.as_str().contains("$deno") {
|
||||
return Err(custom_error(
|
||||
get_module_graph_error_class(&graph_error),
|
||||
format!("{}\n at {}", graph_error, span),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(graph_error.into())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@kitsonk) this should be refactored to get it from the module graph
|
||||
|
|
Loading…
Reference in a new issue