Skip to content

Commit d239314

Browse files
[CAS] Improve when MappedFileRegionBumpPtr is out of space
Fix a corner case for how MappedFileRegionBumpPtr returns the allocator into a valid state after it runs out of space. It makes sure the allocation that bump the allocator out of range needs to reset the pointer into its original state. Also propagate the failed allocation errors to make sure those errors are properly handled without throwing fatal errors to crash the system.
1 parent 41872ac commit d239314

15 files changed

+337
-176
lines changed

llvm/include/llvm/CAS/MappedFileRegionBumpPtr.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,14 @@ class MappedFileRegionBumpPtr {
6363
}
6464

6565
/// Allocate at least \p AllocSize. Rounds up to \a getAlign().
66-
char *allocate(uint64_t AllocSize) {
67-
return data() + allocateOffset(AllocSize);
66+
Expected<char *> allocate(uint64_t AllocSize) {
67+
auto Offset = allocateOffset(AllocSize);
68+
if (LLVM_UNLIKELY(!Offset))
69+
return Offset.takeError();
70+
return data() + *Offset;
6871
}
6972
/// Allocate, returning the offset from \a data() instead of a pointer.
70-
int64_t allocateOffset(uint64_t AllocSize);
73+
Expected<int64_t> allocateOffset(uint64_t AllocSize);
7174

7275
char *data() const { return Region.data(); }
7376
uint64_t size() const { return *BumpPtr; }

llvm/include/llvm/CAS/OnDiskGraphDB.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ class OnDiskGraphDB {
271271

272272
/// Form a reference for the provided hash. The reference can be used as part
273273
/// of a CAS object even if it's not associated with an object yet.
274-
ObjectID getReference(ArrayRef<uint8_t> Hash);
274+
Expected<ObjectID> getReference(ArrayRef<uint8_t> Hash);
275275

276276
/// Get an existing reference to the object \p Digest.
277277
///
@@ -362,7 +362,7 @@ class OnDiskGraphDB {
362362
Error importFullTree(ObjectID PrimaryID, ObjectHandle UpstreamNode);
363363
Error importSingleNode(ObjectID PrimaryID, ObjectHandle UpstreamNode);
364364

365-
IndexProxy indexHash(ArrayRef<uint8_t> Hash);
365+
Expected<IndexProxy> indexHash(ArrayRef<uint8_t> Hash);
366366

367367
Error createStandaloneLeaf(IndexProxy &I, ArrayRef<char> Data);
368368

llvm/include/llvm/CAS/OnDiskHashMappedTrie.h

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/StringRef.h"
1616
#include "llvm/Config/llvm-config.h"
1717
#include "llvm/Support/Casting.h"
18+
#include "llvm/Support/Compiler.h"
1819
#include "llvm/Support/FileSystem.h"
1920
#include <atomic>
2021
#include <mutex>
@@ -253,23 +254,23 @@ class OnDiskHashMappedTrie {
253254
/// The in-memory \a HashMappedTrie uses LazyAtomicPointer to synchronize
254255
/// simultaneous writes, but that seems dangerous to use in a memory-mapped
255256
/// file in case a process crashes in the busy state.
256-
pointer insertLazy(const_pointer Hint, ArrayRef<uint8_t> Hash,
257-
LazyInsertOnConstructCB OnConstruct = nullptr,
258-
LazyInsertOnLeakCB OnLeak = nullptr);
259-
pointer insertLazy(ArrayRef<uint8_t> Hash,
260-
LazyInsertOnConstructCB OnConstruct = nullptr,
261-
LazyInsertOnLeakCB OnLeak = nullptr) {
257+
Expected<pointer> insertLazy(const_pointer Hint, ArrayRef<uint8_t> Hash,
258+
LazyInsertOnConstructCB OnConstruct = nullptr,
259+
LazyInsertOnLeakCB OnLeak = nullptr);
260+
Expected<pointer> insertLazy(ArrayRef<uint8_t> Hash,
261+
LazyInsertOnConstructCB OnConstruct = nullptr,
262+
LazyInsertOnLeakCB OnLeak = nullptr) {
262263
return insertLazy(const_pointer(), Hash, OnConstruct, OnLeak);
263264
}
264265

265-
pointer insert(const_pointer Hint, const ConstValueProxy &Value) {
266+
Expected<pointer> insert(const_pointer Hint, const ConstValueProxy &Value) {
266267
return insertLazy(Hint, Value.Hash, [&](FileOffset, ValueProxy Allocated) {
267268
assert(Allocated.Hash == Value.Hash);
268269
assert(Allocated.Data.size() == Value.Data.size());
269270
llvm::copy(Value.Data, Allocated.Data.begin());
270271
});
271272
}
272-
pointer insert(const ConstValueProxy &Value) {
273+
Expected<pointer> insert(const ConstValueProxy &Value) {
273274
return insert(const_pointer(), Value);
274275
}
275276

@@ -348,13 +349,15 @@ class OnDiskDataAllocator {
348349
const_cast<const OnDiskDataAllocator *>(this)->beginData(Offset));
349350
}
350351

351-
pointer allocate(size_t Size);
352-
pointer save(ArrayRef<char> Data) {
353-
pointer P = allocate(Data.size());
354-
llvm::copy(Data, P->begin());
352+
Expected<pointer> allocate(size_t Size);
353+
Expected<pointer> save(ArrayRef<char> Data) {
354+
auto P = allocate(Data.size());
355+
if (LLVM_UNLIKELY(!P))
356+
return P.takeError();
357+
llvm::copy(Data, (*P)->begin());
355358
return P;
356359
}
357-
pointer save(StringRef Data) {
360+
Expected<pointer> save(StringRef Data) {
358361
return save(ArrayRef<char>(Data.begin(), Data.size()));
359362
}
360363

llvm/lib/CAS/ActionCaches.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/Config/llvm-config.h"
1818
#include "llvm/Support/Alignment.h"
1919
#include "llvm/Support/BLAKE3.h"
20+
#include "llvm/Support/Compiler.h"
2021
#include "llvm/Support/Path.h"
2122

2223
#define DEBUG_TYPE "action-caches"
@@ -212,13 +213,14 @@ UnifiedOnDiskActionCache::getImpl(ArrayRef<uint8_t> Key,
212213
Error UnifiedOnDiskActionCache::putImpl(ArrayRef<uint8_t> Key,
213214
const CASID &Result,
214215
bool /*Globally*/) {
215-
ondisk::ObjectID Expected =
216-
UniDB->getGraphDB().getReference(Result.getHash());
216+
auto Expected = UniDB->getGraphDB().getReference(Result.getHash());
217+
if (LLVM_UNLIKELY(!Expected))
218+
return Expected.takeError();
217219
std::optional<ondisk::ObjectID> Observed;
218-
if (Error E = UniDB->KVPut(Key, Expected).moveInto(Observed))
220+
if (Error E = UniDB->KVPut(Key, *Expected).moveInto(Observed))
219221
return E;
220222

221-
if (Expected == Observed)
223+
if (*Expected == Observed)
222224
return Error::success();
223225

224226
return createResultCachePoisonedError(

llvm/lib/CAS/MappedFileRegionBumpPtr.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,26 @@ void MappedFileRegionBumpPtr::initializeBumpPtr(int64_t BumpPtrOffset) {
220220
"Expected 0, or past the end of the BumpPtr itself");
221221
}
222222

223-
int64_t MappedFileRegionBumpPtr::allocateOffset(uint64_t AllocSize) {
223+
static Error createAllocatorOutOfSpaceError() {
224+
return createStringError(std::make_error_code(std::errc::not_enough_memory),
225+
"memory mapped file allocator is out of space");
226+
}
227+
228+
Expected<int64_t> MappedFileRegionBumpPtr::allocateOffset(uint64_t AllocSize) {
224229
AllocSize = alignTo(AllocSize, getAlign());
225230
int64_t OldEnd = BumpPtr->fetch_add(AllocSize);
226231
int64_t NewEnd = OldEnd + AllocSize;
227232
if (LLVM_UNLIKELY(NewEnd > (int64_t)capacity())) {
228-
// Try to return the allocation.
229-
(void)BumpPtr->compare_exchange_strong(OldEnd, NewEnd);
230-
report_fatal_error(
231-
errorCodeToError(std::make_error_code(std::errc::not_enough_memory)));
233+
// Return the allocation. If the start already passed the end, that means
234+
// some other concurrent allocations already consumed all the capacity.
235+
// There is no need to return the original value. If the start was not
236+
// passed the end, current allocation certainly bumped it passed the end.
237+
// All other allocation afterwards must have failed and current allocation
238+
// is in charge of return the allocation back to a valid value.
239+
if (OldEnd <= (int64_t)capacity())
240+
(void)BumpPtr->exchange(OldEnd);
241+
242+
return createAllocatorOutOfSpaceError();
232243
}
233244
return OldEnd;
234245
}

llvm/lib/CAS/OnDiskCAS.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "BuiltinCAS.h"
1010
#include "llvm/CAS/OnDiskGraphDB.h"
1111
#include "llvm/CAS/UnifiedOnDiskCache.h"
12+
#include "llvm/Support/Compiler.h"
1213
#include "llvm/Support/Path.h"
1314

1415
using namespace llvm;
@@ -125,10 +126,12 @@ Expected<ObjectRef> OnDiskCAS::storeImpl(ArrayRef<uint8_t> ComputedHash,
125126
IDs.push_back(convertRef(Ref));
126127
}
127128

128-
ondisk::ObjectID StoredID = DB->getReference(ComputedHash);
129-
if (Error E = DB->store(StoredID, IDs, Data))
129+
auto StoredID = DB->getReference(ComputedHash);
130+
if (LLVM_UNLIKELY(!StoredID))
131+
return StoredID.takeError();
132+
if (Error E = DB->store(*StoredID, IDs, Data))
130133
return std::move(E);
131-
return convertRef(StoredID);
134+
return convertRef(*StoredID);
132135
}
133136

134137
Error OnDiskCAS::forEachRef(ObjectHandle Node,

llvm/lib/CAS/OnDiskGraphDB.cpp

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,13 @@
5252
#include "llvm/ADT/DenseMap.h"
5353
#include "llvm/ADT/StringExtras.h"
5454
#include "llvm/Support/Alignment.h"
55+
#include "llvm/Support/Compiler.h"
5556
#include "llvm/Support/Errc.h"
57+
#include "llvm/Support/Error.h"
5658
#include "llvm/Support/MemoryBuffer.h"
5759
#include "llvm/Support/Path.h"
5860
#include "llvm/Support/Process.h"
61+
#include <optional>
5962

6063
#define DEBUG_TYPE "on-disk-cas"
6164

@@ -915,17 +918,21 @@ void OnDiskGraphDB::print(raw_ostream &OS) const {
915918
}
916919
}
917920

918-
OnDiskGraphDB::IndexProxy OnDiskGraphDB::indexHash(ArrayRef<uint8_t> Hash) {
919-
OnDiskHashMappedTrie::pointer P = Index.insertLazy(
921+
Expected<OnDiskGraphDB::IndexProxy>
922+
OnDiskGraphDB::indexHash(ArrayRef<uint8_t> Hash) {
923+
auto P = Index.insertLazy(
920924
Hash, [](FileOffset TentativeOffset,
921925
OnDiskHashMappedTrie::ValueProxy TentativeValue) {
922926
assert(TentativeValue.Data.size() == sizeof(TrieRecord));
923927
assert(
924928
isAddrAligned(Align::Of<TrieRecord>(), TentativeValue.Data.data()));
925929
new (TentativeValue.Data.data()) TrieRecord();
926930
});
927-
assert(P && "Expected insertion");
928-
return getIndexProxyFromPointer(P);
931+
if (LLVM_UNLIKELY(!P))
932+
return P.takeError();
933+
934+
assert(*P && "Expected insertion");
935+
return getIndexProxyFromPointer(*P);
929936
}
930937

931938
OnDiskGraphDB::IndexProxy OnDiskGraphDB::getIndexProxyFromPointer(
@@ -937,9 +944,11 @@ OnDiskGraphDB::IndexProxy OnDiskGraphDB::getIndexProxyFromPointer(
937944
reinterpret_cast<const TrieRecord *>(P->Data.data()))};
938945
}
939946

940-
ObjectID OnDiskGraphDB::getReference(ArrayRef<uint8_t> Hash) {
941-
IndexProxy I = indexHash(Hash);
942-
return getExternalReference(I);
947+
Expected<ObjectID> OnDiskGraphDB::getReference(ArrayRef<uint8_t> Hash) {
948+
auto I = indexHash(Hash);
949+
if (LLVM_UNLIKELY(!I))
950+
return I.takeError();
951+
return getExternalReference(*I);
943952
}
944953

945954
ObjectID OnDiskGraphDB::getExternalReference(const IndexProxy &I) {
@@ -954,10 +963,13 @@ OnDiskGraphDB::getExistingReference(ArrayRef<uint8_t> Digest) {
954963
return std::nullopt;
955964
std::optional<ObjectID> UpstreamID =
956965
UpstreamDB->getExistingReference(Digest);
957-
if (!UpstreamID)
966+
if (LLVM_UNLIKELY(!UpstreamID))
967+
return std::nullopt;
968+
auto Ref = expectedToOptional(indexHash(Digest));
969+
if (!Ref)
958970
return std::nullopt;
959971
if (!I)
960-
I.emplace(indexHash(Digest));
972+
I.emplace(*Ref);
961973
return getExternalReference(*I);
962974
};
963975

@@ -1246,14 +1258,16 @@ Error OnDiskGraphDB::store(ObjectID ID, ArrayRef<ObjectID> Refs,
12461258
auto Alloc = [&](size_t Size) -> Expected<char *> {
12471259
if (Size <= TrieRecord::MaxEmbeddedSize) {
12481260
SK = TrieRecord::StorageKind::DataPool;
1249-
OnDiskDataAllocator::pointer P = DataPool.allocate(Size);
1250-
PoolOffset = P.getOffset();
1261+
auto P = DataPool.allocate(Size);
1262+
if (LLVM_UNLIKELY(!P))
1263+
return P.takeError();
1264+
PoolOffset = P->getOffset();
12511265
LLVM_DEBUG({
12521266
dbgs() << "pool-alloc addr=" << (void *)PoolOffset.get()
12531267
<< " size=" << Size
12541268
<< " end=" << (void *)(PoolOffset.get() + Size) << "\n";
12551269
});
1256-
return P->data();
1270+
return (*P)->data();
12571271
}
12581272

12591273
SK = TrieRecord::StorageKind::Standalone;
@@ -1465,19 +1479,21 @@ Error OnDiskGraphDB::importFullTree(ObjectID PrimaryID,
14651479
}
14661480

14671481
ObjectID UpstreamID = *(Cur.RefI++);
1468-
ObjectID PrimaryID = getReference(UpstreamDB->getDigest(UpstreamID));
1469-
if (containsObject(PrimaryID, /*CheckUpstream=*/false)) {
1482+
auto PrimaryID = getReference(UpstreamDB->getDigest(UpstreamID));
1483+
if (LLVM_UNLIKELY(!PrimaryID))
1484+
return PrimaryID.takeError();
1485+
if (containsObject(*PrimaryID, /*CheckUpstream=*/false)) {
14701486
// This \p ObjectID already exists in the primary. Either it was imported
14711487
// via \p importFullTree or the client created it, in which case the
14721488
// client takes responsibility for how it was formed.
1473-
enqueueNode(PrimaryID, std::nullopt);
1489+
enqueueNode(*PrimaryID, std::nullopt);
14741490
continue;
14751491
}
14761492
Expected<std::optional<ObjectHandle>> UpstreamNode =
14771493
UpstreamDB->load(UpstreamID);
14781494
if (!UpstreamNode)
14791495
return UpstreamNode.takeError();
1480-
enqueueNode(PrimaryID, *UpstreamNode);
1496+
enqueueNode(*PrimaryID, *UpstreamNode);
14811497
}
14821498

14831499
assert(PrimaryNodesStack.size() == 1);
@@ -1497,8 +1513,12 @@ Error OnDiskGraphDB::importSingleNode(ObjectID PrimaryID,
14971513
auto UpstreamRefs = UpstreamDB->getObjectRefs(UpstreamNode);
14981514
SmallVector<ObjectID, 64> Refs;
14991515
Refs.reserve(std::distance(UpstreamRefs.begin(), UpstreamRefs.end()));
1500-
for (ObjectID UpstreamRef : UpstreamRefs)
1501-
Refs.push_back(getReference(UpstreamDB->getDigest(UpstreamRef)));
1516+
for (ObjectID UpstreamRef : UpstreamRefs) {
1517+
auto Ref = getReference(UpstreamDB->getDigest(UpstreamRef));
1518+
if (LLVM_UNLIKELY(!Ref))
1519+
return Ref.takeError();
1520+
Refs.push_back(*Ref);
1521+
}
15021522

15031523
return store(PrimaryID, Refs, Data);
15041524
}
@@ -1507,9 +1527,12 @@ Expected<std::optional<ObjectHandle>>
15071527
OnDiskGraphDB::faultInFromUpstream(ObjectID PrimaryID) {
15081528
assert(UpstreamDB);
15091529

1510-
ObjectID UpstreamID = UpstreamDB->getReference(getDigest(PrimaryID));
1530+
auto UpstreamID = UpstreamDB->getReference(getDigest(PrimaryID));
1531+
if (LLVM_UNLIKELY(!UpstreamID))
1532+
return UpstreamID.takeError();
1533+
15111534
Expected<std::optional<ObjectHandle>> UpstreamNode =
1512-
UpstreamDB->load(UpstreamID);
1535+
UpstreamDB->load(*UpstreamID);
15131536
if (!UpstreamNode)
15141537
return UpstreamNode.takeError();
15151538
if (!*UpstreamNode)

0 commit comments

Comments
 (0)