15
15
#include " llvm/Support/Path.h"
16
16
#include " llvm/Support/raw_ostream.h"
17
17
18
+ #ifdef _WIN32
19
+ #include " llvm/Support/Windows/WindowsSupport.h"
20
+ #include " llvm/Support/WindowsError.h"
21
+ #endif
22
+
18
23
#if LLVM_ENABLE_ONDISK_CAS
19
24
20
25
using namespace llvm ;
@@ -50,44 +55,220 @@ using namespace llvm::cas;
50
55
// / 3. Call CreateFileMapping to with 1MB, or existing size.
51
56
// / 4. Call MapViewOfFileN to place it in the reserved memory.
52
57
// / 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
+
53
66
Expected<LazyMappedFileRegion> LazyMappedFileRegion::create (
54
67
const Twine &Path, uint64_t Capacity,
55
68
function_ref<Error(LazyMappedFileRegion &)> NewFileConstructor,
56
69
uint64_t MaxSizeIncrement) {
57
70
LazyMappedFileRegion LMFR;
58
71
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;
60
79
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))
63
145
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 ;
65
253
66
254
{
67
255
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);
70
258
if (EC)
71
259
return createFileError (LMFR.Path , EC);
72
260
LMFR.Map = std::move (Map);
73
261
}
74
262
263
+ if (!LMFR.IsConstructingNewFile )
264
+ return std::move (LMFR);
265
+
75
266
// Lock the file so we can initialize it.
76
267
if (std::error_code EC = sys::fs::lockFile (*LMFR.FD ))
77
268
return createFileError (Path, EC);
78
269
auto Unlock = make_scope_exit ([FD = *LMFR.FD ]() { sys::fs::unlockFile (FD); });
79
270
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
-
89
271
// This is a new file. Resize to NewFileSize and run the constructor.
90
- LMFR.IsConstructingNewFile = true ;
91
272
if (Error E = NewFileConstructor (LMFR))
92
273
return std::move (E);
93
274
assert (LMFR.size () > 0 && " Constructor must set a non-zero size" );
@@ -101,7 +282,7 @@ Error LazyMappedFileRegion::extendSizeImpl(uint64_t MinSize) {
101
282
102
283
// Synchronize with other threads. Skip if constructing a new file since
103
284
// exclusive access is already guaranteed.
104
- Optional <std::lock_guard<std::mutex>> Lock;
285
+ std::optional <std::lock_guard<std::mutex>> Lock;
105
286
if (!IsConstructingNewFile)
106
287
Lock.emplace (Mutex);
107
288
@@ -148,6 +329,7 @@ Error LazyMappedFileRegion::extendSizeImpl(uint64_t MinSize) {
148
329
CachedSize = NewSize;
149
330
return Error::success ();
150
331
}
332
+ #endif
151
333
152
334
Expected<std::shared_ptr<LazyMappedFileRegion>>
153
335
LazyMappedFileRegion::createShared (
@@ -199,4 +381,17 @@ LazyMappedFileRegion::createShared(
199
381
Node->LMFR = SharedLMFR;
200
382
return std::move (SharedLMFR);
201
383
}
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
+
202
397
#endif // LLVM_ENABLE_ONDISK_CAS
0 commit comments