Skip to content

Commit 863d61d

Browse files
cachemeifyoucanakyrtzi
authored andcommitted
[BuiltinCAS] Implement OnDiskCAS for Windows
Implement an example OnDiskCAS for windows 10+. (cherry picked from commit 72ce6ce)
1 parent 20a4da9 commit 863d61d

File tree

3 files changed

+244
-33
lines changed

3 files changed

+244
-33
lines changed

llvm/include/llvm/CAS/LazyMappedFileRegion.h

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ class LazyMappedFileRegion {
7070
/// and/or update callers not to rely on this.
7171
StringRef getPath() const { return Path; }
7272

73-
char *data() const { return Map.data(); }
74-
7573
/// Resize to at least \p MinSize.
7674
///
7775
/// Errors if \p MinSize is bigger than \a capacity() or if the operation
@@ -86,10 +84,18 @@ class LazyMappedFileRegion {
8684
/// Size allocated on disk.
8785
size_t size() const { return CachedSize; }
8886

87+
#ifdef _WIN32
88+
char *data() const { return VM; }
89+
/// Size of the underlying \a mapped_file_region. This cannot be extended.
90+
size_t capacity() const { return MaxSize; }
91+
explicit operator bool() const { return VM; }
92+
#else
93+
char *data() const { return Map.data(); }
8994
/// Size of the underlying \a mapped_file_region. This cannot be extended.
9095
size_t capacity() const { return Map.size(); }
91-
9296
explicit operator bool() const { return bool(Map); }
97+
#endif
98+
9399

94100
~LazyMappedFileRegion() { destroyImpl(); }
95101

@@ -106,27 +112,34 @@ class LazyMappedFileRegion {
106112

107113
private:
108114
Error extendSizeImpl(uint64_t MinSize);
109-
void destroyImpl() {
110-
if (FD) {
111-
sys::fs::closeFile(*FD);
112-
FD = None;
113-
}
114-
}
115+
void destroyImpl();
115116
void moveImpl(LazyMappedFileRegion &RHS) {
116117
Path = std::move(RHS.Path);
117118
FD = std::move(RHS.FD);
118-
RHS.FD = None;
119+
RHS.FD = std::nullopt;
120+
#ifdef _WIN32
121+
VM = RHS.VM;
122+
MaxSize = RHS.MaxSize;
123+
MappedRegions = RHS.MappedRegions;
124+
#else
119125
Map = std::move(RHS.Map);
126+
assert(!RHS.Map &&
127+
"Expected std::optional(std::optional&&) to clear RHS.Map");
128+
#endif
120129
CachedSize = RHS.CachedSize.load();
121130
RHS.CachedSize = 0;
122131
MaxSizeIncrement = RHS.MaxSizeIncrement;
123-
124-
assert(!RHS.Map && "Expected Optional(Optional&&) to clear RHS.Map");
125132
}
126133

127134
std::string Path;
128-
Optional<sys::fs::file_t> FD;
135+
std::optional<int> FD;
136+
#ifdef _WIN32
137+
char *VM = nullptr;
138+
uint64_t MaxSize = 0;
139+
std::vector<void*> MappedRegions;
140+
#else
129141
sys::fs::mapped_file_region Map;
142+
#endif
130143
std::atomic<uint64_t> CachedSize;
131144
std::mutex Mutex;
132145
uint64_t MaxSizeIncrement = 0;

llvm/lib/CAS/LazyMappedFileRegion.cpp

Lines changed: 212 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
#include "llvm/Support/Path.h"
1616
#include "llvm/Support/raw_ostream.h"
1717

18+
#ifdef _WIN32
19+
#include "llvm/Support/Windows/WindowsSupport.h"
20+
#include "llvm/Support/WindowsError.h"
21+
#endif
22+
1823
#if LLVM_ENABLE_ONDISK_CAS
1924

2025
using namespace llvm;
@@ -50,44 +55,220 @@ using namespace llvm::cas;
5055
/// 3. Call CreateFileMapping to with 1MB, or existing size.
5156
/// 4. Call MapViewOfFileN to place it in the reserved memory.
5257
/// 5. Repeat step (3) with the new size and step (4).
58+
#ifdef _WIN32
59+
// FIXME: VirtualAlloc2 declaration for older SDKs and building for older
60+
// windows verions. Currently only support windows 10+.
61+
PVOID WINAPI VirtualAlloc2(HANDLE Process, PVOID BaseAddress, SIZE_T Size,
62+
ULONG AllocationType, ULONG PageProtection,
63+
MEM_EXTENDED_PARAMETER *ExtendedParameters,
64+
ULONG ParameterCount);
65+
5366
Expected<LazyMappedFileRegion> LazyMappedFileRegion::create(
5467
const Twine &Path, uint64_t Capacity,
5568
function_ref<Error(LazyMappedFileRegion &)> NewFileConstructor,
5669
uint64_t MaxSizeIncrement) {
5770
LazyMappedFileRegion LMFR;
5871
LMFR.Path = Path.str();
59-
LMFR.MaxSizeIncrement = MaxSizeIncrement;
72+
LMFR.MaxSizeIncrement = std::min(Capacity, MaxSizeIncrement);
73+
74+
int FD;
75+
if (std::error_code EC = sys::fs::openFileForReadWrite(
76+
LMFR.Path, FD, sys::fs::CD_OpenAlways, sys::fs::OF_None))
77+
return errorCodeToError(EC);
78+
LMFR.FD = FD;
6079

61-
if (Error E = sys::fs::openNativeFileForReadWrite(
62-
LMFR.Path, sys::fs::CD_OpenAlways, sys::fs::OF_None).moveInto(LMFR.FD))
80+
// Lock the file so we can initialize it.
81+
if (std::error_code EC = sys::fs::lockFile(*LMFR.FD))
82+
return createFileError(Path, EC);
83+
auto Unlock = make_scope_exit([FD = *LMFR.FD]() { sys::fs::unlockFile(FD); });
84+
85+
sys::fs::file_t File = sys::fs::convertFDToNativeFile(FD);
86+
// Status the file so we can decide if we need to run init.
87+
sys::fs::file_status Status;
88+
if (std::error_code EC = sys::fs::status(File, Status))
89+
return errorCodeToError(EC);
90+
91+
// Reserve VM using VirtualAlloc2. This will error out on Windows 10 or
92+
// before.
93+
auto pVirtualAlloc2 = (decltype(&::VirtualAlloc2))GetProcAddress(
94+
GetModuleHandle(L"kernelbase"), "VirtualAlloc2");
95+
// FIXME: return error if the new alloc function is not found.
96+
if (!pVirtualAlloc2)
97+
return errorCodeToError(std::make_error_code(std::errc::not_supported));
98+
99+
LMFR.VM = (char *)pVirtualAlloc2(0, 0, Capacity,
100+
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
101+
PAGE_NOACCESS, 0, 0);
102+
if (!LMFR.VM)
103+
return errorCodeToError(mapWindowsError(::GetLastError()));
104+
LMFR.MaxSize = Capacity;
105+
106+
if (Status.getSize() > 0)
107+
// The file was already constructed.
108+
LMFR.CachedSize = Status.getSize();
109+
else
110+
LMFR.IsConstructingNewFile = true;
111+
112+
// Create a memory mapped region. The larger of the current size or the max
113+
// file increment.
114+
uint64_t AllocSize = std::max(LMFR.MaxSizeIncrement, Status.getSize());
115+
sys::fs::file_t FileMap = ::CreateFileMappingA(
116+
File, 0, PAGE_READWRITE, Hi_32(AllocSize), Lo_32(AllocSize), 0);
117+
if (!FileMap)
118+
return errorCodeToError(mapWindowsError(::GetLastError()));
119+
120+
// If there is still space in reserved area after allocation, split the
121+
// placeholder.
122+
if (AllocSize < LMFR.MaxSize) {
123+
if (!VirtualFree(LMFR.VM, AllocSize,
124+
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER))
125+
return errorCodeToError(mapWindowsError(::GetLastError()));
126+
}
127+
128+
// Free up AllocSize from VM then mapped the file in.
129+
if (!VirtualFree(LMFR.VM, 0, MEM_RELEASE))
130+
return errorCodeToError(mapWindowsError(::GetLastError()));
131+
132+
void *Mapped =
133+
::MapViewOfFileEx(FileMap, FILE_MAP_ALL_ACCESS, 0, 0, AllocSize, LMFR.VM);
134+
if (!Mapped)
135+
return errorCodeToError(mapWindowsError(::GetLastError()));
136+
137+
LMFR.MappedRegions.push_back(Mapped);
138+
139+
CloseHandle(FileMap);
140+
if (!LMFR.IsConstructingNewFile)
141+
return std::move(LMFR);
142+
143+
// This is a new file. Resize to NewFileSize and run the constructor.
144+
if (Error E = NewFileConstructor(LMFR))
63145
return std::move(E);
64-
assert(LMFR.FD && "Expected valid file descriptor");
146+
147+
assert(LMFR.size() > 0 && "Constructor must set a non-zero size");
148+
LMFR.IsConstructingNewFile = false;
149+
return std::move(LMFR);
150+
}
151+
152+
Error LazyMappedFileRegion::extendSizeImpl(uint64_t MinSize) {
153+
assert(VM && "Expected a valid map");
154+
assert(FD && "Expected a valid file descriptor");
155+
156+
// Synchronize with other threads. Skip if constructing a new file since
157+
// exclusive access is already guaranteed.
158+
std::optional<std::lock_guard<std::mutex>> Lock;
159+
if (!IsConstructingNewFile)
160+
Lock.emplace(Mutex);
161+
162+
uint64_t OldSize = CachedSize;
163+
if (MinSize <= OldSize)
164+
return Error::success();
165+
166+
// Increase sizes by doubling up to 8MB, and then limit the over-allocation
167+
// to 4MB.
168+
uint64_t NewSize;
169+
if (MinSize < MaxSizeIncrement)
170+
NewSize = NextPowerOf2(MinSize);
171+
else
172+
NewSize = alignTo(MinSize, MaxSizeIncrement);
173+
174+
if (NewSize > MaxSize)
175+
NewSize = MaxSize;
176+
if (NewSize < MinSize)
177+
return errorCodeToError(std::make_error_code(std::errc::not_enough_memory));
178+
179+
// Synchronize with other processes. Skip if constructing a new file since
180+
// file locks are already in place.
181+
if (!IsConstructingNewFile)
182+
if (std::error_code EC = sys::fs::lockFile(*FD))
183+
return errorCodeToError(EC);
184+
auto Unlock = make_scope_exit([&]() {
185+
if (!IsConstructingNewFile)
186+
sys::fs::unlockFile(*FD);
187+
});
188+
189+
sys::fs::file_status Status;
190+
if (std::error_code EC = sys::fs::status(*FD, Status))
191+
return errorCodeToError(EC);
192+
if (Status.getSize() >= MinSize) {
193+
// Another process already resized the file. Be careful not to let size()
194+
// increase beyond capacity(), in case that process used a bigger map size.
195+
CachedSize = std::min(Status.getSize(), MaxSize);
196+
return Error::success();
197+
}
198+
199+
// Resize.
200+
uint64_t AllocSize = NewSize - OldSize;
201+
sys::fs::file_t File = sys::fs::convertFDToNativeFile(*FD);
202+
sys::fs::file_t FileMap = ::CreateFileMappingA(
203+
File, 0, PAGE_READWRITE, Hi_32(NewSize), Lo_32(NewSize), 0);
204+
if (!FileMap)
205+
return errorCodeToError(mapWindowsError(::GetLastError()));
206+
207+
if (NewSize < MaxSize) {
208+
if (!VirtualFree(VM + OldSize, AllocSize,
209+
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER))
210+
return errorCodeToError(mapWindowsError(::GetLastError()));
211+
}
212+
213+
if (!VirtualFree(VM + OldSize, 0, MEM_RELEASE))
214+
return errorCodeToError(mapWindowsError(::GetLastError()));
215+
216+
void *Mapped =
217+
::MapViewOfFileEx(FileMap, FILE_MAP_ALL_ACCESS, Hi_32(OldSize),
218+
Lo_32(OldSize), AllocSize, VM + OldSize);
219+
if (!Mapped)
220+
return errorCodeToError(mapWindowsError(::GetLastError()));
221+
222+
MappedRegions.push_back(Mapped);
223+
CloseHandle(FileMap);
224+
CachedSize = NewSize;
225+
return Error::success();
226+
}
227+
228+
#else
229+
Expected<LazyMappedFileRegion> LazyMappedFileRegion::create(
230+
const Twine &Path, uint64_t Capacity,
231+
function_ref<Error(LazyMappedFileRegion &)> NewFileConstructor,
232+
uint64_t MaxSizeIncrement) {
233+
LazyMappedFileRegion LMFR;
234+
LMFR.Path = Path.str();
235+
LMFR.MaxSizeIncrement = std::min(Capacity, MaxSizeIncrement);
236+
237+
int FD;
238+
if (std::error_code EC = sys::fs::openFileForReadWrite(
239+
LMFR.Path, FD, sys::fs::CD_OpenAlways, sys::fs::OF_None))
240+
return errorCodeToError(EC);
241+
LMFR.FD = FD;
242+
243+
sys::fs::file_t File = sys::fs::convertFDToNativeFile(FD);
244+
245+
sys::fs::file_status Status;
246+
if (std::error_code EC = sys::fs::status(File, Status))
247+
return errorCodeToError(EC);
248+
if (Status.getSize() > 0)
249+
// The file was already constructed.
250+
LMFR.CachedSize = Status.getSize();
251+
else
252+
LMFR.IsConstructingNewFile = true;
65253

66254
{
67255
std::error_code EC;
68-
sys::fs::mapped_file_region
69-
Map(*LMFR.FD, sys::fs::mapped_file_region::readwrite, Capacity, 0, EC);
256+
sys::fs::mapped_file_region Map(
257+
File, sys::fs::mapped_file_region::readwrite, Capacity, 0, EC);
70258
if (EC)
71259
return createFileError(LMFR.Path, EC);
72260
LMFR.Map = std::move(Map);
73261
}
74262

263+
if (!LMFR.IsConstructingNewFile)
264+
return std::move(LMFR);
265+
75266
// Lock the file so we can initialize it.
76267
if (std::error_code EC = sys::fs::lockFile(*LMFR.FD))
77268
return createFileError(Path, EC);
78269
auto Unlock = make_scope_exit([FD = *LMFR.FD]() { sys::fs::unlockFile(FD); });
79270

80-
sys::fs::file_status Status;
81-
if (std::error_code EC = sys::fs::status(*LMFR.FD, Status))
82-
return errorCodeToError(EC);
83-
if (Status.getSize() > 0) {
84-
// The file was already constructed.
85-
LMFR.CachedSize = Status.getSize();
86-
return std::move(LMFR);
87-
}
88-
89271
// This is a new file. Resize to NewFileSize and run the constructor.
90-
LMFR.IsConstructingNewFile = true;
91272
if (Error E = NewFileConstructor(LMFR))
92273
return std::move(E);
93274
assert(LMFR.size() > 0 && "Constructor must set a non-zero size");
@@ -101,7 +282,7 @@ Error LazyMappedFileRegion::extendSizeImpl(uint64_t MinSize) {
101282

102283
// Synchronize with other threads. Skip if constructing a new file since
103284
// exclusive access is already guaranteed.
104-
Optional<std::lock_guard<std::mutex>> Lock;
285+
std::optional<std::lock_guard<std::mutex>> Lock;
105286
if (!IsConstructingNewFile)
106287
Lock.emplace(Mutex);
107288

@@ -148,6 +329,7 @@ Error LazyMappedFileRegion::extendSizeImpl(uint64_t MinSize) {
148329
CachedSize = NewSize;
149330
return Error::success();
150331
}
332+
#endif
151333

152334
Expected<std::shared_ptr<LazyMappedFileRegion>>
153335
LazyMappedFileRegion::createShared(
@@ -199,4 +381,17 @@ LazyMappedFileRegion::createShared(
199381
Node->LMFR = SharedLMFR;
200382
return std::move(SharedLMFR);
201383
}
384+
385+
void LazyMappedFileRegion::destroyImpl() {
386+
if (FD) {
387+
sys::fs::file_t File = sys::fs::convertFDToNativeFile(*FD);
388+
sys::fs::closeFile(File);
389+
FD = std::nullopt;
390+
}
391+
#ifdef _WIN32
392+
for (auto *Region : MappedRegions)
393+
CloseHandle(Region);
394+
#endif
395+
}
396+
202397
#endif // LLVM_ENABLE_ONDISK_CAS

llvm/lib/CAS/OnDiskCAS.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -984,9 +984,11 @@ class OnDiskCAS::MappedTempFile {
984984

985985
Error OnDiskCAS::TempFile::discard() {
986986
Done = true;
987-
if (FD != -1)
988-
if (std::error_code EC = sys::fs::closeFile(FD))
987+
if (FD != -1) {
988+
sys::fs::file_t File = sys::fs::convertFDToNativeFile(FD);
989+
if (std::error_code EC = sys::fs::closeFile(File))
989990
return errorCodeToError(EC);
991+
}
990992
FD = -1;
991993

992994
// Always try to close and remove.
@@ -1008,7 +1010,8 @@ Error OnDiskCAS::TempFile::keep(const Twine &Name) {
10081010
if (!RenameEC)
10091011
TmpName = "";
10101012

1011-
if (std::error_code EC = sys::fs::closeFile(FD))
1013+
sys::fs::file_t File = sys::fs::convertFDToNativeFile(FD);
1014+
if (std::error_code EC = sys::fs::closeFile(File))
10121015
return errorCodeToError(EC);
10131016
FD = -1;
10141017

0 commit comments

Comments
 (0)