Skip to content

Commit 52b690d

Browse files
committed
Fix watch startup build locking (#8413)
* Fix watch startup build locking * Fix * Fix
1 parent 4553473 commit 52b690d

2 files changed

Lines changed: 59 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
- Rewatch: preserve warnings after atomic-save full rebuilds. https://github.com/rescript-lang/rescript/pull/8358
3131
- Fix type lowering for `dict{}` and `async`, so you don't need to annotate one extra time when the type is known. https://github.com/rescript-lang/rescript/pull/8359
3232
- Fix issue where warning 56 would blow up with `dict{}` patterns. https://github.com/rescript-lang/rescript/pull/8403
33-
- Acquire rewatch build lock before initialization. https://github.com/rescript-lang/rescript/pull/8409
34-
- Close remaining rewatch build.lock gaps. https://github.com/rescript-lang/rescript/pull/8410
33+
- Rewatch build lock fixes. https://github.com/rescript-lang/rescript/pull/8409 https://github.com/rescript-lang/rescript/pull/8410 https://github.com/rescript-lang/rescript/pull/8413
3534
- Rewatch: treat transitive workspace dependencies as local packages in monorepo roots. https://github.com/rescript-lang/rescript/pull/8411
3635

3736
#### :nail_care: Polish

rewatch/src/watcher.rs

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use notify::event::ModifyKind;
1515
use notify::{Config, Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
1616
use std::path::{Path, PathBuf};
1717
use std::sync::Arc;
18-
use std::sync::Mutex;
18+
use std::sync::atomic::{AtomicBool, Ordering};
1919
use std::time::{Duration, Instant};
2020

2121
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
@@ -25,6 +25,9 @@ enum CompileType {
2525
None,
2626
}
2727

28+
type WatchPaths = Vec<(PathBuf, RecursiveMode)>;
29+
type StartupBuildResult = (BuildCommandState, WatchPaths, Option<Instant>);
30+
2831
fn is_rescript_file(path_buf: &Path) -> bool {
2932
let extension = path_buf.extension().and_then(|ext| ext.to_str());
3033

@@ -204,11 +207,24 @@ fn carry_forward_compile_warnings(previous: &BuildCommandState, next: &mut Build
204207
}
205208
}
206209

210+
fn cleanup_before_watch_exit(
211+
path: &Path,
212+
build_state: &BuildCommandState,
213+
show_progress: bool,
214+
message: &str,
215+
) {
216+
if show_progress {
217+
println!("{message}");
218+
}
219+
build::with_build_lock(path, || clean::cleanup_after_build(build_state));
220+
}
221+
207222
struct AsyncWatchArgs<'a> {
208223
watcher: &'a mut RecommendedWatcher,
209224
current_watch_paths: Vec<(PathBuf, RecursiveMode)>,
210225
initial_build_state: BuildCommandState,
211226
q: Arc<FifoQueue<Result<Event, Error>>>,
227+
ctrlc_pressed: Arc<AtomicBool>,
212228
path: &'a Path,
213229
show_progress: bool,
214230
filter: &'a Option<regex::Regex>,
@@ -223,6 +239,7 @@ async fn async_watch(
223239
mut current_watch_paths,
224240
initial_build_state,
225241
q,
242+
ctrlc_pressed,
226243
path,
227244
show_progress,
228245
filter,
@@ -233,23 +250,10 @@ async fn async_watch(
233250
) -> Result<()> {
234251
let mut build_state = initial_build_state;
235252
let mut needs_compile_type = CompileType::None;
236-
// create a mutex to capture if ctrl-c was pressed
237-
let ctrlc_pressed = Arc::new(Mutex::new(false));
238-
let ctrlc_pressed_clone = Arc::clone(&ctrlc_pressed);
239-
240-
ctrlc::set_handler(move || {
241-
let pressed = Arc::clone(&ctrlc_pressed);
242-
let mut pressed = pressed.lock().unwrap();
243-
*pressed = true;
244-
})
245-
.expect("Error setting Ctrl-C handler");
246253

247254
loop {
248-
if *ctrlc_pressed_clone.lock().unwrap() {
249-
if show_progress {
250-
println!("\nExiting...");
251-
}
252-
build::with_build_lock(path, || clean::cleanup_after_build(&build_state));
255+
if ctrlc_pressed.load(Ordering::SeqCst) {
256+
cleanup_before_watch_exit(path, &build_state, show_progress, "\nExiting...");
253257
break Ok(());
254258
}
255259
let mut events: Vec<Event> = vec![];
@@ -271,10 +275,12 @@ async fn async_watch(
271275
.any(|path| path.ends_with(LockKind::Watch.file_name()))
272276
&& let EventKind::Remove(_) = event.kind
273277
{
274-
if show_progress {
275-
println!("\nExiting... (lockfile removed)");
276-
}
277-
build::with_build_lock(path, || clean::cleanup_after_build(&build_state));
278+
cleanup_before_watch_exit(
279+
path,
280+
&build_state,
281+
show_progress,
282+
"\nExiting... (lockfile removed)",
283+
);
278284
return Ok(());
279285
}
280286

@@ -500,9 +506,16 @@ pub fn start(
500506

501507
let path = Path::new(folder);
502508

509+
let ctrlc_pressed = Arc::new(AtomicBool::new(false));
510+
let ctrlc_pressed_for_handler = Arc::clone(&ctrlc_pressed);
511+
ctrlc::set_handler(move || {
512+
ctrlc_pressed_for_handler.store(true, Ordering::SeqCst);
513+
})
514+
.expect("Error setting Ctrl-C handler");
515+
503516
// Initialization can clean previous build artifacts, so it has to be serialized
504517
// with the initial compile too.
505-
let (build_state, current_watch_paths): (BuildCommandState, Vec<(PathBuf, RecursiveMode)>) =
518+
let (build_state, current_watch_paths, initial_compile_timing): StartupBuildResult =
506519
build::with_build_lock(path, || {
507520
let mut build_state = build::initialize_build(
508521
None,
@@ -519,7 +532,7 @@ pub fn start(
519532
register_watches(&mut watcher, &current_watch_paths);
520533

521534
let timing_total = Instant::now();
522-
if build::incremental_build_without_lock(
535+
let initial_compile_timing = if build::incremental_build_without_lock(
523536
&mut build_state,
524537
None,
525538
true,
@@ -530,26 +543,40 @@ pub fn start(
530543
)
531544
.is_ok()
532545
{
533-
finish_successful_watch_compile(
534-
after_build.clone(),
535-
timing_total,
536-
show_progress,
537-
plain_output,
538-
"Finished initial compilation",
539-
);
540-
}
546+
Some(timing_total)
547+
} else {
548+
None
549+
};
541550

542-
Ok::<(BuildCommandState, Vec<(PathBuf, RecursiveMode)>), anyhow::Error>((
551+
Ok::<StartupBuildResult, anyhow::Error>((
543552
build_state,
544553
current_watch_paths,
554+
initial_compile_timing,
545555
))
546556
})?;
547557

558+
if ctrlc_pressed.load(Ordering::SeqCst) {
559+
cleanup_before_watch_exit(path, &build_state, show_progress, "\nExiting...");
560+
return Ok(());
561+
}
562+
563+
// Run after-build outside build.lock. Hooks may invoke ReScript commands that need the same lock.
564+
if let Some(timing_total) = initial_compile_timing {
565+
finish_successful_watch_compile(
566+
after_build.clone(),
567+
timing_total,
568+
show_progress,
569+
plain_output,
570+
"Finished initial compilation",
571+
);
572+
}
573+
548574
async_watch(AsyncWatchArgs {
549575
watcher: &mut watcher,
550576
current_watch_paths,
551577
initial_build_state: build_state,
552578
q: consumer,
579+
ctrlc_pressed,
553580
path,
554581
show_progress,
555582
filter,

0 commit comments

Comments
 (0)