summaryrefslogtreecommitdiff
path: root/cli/graph_util.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-02-24 14:42:45 -0500
committerGitHub <noreply@github.com>2023-02-24 14:42:45 -0500
commit9aebc8bc19b1dfd9bce1789018f3f6b784175171 (patch)
tree760daa99df7479e34195a4339401a3f8f55c450e /cli/graph_util.rs
parenta27d0885f489f5640e38922fad8c8a1c49ae0aa4 (diff)
fix: ensure concurrent non-statically analyzable dynamic imports do not sometimes fail (#17923)
Closes #17918
Diffstat (limited to 'cli/graph_util.rs')
-rw-r--r--cli/graph_util.rs111
1 files changed, 111 insertions, 0 deletions
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index 27c5f1e0a..dc07e4b6a 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -3,6 +3,7 @@
use crate::args::CliOptions;
use crate::args::Lockfile;
use crate::args::TsConfigType;
+use crate::args::TsTypeLib;
use crate::args::TypeCheckMode;
use crate::cache;
use crate::cache::TypeCheckCache;
@@ -16,6 +17,7 @@ use crate::tools::check;
use deno_core::anyhow::bail;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
+use deno_core::parking_lot::RwLock;
use deno_core::ModuleSpecifier;
use deno_graph::Module;
use deno_graph::ModuleGraph;
@@ -24,7 +26,11 @@ use deno_graph::ResolutionError;
use deno_graph::SpecifierError;
use deno_runtime::permissions::PermissionsContainer;
use import_map::ImportMapError;
+use std::collections::HashMap;
+use std::collections::HashSet;
use std::sync::Arc;
+use tokio::sync::Semaphore;
+use tokio::sync::SemaphorePermit;
#[derive(Clone, Copy)]
pub struct GraphValidOptions {
@@ -309,6 +315,111 @@ fn get_resolution_error_bare_specifier(
}
}
+#[derive(Default, Debug)]
+struct GraphData {
+ graph: Arc<ModuleGraph>,
+ checked_libs: HashMap<TsTypeLib, HashSet<ModuleSpecifier>>,
+}
+
+/// Holds the `ModuleGraph` and what parts of it are type checked.
+#[derive(Clone)]
+pub struct ModuleGraphContainer {
+ update_semaphore: Arc<Semaphore>,
+ graph_data: Arc<RwLock<GraphData>>,
+}
+
+impl Default for ModuleGraphContainer {
+ fn default() -> Self {
+ Self {
+ update_semaphore: Arc::new(Semaphore::new(1)),
+ graph_data: Default::default(),
+ }
+ }
+}
+
+impl ModuleGraphContainer {
+ /// Acquires a permit to modify the module graph without other code
+ /// having the chance to modify it. In the meantime, other code may
+ /// still read from the existing module graph.
+ pub async fn acquire_update_permit(&self) -> ModuleGraphUpdatePermit {
+ let permit = self.update_semaphore.acquire().await.unwrap();
+ ModuleGraphUpdatePermit {
+ permit,
+ graph_data: self.graph_data.clone(),
+ graph: (*self.graph_data.read().graph).clone(),
+ }
+ }
+
+ pub fn graph(&self) -> Arc<ModuleGraph> {
+ self.graph_data.read().graph.clone()
+ }
+
+ /// Mark `roots` and all of their dependencies as type checked under `lib`.
+ /// Assumes that all of those modules are known.
+ pub fn set_type_checked(&self, roots: &[ModuleSpecifier], lib: TsTypeLib) {
+ // It's ok to analyze and update this while the module graph itself is
+ // being updated in a permit because the module graph update is always
+ // additive and this will be a subset of the original graph
+ let graph = self.graph();
+ let entries = graph.walk(
+ roots,
+ deno_graph::WalkOptions {
+ check_js: true,
+ follow_dynamic: true,
+ follow_type_only: true,
+ },
+ );
+
+ // now update
+ let mut data = self.graph_data.write();
+ let checked_lib_set = data.checked_libs.entry(lib).or_default();
+ for (specifier, _) in entries {
+ checked_lib_set.insert(specifier.clone());
+ }
+ }
+
+ /// Check if `roots` are all marked as type checked under `lib`.
+ pub fn is_type_checked(
+ &self,
+ roots: &[ModuleSpecifier],
+ lib: TsTypeLib,
+ ) -> bool {
+ let data = self.graph_data.read();
+ match data.checked_libs.get(&lib) {
+ Some(checked_lib_set) => roots.iter().all(|r| {
+ let found = data.graph.resolve(r);
+ checked_lib_set.contains(&found)
+ }),
+ None => false,
+ }
+ }
+}
+
+/// A permit for updating the module graph. When complete and
+/// everything looks fine, calling `.commit()` will store the
+/// new graph in the ModuleGraphContainer.
+pub struct ModuleGraphUpdatePermit<'a> {
+ permit: SemaphorePermit<'a>,
+ graph_data: Arc<RwLock<GraphData>>,
+ graph: ModuleGraph,
+}
+
+impl<'a> ModuleGraphUpdatePermit<'a> {
+ /// Gets the module graph for mutation.
+ pub fn graph_mut(&mut self) -> &mut ModuleGraph {
+ &mut self.graph
+ }
+
+ /// Saves the mutated module graph in the container
+ /// and returns an Arc to the new module graph.
+ pub fn commit(self) -> Arc<ModuleGraph> {
+ let graph = Arc::new(self.graph);
+ self.graph_data.write().graph = graph.clone();
+ drop(self.permit); // explicit drop for clarity
+ graph
+ }
+}
+
#[cfg(test)]
mod test {
use std::sync::Arc;