Skip to content

Commit f9e30ff

Browse files
committed
Close watcher by sending EOF
1 parent 19348d3 commit f9e30ff

File tree

2 files changed

+22
-14
lines changed

2 files changed

+22
-14
lines changed

rewatch/src/watcher.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ use futures_timer::Delay;
1414
use notify::event::ModifyKind;
1515
use notify::{Config, Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
1616
use std::collections::HashMap;
17+
use std::io::{IsTerminal, Read};
1718
use std::path::{Path, PathBuf};
1819
use std::sync::Arc;
1920
use std::sync::Mutex;
21+
use std::sync::atomic::{AtomicBool, Ordering};
2022
use std::time::{Duration, Instant, SystemTime};
2123

2224
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
@@ -208,6 +210,19 @@ async fn async_watch(
208210
})
209211
.expect("Error setting Ctrl-C handler");
210212

213+
// When stdin is a pipe (not a TTY), monitor it for EOF so that the
214+
// parent process can signal a graceful shutdown by closing stdin.
215+
let stdin_closed = Arc::new(AtomicBool::new(false));
216+
let stdin_closed_clone = Arc::clone(&stdin_closed);
217+
if !std::io::stdin().is_terminal() {
218+
std::thread::spawn(move || {
219+
let mut buf = [0u8; 1];
220+
// This blocks until EOF (Ok(0)) or an error occurs.
221+
let _ = std::io::stdin().read(&mut buf);
222+
stdin_closed.store(true, Ordering::Relaxed);
223+
});
224+
}
225+
211226
let mut initial_build = true;
212227

213228
// Track file mtimes to deduplicate events.
@@ -217,7 +232,7 @@ async fn async_watch(
217232
let mut last_mtime: HashMap<PathBuf, SystemTime> = HashMap::new();
218233

219234
loop {
220-
if *ctrlc_pressed_clone.lock().unwrap() {
235+
if *ctrlc_pressed_clone.lock().unwrap() || stdin_closed_clone.load(Ordering::Relaxed) {
221236
if show_progress {
222237
println!("\nExiting...");
223238
}

tests/rewatch_tests/helpers/process.mjs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import child_process from "node:child_process";
2-
import { unlink } from "node:fs/promises";
3-
import path from "node:path";
42
import { bsc_exe, rescript_exe, runtimePath } from "./bins.mjs";
53

64
/**
@@ -82,7 +80,7 @@ export function createRescriptCli(cwd, otelEndpoint) {
8280

8381
const proc = child_process.spawn(rescript_exe, ["watch", ...args], {
8482
cwd,
85-
stdio: ["ignore", "pipe", "pipe"],
83+
stdio: ["pipe", "pipe", "pipe"],
8684
env,
8785
signal,
8886
});
@@ -118,21 +116,16 @@ export function createRescriptCli(cwd, otelEndpoint) {
118116
return {
119117
/**
120118
* Stop the watch process gracefully.
121-
* Removes lib/rescript.lock to signal rewatch to exit, then polls
122-
* for the process to exit. Falls back to AbortController if the
123-
* process doesn't exit within the timeout.
119+
* Closes stdin to signal EOF, which rewatch detects and exits.
120+
* Falls back to AbortController if the process doesn't exit
121+
* within the timeout.
124122
* @returns {Promise<void>}
125123
*/
126124
async stop() {
127125
if (exited) return;
128126

129-
// Signal graceful shutdown by removing the lock file
130-
const lockFile = path.join(cwd, "lib", "rescript.lock");
131-
try {
132-
await unlink(lockFile);
133-
} catch {
134-
// Lock file may not exist
135-
}
127+
// Signal graceful shutdown by closing stdin (EOF)
128+
proc.stdin.end();
136129

137130
// Poll for graceful exit
138131
const deadline = Date.now() + 5000;

0 commit comments

Comments
 (0)