mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 18:31:11 +00:00
Hold the POSIX Lock With flock and Leave a Moved-Aside File to the User
An fcntl lock belongs to the process and goes with the first close of any other descriptor to the lock file, so a backup or an export walking the data dir could drop the guard's lock without a trace. On POSIX the guard holds a flock on its own open file instead, which nothing else in the process can release; Windows keeps LockFileEx. The Windows write probe opened the target for writing and took a sharing violation for a refusal, so a file another process merely held open was not saved at all; only a real denial refuses now, since the rename that follows moves an open destination aside. A file moved aside by a refused rename may be the last copy of a file or a file since deleted on purpose, so the sweep logs it and leaves it to the user rather than removing or restoring it. The sweep throttles itself per directory, so a load followed by a save reads each directory once, and the identity check on the lock file runs at most once a second, so a scan of hundreds of presets pays for it once. A config that stays unwritable backs off for longer with each failure in a row, and its backup copy is written after the config, never before. The Windows identity helper is shared with the rename that already computed it, and the header comment that had lost its indentation and its neighbour's description is whole again.
This commit is contained in:
@@ -187,17 +187,14 @@ TEST_CASE("write_file_atomically survives two threads writing one target", "[uti
|
||||
REQUIRE(temporaries == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("remove_stale_temp_files puts back a moved-aside file whose original is missing", "[utils]") {
|
||||
TEST_CASE("remove_stale_temp_files leaves a moved-aside file to the user", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
REQUIRE_FALSE(write_file_atomically((dir.path() / "lost.json.4242.old").string(), "last copy"));
|
||||
REQUIRE_FALSE(write_file_atomically((dir.path() / "lost.json.4242.old").string(), "maybe the last copy"));
|
||||
boost::filesystem::last_write_time(dir.path() / "lost.json.4242.old", std::time(nullptr) - 7200);
|
||||
|
||||
REQUIRE(remove_stale_temp_files(dir.path()) == 0);
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "lost.json"));
|
||||
REQUIRE_FALSE(boost::filesystem::exists(dir.path() / "lost.json.4242.old"));
|
||||
std::string content;
|
||||
load_string_file(dir.path() / "lost.json", content);
|
||||
REQUIRE(content == "last copy");
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "lost.json.4242.old"));
|
||||
REQUIRE_FALSE(boost::filesystem::exists(dir.path() / "lost.json"));
|
||||
}
|
||||
|
||||
TEST_CASE("remove_stale_temp_files removes only old <name>.<pid>.<n>.tmp files", "[utils]") {
|
||||
@@ -211,22 +208,23 @@ TEST_CASE("remove_stale_temp_files removes only old <name>.<pid>.<n>.tmp files",
|
||||
// Just written: possibly another instance's in-flight save, so it stays.
|
||||
REQUIRE_FALSE(write_file_atomically((dir.path() / "g.json.7.2.tmp").string(), "x"));
|
||||
|
||||
SECTION("with a name prefix only matching names go; a numbered backup or a one-segment name is not a temporary") {
|
||||
REQUIRE(remove_stale_temp_files(dir.path(), "a.json") == 2);
|
||||
SECTION("with a name prefix only matching names go; a numbered backup, a one-segment name or an .old is not removed") {
|
||||
REQUIRE(remove_stale_temp_files(dir.path(), "a.json") == 1);
|
||||
REQUIRE_FALSE(boost::filesystem::exists(dir.path() / "a.json.123.7.tmp"));
|
||||
REQUIRE_FALSE(boost::filesystem::exists(dir.path() / "a.json.123.old"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "a.json.123.old"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "a.json.99"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "a.json.12.tmp"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "b.info.4.0.tmp"));
|
||||
}
|
||||
SECTION("without a prefix every stale temporary goes and nothing else") {
|
||||
REQUIRE(remove_stale_temp_files(dir.path()) == 3);
|
||||
REQUIRE(remove_stale_temp_files(dir.path()) == 2);
|
||||
size_t entries = 0;
|
||||
for (auto &entry : boost::filesystem::directory_iterator(dir.path())) {
|
||||
(void) entry;
|
||||
++entries;
|
||||
}
|
||||
REQUIRE(entries == 9);
|
||||
REQUIRE(entries == 10);
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "a.json.123.old"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "a.json"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "h.json.old"));
|
||||
REQUIRE(boost::filesystem::exists(dir.path() / "a.json.99"));
|
||||
|
||||
Reference in New Issue
Block a user