Merge PR #2725: LRC-LIB-CATALOG-LOCK: quitting on macOS removes the lock's owner note

This commit is contained in:
aiongg
2026-10-31 02:08:09 +00:00
5 changed files with 81 additions and 2 deletions
+6
View File
@@ -116,6 +116,12 @@ impl eframe::App for App {
if !self.0.shutdown(EXIT_WAIT) {
log::warn!("quit: background work was still running after {EXIT_WAIT:?}");
}
// macOS only: the process ends below without running destructors, and only the lock's
// `Drop` removes `catalog.lock.owner` (the OS frees the lock, not the note), so let go of
// the library's lock here, saved or not (we leave either way). Elsewhere eframe drops the
// app after this and the session's last writes (face data) still go out under the lock.
#[cfg(target_os = "macos")]
self.0.session.release_library_lock();
// macOS (#1331): end the process here, before eframe tears the window down, as Quit from
// the menu bar already does (PR #444). AccessKit's macOS adapter subclasses the window's
// content view and restores its class on teardown; AppKit's Touch Bar support observes
+35
View File
@@ -462,3 +462,38 @@ fn on_exit_ends_the_process_on_macos() {
assert!(stdout.contains("calling on_exit"), "the child didn't run the test: {stdout}");
assert_eq!(stdout.contains("on_exit returned"), !cfg!(target_os = "macos"), "{stdout}");
}
/// Quitting leaves no `catalog.lock.owner` behind, though the process ends without running
/// destructors (macOS): `on_exit` releases the library's lock itself. The child opens a library
/// and calls `on_exit` (on macOS that ends it); the parent looks at the folder afterwards. Other
/// systems drop the app after `on_exit`, which removes the note anyway.
#[test]
fn on_exit_removes_the_lock_owner_note() {
if let Some(dir) = std::env::var_os("LIGHTCRAFT_TEST_LOCK_DIR") {
let mut session = lightcraft_engine::Session::new();
session.open_library(&dir, false).unwrap();
let mut app = App(
LightcraftApp::new(session, Services::default()),
PrefsWriter::default(),
#[cfg(target_os = "macos")]
None,
);
eframe::App::on_exit(&mut app, None);
return;
}
let dir = std::env::temp_dir().join(format!("lc-app-lock-owner-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let out = std::process::Command::new(std::env::current_exe().unwrap())
.args(["--exact", "tests::on_exit_removes_the_lock_owner_note", "--nocapture"])
.env("LIGHTCRAFT_TEST_LOCK_DIR", &dir)
.env("LIGHTCRAFT_NO_PREFS", "1")
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(out.status.success(), "{stdout}");
assert!(dir.join("catalog.snap").exists(), "the child didn't open and save the library: {stdout}");
assert!(!dir.join("catalog.lock.owner").exists(), "the owner note was left behind");
assert!(lightcraft_engine::Session::new().open_library(&dir, false).is_ok(), "the library is still locked");
let _ = std::fs::remove_dir_all(&dir);
}