|
1 | | -use std::sync::{Arc, atomic::Ordering, mpsc::channel}; |
| 1 | +use std::{ |
| 2 | + sync::{Arc, atomic::Ordering, mpsc::channel}, |
| 3 | + time::Duration, |
| 4 | +}; |
2 | 5 |
|
3 | 6 | use napi::{ |
4 | 7 | Status, |
@@ -326,8 +329,40 @@ fn wrap_create_workspace(cb: JsCreateWorkspaceCb) -> ExternalLinterCreateWorkspa |
326 | 329 | } |
327 | 330 |
|
328 | 331 | /// Wrap `destroyWorkspace` JS callback as a normal Rust function. |
| 332 | +/// |
| 333 | +/// The JS-side `destroyWorkspace` function is synchronous, but it's wrapped in a `ThreadsafeFunction`, |
| 334 | +/// so cannot be called synchronously. Use an `mpsc::channel` to wait for the result from JS side. |
| 335 | +/// |
| 336 | +/// Uses a timeout to prevent indefinite blocking during shutdown, which can cause issues |
| 337 | +/// in multi-root workspace scenarios where multiple workspaces are being destroyed concurrently. |
329 | 338 | fn wrap_destroy_workspace(cb: JsDestroyWorkspaceCb) -> ExternalLinterDestroyWorkspaceCb { |
330 | 339 | Arc::new(Box::new(move |workspace_uri| { |
331 | | - let _ = cb.call(workspace_uri, ThreadsafeFunctionCallMode::NonBlocking); |
| 340 | + let (tx, rx) = channel(); |
| 341 | + |
| 342 | + // Send data to JS |
| 343 | + let status = cb.call_with_return_value( |
| 344 | + workspace_uri, |
| 345 | + ThreadsafeFunctionCallMode::NonBlocking, |
| 346 | + move |result, _env| { |
| 347 | + // Ignore send errors - the receiver may have timed out |
| 348 | + let _ = tx.send(result); |
| 349 | + Ok(()) |
| 350 | + }, |
| 351 | + ); |
| 352 | + |
| 353 | + if status == Status::Ok { |
| 354 | + // Use a timeout to prevent blocking indefinitely during shutdown. |
| 355 | + // If JS side doesn't respond within the timeout, we proceed with shutdown anyway. |
| 356 | + match rx.recv_timeout(Duration::from_secs(5)) { |
| 357 | + // Destroying workspace succeeded |
| 358 | + Ok(Ok(())) => Ok(()), |
| 359 | + // `destroyWorkspace` threw an error |
| 360 | + Ok(Err(err)) => Err(format!("`destroyWorkspace` threw an error: {err}")), |
| 361 | + // Timeout or sender dropped - proceed with shutdown |
| 362 | + Err(_) => Ok(()), |
| 363 | + } |
| 364 | + } else { |
| 365 | + Err(format!("Failed to schedule `destroyWorkspace` callback: {status:?}")) |
| 366 | + } |
332 | 367 | })) |
333 | 368 | } |
0 commit comments