]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_query_system/src/query/job.rs
f1316557c2985f206745ce1cd640c6d41a269832
[rust.git] / compiler / rustc_query_system / src / query / job.rs
1 use crate::dep_graph::DepContext;
2 use crate::query::plumbing::CycleError;
3 use crate::query::{QueryContext, QueryStackFrame};
4 use rustc_hir::def::DefKind;
5
6 use rustc_data_structures::fx::FxHashMap;
7 use rustc_errors::{
8     struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level,
9 };
10 use rustc_session::Session;
11 use rustc_span::Span;
12
13 use std::hash::Hash;
14 use std::num::NonZeroU64;
15
16 #[cfg(parallel_compiler)]
17 use {
18     parking_lot::{Condvar, Mutex},
19     rustc_data_structures::fx::FxHashSet,
20     rustc_data_structures::sync::Lock,
21     rustc_data_structures::sync::Lrc,
22     rustc_data_structures::{jobserver, OnDrop},
23     rustc_rayon_core as rayon_core,
24     rustc_span::DUMMY_SP,
25     std::iter::{self, FromIterator},
26     std::{mem, process},
27 };
28
29 /// Represents a span and a query key.
30 #[derive(Clone, Debug)]
31 pub struct QueryInfo {
32     /// The span corresponding to the reason for which this query was required.
33     pub span: Span,
34     pub query: QueryStackFrame,
35 }
36
37 pub type QueryMap = FxHashMap<QueryJobId, QueryJobInfo>;
38
39 /// A value uniquely identifying an active query job.
40 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
41 pub struct QueryJobId(pub NonZeroU64);
42
43 impl QueryJobId {
44     fn query(self, map: &QueryMap) -> QueryStackFrame {
45         map.get(&self).unwrap().query.clone()
46     }
47
48     #[cfg(parallel_compiler)]
49     fn span(self, map: &QueryMap) -> Span {
50         map.get(&self).unwrap().job.span
51     }
52
53     #[cfg(parallel_compiler)]
54     fn parent(self, map: &QueryMap) -> Option<QueryJobId> {
55         map.get(&self).unwrap().job.parent
56     }
57
58     #[cfg(parallel_compiler)]
59     fn latch<'a>(self, map: &'a QueryMap) -> Option<&'a QueryLatch> {
60         map.get(&self).unwrap().job.latch.as_ref()
61     }
62 }
63
64 pub struct QueryJobInfo {
65     pub query: QueryStackFrame,
66     pub job: QueryJob,
67 }
68
69 /// Represents an active query job.
70 #[derive(Clone)]
71 pub struct QueryJob {
72     pub id: QueryJobId,
73
74     /// The span corresponding to the reason for which this query was required.
75     pub span: Span,
76
77     /// The parent query job which created this job and is implicitly waiting on it.
78     pub parent: Option<QueryJobId>,
79
80     /// The latch that is used to wait on this job.
81     #[cfg(parallel_compiler)]
82     latch: Option<QueryLatch>,
83 }
84
85 impl QueryJob {
86     /// Creates a new query job.
87     pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
88         QueryJob {
89             id,
90             span,
91             parent,
92             #[cfg(parallel_compiler)]
93             latch: None,
94         }
95     }
96
97     #[cfg(parallel_compiler)]
98     pub(super) fn latch(&mut self) -> QueryLatch {
99         if self.latch.is_none() {
100             self.latch = Some(QueryLatch::new());
101         }
102         self.latch.as_ref().unwrap().clone()
103     }
104
105     /// Signals to waiters that the query is complete.
106     ///
107     /// This does nothing for single threaded rustc,
108     /// as there are no concurrent jobs which could be waiting on us
109     pub fn signal_complete(self) {
110         #[cfg(parallel_compiler)]
111         {
112             if let Some(latch) = self.latch {
113                 latch.set();
114             }
115         }
116     }
117 }
118
119 #[cfg(not(parallel_compiler))]
120 impl QueryJobId {
121     #[cold]
122     #[inline(never)]
123     pub(super) fn find_cycle_in_stack(
124         &self,
125         query_map: QueryMap,
126         current_job: &Option<QueryJobId>,
127         span: Span,
128     ) -> CycleError {
129         // Find the waitee amongst `current_job` parents
130         let mut cycle = Vec::new();
131         let mut current_job = Option::clone(current_job);
132
133         while let Some(job) = current_job {
134             let info = query_map.get(&job).unwrap();
135             cycle.push(QueryInfo { span: info.job.span, query: info.query.clone() });
136
137             if job == *self {
138                 cycle.reverse();
139
140                 // This is the end of the cycle
141                 // The span entry we included was for the usage
142                 // of the cycle itself, and not part of the cycle
143                 // Replace it with the span which caused the cycle to form
144                 cycle[0].span = span;
145                 // Find out why the cycle itself was used
146                 let usage = info
147                     .job
148                     .parent
149                     .as_ref()
150                     .map(|parent| (info.job.span, parent.query(&query_map)));
151                 return CycleError { usage, cycle };
152             }
153
154             current_job = info.job.parent;
155         }
156
157         panic!("did not find a cycle")
158     }
159 }
160
161 #[cfg(parallel_compiler)]
162 struct QueryWaiter {
163     query: Option<QueryJobId>,
164     condvar: Condvar,
165     span: Span,
166     cycle: Lock<Option<CycleError>>,
167 }
168
169 #[cfg(parallel_compiler)]
170 impl QueryWaiter {
171     fn notify(&self, registry: &rayon_core::Registry) {
172         rayon_core::mark_unblocked(registry);
173         self.condvar.notify_one();
174     }
175 }
176
177 #[cfg(parallel_compiler)]
178 struct QueryLatchInfo {
179     complete: bool,
180     waiters: Vec<Lrc<QueryWaiter>>,
181 }
182
183 #[cfg(parallel_compiler)]
184 #[derive(Clone)]
185 pub(super) struct QueryLatch {
186     info: Lrc<Mutex<QueryLatchInfo>>,
187 }
188
189 #[cfg(parallel_compiler)]
190 impl QueryLatch {
191     fn new() -> Self {
192         QueryLatch {
193             info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
194         }
195     }
196
197     /// Awaits for the query job to complete.
198     pub(super) fn wait_on(&self, query: Option<QueryJobId>, span: Span) -> Result<(), CycleError> {
199         let waiter =
200             Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() });
201         self.wait_on_inner(&waiter);
202         // FIXME: Get rid of this lock. We have ownership of the QueryWaiter
203         // although another thread may still have a Lrc reference so we cannot
204         // use Lrc::get_mut
205         let mut cycle = waiter.cycle.lock();
206         match cycle.take() {
207             None => Ok(()),
208             Some(cycle) => Err(cycle),
209         }
210     }
211
212     /// Awaits the caller on this latch by blocking the current thread.
213     fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter>) {
214         let mut info = self.info.lock();
215         if !info.complete {
216             // We push the waiter on to the `waiters` list. It can be accessed inside
217             // the `wait` call below, by 1) the `set` method or 2) by deadlock detection.
218             // Both of these will remove it from the `waiters` list before resuming
219             // this thread.
220             info.waiters.push(waiter.clone());
221
222             // If this detects a deadlock and the deadlock handler wants to resume this thread
223             // we have to be in the `wait` call. This is ensured by the deadlock handler
224             // getting the self.info lock.
225             rayon_core::mark_blocked();
226             jobserver::release_thread();
227             waiter.condvar.wait(&mut info);
228             // Release the lock before we potentially block in `acquire_thread`
229             mem::drop(info);
230             jobserver::acquire_thread();
231         }
232     }
233
234     /// Sets the latch and resumes all waiters on it
235     fn set(&self) {
236         let mut info = self.info.lock();
237         debug_assert!(!info.complete);
238         info.complete = true;
239         let registry = rayon_core::Registry::current();
240         for waiter in info.waiters.drain(..) {
241             waiter.notify(&registry);
242         }
243     }
244
245     /// Removes a single waiter from the list of waiters.
246     /// This is used to break query cycles.
247     fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter> {
248         let mut info = self.info.lock();
249         debug_assert!(!info.complete);
250         // Remove the waiter from the list of waiters
251         info.waiters.remove(waiter)
252     }
253 }
254
255 /// A resumable waiter of a query. The usize is the index into waiters in the query's latch
256 #[cfg(parallel_compiler)]
257 type Waiter = (QueryJobId, usize);
258
259 /// Visits all the non-resumable and resumable waiters of a query.
260 /// Only waiters in a query are visited.
261 /// `visit` is called for every waiter and is passed a query waiting on `query_ref`
262 /// and a span indicating the reason the query waited on `query_ref`.
263 /// If `visit` returns Some, this function returns.
264 /// For visits of non-resumable waiters it returns the return value of `visit`.
265 /// For visits of resumable waiters it returns Some(Some(Waiter)) which has the
266 /// required information to resume the waiter.
267 /// If all `visit` calls returns None, this function also returns None.
268 #[cfg(parallel_compiler)]
269 fn visit_waiters<F>(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option<Option<Waiter>>
270 where
271     F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
272 {
273     // Visit the parent query which is a non-resumable waiter since it's on the same stack
274     if let Some(parent) = query.parent(query_map) {
275         if let Some(cycle) = visit(query.span(query_map), parent) {
276             return Some(cycle);
277         }
278     }
279
280     // Visit the explicit waiters which use condvars and are resumable
281     if let Some(latch) = query.latch(query_map) {
282         for (i, waiter) in latch.info.lock().waiters.iter().enumerate() {
283             if let Some(waiter_query) = waiter.query {
284                 if visit(waiter.span, waiter_query).is_some() {
285                     // Return a value which indicates that this waiter can be resumed
286                     return Some(Some((query, i)));
287                 }
288             }
289         }
290     }
291
292     None
293 }
294
295 /// Look for query cycles by doing a depth first search starting at `query`.
296 /// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
297 /// If a cycle is detected, this initial value is replaced with the span causing
298 /// the cycle.
299 #[cfg(parallel_compiler)]
300 fn cycle_check(
301     query_map: &QueryMap,
302     query: QueryJobId,
303     span: Span,
304     stack: &mut Vec<(Span, QueryJobId)>,
305     visited: &mut FxHashSet<QueryJobId>,
306 ) -> Option<Option<Waiter>> {
307     if !visited.insert(query) {
308         return if let Some(p) = stack.iter().position(|q| q.1 == query) {
309             // We detected a query cycle, fix up the initial span and return Some
310
311             // Remove previous stack entries
312             stack.drain(0..p);
313             // Replace the span for the first query with the cycle cause
314             stack[0].0 = span;
315             Some(None)
316         } else {
317             None
318         };
319     }
320
321     // Query marked as visited is added it to the stack
322     stack.push((span, query));
323
324     // Visit all the waiters
325     let r = visit_waiters(query_map, query, |span, successor| {
326         cycle_check(query_map, successor, span, stack, visited)
327     });
328
329     // Remove the entry in our stack if we didn't find a cycle
330     if r.is_none() {
331         stack.pop();
332     }
333
334     r
335 }
336
337 /// Finds out if there's a path to the compiler root (aka. code which isn't in a query)
338 /// from `query` without going through any of the queries in `visited`.
339 /// This is achieved with a depth first search.
340 #[cfg(parallel_compiler)]
341 fn connected_to_root(
342     query_map: &QueryMap,
343     query: QueryJobId,
344     visited: &mut FxHashSet<QueryJobId>,
345 ) -> bool {
346     // We already visited this or we're deliberately ignoring it
347     if !visited.insert(query) {
348         return false;
349     }
350
351     // This query is connected to the root (it has no query parent), return true
352     if query.parent(query_map).is_none() {
353         return true;
354     }
355
356     visit_waiters(query_map, query, |_, successor| {
357         connected_to_root(query_map, successor, visited).then_some(None)
358     })
359     .is_some()
360 }
361
362 // Deterministically pick an query from a list
363 #[cfg(parallel_compiler)]
364 fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T
365 where
366     F: Fn(&T) -> (Span, QueryJobId),
367 {
368     // Deterministically pick an entry point
369     // FIXME: Sort this instead
370     queries
371         .iter()
372         .min_by_key(|v| {
373             let (span, query) = f(v);
374             let hash = query.query(query_map).hash;
375             // Prefer entry points which have valid spans for nicer error messages
376             // We add an integer to the tuple ensuring that entry points
377             // with valid spans are picked first
378             let span_cmp = if span == DUMMY_SP { 1 } else { 0 };
379             (span_cmp, hash)
380         })
381         .unwrap()
382 }
383
384 /// Looks for query cycles starting from the last query in `jobs`.
385 /// If a cycle is found, all queries in the cycle is removed from `jobs` and
386 /// the function return true.
387 /// If a cycle was not found, the starting query is removed from `jobs` and
388 /// the function returns false.
389 #[cfg(parallel_compiler)]
390 fn remove_cycle(
391     query_map: &QueryMap,
392     jobs: &mut Vec<QueryJobId>,
393     wakelist: &mut Vec<Lrc<QueryWaiter>>,
394 ) -> bool {
395     let mut visited = FxHashSet::default();
396     let mut stack = Vec::new();
397     // Look for a cycle starting with the last query in `jobs`
398     if let Some(waiter) =
399         cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
400     {
401         // The stack is a vector of pairs of spans and queries; reverse it so that
402         // the earlier entries require later entries
403         let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip();
404
405         // Shift the spans so that queries are matched with the span for their waitee
406         spans.rotate_right(1);
407
408         // Zip them back together
409         let mut stack: Vec<_> = iter::zip(spans, queries).collect();
410
411         // Remove the queries in our cycle from the list of jobs to look at
412         for r in &stack {
413             if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
414                 jobs.remove(pos);
415             }
416         }
417
418         // Find the queries in the cycle which are
419         // connected to queries outside the cycle
420         let entry_points = stack
421             .iter()
422             .filter_map(|&(span, query)| {
423                 if query.parent(query_map).is_none() {
424                     // This query is connected to the root (it has no query parent)
425                     Some((span, query, None))
426                 } else {
427                     let mut waiters = Vec::new();
428                     // Find all the direct waiters who lead to the root
429                     visit_waiters(query_map, query, |span, waiter| {
430                         // Mark all the other queries in the cycle as already visited
431                         let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1));
432
433                         if connected_to_root(query_map, waiter, &mut visited) {
434                             waiters.push((span, waiter));
435                         }
436
437                         None
438                     });
439                     if waiters.is_empty() {
440                         None
441                     } else {
442                         // Deterministically pick one of the waiters to show to the user
443                         let waiter = *pick_query(query_map, &waiters, |s| *s);
444                         Some((span, query, Some(waiter)))
445                     }
446                 }
447             })
448             .collect::<Vec<(Span, QueryJobId, Option<(Span, QueryJobId)>)>>();
449
450         // Deterministically pick an entry point
451         let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1));
452
453         // Shift the stack so that our entry point is first
454         let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point);
455         if let Some(pos) = entry_point_pos {
456             stack.rotate_left(pos);
457         }
458
459         let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map)));
460
461         // Create the cycle error
462         let error = CycleError {
463             usage,
464             cycle: stack
465                 .iter()
466                 .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) })
467                 .collect(),
468         };
469
470         // We unwrap `waiter` here since there must always be one
471         // edge which is resumable / waited using a query latch
472         let (waitee_query, waiter_idx) = waiter.unwrap();
473
474         // Extract the waiter we want to resume
475         let waiter = waitee_query.latch(query_map).unwrap().extract_waiter(waiter_idx);
476
477         // Set the cycle error so it will be picked up when resumed
478         *waiter.cycle.lock() = Some(error);
479
480         // Put the waiter on the list of things to resume
481         wakelist.push(waiter);
482
483         true
484     } else {
485         false
486     }
487 }
488
489 /// Detects query cycles by using depth first search over all active query jobs.
490 /// If a query cycle is found it will break the cycle by finding an edge which
491 /// uses a query latch and then resuming that waiter.
492 /// There may be multiple cycles involved in a deadlock, so this searches
493 /// all active queries for cycles before finally resuming all the waiters at once.
494 #[cfg(parallel_compiler)]
495 pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
496     let on_panic = OnDrop(|| {
497         eprintln!("deadlock handler panicked, aborting process");
498         process::abort();
499     });
500
501     let mut wakelist = Vec::new();
502     let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
503
504     let mut found_cycle = false;
505
506     while jobs.len() > 0 {
507         if remove_cycle(&query_map, &mut jobs, &mut wakelist) {
508             found_cycle = true;
509         }
510     }
511
512     // Check that a cycle was found. It is possible for a deadlock to occur without
513     // a query cycle if a query which can be waited on uses Rayon to do multithreading
514     // internally. Such a query (X) may be executing on 2 threads (A and B) and A may
515     // wait using Rayon on B. Rayon may then switch to executing another query (Y)
516     // which in turn will wait on X causing a deadlock. We have a false dependency from
517     // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
518     // only considers the true dependency and won't detect a cycle.
519     assert!(found_cycle);
520
521     // FIXME: Ensure this won't cause a deadlock before we return
522     for waiter in wakelist.into_iter() {
523         waiter.notify(registry);
524     }
525
526     on_panic.disable();
527 }
528
529 #[inline(never)]
530 #[cold]
531 pub(crate) fn report_cycle<'a>(
532     sess: &'a Session,
533     CycleError { usage, cycle: stack }: CycleError,
534 ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
535     assert!(!stack.is_empty());
536
537     let fix_span = |span: Span, query: &QueryStackFrame| {
538         sess.source_map().guess_head_span(query.default_span(span))
539     };
540
541     let span = fix_span(stack[1 % stack.len()].span, &stack[0].query);
542     let mut err =
543         struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description);
544
545     for i in 1..stack.len() {
546         let query = &stack[i].query;
547         let span = fix_span(stack[(i + 1) % stack.len()].span, query);
548         err.span_note(span, &format!("...which requires {}...", query.description));
549     }
550
551     if stack.len() == 1 {
552         err.note(&format!("...which immediately requires {} again", stack[0].query.description));
553     } else {
554         err.note(&format!(
555             "...which again requires {}, completing the cycle",
556             stack[0].query.description
557         ));
558     }
559
560     if stack.iter().all(|entry| {
561         entry
562             .query
563             .def_kind
564             .map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias))
565     }) {
566         if stack.iter().all(|entry| {
567             entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias))
568         }) {
569             err.note("type aliases cannot be recursive");
570             err.help("consider using a struct, enum, or union instead to break the cycle");
571             err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
572         } else {
573             err.note("trait aliases cannot be recursive");
574         }
575     }
576
577     if let Some((span, query)) = usage {
578         err.span_note(fix_span(span, &query), &format!("cycle used when {}", query.description));
579     }
580
581     err
582 }
583
584 pub fn print_query_stack<CTX: QueryContext>(
585     tcx: CTX,
586     mut current_query: Option<QueryJobId>,
587     handler: &Handler,
588     num_frames: Option<usize>,
589 ) -> usize {
590     // Be careful relying on global state here: this code is called from
591     // a panic hook, which means that the global `Handler` may be in a weird
592     // state if it was responsible for triggering the panic.
593     let mut i = 0;
594     let query_map = tcx.try_collect_active_jobs();
595
596     while let Some(query) = current_query {
597         if Some(i) == num_frames {
598             break;
599         }
600         let Some(query_info) = query_map.as_ref().and_then(|map| map.get(&query)) else {
601             break;
602         };
603         let mut diag = Diagnostic::new(
604             Level::FailureNote,
605             &format!("#{} [{}] {}", i, query_info.query.name, query_info.query.description),
606         );
607         diag.span =
608             tcx.dep_context().sess().source_map().guess_head_span(query_info.job.span).into();
609         handler.force_print_diagnostic(diag);
610
611         current_query = query_info.job.parent;
612         i += 1;
613     }
614
615     i
616 }