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
This commit is contained in:
Lukas Wirth 2025-11-17 10:36:54 +01:00
parent 4bf516ee5a
commit a3910c6f14

View file

@ -67,7 +67,7 @@ pub use smol_str::{SmolStr, SmolStrBuilder, ToSmolStr, format_smolstr};
/// files. /// files.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> { pub struct Parse<T> {
green: GreenNode, green: Option<GreenNode>,
errors: Option<Arc<[SyntaxError]>>, errors: Option<Arc<[SyntaxError]>>,
_ty: PhantomData<fn() -> T>, _ty: PhantomData<fn() -> T>,
} }
@ -81,14 +81,14 @@ impl<T> Clone for Parse<T> {
impl<T> Parse<T> { impl<T> Parse<T> {
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> { fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
Parse { Parse {
green, green: Some(green),
errors: if errors.is_empty() { None } else { Some(errors.into()) }, errors: if errors.is_empty() { None } else { Some(errors.into()) },
_ty: PhantomData, _ty: PhantomData,
} }
} }
pub fn syntax_node(&self) -> SyntaxNode { pub fn syntax_node(&self) -> SyntaxNode {
SyntaxNode::new_root(self.green.clone()) SyntaxNode::new_root(self.green.as_ref().unwrap().clone())
} }
pub fn errors(&self) -> Vec<SyntaxError> { pub fn errors(&self) -> Vec<SyntaxError> {
@ -100,8 +100,10 @@ impl<T> Parse<T> {
impl<T: AstNode> Parse<T> { impl<T: AstNode> Parse<T> {
/// Converts this parse result into a parse result for an untyped syntax tree. /// Converts this parse result into a parse result for an untyped syntax tree.
pub fn to_syntax(self) -> Parse<SyntaxNode> { pub fn to_syntax(mut self) -> Parse<SyntaxNode> {
Parse { green: self.green, errors: self.errors, _ty: PhantomData } let green = self.green.take();
let errors = self.errors.take();
Parse { green, errors, _ty: PhantomData }
} }
/// Gets the parsed syntax tree as a typed ast node. /// Gets the parsed syntax tree as a typed ast node.
@ -124,9 +126,9 @@ impl<T: AstNode> Parse<T> {
} }
impl Parse<SyntaxNode> { impl Parse<SyntaxNode> {
pub fn cast<N: AstNode>(self) -> Option<Parse<N>> { pub fn cast<N: AstNode>(mut self) -> Option<Parse<N>> {
if N::cast(self.syntax_node()).is_some() { if N::cast(self.syntax_node()).is_some() {
Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData }) Some(Parse { green: self.green.take(), errors: self.errors.take(), _ty: PhantomData })
} else { } else {
None None
} }
@ -162,7 +164,7 @@ impl Parse<SourceFile> {
edition, edition,
) )
.map(|(green_node, errors, _reparsed_range)| Parse { .map(|(green_node, errors, _reparsed_range)| Parse {
green: green_node, green: Some(green_node),
errors: if errors.is_empty() { None } else { Some(errors.into()) }, errors: if errors.is_empty() { None } else { Some(errors.into()) },
_ty: PhantomData, _ty: PhantomData,
}) })
@ -198,6 +200,27 @@ impl ast::Expr {
} }
} }
impl<T> Drop for Parse<T> {
fn drop(&mut self) {
let Some(green) = self.green.take() else {
return;
};
static PARSE_DROP_THREAD: std::sync::OnceLock<std::sync::mpsc::Sender<GreenNode>> =
std::sync::OnceLock::new();
PARSE_DROP_THREAD
.get_or_init(|| {
let (sender, receiver) = std::sync::mpsc::channel::<GreenNode>();
std::thread::Builder::new()
.name("ParseNodeDropper".to_owned())
.spawn(move || receiver.iter().for_each(drop))
.unwrap();
sender
})
.send(green)
.unwrap();
}
}
/// `SourceFile` represents a parse tree for a single Rust file. /// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile; pub use crate::ast::SourceFile;