]>
Commit | Line | Data |
---|---|---|
9cc50fc6 SL |
1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Manages the communication between the compiler's main thread and | |
12 | //! the thread that constructs the dependency graph. The basic idea is | |
13 | //! to use double buffering to lower the cost of producing a message. | |
14 | //! In the compiler thread, we accumulate messages in a vector until | |
15 | //! the vector is full, or until we want to query the graph, and then | |
16 | //! we send that vector over to the depgraph thread. At the same time, | |
17 | //! we receive an empty vector from the depgraph thread that we can use | |
18 | //! to accumulate more messages. This way we only ever have two vectors | |
19 | //! allocated (and both have a fairly large capacity). | |
20 | ||
54a0048b | 21 | use hir::def_id::DefId; |
9cc50fc6 | 22 | use rustc_data_structures::veccell::VecCell; |
7453a54e | 23 | use std::cell::Cell; |
9cc50fc6 SL |
24 | use std::sync::mpsc::{self, Sender, Receiver}; |
25 | use std::thread; | |
26 | ||
27 | use super::DepGraphQuery; | |
28 | use super::DepNode; | |
29 | use super::edges::DepGraphEdges; | |
30 | ||
a7813a04 | 31 | #[derive(Debug)] |
9cc50fc6 | 32 | pub enum DepMessage { |
54a0048b SL |
33 | Read(DepNode<DefId>), |
34 | Write(DepNode<DefId>), | |
35 | PushTask(DepNode<DefId>), | |
36 | PopTask(DepNode<DefId>), | |
9cc50fc6 SL |
37 | PushIgnore, |
38 | PopIgnore, | |
39 | Query, | |
40 | } | |
41 | ||
42 | pub struct DepGraphThreadData { | |
43 | enabled: bool, | |
44 | ||
7453a54e SL |
45 | // Local counter that just tracks how many tasks are pushed onto the |
46 | // stack, so that we still get an error in the case where one is | |
47 | // missing. If dep-graph construction is enabled, we'd get the same | |
48 | // error when processing tasks later on, but that's annoying because | |
49 | // it lacks precision about the source of the error. | |
50 | tasks_pushed: Cell<usize>, | |
51 | ||
9cc50fc6 SL |
52 | // current buffer, where we accumulate messages |
53 | messages: VecCell<DepMessage>, | |
54 | ||
55 | // whence to receive new buffer when full | |
56 | swap_in: Receiver<Vec<DepMessage>>, | |
57 | ||
58 | // where to send buffer when full | |
59 | swap_out: Sender<Vec<DepMessage>>, | |
60 | ||
61 | // where to receive query results | |
54a0048b | 62 | query_in: Receiver<DepGraphQuery<DefId>>, |
9cc50fc6 SL |
63 | } |
64 | ||
65 | const INITIAL_CAPACITY: usize = 2048; | |
66 | ||
67 | impl DepGraphThreadData { | |
68 | pub fn new(enabled: bool) -> DepGraphThreadData { | |
69 | let (tx1, rx1) = mpsc::channel(); | |
70 | let (tx2, rx2) = mpsc::channel(); | |
71 | let (txq, rxq) = mpsc::channel(); | |
7453a54e | 72 | |
9cc50fc6 SL |
73 | if enabled { |
74 | thread::spawn(move || main(rx1, tx2, txq)); | |
75 | } | |
7453a54e | 76 | |
9cc50fc6 SL |
77 | DepGraphThreadData { |
78 | enabled: enabled, | |
7453a54e | 79 | tasks_pushed: Cell::new(0), |
9cc50fc6 SL |
80 | messages: VecCell::with_capacity(INITIAL_CAPACITY), |
81 | swap_in: rx2, | |
82 | swap_out: tx1, | |
83 | query_in: rxq, | |
84 | } | |
85 | } | |
86 | ||
7453a54e SL |
87 | #[inline] |
88 | pub fn enabled(&self) -> bool { | |
89 | self.enabled | |
90 | } | |
91 | ||
9cc50fc6 SL |
92 | /// Sends the current batch of messages to the thread. Installs a |
93 | /// new vector of messages. | |
94 | fn swap(&self) { | |
95 | assert!(self.enabled, "should never swap if not enabled"); | |
96 | ||
97 | // should be a buffer waiting for us (though of course we may | |
98 | // have to wait for depgraph thread to finish processing the | |
99 | // old messages) | |
100 | let new_messages = self.swap_in.recv().unwrap(); | |
101 | assert!(new_messages.is_empty()); | |
102 | ||
103 | // swap in the empty buffer and extract the full one | |
104 | let old_messages = self.messages.swap(new_messages); | |
105 | ||
106 | // send full buffer to depgraph thread to be processed | |
107 | self.swap_out.send(old_messages).unwrap(); | |
108 | } | |
109 | ||
54a0048b | 110 | pub fn query(&self) -> DepGraphQuery<DefId> { |
9cc50fc6 SL |
111 | assert!(self.enabled, "cannot query if dep graph construction not enabled"); |
112 | self.enqueue(DepMessage::Query); | |
113 | self.swap(); | |
114 | self.query_in.recv().unwrap() | |
115 | } | |
116 | ||
117 | /// Enqueue a message to be sent when things are next swapped. (If | |
118 | /// the buffer is full, this may swap.) | |
119 | #[inline] | |
120 | pub fn enqueue(&self, message: DepMessage) { | |
7453a54e SL |
121 | // Regardless of whether dep graph construction is enabled, we |
122 | // still want to check that we always have a valid task on the | |
123 | // stack when a read/write/etc event occurs. | |
124 | match message { | |
125 | DepMessage::Read(_) | DepMessage::Write(_) => | |
126 | if self.tasks_pushed.get() == 0 { | |
127 | self.invalid_message("read/write but no current task") | |
128 | }, | |
129 | DepMessage::PushTask(_) | DepMessage::PushIgnore => | |
130 | self.tasks_pushed.set(self.tasks_pushed.get() + 1), | |
131 | DepMessage::PopTask(_) | DepMessage::PopIgnore => | |
132 | self.tasks_pushed.set(self.tasks_pushed.get() - 1), | |
133 | DepMessage::Query => | |
134 | (), | |
135 | } | |
136 | ||
9cc50fc6 | 137 | if self.enabled { |
7453a54e SL |
138 | self.enqueue_enabled(message); |
139 | } | |
140 | } | |
141 | ||
142 | // Outline this fn since I expect it may want to be inlined | |
143 | // separately. | |
144 | fn enqueue_enabled(&self, message: DepMessage) { | |
145 | let len = self.messages.push(message); | |
146 | if len == INITIAL_CAPACITY { | |
147 | self.swap(); | |
9cc50fc6 SL |
148 | } |
149 | } | |
7453a54e SL |
150 | |
151 | // Outline this too. | |
152 | fn invalid_message(&self, string: &str) { | |
54a0048b | 153 | bug!("{}; see src/librustc/dep_graph/README.md for more information", string) |
7453a54e | 154 | } |
9cc50fc6 SL |
155 | } |
156 | ||
157 | /// Definition of the depgraph thread. | |
158 | pub fn main(swap_in: Receiver<Vec<DepMessage>>, | |
159 | swap_out: Sender<Vec<DepMessage>>, | |
54a0048b | 160 | query_out: Sender<DepGraphQuery<DefId>>) { |
9cc50fc6 SL |
161 | let mut edges = DepGraphEdges::new(); |
162 | ||
163 | // the compiler thread always expects a fresh buffer to be | |
164 | // waiting, so queue one up | |
165 | swap_out.send(Vec::with_capacity(INITIAL_CAPACITY)).unwrap(); | |
166 | ||
167 | // process the buffers from compiler thread as we receive them | |
168 | for mut messages in swap_in { | |
169 | for msg in messages.drain(..) { | |
170 | match msg { | |
171 | DepMessage::Read(node) => edges.read(node), | |
172 | DepMessage::Write(node) => edges.write(node), | |
173 | DepMessage::PushTask(node) => edges.push_task(node), | |
174 | DepMessage::PopTask(node) => edges.pop_task(node), | |
175 | DepMessage::PushIgnore => edges.push_ignore(), | |
176 | DepMessage::PopIgnore => edges.pop_ignore(), | |
177 | DepMessage::Query => query_out.send(edges.query()).unwrap(), | |
178 | } | |
179 | } | |
a7813a04 XL |
180 | if let Err(_) = swap_out.send(messages) { |
181 | // the receiver must have been dropped already | |
182 | break; | |
183 | } | |
9cc50fc6 SL |
184 | } |
185 | } |