mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 20:28:58 +00:00
90 lines
3 KiB
Rust
90 lines
3 KiB
Rust
//! Parse files in parallel and then `Send` them to the main thread for processing.
|
|
#![allow(clippy::print_stdout)]
|
|
#![allow(clippy::future_not_send)] // clippy warns `Allocator` is not `Send`
|
|
#![allow(clippy::redundant_pub_crate)] // comes from `ouroboros`'s macro
|
|
|
|
// Instruction:
|
|
// run `cargo run -p oxc_parser --example multi-thread`
|
|
// or `cargo watch -x "run -p oxc_parser --example multi-thread"`
|
|
|
|
use std::{
|
|
sync::{mpsc, Arc},
|
|
thread,
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
};
|
|
|
|
use oxc_allocator::Allocator;
|
|
use oxc_ast::ast::Program;
|
|
use oxc_parser::Parser;
|
|
use oxc_span::SourceType;
|
|
|
|
/// Wrap the AST for unsafe `Send` and `Sync`
|
|
struct BumpaloProgram<'a>(Program<'a>);
|
|
|
|
#[allow(clippy::non_send_fields_in_send_ty)]
|
|
#[allow(unsafe_code)]
|
|
// SAFETY: It is now our responsibility to never simultaneously mutate the AST across threads.
|
|
unsafe impl<'a> Send for BumpaloProgram<'a> {}
|
|
#[allow(unsafe_code)]
|
|
// SAFETY: It is now our responsibility to never simultaneously mutate the AST across threads.
|
|
unsafe impl<'a> Sync for BumpaloProgram<'a> {}
|
|
|
|
/// `ouroboros` is used to "bind" the allocator and AST together to remove the lifetime.
|
|
#[ouroboros::self_referencing]
|
|
struct AST {
|
|
index: usize,
|
|
allocator: Allocator,
|
|
source_text: Arc<str>,
|
|
#[borrows(allocator, source_text)]
|
|
#[covariant]
|
|
ast: &'this BumpaloProgram<'this>,
|
|
}
|
|
|
|
/// Example output:
|
|
/// ```
|
|
/// sent ast(0) in ThreadId(2) at 1691652865800
|
|
/// sent ast(1) in ThreadId(3) at 1691652865801
|
|
/// sent ast(2) in ThreadId(4) at 1691652865801
|
|
/// received ast(0) in ThreadId(1) at 1691652865801
|
|
/// received ast(1) in ThreadId(1) at 1691652865801
|
|
/// received ast(2) in ThreadId(1) at 1691652865801
|
|
/// ```
|
|
fn main() {
|
|
let (ast_tx, ast_rx) = mpsc::channel::<AST>();
|
|
let sources = (0..3).map(|i| Arc::from(format!("const a = {i};"))).collect::<Vec<_>>();
|
|
|
|
// Construct AST from different threads
|
|
for (index, source_text) in sources.iter().enumerate() {
|
|
let ast_tx = ast_tx.clone();
|
|
let source_text = Arc::clone(source_text);
|
|
|
|
_ = thread::spawn(move || {
|
|
let ast = ASTBuilder {
|
|
index,
|
|
allocator: Allocator::default(),
|
|
source_text,
|
|
ast_builder: |allocator, source_text| {
|
|
let ret = Parser::new(allocator, source_text, SourceType::default()).parse();
|
|
allocator.alloc(BumpaloProgram(ret.program))
|
|
},
|
|
}
|
|
.build();
|
|
|
|
ast_tx.send(ast).unwrap();
|
|
println!("sent ast({index}) in {:?} at {}", thread::current().id(), timestamp());
|
|
})
|
|
.join();
|
|
}
|
|
|
|
// Collect all ASTs on the main thread
|
|
for _ in 0..sources.len() {
|
|
let ast = ast_rx.recv().unwrap();
|
|
let index = ast.borrow_index();
|
|
println!("received ast({index}) in {:?} at {}", thread::current().id(), timestamp());
|
|
println!("AST span: {:?}", ast.borrow_ast().0.span);
|
|
}
|
|
}
|
|
|
|
fn timestamp() -> u128 {
|
|
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis()
|
|
}
|