Make the retained dep graph deterministic under the parallel frontend#157352
Make the retained dep graph deterministic under the parallel frontend#157352xmakro wants to merge 2 commits into
Conversation
0a6b168 to
e4aedca
Compare
The in-memory dep graph kept for `-Zquery-dep-graph` is built by `GraphEncoder::record`, which pushed each node using `try_lock` and silently dropped the node when the lock was contended. Single-threaded the only contention is a query forced re-entrantly from within `with_retained_dep_graph`, so dropping was harmless. Under the parallel frontend several encoder threads record nodes at the same time, so a contended `try_lock` dropped nodes and edges at random, leaving the retained graph nondeterministic and making the dep-graph ui tests, which assert on its contents, fail intermittently. Remove that reentrancy in `check_paths`: read the graph into owned results while the lock is held, then emit the diagnostics afterwards. The only query forced inside the old closure was `def_path_str` (for the error messages), which can create dep nodes and re-enter the encoder; doing the emission after the lock is released means `record` never re-enters while the lock is held. `record` can then block on the lock unconditionally instead of using `try_lock`, so concurrent encoders never drop a node or edge. This only affects compilation with `-Zquery-dep-graph`; with the flag off the retained graph is absent and nothing changes.
e4aedca to
4f45cde
Compare
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Kivooeo (or someone else) some time within the next two weeks. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
Why was this reviewer chosen?The reviewer was selected based on:
|
|
You see the problem is that we are trying to keep dep graph's reflexive-transitive closure confluent. As such for parallel frontend we need to emit errors while holding a mutex lock. However looking at your PR made me think of a different way of keeping this property. You can sort the |
|
I'm not a good reviewer for that r? nnethercote |
These tests assert on the contents of the retained dep graph, which is now built deterministically under the parallel frontend, so they no longer need to be ignored there. Replace the ignore-parallel-frontend directives with blank lines rather than deleting them, so the following line numbers stay the same and the expected output is unchanged.
4f45cde to
6238c17
Compare
Thanks for the comment. My understanding is that The important part is that the |
Part of #154314.
The in-memory dep graph kept for
-Zquery-dep-graphis built byGraphEncoder::record, which pushed each node usingtry_lockand silently dropped the node when the lock was contended. Single-threaded the only contention is a query forced re-entrantly from withinwith_retained_dep_graph, so dropping was harmless. Under the parallel frontend several encoder threads record nodes at the same time, so a contendedtry_lockdropped nodes and edges at random, leaving the retained graph nondeterministic. The dep-graph ui tests, which assert on its contents, then failed intermittently.The reentrancy came from
check_pathsemitting its diagnostics while holding the retained graph: the error path callsdef_path_str, which can create new dep nodes and re-enter the encoder. This reads the path results into owned values while the lock is held and emits the diagnostics afterwards, sorecordnever re-enters while the lock is held.recordcan then block on the lock unconditionally instead of usingtry_lock, so concurrent encoders never drop a node or edge.The retained graph has only one other reader,
dump_graph(-Zdump-dep-graph), which also holds the lock while it formats nodes. Formatting aDepNodegoes through itsDebugimpl, which usesdef_path_debug_str; that path is deliberately query-free (it reads the untracked definitions and crate store), so it never records a node and cannot re-enterrecord. Concurrent encoder threads blocking on the lock is ordinary contention rather than a deadlock, since a thread holding the lock never waits on another thread to record.This only affects compilation with
-Zquery-dep-graph; with the flag off the retained graph is absent and nothing changes.The 7 dep-graph ui tests are re-enabled under the parallel frontend. Validated with the directives neutralized: all 7 fail at
--parallel-frontend-threads=16 --iteration-count=30; with the fix they pass at 300 iterations, single-threaded exact comparison, and acrosstests/incremental.