Open the Lock File Per Guard and Keep Directory Removal Out of the Lock

The lock file stayed open for the life of the process, which pinned the
data dir on Windows so the preset tests leaked a directory each, and
asked for a check that the file behind the path was still the one
opened. The outermost guard opens the file and closes it when it goes,
so whatever is at the path is what gets locked and nothing stays open
between saves; the identity check and its interval are gone with it.

Removing a bundle or a user folder held the lock for the whole tree, and
the wait for the in-process mutex is bounded only by the longest
critical section, so a save on the GUI thread could wait for a tree of
files to go on a slow share. The tree is renamed aside under the lock in
one step and removed afterwards. A bundle import extracts into a
per-process folder, so two instances importing at once do not clear
each other's extraction. A lock file that cannot be opened or locked
backs off for longer with each failure, like a timed-out wait does; the
preset scan's three removal sites share one helper; a restore that fails
after a refused rename names where the previous content went; and the
warnings about a set-aside file and an in-place fallback are logged once
per file.
This commit is contained in:
Hanif Koh
2026-09-25 01:05:54 +08:00
parent 5603ed66c0
commit 6acaf7356a
9 changed files with 113 additions and 111 deletions
+1 -4
View File
@@ -109,7 +109,6 @@ TEST_CASE("InstanceLock reopens a lock file that was replaced on disk", "[Instan
{
ScopedTemporaryFile lock_file(".lock");
const std::string path = lock_file.string();
ScopedStaticValue interval(InstanceLock::identity_check_interval, 0ms);
{
InstanceLock lock(path);
REQUIRE(lock.locked());
@@ -118,9 +117,7 @@ TEST_CASE("InstanceLock reopens a lock file that was replaced on disk", "[Instan
boost::filesystem::remove(path);
InstanceLock lock(path);
REQUIRE(lock.locked());
// Only a reopen recreates the file; a guard still holding the unlinked one
// would leave the path missing. (The inode number itself may be reused once
// the old handle is closed, so it is no proof either way.)
// Each outermost guard opens the file afresh, so the deleted path is back.
REQUIRE(boost::filesystem::exists(path));
}