Skip to content

Commit a3910c6

Browse files
committed
syntax: Drop Parse on separate thread
Rowan's green nodes are super drop heavy and as lru eviction happens on cancellation this can block for quite some time, especially after cache priming
1 parent 4bf516e commit a3910c6

File tree

1 file changed

+31
-8
lines changed

1 file changed

+31
-8
lines changed

crates/syntax/src/lib.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ pub use smol_str::{SmolStr, SmolStrBuilder, ToSmolStr, format_smolstr};
6767
/// files.
6868
#[derive(Debug, PartialEq, Eq)]
6969
pub struct Parse<T> {
70-
green: GreenNode,
70+
green: Option<GreenNode>,
7171
errors: Option<Arc<[SyntaxError]>>,
7272
_ty: PhantomData<fn() -> T>,
7373
}
@@ -81,14 +81,14 @@ impl<T> Clone for Parse<T> {
8181
impl<T> Parse<T> {
8282
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
8383
Parse {
84-
green,
84+
green: Some(green),
8585
errors: if errors.is_empty() { None } else { Some(errors.into()) },
8686
_ty: PhantomData,
8787
}
8888
}
8989

9090
pub fn syntax_node(&self) -> SyntaxNode {
91-
SyntaxNode::new_root(self.green.clone())
91+
SyntaxNode::new_root(self.green.as_ref().unwrap().clone())
9292
}
9393

9494
pub fn errors(&self) -> Vec<SyntaxError> {
@@ -100,8 +100,10 @@ impl<T> Parse<T> {
100100

101101
impl<T: AstNode> Parse<T> {
102102
/// Converts this parse result into a parse result for an untyped syntax tree.
103-
pub fn to_syntax(self) -> Parse<SyntaxNode> {
104-
Parse { green: self.green, errors: self.errors, _ty: PhantomData }
103+
pub fn to_syntax(mut self) -> Parse<SyntaxNode> {
104+
let green = self.green.take();
105+
let errors = self.errors.take();
106+
Parse { green, errors, _ty: PhantomData }
105107
}
106108

107109
/// Gets the parsed syntax tree as a typed ast node.
@@ -124,9 +126,9 @@ impl<T: AstNode> Parse<T> {
124126
}
125127

126128
impl Parse<SyntaxNode> {
127-
pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
129+
pub fn cast<N: AstNode>(mut self) -> Option<Parse<N>> {
128130
if N::cast(self.syntax_node()).is_some() {
129-
Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
131+
Some(Parse { green: self.green.take(), errors: self.errors.take(), _ty: PhantomData })
130132
} else {
131133
None
132134
}
@@ -162,7 +164,7 @@ impl Parse<SourceFile> {
162164
edition,
163165
)
164166
.map(|(green_node, errors, _reparsed_range)| Parse {
165-
green: green_node,
167+
green: Some(green_node),
166168
errors: if errors.is_empty() { None } else { Some(errors.into()) },
167169
_ty: PhantomData,
168170
})
@@ -198,6 +200,27 @@ impl ast::Expr {
198200
}
199201
}
200202

203+
impl<T> Drop for Parse<T> {
204+
fn drop(&mut self) {
205+
let Some(green) = self.green.take() else {
206+
return;
207+
};
208+
static PARSE_DROP_THREAD: std::sync::OnceLock<std::sync::mpsc::Sender<GreenNode>> =
209+
std::sync::OnceLock::new();
210+
PARSE_DROP_THREAD
211+
.get_or_init(|| {
212+
let (sender, receiver) = std::sync::mpsc::channel::<GreenNode>();
213+
std::thread::Builder::new()
214+
.name("ParseNodeDropper".to_owned())
215+
.spawn(move || receiver.iter().for_each(drop))
216+
.unwrap();
217+
sender
218+
})
219+
.send(green)
220+
.unwrap();
221+
}
222+
}
223+
201224
/// `SourceFile` represents a parse tree for a single Rust file.
202225
pub use crate::ast::SourceFile;
203226

0 commit comments

Comments
 (0)