Skip to content

Commit e5bb727

Browse files
committed
fix(build): try atomic no-replace for absent status records
1 parent c6d4a73 commit e5bb727

4 files changed

Lines changed: 33 additions & 2 deletions

File tree

‎src/build/schedule/compiler_transaction.cpp‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,25 @@ bool replace_record(const fs::path& path,std::string_view bytes,std::string* dia
3232
std::error_code sourceError, targetError;
3333
const auto source=fs::symlink_status(temporary,sourceError);
3434
const auto target=fs::symlink_status(path,targetError);
35-
*diagnostic=std::format("rename temporary (condition {}, native {}; source regular {} ec {}; target exists {} ec {})",
35+
// A final status is a new name. If the replace form itself was
36+
// refused as invalid while that name is absent, try the atomic
37+
// no-replace form on the exact same source and destination. Its
38+
// refusal is preserved as evidence rather than hidden by a loop.
39+
int noReplaceCode=0;
40+
const bool targetAbsent=target.type()==fs::file_type::not_found
41+
|| targetError==std::errc::no_such_file_or_directory;
42+
if(renamed.error().code==os::errc::invalid && fs::is_regular_file(source)
43+
&& targetAbsent) {
44+
auto noReplace=os::fs::rename(temporary,path,false);
45+
if(noReplace) {
46+
*diagnostic="replace form refused absent target; no-replace published";
47+
return true;
48+
}
49+
noReplaceCode=static_cast<int>(noReplace.error().code);
50+
}
51+
*diagnostic=std::format("rename temporary (condition {}, native {}; source regular {} ec {}; target exists {} ec {}; no-replace condition {})",
3652
static_cast<int>(renamed.error().code),renamed.error().native,
37-
fs::is_regular_file(source),sourceError.value(),fs::exists(target),targetError.value());
53+
fs::is_regular_file(source),sourceError.value(),fs::exists(target),targetError.value(),noReplaceCode);
3854
}
3955
} else if(!written) {
4056
fail("write/flush temporary");

‎src/build/schedule/compiler_transaction.cppm‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
export module mcpp.build.schedule.compiler_transaction;
44
import std;
55
import mcpplibs.os.fs;
6+
import mcpplibs.os.error;
67
import mcpplibs.os.process;
78
import mcpplibs.os.ipc;
89
import mcpplibs.os.dirs;

‎src/build/schedule/detach_codegen.cppm‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,8 @@ int supervise(const std::filesystem::path& slot,
893893
record_supervisor_error(slot,std::format("cannot publish compiler final status: {}",status_failure));
894894
return 1;
895895
}
896+
if(!status_failure.empty())
897+
std::println(std::cerr,"mcpp: {}",status_failure);
896898
if(!transactions::release_reservation(permit->reservation,attempt.directory)) {
897899
record_supervisor_error(slot,"cannot release compiler reservation");return 1;
898900
}

‎tests/unit/test_compiler_transaction.cpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import mcpp.run;
66
import mcpp.invocation;
77
import mcpp.runtime.elf;
88
import mcpplibs.os.platform;
9+
import mcpplibs.os.fs;
910

1011
namespace tx = mcpp::build::schedule::transaction;
1112
namespace fs = std::filesystem;
@@ -250,6 +251,17 @@ TEST(CompilerTransaction, ReaderHeldTargetKeepsAnAtomicRecord) {
250251
EXPECT_EQ(Transaction::contents(record),replaced?"new":"old");
251252
EXPECT_EQ(std::string(std::istreambuf_iterator<char>(held),std::istreambuf_iterator<char>()),"old");
252253
}
254+
TEST(CompilerTransaction, NoReplacePublishesOnlyAnAbsentRecord) {
255+
Transaction t;
256+
const auto source=t.root/"new";
257+
const auto target=t.root/"status";
258+
std::ofstream(source)<<"ready";
259+
ASSERT_TRUE(mcpplibs::os::fs::rename(source,target,false));
260+
EXPECT_EQ(Transaction::contents(target),"ready");
261+
std::ofstream(source)<<"later";
262+
EXPECT_FALSE(mcpplibs::os::fs::rename(source,target,false));
263+
EXPECT_EQ(Transaction::contents(target),"ready");
264+
}
253265
TEST(CompilerTransaction, RecordOpenAppearanceDriver) {
254266
if(!mcpp::invocation::get("MCPP_TEST_RECORD_OPEN_DRIVER"))GTEST_SKIP()<<"subprocess driver";
255267
auto record=tx::read("record","schema");

0 commit comments

Comments
 (0)