Skip to content

Commit 6ab6a25

Browse files
brittanyreymeta-codesync[bot]
authored andcommitted
Make propagate_side_effect_imports deterministic
Summary: I was seeing nondeterminism on the XL target. **Root cause:** the rayon par_iter was both reading and writing to the same data strucutre: lazy_eligible **Fix:** snapshot the set of modules that already have non-empty failing deps before the parallel pass Reviewed By: alexmalyshev Differential Revision: D107396761 fbshipit-source-id: 04182d940a41c4fdc0d703511231a6bf75fddeba
1 parent 7dd3c7e commit 6ab6a25

1 file changed

Lines changed: 83 additions & 15 deletions

File tree

src/output.rs

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -549,27 +549,30 @@ impl LifeGuardAnalysis {
549549
/// and B is a passing module with non-empty failing deps, add B to A's failing
550550
/// deps so B is eagerly imported.
551551
pub fn propagate_side_effect_imports(&mut self, side_effect_imports: &SideEffectMap) {
552+
let has_failing_deps: AHashSet<ModuleName> = self
553+
.output
554+
.lazy_eligible
555+
.iter()
556+
.filter(|entry| !entry.value().is_empty())
557+
.map(|entry| *entry.key())
558+
.collect();
559+
552560
side_effect_imports
553561
.par_iter()
554562
.for_each(|(module_name, se_imports)| {
555563
if !self.passing_modules.contains(module_name) {
556564
return;
557565
}
558-
let mut new_deps: SmallSet<ModuleName> = SmallSet::new();
559-
for se_import in se_imports {
560-
if let Some(deps) = self.output.lazy_eligible.get(se_import) {
561-
if !deps.is_empty() {
562-
new_deps.insert(*se_import);
563-
}
564-
}
565-
}
566-
if !new_deps.is_empty() {
567-
self.output
568-
.lazy_eligible
569-
.entry(*module_name)
570-
.or_default()
571-
.extend(new_deps);
572-
}
566+
self.output
567+
.lazy_eligible
568+
.entry(*module_name)
569+
.or_default()
570+
.extend(
571+
se_imports
572+
.iter()
573+
.filter(|se_import| has_failing_deps.contains(*se_import))
574+
.copied(),
575+
);
573576
});
574577
}
575578

@@ -1055,6 +1058,71 @@ mod tests {
10551058
assert!(child_deps.contains(&b));
10561059
}
10571060

1061+
#[test]
1062+
fn test_side_effect_imports_do_not_observe_same_pass_updates() {
1063+
let options = Options {
1064+
verbose_output_path: None,
1065+
sorted_output: true,
1066+
main_module: None,
1067+
};
1068+
let exports = Exports::empty();
1069+
1070+
for iteration in 0..64 {
1071+
let safety_map = SafetyMap::new();
1072+
let mut import_graph = ImportGraph::new();
1073+
let mut side_effect_imports: SideEffectMap = AHashMap::new();
1074+
let mut chains = Vec::new();
1075+
1076+
for chain_idx in 0..16 {
1077+
let outer = mn(&format!("outer_{iteration}_{chain_idx}"));
1078+
let middle = mn(&format!("middle_{iteration}_{chain_idx}"));
1079+
let inner = mn(&format!("inner_{iteration}_{chain_idx}"));
1080+
let leaf = mn(&format!("leaf_{iteration}_{chain_idx}"));
1081+
1082+
import_graph.graph.add_node(&outer);
1083+
import_graph.graph.add_node(&middle);
1084+
import_graph.graph.add_node(&inner);
1085+
import_graph.graph.add_node(&leaf);
1086+
import_graph.graph.add_edge(&outer, &middle);
1087+
import_graph.graph.add_edge(&middle, &inner);
1088+
import_graph.graph.add_edge(&inner, &leaf);
1089+
1090+
safety_map.insert(outer, SafetyResult::Ok(ModuleSafety::new()));
1091+
safety_map.insert(middle, SafetyResult::Ok(ModuleSafety::new()));
1092+
safety_map.insert(inner, SafetyResult::Ok(ModuleSafety::new()));
1093+
1094+
let mut failing = ModuleSafety::new();
1095+
failing.add_error(make_error(
1096+
ErrorKind::UnknownDecoratorCall,
1097+
"unknown-decorator-call",
1098+
chain_idx,
1099+
));
1100+
safety_map.insert(leaf, SafetyResult::Ok(failing));
1101+
1102+
side_effect_imports.insert(middle, [inner].into_iter().collect());
1103+
side_effect_imports.insert(outer, [middle].into_iter().collect());
1104+
chains.push((outer, middle, inner));
1105+
}
1106+
1107+
let mut analysis = LifeGuardAnalysis::new(safety_map, import_graph, &exports, &options);
1108+
analysis.propagate_side_effect_imports(&side_effect_imports);
1109+
1110+
for (outer, middle, inner) in chains {
1111+
let middle_deps = analysis.output.lazy_eligible.get(&middle).unwrap();
1112+
assert!(
1113+
middle_deps.contains(&inner),
1114+
"{middle} should be guarded by {inner}",
1115+
);
1116+
1117+
let outer_deps = analysis.output.lazy_eligible.get(&outer).unwrap();
1118+
assert!(
1119+
!outer_deps.contains(&middle),
1120+
"{outer} should not observe side-effect deps added to {middle} during the same propagation pass",
1121+
);
1122+
}
1123+
}
1124+
}
1125+
10581126
// ---- build_re_export_map tests ----
10591127

10601128
fn attr(module: &str, name: &str) -> Attribute {

0 commit comments

Comments
 (0)