The Pedigree Project 0.1
MemoryMappedFile-map.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/utilities/Vector.h"
9
10#include "File.h"
11#include "MemoryMappedFile.h"
12
13namespace {
15using Status = MemoryMapManager::MapStatus;
16using Placement = MemoryMapManager::Placement;
17MemoryAllocator* allocatorFor(Snapshot& snapshot, VirtualAddressSpace& space, uintptr_t base,
18 size_t length) {
19 if (space.getDynamicStart() && base >= space.getDynamicStart() &&
20 base + length <= space.getDynamicEnd())
21 return &snapshot.dynamic;
22 if (base >= space.getUserStart() && base + length <= space.getUserReservedStart())
23 return &snapshot.normal;
24 return nullptr;
25}
26bool reserveFree(MemoryAllocator& allocator, uintptr_t base, size_t length) {
27 while (true) {
28 bool found = false;
29 for (size_t i = 0; i < allocator.size(); ++i) {
30 MemoryAllocator::Range range(0, 0);
31 if (!allocator.getRange(i, range))
32 continue;
33 const uintptr_t first = range.address > base ? range.address : base;
34 const uintptr_t rangeEnd = range.length > ~uintptr_t(0) - range.address
35 ? ~uintptr_t(0)
36 : range.address + range.length;
37 const uintptr_t last = rangeEnd < base + length ? rangeEnd : base + length;
38 if (first >= last)
39 continue;
40 if (!allocator.allocateSpecific(first, last - first))
41 return false;
42 found = true;
43 break;
44 }
45 if (!found)
46 return true;
47 }
48}
49Status place(Snapshot& snapshot, VirtualAddressSpace& space, uintptr_t& address, size_t length,
50 Placement placement, size_t mask) {
51 if (address) {
52 auto* allocator = allocatorFor(snapshot, space, address, length);
53 if (allocator && allocator->allocateSpecific(address, length))
54 return Status::Success;
55 if (placement == Placement::FixedNoReplace)
56 return Status::AddressInUse;
57 if (placement == Placement::FixedReplace)
58 return reserveFree(snapshot.dynamic, address, length) &&
59 reserveFree(snapshot.normal, address, length)
60 ? Status::Success
61 : Status::NoMemory;
62 }
63 MemoryAllocator* allocators[] = {&snapshot.dynamic, &snapshot.normal};
64 for (auto* allocator : allocators) {
65 uintptr_t allocation = 0;
66 if (!allocator->allocate(length + mask, allocation))
67 continue;
68 address = (allocation + mask) & ~mask;
69 if (address != allocation && !allocator->tryFree(allocation, address - allocation))
70 return Status::NoMemory;
71 const uintptr_t end = allocation + length + mask;
72 if (address + length < end && !allocator->tryFree(address + length, end - address - length))
73 return Status::NoMemory;
74 return Status::Success;
75 }
76 return Status::NoMemory;
77}
78struct MappingPlan {
79 MappingList<MemoryMappedObject>* replacement = nullptr;
80 Vector<MemoryMappedObject*> staged, retired;
81 MemoryMappedObject* inserted = nullptr;
82 bool committed = false;
83 ~MappingPlan() {
84 for (auto* object : committed ? retired : staged)
85 delete object;
86 delete replacement;
87 }
88 bool appendSlice(MemoryMappedObject* owner, uintptr_t first, uintptr_t end) {
89 if (first >= end)
90 return true;
91 // Only surviving tracked pages need staging; sparse virtual extent is
92 // not a useful bound on the metadata needed to split or retire an owner.
93 auto* object = owner->stageSlice(first, end - first, first, end - first);
94 if (!object)
95 return false;
96 staged.pushBack(object);
97 return replacement->tryPushBack(object);
98 }
99};
100
101class DirectReservation {
102 public:
103 DirectReservation()
104 : m_Process(nullptr),
105 m_Region(Process::UserRegion::Normal),
106 m_Base(0),
107 m_Length(0),
108 m_Committed(false) {}
109
110 void arm(Process* process, Process::UserRegion region, uintptr_t base, size_t length) {
111 m_Process = process;
112 m_Region = region;
113 m_Base = base;
114 m_Length = length;
115 }
116
117 void commit() {
118 m_Committed = true;
119 }
120
121 ~DirectReservation() {
122 if (m_Process && !m_Committed)
123 m_Process->freeUserRange(m_Region, m_Base, m_Length);
124 }
125
126 private:
127 Process* m_Process;
128 Process::UserRegion m_Region;
129 uintptr_t m_Base;
130 size_t m_Length;
131 bool m_Committed;
132};
133
134bool allocateDirect(Process& process, VirtualAddressSpace& space, size_t length, size_t mask,
135 uintptr_t& address, Process::UserRegion& region) {
136 const size_t allocationLength = length + mask;
137 auto allocate = [&](Process::UserRegion candidate) {
138 uintptr_t allocation = 0;
139 if (!process.allocateUserRange(candidate, allocationLength, allocation))
140 return false;
141 if (allocation > ~uintptr_t(0) - mask) {
142 process.freeUserRange(candidate, allocation, allocationLength);
143 return false;
144 }
145 const uintptr_t aligned = (allocation + mask) & ~mask;
146 if (aligned > ~uintptr_t(0) - length || allocationLength > ~uintptr_t(0) - allocation) {
147 process.freeUserRange(candidate, allocation, allocationLength);
148 return false;
149 }
150 const uintptr_t allocationEnd = allocation + allocationLength;
151 const uintptr_t usedEnd = aligned + length;
152 if (aligned != allocation)
153 process.freeUserRange(candidate, allocation, aligned - allocation);
154 if (usedEnd != allocationEnd)
155 process.freeUserRange(candidate, usedEnd, allocationEnd - usedEnd);
156 address = aligned;
157 region = candidate;
158 return true;
159 };
160
161 if (space.getDynamicStart() && allocate(Process::UserRegion::Dynamic))
162 return true;
163 return allocate(Process::UserRegion::Normal);
164}
165} // namespace
166
167MemoryMappedObject* MemoryMapManager::mapFile(File* file, uintptr_t& address, size_t length,
168 MemoryMappedObject::Permissions perms, size_t offset,
169 bool copyOnWrite) {
170 return mapFile(file, address, length, perms, offset, copyOnWrite, Placement::FixedReplace,
171 nullptr);
172}
173MemoryMappedObject* MemoryMapManager::mapFile(File* file, uintptr_t& address, size_t length,
174 MemoryMappedObject::Permissions perms, size_t offset,
175 bool copyOnWrite, Placement placement,
176 MapStatus* status,
178 const SharedPointer<MappingAttachment>& attachment,
179 MemoryLockMode requestedLock,
180 const FileMappingOrigin& origin) {
181 OperationGuard operation(*this);
182 bool mayWrite = maximumPerms & MemoryMappedObject::Write;
183 if (!file->allowMapping(!copyOnWrite, perms & MemoryMappedObject::Write, mayWrite)) {
184 if (status)
185 *status = MapStatus::PolicyDenied;
186 return nullptr;
187 }
188 if (!mayWrite)
189 maximumPerms &= ~MemoryMappedObject::Write;
190 return publishMapping(file, address, length, perms, offset, copyOnWrite, placement, status,
191 maximumPerms, attachment, requestedLock, origin);
192}
193MemoryMappedObject* MemoryMapManager::mapAnon(uintptr_t& address, size_t length,
195 return mapAnon(address, length, perms, Placement::FixedReplace, nullptr);
196}
197MemoryMappedObject* MemoryMapManager::mapAnon(uintptr_t& address, size_t length,
199 Placement placement, MapStatus* status,
200 MemoryLockMode requestedLock) {
201 OperationGuard operation(*this);
202 return publishMapping(
203 nullptr, address, length, perms, 0, true, placement, status,
204 MemoryMappedObject::Read | MemoryMappedObject::Write | MemoryMappedObject::Exec,
205 SharedPointer<MappingAttachment>(), requestedLock);
206}
207
208MemoryMappedObject* MemoryMapManager::publishMapping(
209 File* file, uintptr_t& address, size_t length, MemoryMappedObject::Permissions perms,
210 size_t offset, bool copyOnWrite, Placement placement, MapStatus* status,
212 const SharedPointer<MappingAttachment>& attachment, MemoryLockMode requestedLock,
213 const FileMappingOrigin& origin) {
214 if (status)
215 *status = MapStatus::NoMemory;
216 const size_t pageSize = PhysicalMemoryManager::getPageSize(), mask = pageSize - 1;
217 const size_t actualLength = length;
218 if (!length || length > ~size_t(0) - mask)
219 return nullptr;
220 length = (length + mask) & ~mask;
221 if (length > ~size_t(0) - mask || length > ~uintptr_t(0) - address)
222 return nullptr;
223 auto& space = Processor::information().getVirtualAddressSpace();
224 auto* process = Processor::information().getCurrentThread()->getParent();
225#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
226 process->recordBenchmarkVmCounter(Process::VmPublishCalls);
227#endif
228 auto* account = space.memoryLockAccount();
229 const MemoryLockMode mode = requestedLock != MemoryLockMode::None ? requestedLock
230 : account ? account->futureMode()
231 : MemoryLockMode::None;
232 auto* objects = m_MmObjectLists.lookup(&space);
233 // Preallocate the empty registry entry before a reservation or PTE changes.
234 if (!objects) {
235 auto* empty = new MmObjectList;
236 if (!empty)
237 return nullptr;
238 if (!m_MmObjectLists.tryInsert(&space, empty)) {
239 delete empty;
240 return nullptr;
241 }
242 objects = empty;
243 }
244 constexpr size_t MaximumObjects = 4096;
245 if (objects->count() > MaximumObjects)
246 return nullptr;
247#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
248 process->recordBenchmarkVmCounter(Process::VmPublishObjectCount, objects->count());
249#endif
250 const uintptr_t requested = address;
251 const bool directPlacement = requested == 0 && placement == Placement::Hint;
252 for (size_t attempt = 0; attempt < 32; ++attempt) {
253 Snapshot snapshot;
254 uintptr_t destination = requested;
255 Process::UserRegion directRegion = Process::UserRegion::Normal;
256 DirectReservation directReservation;
257 bool direct = false;
258 if (directPlacement) {
259 direct = allocateDirect(*process, space, length, mask, destination, directRegion);
260 if (!direct)
261 return nullptr;
262 directReservation.arm(process, directRegion, destination, length);
263 } else {
264 if (!process->snapshotUserReservations(snapshot))
265 return nullptr;
266 auto placementStatus = place(snapshot, space, destination, length, placement, mask);
267 if (placementStatus != MapStatus::Success) {
268 if (status)
269 *status = placementStatus;
270 return nullptr;
271 }
272 }
273 if (space.runtimeMappingPages(destination, length)) {
274 if (status)
275 *status = MapStatus::PolicyDenied;
276 return nullptr;
277 }
279 auto rawStatus = space.rawUserMemory().prepareReplacement(destination, length, raw);
280 if (rawStatus != MemoryLockStatus::Success)
281 return nullptr;
282 bool overlaps = false;
283#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
284 size_t objectVisits = 0;
285#endif
286 if (!direct)
287 for (auto* object : *objects) {
288#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
289 ++objectVisits;
290#endif
291 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
292 if (destination < end && object->address() < destination + length) {
293 overlaps = true;
294 break;
295 }
296 }
297#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
298 process->recordBenchmarkVmCounter(Process::VmPublishOverlapProbeVisits, objectVisits);
299 if (overlaps)
300 process->recordBenchmarkVmCounter(Process::VmPublishOverlapHits);
301#endif
302 MappingPlan plan;
303 if (!plan.staged.tryReserve(overlaps ? objects->count() * 2 + 1 : 1))
304 return nullptr;
305 size_t removedPages = 0;
306 if (overlaps) {
307 if (!plan.retired.tryReserve(objects->count()))
308 return nullptr;
309 plan.replacement = new MmObjectList;
310 if (!plan.replacement)
311 return nullptr;
312 for (auto* object : *objects) {
313 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
314 if (destination >= end || object->address() >= destination + length) {
315 if (!plan.replacement->tryPushBack(object))
316 return nullptr;
317 continue;
318 }
319 if (placement != Placement::FixedReplace)
320 return nullptr;
321 const uintptr_t first = object->address() > destination ? object->address() : destination;
322 const uintptr_t last = end < destination + length ? end : destination + length;
323 if (object->m_LockMode != MemoryLockMode::None)
324 removedPages += (last - first) / pageSize;
325 plan.retired.pushBack(object);
326 if (!plan.appendSlice(object, object->address(), first) ||
327 !plan.appendSlice(object, last, end))
328 return nullptr;
329 }
330 }
331 auto charge = account ? account->charge() : MemoryLockCharge{};
332 if (account) {
333 const size_t rawRemovedPages = raw ? raw.get()->removedPages() : 0;
334 assert(removedPages <= charge.managedPages && rawRemovedPages <= charge.rawPages);
335 charge.managedPages -= removedPages;
336 charge.rawPages -= rawRemovedPages;
337 if (mode != MemoryLockMode::None) {
338 const size_t added = length / pageSize;
339 if (added > ~size_t(0) - charge.managedPages)
340 return nullptr;
341 charge.managedPages += added;
342 if (charge.rawPages > ~size_t(0) - charge.managedPages ||
343 !account->permitsTotalPages(charge.managedPages + charge.rawPages,
344 process->getEffectiveUserId() == 0)) {
345 if (status)
346 *status = MapStatus::LockLimit;
347 return nullptr;
348 }
349 }
350 }
351 plan.inserted =
352 file ? static_cast<MemoryMappedObject*>(
353 new MemoryMappedFile(destination, actualLength, offset, file, copyOnWrite, perms,
354 maximumPerms, attachment, origin))
355 : static_cast<MemoryMappedObject*>(new AnonymousMemoryMap(destination, length, perms));
356 if (!plan.inserted)
357 return nullptr;
358 plan.inserted->m_OwnerProcess = process->addressSpaceOwner();
359 plan.inserted->m_OwnsMappings = false;
360 plan.inserted->m_LockMode = mode;
361 plan.staged.pushBack(plan.inserted);
362 if (file && !static_cast<MemoryMappedFile*>(plan.inserted)->m_UseAdmitted) {
363 if (status)
364 *status = MapStatus::TextBusy;
365 return nullptr;
366 }
367 auto* publication = overlaps ? plan.replacement : objects;
368 if (publication->count() >= MaximumObjects || !publication->tryPushBack(plan.inserted))
369 return nullptr;
370 if (!direct && !process->commitUserReservations(snapshot.generation, snapshot)) {
371#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
372 process->recordBenchmarkVmCounter(Process::VmPublishCommitRetries);
373#endif
374 // The operation gate excludes other executions; the allocation-free
375 // reservation commit cannot reenter this registry. Undo the provisional
376 // append before destroying the staged object.
377 if (!overlaps) {
378 [[maybe_unused]] auto* removed = objects->popBack();
379 assert(removed == plan.inserted);
380 }
381 continue;
382 }
383 directReservation.commit();
384 // Every recoverable preparation failure precedes retirement. Latest PTEs
385 // are detached by each owner, preserving independent stale CoW cache loans.
386 for (auto* object : plan.retired) {
387 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
388 const uintptr_t first = object->address() > destination ? object->address() : destination;
389 const uintptr_t last = end < destination + length ? end : destination + length;
390 object->discardRange(space, first, last - first);
391 object->m_OwnsMappings = false;
392 }
393 if (raw)
394 raw.get()->commit();
395 for (auto* object : plan.staged)
396 object->m_OwnsMappings = true;
397 if (overlaps) {
399 m_MmObjectLists.insert(&space, plan.replacement);
400 }
401 plan.replacement = nullptr;
402 plan.committed = true;
403 if (overlaps)
404 delete objects;
405 if (account)
406 account->publish(charge, account->futureMode());
407 address = destination;
408 if (status)
409 *status = MapStatus::Success;
410 if (mode == MemoryLockMode::Eager)
411 for (uintptr_t page = destination; page < destination + length; page += pageSize)
412 plan.inserted->populatePage(space, page);
413 return plan.inserted;
414 }
415 return nullptr;
416}
417
418size_t MemoryMapManager::removeInternal(uintptr_t base, size_t length, bool releaseReservations,
419 VmStatus* status) {
420 OperationGuard operation(*this);
421 if (status)
422 *status = VmStatus::InvalidRange;
423 const size_t pageSize = PhysicalMemoryManager::getPageSize(), mask = pageSize - 1;
424 if (!length || (base & mask) || length > ~size_t(0) - mask)
425 return 0;
426 length = (length + mask) & ~mask;
427 if (length > ~uintptr_t(0) - base)
428 return 0;
429 auto& space = Processor::information().getVirtualAddressSpace();
430 auto* objects = m_MmObjectLists.lookup(&space);
431 if (!objects) {
432 if (status)
433 *status = VmStatus::Success;
434 return 0;
435 }
436 if (status)
437 *status = VmStatus::NoMemory;
438 if (objects->count() > 4096)
439 return 0;
440 auto* process = Processor::information().getCurrentThread()->getParent();
441#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
442 process->recordBenchmarkVmCounter(Process::VmRemoveCalls);
443 process->recordBenchmarkVmCounter(Process::VmRemoveObjectCount, objects->count());
444 size_t objectVisits = 0;
445#endif
446
447 // Mmap users normally unmap the exact mapping they just created.
448 // The registry is append-ordered, so find and retire that common case from
449 // the tail without rebuilding a reservation or mapping snapshot.
450 for (auto it = objects->rbegin(); it != objects->rend(); ++it) {
451 auto* object = *it;
452#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
453 ++objectVisits;
454#endif
455 const uintptr_t objectEnd = (object->address() + object->length() + mask) & ~mask;
456 if (object->address() != base || objectEnd != base + length)
457 continue;
458
459 const size_t removedPages =
460 object->m_LockMode == MemoryLockMode::None ? 0 : (objectEnd - base) / pageSize;
461 object->discardRange(space, base, length);
462 object->m_OwnsMappings = false;
463 if (releaseReservations)
464 releaseReservation(process, space, base, length);
465 objects->erase(it);
466 delete object;
467 retireLockedPages(space, removedPages);
468#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
469 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
470 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, 1);
471#endif
472 if (status)
473 *status = VmStatus::Success;
474 return 1;
475 }
476
477#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
478 objectVisits = 0;
479#endif
480 bool needsSlices = false;
481 for (auto* object : *objects) {
482#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
483 ++objectVisits;
484#endif
485 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
486 if (object->address() < base + length && base < end &&
487 (object->address() < base || end > base + length)) {
488 needsSlices = true;
489 break;
490 }
491 }
492#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
493 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
494#endif
495 if (!needsSlices) {
496 size_t affected = 0, removedPages = 0;
497#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
498 objectVisits = 0;
499#endif
500 for (auto it = objects->begin(); it != objects->end();) {
501#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
502 ++objectVisits;
503#endif
504 auto* object = *it;
505 const uintptr_t first = object->address();
506 const uintptr_t end = (first + object->length() + mask) & ~mask;
507 if (first >= base + length || end <= base) {
508 ++it;
509 continue;
510 }
511 if (object->m_LockMode != MemoryLockMode::None)
512 removedPages += (end - first) / pageSize;
513 object->discardRange(space, first, end - first);
514 object->m_OwnsMappings = false;
515 if (releaseReservations)
516 releaseReservation(process, space, first, end - first);
517 it = objects->erase(it);
518 delete object;
519 ++affected;
520 }
521#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
522 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
523 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, affected);
524#endif
525 retireLockedPages(space, removedPages);
526 if (status)
527 *status = VmStatus::Success;
528 return affected;
529 }
530 MappingPlan plan;
531 if (!plan.staged.tryReserve(objects->count() * 2) || !plan.retired.tryReserve(objects->count()))
532 return 0;
533 plan.replacement = new MmObjectList;
534 if (!plan.replacement)
535 return 0;
536 size_t removedPages = 0;
537#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
538 objectVisits = 0;
539#endif
540 for (auto* object : *objects) {
541#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
542 ++objectVisits;
543#endif
544 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
545 const uintptr_t first = object->address() > base ? object->address() : base;
546 const uintptr_t last = end < base + length ? end : base + length;
547 if (first >= last) {
548 if (!plan.replacement->tryPushBack(object))
549 return 0;
550 continue;
551 }
552 if (object->m_LockMode != MemoryLockMode::None)
553 removedPages += (last - first) / pageSize;
554 plan.retired.pushBack(object);
555 if (!plan.appendSlice(object, object->address(), first) || !plan.appendSlice(object, last, end))
556 return 0;
557 }
558#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
559 process->recordBenchmarkVmCounter(Process::VmRemoveObjectVisits, objectVisits);
560 process->recordBenchmarkVmCounter(Process::VmRemoveSliceCalls);
561 process->recordBenchmarkVmCounter(Process::VmRemoveAffectedObjects, plan.retired.count());
562#endif
563 if (plan.replacement->count() > 4096)
564 return 0;
565 for (auto* object : plan.retired) {
566 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
567 const uintptr_t first = object->address() > base ? object->address() : base;
568 const uintptr_t last = end < base + length ? end : base + length;
569 object->discardRange(space, first, last - first);
570 object->m_OwnsMappings = false;
571 if (releaseReservations)
572 releaseReservation(process, space, first, last - first);
573 }
574 for (auto* object : plan.staged)
575 object->m_OwnsMappings = true;
576 {
578 m_MmObjectLists.insert(&space, plan.replacement);
579 }
580 plan.replacement = nullptr;
581 plan.committed = true;
582 delete objects;
583 retireLockedPages(space, removedPages);
584 if (status)
585 *status = VmStatus::Success;
586 return plan.retired.count();
587}
Memory-mapped file interface.
Definition File.h:74
virtual bool allowMapping(bool shared, bool writeRequested, bool &mayWrite)
Definition File.cc:1049
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
Process * getParent()
Definition Process.h:568
Process * addressSpaceOwner()
Definition Process.h:493
bool commitUserReservations(uint64_t expectedGeneration, UserReservationSnapshot &replacement)
static ProcessorInformation & information()
bool getRange(size_t index, Range &range) const
Definition RangeList.h:419
bool allocate(T length, T &address)
Definition RangeList.h:318
size_t size() const
Definition RangeList.h:104
bool allocateSpecific(T address, T length)
Definition RangeList.h:363
MemoryLockStatus prepareReplacement(uintptr_t base, size_t length, UniquePointer< PreparedMemoryLock > &result)
A vector / dynamic array.
Definition Vector.h:33
virtual uintptr_t getUserReservedStart() const =0
virtual uintptr_t getUserStart() const =0
void pushBack(const T &value)
Definition Vector.h:275