Move lots of things into Env

This commit is contained in:
Richard Feldman 2020-07-28 21:43:00 -04:00
parent 4ebe36fa4a
commit 931c558b5a

View file

@ -1,4 +1,7 @@
use bumpalo::Bump; use bumpalo::Bump;
use crossbeam::channel::{unbounded, Receiver, Sender};
use crossbeam::deque::{Injector, Steal, Stealer, Worker};
use crossbeam::thread::{self, Scope};
use roc_builtins::std::{Mode, StdLib}; use roc_builtins::std::{Mode, StdLib};
use roc_can::constraint::Constraint; use roc_can::constraint::Constraint;
use roc_can::def::Declaration; use roc_can::def::Declaration;
@ -12,8 +15,6 @@ use roc_module::ident::{Ident, ModuleName};
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol}; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol};
use roc_parse::ast::{self, Attempting, ExposesEntry, ImportsEntry}; use roc_parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
use roc_parse::module::module_defs; use roc_parse::module::module_defs;
use crossbeam::deque::{Injector, Steal, Stealer, Worker};
use std::iter;
use roc_parse::parser::{Fail, Parser, State}; use roc_parse::parser::{Fail, Parser, State};
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_solve::module::SolvedModule; use roc_solve::module::SolvedModule;
@ -23,11 +24,10 @@ use roc_types::subs::{Subs, VarStore, Variable};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fs; use std::fs;
use std::io; use std::io;
use std::iter;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::from_utf8_unchecked; use std::str::from_utf8_unchecked;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use tokio::task::spawn_blocking;
/// Filename extension for normal Roc modules /// Filename extension for normal Roc modules
const ROC_FILE_EXTENSION: &str = "roc"; const ROC_FILE_EXTENSION: &str = "roc";
@ -47,11 +47,6 @@ pub struct LoadedModule {
pub src: Box<str>, pub src: Box<str>,
} }
#[derive(Debug, Clone)]
struct Env {
pub src_dir: PathBuf,
}
#[derive(Debug)] #[derive(Debug)]
pub enum BuildProblem<'a> { pub enum BuildProblem<'a> {
FileNotFound(&'a Path), FileNotFound(&'a Path),
@ -112,8 +107,8 @@ enum MaybeShared<'a, 'b, A, B> {
type SharedModules<'a, 'b> = MaybeShared<'a, 'b, ModuleIds, IdentIdsByModule>; type SharedModules<'a, 'b> = MaybeShared<'a, 'b, ModuleIds, IdentIdsByModule>;
type IdentIdsByModule = MutMap<ModuleId, IdentIds>; type IdentIdsByModule = MutMap<ModuleId, IdentIds>;
type MsgSender = mpsc::Sender<Msg>; type MsgSender = Sender<Msg>;
type MsgReceiver = mpsc::Receiver<Msg>; type MsgReceiver = Receiver<Msg>;
/// The loading process works like this, starting from the given filename (e.g. "main.roc"): /// The loading process works like this, starting from the given filename (e.g. "main.roc"):
/// ///
@ -167,7 +162,7 @@ pub fn load<'a>(
) -> Result<LoadedModule, LoadingProblem> { ) -> Result<LoadedModule, LoadingProblem> {
use self::MaybeShared::*; use self::MaybeShared::*;
let (msg_tx, msg_rx): (MsgSender, MsgReceiver) = mpsc::channel(1024); let (msg_tx, msg_rx): (MsgSender, MsgReceiver) = unbounded();
let mut module_ids = ModuleIds::default(); let mut module_ids = ModuleIds::default();
let mut root_exposed_ident_ids: IdentIdsByModule = IdentIds::exposed_builtins(0); let mut root_exposed_ident_ids: IdentIdsByModule = IdentIds::exposed_builtins(0);
@ -178,10 +173,80 @@ pub fn load<'a>(
Unique(&mut module_ids, &mut root_exposed_ident_ids), Unique(&mut module_ids, &mut root_exposed_ident_ids),
)?; )?;
load_deps(root_id, msg_tx, msg_rx, stdlib, src_dir, module_ids, root_exposed_ident_ids, exposed_types).await load_deps(
root_id,
msg_tx,
msg_rx,
stdlib,
src_dir,
module_ids,
root_exposed_ident_ids,
exposed_types,
)
} }
async fn load_deps<'a>( #[derive(Debug)]
struct Env {
pub root_id: ModuleId,
pub src_dir: PathBuf,
pub exposed_types: SubsByModule,
pub can_problems: Vec<roc_problem::can::Problem>,
pub headers_parsed: MutSet<ModuleId>,
pub type_problems: Vec<solve::TypeError>,
/// This is the "final" list of IdentIds, after canonicalization and constraint gen
/// have completed for a given module.
pub constrained_ident_ids: MutMap<ModuleId, IdentIds>,
/// From now on, these will be used by multiple threads; time to make an Arc<Mutex<_>>!
pub arc_modules: Arc<Mutex<ModuleIds>>,
pub ident_ids_by_module: Arc<Mutex<IdentIdsByModule>>,
/// All the dependent modules we've already begun loading -
/// meaning we should never kick off another load_module on them!
pub loading_started: MutSet<ModuleId>,
pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>,
pub exposed_symbols_by_module: MutMap<ModuleId, MutSet<Symbol>>,
/// Modules which are waiting for certain headers to be parsed
pub waiting_for_headers: MutMap<ModuleId, MutSet<ModuleId>>,
// When the key ModuleId gets solved, iterate through each of the given modules
// and remove that ModuleId from the appropriate waiting_for_headers entry.
// If the relevant module's waiting_for_headers entry is now empty, canonicalize the module.
pub header_listeners: MutMap<ModuleId, Vec<ModuleId>>,
pub unparsed_modules: MutMap<ModuleId, ModuleHeader>,
// Modules which are waiting for certain deps to be solved
pub waiting_for_solve: MutMap<ModuleId, MutSet<ModuleId>>,
// When the key ModuleId gets solved, iterate through each of the given modules
// and remove that ModuleId from the appropriate waiting_for_solve entry.
// If the relevant module's waiting_for_solve entry is now empty, solve the module.
pub solve_listeners: MutMap<ModuleId, Vec<ModuleId>>,
#[allow(clippy::type_complexity)]
pub unsolved_modules: MutMap<
ModuleId,
(Module, Box<str>, MutSet<ModuleId>, Constraint, VarStore),
>,
/// Idle worker threads will try to take from this first, and if it's empty,
/// steal from each other.
pub main_task_queue: Injector<BuildTask>,
}
enum BuildTask {
NoOp
}
fn load_deps<'a>(
root_id: ModuleId, root_id: ModuleId,
msg_tx: MsgSender, msg_tx: MsgSender,
mut msg_rx: MsgReceiver, mut msg_rx: MsgReceiver,
@ -191,327 +256,340 @@ async fn load_deps<'a>(
root_exposed_ident_ids: IdentIdsByModule, root_exposed_ident_ids: IdentIdsByModule,
mut exposed_types: SubsByModule, mut exposed_types: SubsByModule,
) -> Result<LoadedModule, LoadingProblem> { ) -> Result<LoadedModule, LoadingProblem> {
thread::scope(|scope| {
let mut headers_parsed = MutSet::default();
// We've already parsed the root's header. (But only its header, so far.)
headers_parsed.insert(root_id);
let mut loading_started = MutSet::default();
// If the root module we're still processing happens to be an interface,
// it's possible that something else will import it. That will
// necessarily cause a cyclic import error, but in the meantime
// we still shouldn't load it.
loading_started.insert(root_id);
let env = Env {
root_id,
src_dir,
exposed_types,
headers_parsed,
loading_started,
can_problems: Vec::new(),
type_problems: Vec::new(),
arc_modules: Arc::new(Mutex::new(module_ids)),
constrained_ident_ids: IdentIds::exposed_builtins(0),
ident_ids_by_module: Arc::new(Mutex::new(root_exposed_ident_ids)),
declarations_by_id: MutMap::default(),
exposed_symbols_by_module: MutMap::default(),
waiting_for_headers: MutMap::default(),
header_listeners: MutMap::default(),
unparsed_modules: MutMap::default(),
waiting_for_solve: MutMap::default(),
solve_listeners: MutMap::default(),
unsolved_modules: MutMap::default(),
main_task_queue: Injector::new()
};
// The root module will have already queued up messages to process,
// and processing those messages will in turn queue up more messages.
while let Ok(msg) = msg_rx.recv() {
match update(env, stdlib, msg_tx, msg_rx, msg) {
Ok(Some(loaded_module)) => {
// We're done!
return Ok(loaded_module);
}
Ok(None) => {
// We have more messages to process. Keep waiting!
}
Err(loading_problem) => {
// We hit an unrecoverable LoadingProblem. Bail out!
return Err(loading_problem);
}
}
}
// The msg_rx receiver closed unexpectedly before we finished solving everything
Err(LoadingProblem::MsgChannelDied)
})
.unwrap()
}
fn update(mut env: Env, stdlib: &StdLib, msg_tx: MsgSender, mut msg_rx: MsgReceiver, msg: Msg) -> Result<Option<LoadedModule>, LoadingProblem> {
use self::MaybeShared::*; use self::MaybeShared::*;
use self::Msg::*;
let mut type_problems = Vec::new(); let Env {
let mut can_problems = Vec::new(); root_id,
let mut headers_parsed = MutSet::default(); src_dir,
exposed_types,
can_problems,
type_problems,
headers_parsed,
arc_modules,
constrained_ident_ids,
ident_ids_by_module,
loading_started,
declarations_by_id,
exposed_symbols_by_module,
waiting_for_headers,
header_listeners,
unparsed_modules,
waiting_for_solve,
solve_listeners,
unsolved_modules,
main_task_queue,
} = env;
headers_parsed.insert(root_id); match msg {
Header(header) => {
let home = header.module_id;
let deps_by_name = &header.deps_by_name;
let mut headers_needed =
HashSet::with_capacity_and_hasher(deps_by_name.len(), default_hasher());
let env = Env { headers_parsed.insert(home);
src_dir: src_dir.clone(),
};
// This is the "final" list of IdentIds, after canonicalization and constraint gen for dep_id in deps_by_name.values() {
// have completed for a given module. if !headers_parsed.contains(&dep_id) {
let mut constrained_ident_ids = IdentIds::exposed_builtins(0); headers_needed.insert(*dep_id);
}
}
// From now on, these will be used by multiple threads; time to make an Arc<Mutex<_>>! // This was a dependency. Write it down and keep processing messaages.
let arc_modules = Arc::new(Mutex::new(module_ids)); let mut exposed_symbols: MutSet<Symbol> =
let ident_ids_by_module: Arc<Mutex<IdentIdsByModule>> = HashSet::with_capacity_and_hasher(header.exposes.len(), default_hasher());
Arc::new(Mutex::new(root_exposed_ident_ids));
// All the dependent modules we've already begun loading - // TODO can we avoid this loop by storing them as a Set in Header to begin with?
// meaning we should never kick off another load_module on them! for symbol in header.exposes.iter() {
let mut loading_started: MutSet<ModuleId> = MutSet::default(); exposed_symbols.insert(*symbol);
}
// If the root module we're compiling happens to be an interface, debug_assert!(!exposed_symbols_by_module.contains_key(&home));
// it's possible that something else will import it. That will exposed_symbols_by_module.insert(home, exposed_symbols);
// necessarily cause a cyclic import error, but in the meantime
// we still shouldn't load it.
loading_started.insert(root_id);
// The declarations we'll ultimately be returning // Notify all the listeners that headers are now available for this module.
let mut declarations_by_id: MutMap<ModuleId, Vec<Declaration>> = MutMap::default(); if let Some(listeners) = header_listeners.remove(&home) {
for listener_id in listeners {
// This listener is longer waiting for this module,
// because this module's headers are now available!
let waiting_for = waiting_for_headers
.get_mut(&listener_id)
.expect("Unable to find module ID in waiting_for_headers");
let mut exposed_symbols_by_module: MutMap<ModuleId, MutSet<Symbol>> = MutMap::default(); waiting_for.remove(&home);
// Modules which are waiting for certain headers to be parsed // If it's no longer waiting for anything else, solve it.
let mut waiting_for_headers: MutMap<ModuleId, MutSet<ModuleId>> = MutMap::default(); if waiting_for.is_empty() {
let header = unparsed_modules
.remove(&listener_id)
.expect("Could not find listener ID in unparsed_modules");
// When the key ModuleId gets solved, iterate through each of the given modules let exposed_symbols = exposed_symbols_by_module
// and remove that ModuleId from the appropriate waiting_for_headers entry. .remove(&listener_id)
// If the relevant module's waiting_for_headers entry is now empty, canonicalize the module. .expect("Could not find listener ID in exposed_symbols_by_module");
let mut header_listeners: MutMap<ModuleId, Vec<ModuleId>> = MutMap::default();
let mut unparsed_modules: MutMap<ModuleId, ModuleHeader> = MutMap::default(); spawn_parse_and_constrain(
header,
// Modules which are waiting for certain deps to be solved stdlib.mode,
let mut waiting_for_solve: MutMap<ModuleId, MutSet<ModuleId>> = MutMap::default(); Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module),
// When the key ModuleId gets solved, iterate through each of the given modules &exposed_types,
// and remove that ModuleId from the appropriate waiting_for_solve entry. exposed_symbols.clone(),
// If the relevant module's waiting_for_solve entry is now empty, solve the module. &mut waiting_for_solve,
let mut solve_listeners: MutMap<ModuleId, Vec<ModuleId>> = MutMap::default(); msg_tx.clone(),
)
#[allow(clippy::type_complexity)]
let mut unsolved_modules: MutMap<
ModuleId,
(Module, Box<str>, MutSet<ModuleId>, Constraint, VarStore),
> = MutMap::default();
// Parse and canonicalize the module's deps
while let Some(msg) = msg_rx.recv().await {
use self::Msg::*;
match msg {
Header(header) => {
let home = header.module_id;
let deps_by_name = &header.deps_by_name;
let mut headers_needed =
HashSet::with_capacity_and_hasher(deps_by_name.len(), default_hasher());
headers_parsed.insert(home);
for dep_id in deps_by_name.values() {
if !headers_parsed.contains(&dep_id) {
headers_needed.insert(*dep_id);
} }
} }
}
// This was a dependency. Write it down and keep processing messaages. // If any of our deps weren't loaded before, start loading them.
let mut exposed_symbols: MutSet<Symbol> = for (dep_name, dep_id) in deps_by_name.iter() {
HashSet::with_capacity_and_hasher(header.exposes.len(), default_hasher()); if !loading_started.contains(&dep_id) {
// Record that we've started loading the module *before*
// we actually start loading it.
loading_started.insert(*dep_id);
// TODO can we avoid this loop by storing them as a Set in Header to begin with? let msg_tx = msg_tx.clone();
for symbol in header.exposes.iter() { let dep_name = dep_name.clone();
exposed_symbols.insert(*symbol);
// Provide mutexes of ModuleIds and IdentIds by module,
// so other modules can populate them as they load.
let shared = Shared(Arc::clone(&arc_modules), Arc::clone(&ident_ids_by_module));
// Start loading this module in the background.
spawn_blocking(move || load_module(src_dir.as_path(), dep_name, msg_tx, shared));
}
}
if headers_needed.is_empty() {
let exposed_symbols = exposed_symbols_by_module
.remove(&home)
.expect("Could not find listener ID in exposed_symbols_by_module");
spawn_parse_and_constrain(
header,
stdlib.mode,
Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module),
&exposed_types,
exposed_symbols,
&mut waiting_for_solve,
msg_tx.clone(),
)
} else {
// We will have to wait for our deps' headers to be parsed,
// so we can access their IdentId, which we need for canonicalization.
debug_assert!(!unparsed_modules.contains_key(&home));
unparsed_modules.insert(home, header);
// Register a listener with each of these.
for dep_id in headers_needed.iter() {
let listeners = header_listeners
.entry(*dep_id)
.or_insert_with(|| Vec::with_capacity(1));
(*listeners).push(home);
} }
debug_assert!(!exposed_symbols_by_module.contains_key(&home)); debug_assert!(!waiting_for_headers.contains_key(&home));
exposed_symbols_by_module.insert(home, exposed_symbols); waiting_for_headers.insert(home, headers_needed);
}
}
Constrained {
module,
declarations,
src,
ident_ids,
imported_modules,
constraint,
problems,
var_store,
} => {
can_problems.extend(problems);
// Notify all the listeners that headers are now available for this module. let module_id = module.module_id;
if let Some(listeners) = header_listeners.remove(&home) { let waiting_for = waiting_for_solve.get_mut(&module_id).unwrap_or_else(|| {
panic!(
"Could not find module ID {:?} in waiting_for_solve",
module_id
)
});
// Record the final IdentIds
debug_assert!(!constrained_ident_ids.contains_key(&module_id));
constrained_ident_ids.insert(module_id, ident_ids);
// It's possible that some modules have been solved since
// we began waiting for them. Remove those from waiting_for,
// because we no longer need to wait for them!
waiting_for.retain(|id| !exposed_types.contains_key(id));
declarations_by_id.insert(module_id, declarations);
if waiting_for.is_empty() {
// All of our dependencies have already been solved. Great!
// That means we can proceed directly to solving.
spawn_solve_module(
module,
src,
constraint,
var_store,
imported_modules,
msg_tx.clone(),
&mut exposed_types,
stdlib,
);
} else {
// We will have to wait for our dependencies to be solved.
debug_assert!(!unsolved_modules.contains_key(&module_id));
unsolved_modules.insert(
module_id,
(module, src, imported_modules, constraint, var_store),
);
// Register a listener with each of these.
for dep_id in waiting_for.iter() {
let listeners = solve_listeners
.entry(*dep_id)
.or_insert_with(|| Vec::with_capacity(1));
(*listeners).push(module_id);
}
}
}
Solved {
src,
module_id,
solved_module,
solved_subs,
} => {
type_problems.extend(solved_module.problems);
if module_id == root_id {
let solved = Arc::try_unwrap(solved_subs).unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to Solved<Subs>")
});
let module_ids = Arc::try_unwrap(arc_modules)
.unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to module_ids")
})
.into_inner()
.expect("Unwrapping mutex for module_ids");
let interns = Interns {
module_ids,
all_ident_ids: constrained_ident_ids,
};
return Ok(Some(LoadedModule {
module_id: root_id,
interns,
solved,
can_problems,
type_problems,
declarations_by_id,
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
src,
}));
} else {
// This was a dependency. Write it down and keep processing messages.
debug_assert!(!exposed_types.contains_key(&module_id));
exposed_types.insert(
module_id,
ExposedModuleTypes::Valid(solved_module.solved_types, solved_module.aliases),
);
// Notify all the listeners that this solved.
if let Some(listeners) = solve_listeners.remove(&module_id) {
for listener_id in listeners { for listener_id in listeners {
// This listener is longer waiting for this module, // This listener is longer waiting for this module,
// because this module's headers are now available! // because this module has now been solved!
let waiting_for = waiting_for_headers let waiting_for = waiting_for_solve
.get_mut(&listener_id) .get_mut(&listener_id)
.expect("Unable to find module ID in waiting_for_headers"); .expect("Unable to find module ID in waiting_for_solve");
waiting_for.remove(&home); waiting_for.remove(&module_id);
// If it's no longer waiting for anything else, solve it. // If it's no longer waiting for anything else, solve it.
if waiting_for.is_empty() { if waiting_for.is_empty() {
let header = unparsed_modules let (module, src, imported_modules, constraint, var_store) =
.remove(&listener_id) unsolved_modules
.expect("Could not find listener ID in unparsed_modules"); .remove(&listener_id)
.expect("Could not find listener ID in unsolved_modules");
let exposed_symbols = exposed_symbols_by_module spawn_solve_module(
.remove(&listener_id) module,
.expect("Could not find listener ID in exposed_symbols_by_module"); src,
constraint,
spawn_parse_and_constrain( var_store,
header, imported_modules,
stdlib.mode,
Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module),
&exposed_types,
exposed_symbols.clone(),
&mut waiting_for_solve,
msg_tx.clone(), msg_tx.clone(),
) &mut exposed_types,
} stdlib,
} );
}
// If any of our deps weren't loaded before, start loading them.
for (dep_name, dep_id) in deps_by_name.iter() {
if !loading_started.contains(&dep_id) {
// Record that we've started loading the module *before*
// we actually start loading it.
loading_started.insert(*dep_id);
let env = env.clone();
let msg_tx = msg_tx.clone();
let dep_name = dep_name.clone();
// Provide mutexes of ModuleIds and IdentIds by module,
// so other modules can populate them as they load.
let shared =
Shared(Arc::clone(&arc_modules), Arc::clone(&ident_ids_by_module));
// Start loading this module in the background.
spawn_blocking(move || load_module(env, dep_name, msg_tx, shared));
}
}
if headers_needed.is_empty() {
let exposed_symbols = exposed_symbols_by_module
.remove(&home)
.expect("Could not find listener ID in exposed_symbols_by_module");
spawn_parse_and_constrain(
header,
stdlib.mode,
Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module),
&exposed_types,
exposed_symbols,
&mut waiting_for_solve,
msg_tx.clone(),
)
} else {
// We will have to wait for our deps' headers to be parsed,
// so we can access their IdentId, which we need for canonicalization.
debug_assert!(!unparsed_modules.contains_key(&home));
unparsed_modules.insert(home, header);
// Register a listener with each of these.
for dep_id in headers_needed.iter() {
let listeners = header_listeners
.entry(*dep_id)
.or_insert_with(|| Vec::with_capacity(1));
(*listeners).push(home);
}
debug_assert!(!waiting_for_headers.contains_key(&home));
waiting_for_headers.insert(home, headers_needed);
}
}
Constrained {
module,
declarations,
src,
ident_ids,
imported_modules,
constraint,
problems,
var_store,
} => {
can_problems.extend(problems);
let module_id = module.module_id;
let waiting_for = waiting_for_solve.get_mut(&module_id).unwrap_or_else(|| {
panic!(
"Could not find module ID {:?} in waiting_for_solve",
module_id
)
});
// Record the final IdentIds
debug_assert!(!constrained_ident_ids.contains_key(&module_id));
constrained_ident_ids.insert(module_id, ident_ids);
// It's possible that some modules have been solved since
// we began waiting for them. Remove those from waiting_for,
// because we no longer need to wait for them!
waiting_for.retain(|id| !exposed_types.contains_key(id));
declarations_by_id.insert(module_id, declarations);
if waiting_for.is_empty() {
// All of our dependencies have already been solved. Great!
// That means we can proceed directly to solving.
spawn_solve_module(
module,
src,
constraint,
var_store,
imported_modules,
msg_tx.clone(),
&mut exposed_types,
stdlib,
);
} else {
// We will have to wait for our dependencies to be solved.
debug_assert!(!unsolved_modules.contains_key(&module_id));
unsolved_modules.insert(
module_id,
(module, src, imported_modules, constraint, var_store),
);
// Register a listener with each of these.
for dep_id in waiting_for.iter() {
let listeners = solve_listeners
.entry(*dep_id)
.or_insert_with(|| Vec::with_capacity(1));
(*listeners).push(module_id);
}
}
}
Solved {
src,
module_id,
solved_module,
solved_subs,
} => {
type_problems.extend(solved_module.problems);
if module_id == root_id {
// Once we've solved the originally requested module, we're done!
msg_rx.close();
let solved = Arc::try_unwrap(solved_subs).unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to Solved<Subs>")
});
let module_ids = Arc::try_unwrap(arc_modules)
.unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to module_ids")
})
.into_inner()
.expect("Unwrapping mutex for module_ids");
let interns = Interns {
module_ids,
all_ident_ids: constrained_ident_ids,
};
return Ok(LoadedModule {
module_id: root_id,
interns,
solved,
can_problems,
type_problems,
declarations_by_id,
exposed_vars_by_symbol: solved_module.exposed_vars_by_symbol,
src,
});
} else {
// This was a dependency. Write it down and keep processing messages.
debug_assert!(!exposed_types.contains_key(&module_id));
exposed_types.insert(
module_id,
ExposedModuleTypes::Valid(
solved_module.solved_types,
solved_module.aliases,
),
);
// Notify all the listeners that this solved.
if let Some(listeners) = solve_listeners.remove(&module_id) {
for listener_id in listeners {
// This listener is longer waiting for this module,
// because this module has now been solved!
let waiting_for = waiting_for_solve
.get_mut(&listener_id)
.expect("Unable to find module ID in waiting_for_solve");
waiting_for.remove(&module_id);
// If it's no longer waiting for anything else, solve it.
if waiting_for.is_empty() {
let (module, src, imported_modules, constraint, var_store) =
unsolved_modules
.remove(&listener_id)
.expect("Could not find listener ID in unsolved_modules");
spawn_solve_module(
module,
src,
constraint,
var_store,
imported_modules,
msg_tx.clone(),
&mut exposed_types,
stdlib,
);
}
} }
} }
} }
@ -519,20 +597,21 @@ async fn load_deps<'a>(
} }
} }
// The msg_rx receiver closed unexpectedly before we finished solving everything // We did some work but haven't finish loading the module yet.
Err(LoadingProblem::MsgChannelDied) // If we had, this would have been Ok(Some(loaded_module)) instead.
Ok(None)
} }
/// Load a module by its module name, rather than by its filename /// Load a module by its module name, rather than by its filename
fn load_module( fn load_module(
env: Env, src_dir: &Path,
module_name: ModuleName, module_name: ModuleName,
msg_tx: MsgSender, msg_tx: MsgSender,
module_ids: SharedModules<'_, '_>, module_ids: SharedModules<'_, '_>,
) -> Result<ModuleId, LoadingProblem> { ) -> Result<ModuleId, LoadingProblem> {
let mut filename = PathBuf::new(); let mut filename = PathBuf::new();
filename.push(env.src_dir); filename.push(src_dir);
// Convert dots in module name to directories // Convert dots in module name to directories
for part in module_name.as_str().split(MODULE_SEPARATOR) { for part in module_name.as_str().split(MODULE_SEPARATOR) {
@ -553,17 +632,14 @@ fn load_module(
/// corresponding Stealer, which can steal from it. Stealers can be shared across threads.) /// corresponding Stealer, which can steal from it. Stealers can be shared across threads.)
/// ///
/// Based on https://docs.rs/crossbeam/0.7.3/crossbeam/deque/index.html#examples /// Based on https://docs.rs/crossbeam/0.7.3/crossbeam/deque/index.html#examples
fn find_task<T>( fn find_task<T>(local: &Worker<T>, global: &Injector<T>, stealers: &[Stealer<T>]) -> Option<T> {
local: &Worker<T>,
global: &Injector<T>,
stealers: &[Stealer<T>],
) -> Option<T> {
// Pop a task from the local queue, if not empty. // Pop a task from the local queue, if not empty.
local.pop().or_else(|| { local.pop().or_else(|| {
// Otherwise, we need to look for a task elsewhere. // Otherwise, we need to look for a task elsewhere.
iter::repeat_with(|| { iter::repeat_with(|| {
// Try stealing a task from the global queue. // Try stealing a task from the global queue.
global.steal() global
.steal()
// Or try stealing a task from one of the other threads. // Or try stealing a task from one of the other threads.
.or_else(|| stealers.iter().map(|s| s.steal()).collect()) .or_else(|| stealers.iter().map(|s| s.steal()).collect())
}) })