Skip to content

Commit c3f815b

Browse files
anjennerdurin42vitalybuka
authored
Modify the localCache API to require an explicit commit on CachedFile… (#136121)
…Stream. CachedFileStream has previously performed the commit step in its destructor, but this means its only recourse for error handling is report_fatal_error. Modify this to add an explicit commit() method, and call this in the appropriate places with appropriate error handling for the location. Currently the destructor of CacheStream gives an assert failure in Debug builds if commit() was not called. This will help track down any remaining uses of the API that assume the old destructior behaviour. In Release builds we fall back to the previous behaviour and call report_fatal_error if the commit fails. This is version 2 of this PR, superseding reverted PR #115331 . I have incorporated a change to the testcase to make it more reliable on Windows, as well as two follow-up changes (df79000 and b0baa1d) that were also reverted when 115331 was reverted. --------- Co-authored-by: Augie Fackler <[email protected]> Co-authored-by: Vitaly Buka <[email protected]>
1 parent a35f940 commit c3f815b

File tree

10 files changed

+225
-17
lines changed

10 files changed

+225
-17
lines changed

lldb/source/Core/DataFileCache.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ bool DataFileCache::SetCachedData(llvm::StringRef key,
132132
if (file_or_err) {
133133
llvm::CachedFileStream *cfs = file_or_err->get();
134134
cfs->OS->write((const char *)data.data(), data.size());
135+
if (llvm::Error err = cfs->commit()) {
136+
Log *log = GetLog(LLDBLog::Modules);
137+
LLDB_LOG_ERROR(log, std::move(err),
138+
"failed to commit to the cache for key: {0}");
139+
}
135140
return true;
136141
} else {
137142
Log *log = GetLog(LLDBLog::Modules);

llvm/include/llvm/Support/Caching.h

+19-2
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,32 @@ class MemoryBuffer;
2424
/// This class wraps an output stream for a file. Most clients should just be
2525
/// able to return an instance of this base class from the stream callback, but
2626
/// if a client needs to perform some action after the stream is written to,
27-
/// that can be done by deriving from this class and overriding the destructor.
27+
/// that can be done by deriving from this class and overriding the destructor
28+
/// or the commit() method.
2829
class CachedFileStream {
2930
public:
3031
CachedFileStream(std::unique_ptr<raw_pwrite_stream> OS,
3132
std::string OSPath = "")
3233
: OS(std::move(OS)), ObjectPathName(OSPath) {}
34+
35+
/// Must be called exactly once after the writes to OS have been completed
36+
/// but before the CachedFileStream object is destroyed.
37+
virtual Error commit() {
38+
if (Committed)
39+
return createStringError(make_error_code(std::errc::invalid_argument),
40+
Twine("CacheStream already committed."));
41+
Committed = true;
42+
43+
return Error::success();
44+
}
45+
46+
bool Committed = false;
3347
std::unique_ptr<raw_pwrite_stream> OS;
3448
std::string ObjectPathName;
35-
virtual ~CachedFileStream() = default;
49+
virtual ~CachedFileStream() {
50+
if (!Committed)
51+
report_fatal_error("CachedFileStream was not committed.\n");
52+
}
3653
};
3754

3855
/// This type defines the callback to add a file that is generated on the fly.

llvm/lib/CGData/CodeGenData.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ void saveModuleForTwoRounds(const Module &TheModule, unsigned Task,
233233

234234
WriteBitcodeToFile(TheModule, *Stream->OS,
235235
/*ShouldPreserveUseListOrder=*/true);
236+
237+
if (Error Err = Stream->commit())
238+
report_fatal_error(std::move(Err));
236239
}
237240

238241
std::unique_ptr<Module> loadModuleForTwoRounds(BitcodeModule &OrigModule,

llvm/lib/Debuginfod/Debuginfod.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ class StreamedHTTPResponseHandler : public HTTPResponseHandler {
188188
public:
189189
StreamedHTTPResponseHandler(CreateStreamFn CreateStream, HTTPClient &Client)
190190
: CreateStream(CreateStream), Client(Client) {}
191+
192+
/// Must be called exactly once after the writes have been completed
193+
/// but before the StreamedHTTPResponseHandler object is destroyed.
194+
Error commit();
195+
191196
virtual ~StreamedHTTPResponseHandler() = default;
192197

193198
Error handleBodyChunk(StringRef BodyChunk) override;
@@ -210,6 +215,12 @@ Error StreamedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
210215
return Error::success();
211216
}
212217

218+
Error StreamedHTTPResponseHandler::commit() {
219+
if (FileStream)
220+
return FileStream->commit();
221+
return Error::success();
222+
}
223+
213224
// An over-accepting simplification of the HTTP RFC 7230 spec.
214225
static bool isHeader(StringRef S) {
215226
StringRef Name;
@@ -298,6 +309,8 @@ Expected<std::string> getCachedOrDownloadArtifact(
298309
Error Err = Client.perform(Request, Handler);
299310
if (Err)
300311
return std::move(Err);
312+
if ((Err = Handler.commit()))
313+
return std::move(Err);
301314

302315
unsigned Code = Client.responseCode();
303316
if (Code && Code != 200)

llvm/lib/LTO/LTOBackend.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,9 @@ static void codegen(const Config &Conf, TargetMachine *TM,
460460

461461
if (DwoOut)
462462
DwoOut->keep();
463+
464+
if (Error Err = Stream->commit())
465+
report_fatal_error(std::move(Err));
463466
}
464467

465468
static void splitCodeGen(const Config &C, TargetMachine *TM,

llvm/lib/Support/Caching.cpp

+17-12
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,10 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
8888
AddBuffer(std::move(AddBuffer)), TempFile(std::move(TempFile)),
8989
ModuleName(ModuleName), Task(Task) {}
9090

91-
~CacheStream() {
92-
// TODO: Manually commit rather than using non-trivial destructor,
93-
// allowing to replace report_fatal_errors with a return Error.
91+
Error commit() override {
92+
Error E = CachedFileStream::commit();
93+
if (E)
94+
return E;
9495

9596
// Make sure the stream is closed before committing it.
9697
OS.reset();
@@ -100,10 +101,12 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
100101
MemoryBuffer::getOpenFile(
101102
sys::fs::convertFDToNativeFile(TempFile.FD), ObjectPathName,
102103
/*FileSize=*/-1, /*RequiresNullTerminator=*/false);
103-
if (!MBOrErr)
104-
report_fatal_error(Twine("Failed to open new cache file ") +
105-
TempFile.TmpName + ": " +
106-
MBOrErr.getError().message() + "\n");
104+
if (!MBOrErr) {
105+
std::error_code EC = MBOrErr.getError();
106+
return createStringError(EC, Twine("Failed to open new cache file ") +
107+
TempFile.TmpName + ": " +
108+
EC.message() + "\n");
109+
}
107110

108111
// On POSIX systems, this will atomically replace the destination if
109112
// it already exists. We try to emulate this on Windows, but this may
@@ -114,11 +117,14 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
114117
// AddBuffer a copy of the bytes we wrote in that case. We do this
115118
// instead of just using the existing file, because the pruner might
116119
// delete the file before we get a chance to use it.
117-
Error E = TempFile.keep(ObjectPathName);
120+
E = TempFile.keep(ObjectPathName);
118121
E = handleErrors(std::move(E), [&](const ECError &E) -> Error {
119122
std::error_code EC = E.convertToErrorCode();
120123
if (EC != errc::permission_denied)
121-
return errorCodeToError(EC);
124+
return createStringError(
125+
EC, Twine("Failed to rename temporary file ") +
126+
TempFile.TmpName + " to " + ObjectPathName + ": " +
127+
EC.message() + "\n");
122128

123129
auto MBCopy = MemoryBuffer::getMemBufferCopy((*MBOrErr)->getBuffer(),
124130
ObjectPathName);
@@ -131,11 +137,10 @@ Expected<FileCache> llvm::localCache(const Twine &CacheNameRef,
131137
});
132138

133139
if (E)
134-
report_fatal_error(Twine("Failed to rename temporary file ") +
135-
TempFile.TmpName + " to " + ObjectPathName + ": " +
136-
toString(std::move(E)) + "\n");
140+
return E;
137141

138142
AddBuffer(Task, ModuleName, std::move(*MBOrErr));
143+
return Error::success();
139144
}
140145
};
141146

llvm/tools/gold/gold-plugin.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -1117,9 +1117,11 @@ static std::vector<std::pair<SmallString<128>, bool>> runLTO() {
11171117
std::make_unique<llvm::raw_fd_ostream>(FD, true));
11181118
};
11191119

1120-
auto AddBuffer = [&](size_t Task, const Twine &moduleName,
1120+
auto AddBuffer = [&](size_t Task, const Twine &ModuleName,
11211121
std::unique_ptr<MemoryBuffer> MB) {
1122-
*AddStream(Task, moduleName)->OS << MB->getBuffer();
1122+
auto Stream = AddStream(Task, ModuleName);
1123+
*Stream->OS << MB->getBuffer();
1124+
check(Stream->commit(), "Failed to commit cache");
11231125
};
11241126

11251127
FileCache Cache;

llvm/tools/llvm-lto2/llvm-lto2.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,9 @@ static int run(int argc, char **argv) {
443443

444444
auto AddBuffer = [&](size_t Task, const Twine &ModuleName,
445445
std::unique_ptr<MemoryBuffer> MB) {
446-
*AddStream(Task, ModuleName)->OS << MB->getBuffer();
446+
auto Stream = AddStream(Task, ModuleName);
447+
*Stream->OS << MB->getBuffer();
448+
check(Stream->commit(), "Failed to commit cache");
447449
};
448450

449451
FileCache Cache;

llvm/unittests/Support/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ add_llvm_unittest(SupportTests
1818
BranchProbabilityTest.cpp
1919
CachePruningTest.cpp
2020
CrashRecoveryTest.cpp
21+
Caching.cpp
2122
Casting.cpp
2223
CheckedArithmeticTest.cpp
2324
Chrono.cpp

llvm/unittests/Support/Caching.cpp

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//===- Caching.cpp --------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Support/Caching.h"
10+
#include "llvm/Support/Error.h"
11+
#include "llvm/Support/MemoryBuffer.h"
12+
#include "llvm/Support/Path.h"
13+
#include "llvm/Testing/Support/Error.h"
14+
#include "gtest/gtest.h"
15+
16+
using namespace llvm;
17+
18+
#define ASSERT_NO_ERROR(x) \
19+
if (std::error_code ASSERT_NO_ERROR_ec = x) { \
20+
SmallString<128> MessageStorage; \
21+
raw_svector_ostream Message(MessageStorage); \
22+
Message << #x ": did not return errc::success.\n" \
23+
<< "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \
24+
<< "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \
25+
GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
26+
} else { \
27+
}
28+
29+
char data[] = "some data";
30+
31+
TEST(Caching, Normal) {
32+
SmallString<256> CacheDir;
33+
sys::fs::createUniquePath("llvm_test_cache-%%%%%%", CacheDir, true);
34+
35+
sys::fs::remove_directories(CacheDir.str());
36+
37+
std::unique_ptr<MemoryBuffer> CachedBuffer;
38+
auto AddBuffer = [&CachedBuffer](unsigned Task, const Twine &ModuleName,
39+
std::unique_ptr<MemoryBuffer> M) {
40+
CachedBuffer = std::move(M);
41+
};
42+
auto CacheOrErr =
43+
localCache("LLVMTestCache", "LLVMTest", CacheDir, AddBuffer);
44+
ASSERT_TRUE(bool(CacheOrErr));
45+
46+
FileCache &Cache = *CacheOrErr;
47+
48+
{
49+
auto AddStreamOrErr = Cache(1, "foo", "");
50+
ASSERT_TRUE(bool(AddStreamOrErr));
51+
52+
AddStreamFn &AddStream = *AddStreamOrErr;
53+
ASSERT_TRUE(AddStream);
54+
55+
auto FileOrErr = AddStream(1, "");
56+
ASSERT_TRUE(bool(FileOrErr));
57+
58+
CachedFileStream *CFS = FileOrErr->get();
59+
(*CFS->OS).write(data, sizeof(data));
60+
ASSERT_THAT_ERROR(CFS->commit(), Succeeded());
61+
}
62+
63+
{
64+
auto AddStreamOrErr = Cache(1, "foo", "");
65+
ASSERT_TRUE(bool(AddStreamOrErr));
66+
67+
AddStreamFn &AddStream = *AddStreamOrErr;
68+
ASSERT_FALSE(AddStream);
69+
70+
ASSERT_TRUE(CachedBuffer->getBufferSize() == sizeof(data));
71+
StringRef readData = CachedBuffer->getBuffer();
72+
ASSERT_TRUE(memcmp(data, readData.data(), sizeof(data)) == 0);
73+
}
74+
75+
ASSERT_NO_ERROR(sys::fs::remove_directories(CacheDir.str()));
76+
}
77+
78+
TEST(Caching, WriteAfterCommit) {
79+
SmallString<256> CacheDir;
80+
sys::fs::createUniquePath("llvm_test_cache-%%%%%%", CacheDir, true);
81+
82+
sys::fs::remove_directories(CacheDir.str());
83+
84+
std::unique_ptr<MemoryBuffer> CachedBuffer;
85+
auto AddBuffer = [&CachedBuffer](unsigned Task, const Twine &ModuleName,
86+
std::unique_ptr<MemoryBuffer> M) {
87+
CachedBuffer = std::move(M);
88+
};
89+
auto CacheOrErr =
90+
localCache("LLVMTestCache", "LLVMTest", CacheDir, AddBuffer);
91+
ASSERT_TRUE(bool(CacheOrErr));
92+
93+
FileCache &Cache = *CacheOrErr;
94+
95+
auto AddStreamOrErr = Cache(1, "foo", "");
96+
ASSERT_TRUE(bool(AddStreamOrErr));
97+
98+
AddStreamFn &AddStream = *AddStreamOrErr;
99+
ASSERT_TRUE(AddStream);
100+
101+
auto FileOrErr = AddStream(1, "");
102+
ASSERT_TRUE(bool(FileOrErr));
103+
104+
CachedFileStream *CFS = FileOrErr->get();
105+
(*CFS->OS).write(data, sizeof(data));
106+
ASSERT_THAT_ERROR(CFS->commit(), Succeeded());
107+
108+
EXPECT_DEATH(
109+
{ (*CFS->OS).write(data, sizeof(data)); }, "")
110+
<< "Write after commit did not cause abort";
111+
112+
ASSERT_NO_ERROR(sys::fs::remove_directories(CacheDir.str()));
113+
}
114+
115+
TEST(Caching, NoCommit) {
116+
SmallString<256> CacheDir;
117+
sys::fs::createUniquePath("llvm_test_cache-%%%%%%", CacheDir, true);
118+
119+
sys::fs::remove_directories(CacheDir.str());
120+
121+
std::unique_ptr<MemoryBuffer> CachedBuffer;
122+
auto AddBuffer = [&CachedBuffer](unsigned Task, const Twine &ModuleName,
123+
std::unique_ptr<MemoryBuffer> M) {
124+
CachedBuffer = std::move(M);
125+
};
126+
auto CacheOrErr =
127+
localCache("LLVMTestCache", "LLVMTest", CacheDir, AddBuffer);
128+
ASSERT_TRUE(bool(CacheOrErr));
129+
130+
FileCache &Cache = *CacheOrErr;
131+
132+
auto AddStreamOrErr = Cache(1, "foo", "");
133+
ASSERT_TRUE(bool(AddStreamOrErr));
134+
135+
AddStreamFn &AddStream = *AddStreamOrErr;
136+
ASSERT_TRUE(AddStream);
137+
138+
auto FileOrErr = AddStream(1, "");
139+
ASSERT_TRUE(bool(FileOrErr));
140+
141+
CachedFileStream *CFS = FileOrErr->get();
142+
(*CFS->OS).write(data, sizeof(data));
143+
ASSERT_THAT_ERROR(CFS->commit(), Succeeded());
144+
145+
EXPECT_DEATH(
146+
{
147+
auto FileOrErr = AddStream(1, "");
148+
ASSERT_TRUE(bool(FileOrErr));
149+
150+
CachedFileStream *CFS = FileOrErr->get();
151+
(*CFS->OS).write(data, sizeof(data));
152+
},
153+
"")
154+
<< "destruction without commit did not cause error";
155+
156+
ASSERT_NO_ERROR(sys::fs::remove_directories(CacheDir.str()));
157+
}

0 commit comments

Comments
 (0)