Retry a Failed Lock File, Keep Special Targets in Place and Sweep Stale Temporaries

A lock file that failed to open once stayed unopened for the rest of the
process, so a scanner holding the fresh file for a moment on Windows
silently disabled the lock for the session, and a lock call that throws,
as it does on a share without a lock service, was re-attempted and logged
by every guard. Both are now left alone for the cool-down and then retried.

Renaming a fresh file over the target turned a symlinked preset into a
plain file, reset its permissions to the umask default, and could not
work at all for a settings export to a device or pipe, since a temporary
cannot be created beside /dev/stdout. A target that is not a regular file
is now written in place, an existing target keeps its permissions, and a
directory that refuses the temporary but not the file falls back too.

The inline PhysicalPrinter::save() overload was the one physical printer
writer without the guard. load_info() took the lock redundantly under the
scan guard and, in read-only mode, created user.lock per file; the bundle
metadata reader now honours read_only too. The plugin config, the setup
guide's profile cache and the vendor preset cache wrote through a
temporary by hand; they use the helper, with a binary mode for the cache.
A crash between temporary and rename left a <name>.<pid>.tmp behind for
good; AppConfig::load() and the preset scan remove stale ones while they
hold the lock, and AppConfig::save() names its temporary the same way.
This commit is contained in:
Hanif Koh
2026-09-24 17:26:46 +08:00
parent 651e48723d
commit 18a4d70d41
14 changed files with 216 additions and 97 deletions
+29
View File
@@ -62,6 +62,35 @@ TEST_CASE("InstanceLock is a no-op for an empty path and survives an unwritable
REQUIRE_FALSE(unwritable.locked());
}
TEST_CASE("InstanceLock retries a lock file it could not open once the cool-down passes", "[InstanceLock]")
{
ScopedTemporaryDir dir;
const std::string path = (dir.path() / "later" / "shared.lock").string();
const auto saved_cooldown = InstanceLock::cooldown;
InstanceLock::cooldown = 50ms;
bool before_dir, during_cooldown, after_cooldown;
{
InstanceLock lock(path, 100ms);
before_dir = lock.locked();
}
boost::filesystem::create_directories(dir.path() / "later");
{
InstanceLock lock(path, 100ms);
during_cooldown = lock.locked();
}
std::this_thread::sleep_for(80ms);
{
InstanceLock lock(path, 100ms);
after_cooldown = lock.locked();
}
InstanceLock::cooldown = saved_cooldown;
REQUIRE_FALSE(before_dir);
REQUIRE_FALSE(during_cooldown);
REQUIRE(after_cooldown);
}
TEST_CASE("InstanceLock serialises the threads of one process", "[InstanceLock]")
{
ScopedTemporaryFile lock_file(".lock");