diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs index 654bd9943eb..5054bc7643d 100644 --- a/crates/proc_macro_api/src/lib.rs +++ b/crates/proc_macro_api/src/lib.rs @@ -20,7 +20,7 @@ use tt::{SmolStr, Subtree}; -use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; +use crate::process::ProcMacroProcessSrv; pub use rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; pub use version::{read_dylib_info, RustCInfo}; @@ -64,16 +64,16 @@ fn expand( #[derive(Debug)] pub struct ProcMacroClient { process: Arc, - thread: ProcMacroProcessThread, } impl ProcMacroClient { + /// Spawns an external process as the proc macro server and returns a client connected to it. pub fn extern_process( process_path: PathBuf, args: impl IntoIterator>, ) -> io::Result { - let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?; - Ok(ProcMacroClient { process: Arc::new(process), thread }) + let process = ProcMacroProcessSrv::run(process_path, args)?; + Ok(ProcMacroClient { process: Arc::new(process) }) } pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec { diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs index 91d6a3811be..592c1282c0b 100644 --- a/crates/proc_macro_api/src/process.rs +++ b/crates/proc_macro_api/src/process.rs @@ -3,13 +3,13 @@ use std::{ convert::{TryFrom, TryInto}, ffi::{OsStr, OsString}, + fmt, io::{self, BufRead, BufReader, Write}, path::{Path, PathBuf}, - process::{Child, Command, Stdio}, - sync::{Arc, Weak}, + process::{Child, ChildStdin, ChildStdout, Command, Stdio}, + sync::Mutex, }; -use crossbeam_channel::{bounded, Receiver, Sender}; use stdx::JodChild; use crate::{ @@ -17,38 +17,31 @@ rpc::{ListMacrosResult, ListMacrosTask, ProcMacroKind}, }; -#[derive(Debug, Default)] pub(crate) struct ProcMacroProcessSrv { - inner: Weak>, + process: Mutex, + stdio: Mutex<(ChildStdin, BufReader)>, } -#[derive(Debug)] -pub(crate) struct ProcMacroProcessThread { - // XXX: drop order is significant - sender: Arc>, - handle: jod_thread::JoinHandle<()>, +impl fmt::Debug for ProcMacroProcessSrv { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ProcMacroProcessSrv").field("process", &self.process).finish() + } } impl ProcMacroProcessSrv { pub(crate) fn run( process_path: PathBuf, args: impl IntoIterator>, - ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> { - let process = Process::run(process_path, args)?; + ) -> io::Result { + let mut process = Process::run(process_path, args)?; + let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); - let (task_tx, task_rx) = bounded(0); - let handle = jod_thread::Builder::new() - .name("ProcMacroClient".to_owned()) - .spawn(move || { - client_loop(task_rx, process); - }) - .expect("failed to spawn thread"); + let srv = ProcMacroProcessSrv { + process: Mutex::new(process), + stdio: Mutex::new((stdin, stdout)), + }; - let task_tx = Arc::new(task_tx); - let srv = ProcMacroProcessSrv { inner: Arc::downgrade(&task_tx) }; - let thread = ProcMacroProcessThread { handle, sender: task_tx }; - - Ok((thread, srv)) + Ok(srv) } pub(crate) fn find_proc_macros( @@ -65,18 +58,27 @@ pub(crate) fn send_task(&self, req: Request) -> Result where R: TryFrom, { - let (result_tx, result_rx) = bounded(0); - let sender = match self.inner.upgrade() { - None => return Err(tt::ExpansionError::Unknown("proc macro process is closed".into())), - Some(it) => it, - }; - sender - .send(Task { req, result_tx }) - .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?; + let mut guard = self.stdio.lock().unwrap_or_else(|e| e.into_inner()); + let stdio = &mut *guard; + let (stdin, stdout) = (&mut stdio.0, &mut stdio.1); - let res = result_rx - .recv() - .map_err(|_| tt::ExpansionError::Unknown("proc macro server crashed".into()))?; + let mut buf = String::new(); + let res = match send_request(stdin, stdout, req, &mut buf) { + Ok(res) => res, + Err(err) => { + let mut process = self.process.lock().unwrap_or_else(|e| e.into_inner()); + log::error!( + "proc macro server crashed, server process state: {:?}, server request error: {:?}", + process.child.try_wait(), + err + ); + let res = Response::Error(ResponseError { + code: ErrorCode::ServerErrorEnd, + message: "proc macro server crashed".into(), + }); + Some(res) + } + }; match res { Some(Response::Error(err)) => Err(tt::ExpansionError::ExpansionError(err.message)), @@ -88,37 +90,7 @@ pub(crate) fn send_task(&self, req: Request) -> Result } } -fn client_loop(task_rx: Receiver, mut process: Process) { - let (mut stdin, mut stdout) = process.stdio().expect("couldn't access child stdio"); - - let mut buf = String::new(); - - for Task { req, result_tx } in task_rx { - match send_request(&mut stdin, &mut stdout, req, &mut buf) { - Ok(res) => result_tx.send(res).unwrap(), - Err(err) => { - log::error!( - "proc macro server crashed, server process state: {:?}, server request error: {:?}", - process.child.try_wait(), - err - ); - let res = Response::Error(ResponseError { - code: ErrorCode::ServerErrorEnd, - message: "proc macro server crashed".into(), - }); - result_tx.send(res.into()).unwrap(); - // Exit the thread. - break; - } - } - } -} - -struct Task { - req: Request, - result_tx: Sender>, -} - +#[derive(Debug)] struct Process { child: JodChild, } @@ -133,7 +105,7 @@ fn run( Ok(Process { child }) } - fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { + fn stdio(&mut self) -> Option<(ChildStdin, BufReader)> { let stdin = self.child.stdin.take()?; let stdout = self.child.stdout.take()?; let read = BufReader::new(stdout); diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 2963484faef..e83d5db437d 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -111,6 +111,7 @@ fn drop(&mut self) { } #[cfg_attr(not(target_arch = "wasm32"), repr(transparent))] +#[derive(Debug)] pub struct JodChild(pub std::process::Child); impl ops::Deref for JodChild {