singrdk/base/Imported/Bartok/runtime/shared/GCs/MemoryManager.cs

363 lines
15 KiB
C#
Raw Normal View History

2008-11-17 18:29:00 -05:00
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
2008-03-05 09:52:00 -05:00
/*******************************************************************/
/* WARNING */
/* This file should be identical in the Bartok and Singularity */
/* depots. Master copy resides in Bartok Depot. Changes should be */
/* made to Bartok Depot and propagated to Singularity Depot. */
/*******************************************************************/
namespace System.GCs {
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SINGULARITY_KERNEL
using Microsoft.Singularity;
using Microsoft.Singularity.Memory;
using Sing_MemoryManager = Microsoft.Singularity.Memory.MemoryManager;
#elif SINGULARITY_PROCESS
using Microsoft.Singularity;
using Microsoft.Singularity.V1.Services;
#endif
internal class MemoryManager
{
private static UIntPtr allocationGranularity;
private static UIntPtr totalMemorySize;
private static UIntPtr operatingSystemSize;
private static bool inAllocator;
#if SINGULARITY
internal static unsafe void Initialize()
{
allocationGranularity = (UIntPtr)0x10000;
totalMemorySize = PageTable.PageSize * PageTable.pageTableCount;
operatingSystemSize = totalMemorySize / 16;
}
//////////////////////////////////// Allocation and Free Routines.
//
// Allocation is optimized for the case where an allocation starts
// with a relatively small amount of memory and grows over time.
// This is exactly the behavior exhibited by stacks and GC heaps.
//
// The allocation strategy also works well for large initial
// allocations. The strategy would be very inefficient if a very
// large number of small, completely independent allocations are
// made.
//
// AllocateMemory(size) performs an initial allocation.
// AllocateMemory(startAddr, size) performs growing allocations.
//
internal static unsafe UIntPtr AllocateMemory(UIntPtr size)
{
VTable.Assert(PageTable.PageAligned(size));
#if SINGULARITY_KERNEL
UIntPtr addr = Sing_MemoryManager.KernelAllocate(
Sing_MemoryManager.PagesFromBytes(size),
Process.kernelProcess, 0, PageType.Unknown);
#elif SINGULARITY_PROCESS
UIntPtr addr = PageTableService.Allocate(size);
#endif
#if SINGULARITY_KERNEL
Kernel.Waypoint((int)size);
Kernel.Waypoint(811);
#endif // SINGULARITY_KERNEL
if (addr != UIntPtr.Zero) {
Util.MemClear(addr, size);
}
return addr;
}
internal static unsafe bool AllocateMemory(UIntPtr startAddr,
UIntPtr size)
{
VTable.Deny(inAllocator);
inAllocator = true;
VTable.Assert(PageTable.PageAligned(startAddr));
VTable.Assert(PageTable.PageAligned(size));
#if SINGULARITY_KERNEL
UIntPtr addr = Sing_MemoryManager.KernelExtend(
startAddr, Sing_MemoryManager.PagesFromBytes(size),
Process.kernelProcess, PageType.Unknown);
#elif SINGULARITY_PROCESS
UIntPtr addr = PageTableService.AllocateExtend(startAddr, size);
#endif
inAllocator = false;
if (addr != UIntPtr.Zero) {
Util.MemClear(addr, size);
return true;
}
return false;
}
[Inline]
internal static void FreeMemory(UIntPtr startAddr, UIntPtr size)
{
#if SINGULARITY_KERNEL
DebugStub.Assert(Sing_MemoryManager.IsPageAligned(size));
Sing_MemoryManager.KernelFree(
startAddr, Sing_MemoryManager.PagesFromBytes(size),
Process.kernelProcess);
#elif SINGULARITY_PROCESS
VTable.Assert((size & PageTable.PageMask) == 0);
PageTableService.Free(startAddr, size);
#endif
}
internal static void IgnoreMemoryContents(UIntPtr startAddr,
UIntPtr regionSize)
{
// Since we don't do swapping, we simply ignore this information
}
internal static bool QueryMemory(UIntPtr queryAddr,
out UIntPtr regionAddr,
out UIntPtr regionSize)
{
#if SINGULARITY_KERNEL
PageType type = Sing_MemoryManager.KernelQuery(
queryAddr, out regionAddr, out regionSize);
return (type != PageType.Unknown);
#elif SINGULARITY_PROCESS
return PageTableService.Query(queryAddr, out regionAddr, out regionSize);
#endif
}
#else // not SINGULARITY
2008-11-17 18:29:00 -05:00
[NoBarriers]
2008-03-05 09:52:00 -05:00
[PreInitRefCounts]
2008-11-17 18:29:00 -05:00
[NoStackLinkCheckTrans]
2008-03-05 09:52:00 -05:00
internal static unsafe void Initialize() {
SYSTEM_INFO systemInfo;
GetSystemInfo(out systemInfo);
allocationGranularity =
new UIntPtr(systemInfo.AllocationGranularity);
MEMORYSTATUSEX memoryStatus = new MEMORYSTATUSEX();
memoryStatus.Length = (uint) sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(ref memoryStatus);
if (memoryStatus.TotalPhysical <= ((ulong)UIntPtr.MaxValue)) {
totalMemorySize = (UIntPtr) memoryStatus.TotalPhysical;
} else {
// TotalPhysical exceeds UIntPtr.MaxValue running in 32bit
// mode on a machine with more than 4GB of RAM
totalMemorySize = UIntPtr.MaxValue;
}
// We are just making a random guess here
operatingSystemSize = (UIntPtr) 1 << 26;
}
// Low-level routines based on Win32
internal const int PAGE_READWRITE = 0x04;
internal const int PAGE_EXECUTE_READWRITE = 0x40;
internal const int PAGE_NOACCESS = 0x01;
internal const int MEM_COMMIT = 0x1000;
internal const int MEM_RESERVE = 0x2000;
private const int MEM_DECOMMIT = 0x4000;
internal const int MEM_RELEASE = 0x8000;
private const int MEM_FREE = 0x10000;
private const int MEM_RESET = 0x80000;
[StructLayout(LayoutKind.Sequential)]
private struct MEMORY_BASIC_INFORMATION {
internal UIntPtr BaseAddress;
internal UIntPtr AllocationBase;
internal int AllocationProtect;
internal UIntPtr RegionSize;
internal int State;
internal int Protect;
internal int Type;
}
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_INFO {
internal ushort ProcessorArchitecture;
internal ushort Reserved;
internal uint PageSize;
internal UIntPtr MinimumApplicationAddress;
internal UIntPtr MaximumApplicationAddress;
internal uint ActiveProcessorMask;
internal uint NumberOfProcessors;
internal uint ProcessorType;
internal uint AllocationGranularity;
internal ushort ProcessorLevel;
internal ushort ProcessorRevision;
}
[StructLayout(LayoutKind.Sequential)]
private struct MEMORYSTATUSEX {
internal uint Length;
internal uint MemoryLoad;
internal ulong TotalPhysical;
internal ulong AvailPhysical;
internal ulong TotalPageFile;
internal ulong AvailPageFile;
internal ulong TotalVirtual;
internal ulong AvailVirtual;
internal ulong AvailExtendedVirtual;
}
// Kernel32.dll functions (via MSVCRT.dll)
[DllImport("BRT")]
[GCAnnotation(GCOption.NOGC)]
[NoStackLinkCheck]
[StackBound(1024)]
public static unsafe extern void *VirtualAlloc(void *lpAddress,
UIntPtr dwSize,
int fIAllocationType,
int fIProtect);
[DllImport("BRT")]
[GCAnnotation(GCOption.NOGC)]
[StackBound(1024)]
internal static unsafe extern int VirtualFree(void *lpAddress,
UIntPtr dwSize,
int dwFreeType);
[DllImport("BRT")]
[GCAnnotation(GCOption.NOGC)]
[StackBound(1024)]
private static unsafe extern UIntPtr VirtualQuery(void *lpAddress, out MEMORY_BASIC_INFORMATION memInfo, UIntPtr dwLength);
[DllImport("BRT")]
[GCAnnotation(GCOption.NOGC)]
[NoStackLinkCheck]
[StackBound(1024)]
2008-11-17 18:29:00 -05:00
[NoBarriers]
2008-03-05 09:52:00 -05:00
private static unsafe extern void GetSystemInfo(out SYSTEM_INFO systemInfo);
[DllImport("BRT")]
[GCAnnotation(GCOption.NOGC)]
[NoStackLinkCheck]
[StackBound(1024)]
2008-11-17 18:29:00 -05:00
[NoBarriers]
2008-03-05 09:52:00 -05:00
private static unsafe extern void GlobalMemoryStatusEx(ref MEMORYSTATUSEX memoryStatus);
// Low-level routines based on the operating system interface
private static unsafe UIntPtr AllocateMemoryHelper(UIntPtr startAddr,
UIntPtr size)
{
void *result = VirtualAlloc((void *) startAddr,
size, MEM_RESERVE, PAGE_READWRITE);
VTable.Assert
(PageTable.Page((UIntPtr) result + size - 1)
< PageTable.pageTableCount,
"OutOfMemory: MemoryManager: memory doesn't fit in page table");
if (result != null) {
Trace.Log(Trace.Area.Page,
"VirtualAlloc {0} at {1}",
__arglist(size, result));
VTable.Assert((startAddr == UIntPtr.Zero)
|| (result == (void *) startAddr));
void *area = VirtualAlloc(result, size,
MEM_COMMIT, PAGE_READWRITE);
if (PageTable.halPageDescriptor != null) {
PageTable.CreateNewPageTablesIfNecessary(PageTable.Page((UIntPtr)result), PageTable.PageCount(size));
PageTable.SetProcess(PageTable.Page((UIntPtr) area),
PageTable.PageCount(size));
}
VTable.Assert(result == area);
#if HIMEM
// This assertion intends only to catch bugs in Bartok. But if
// the system (windows) frees memory before, the memory
// may be in low memory, and if we happen to get that
// memory, this assertion itself may not hold
VTable.Assert((UIntPtr) result >= PageTable.HIMEMStart,
("High memory is expected to be allocated in HIMEM mode"));
#endif
}
return new UIntPtr(result);
}
internal static unsafe bool AllocateMemory(UIntPtr startAddr,
UIntPtr size) {
UIntPtr addr = AllocateMemoryHelper(startAddr, size);
return addr != UIntPtr.Zero;
}
internal static unsafe UIntPtr AllocateMemory(UIntPtr size) {
return AllocateMemoryHelper(UIntPtr.Zero, size);
}
internal static unsafe void FreeMemory(UIntPtr addr, UIntPtr size) {
Trace.Log(Trace.Area.Page,
"VirtualFree {0} at {1}",
__arglist(size, addr));
int success = VirtualFree((void *) addr, UIntPtr.Zero,
MEM_RELEASE);
VTable.Assert(success != 0);
}
// Indicate that we still own the memory but that we don't care
// about the content of the memory. The virtual memory system
// does not have to write the memory to the swap file in order
// to reuse the physical memory for other purposes.
internal static unsafe void IgnoreMemoryContents(UIntPtr startAddr,
UIntPtr regionSize)
{
Trace.Log(Trace.Area.Page,
"VirtualAlloc-Reset {0} at {1}",
__arglist(regionSize, startAddr));
void * result = VirtualAlloc((void *) startAddr, regionSize,
MEM_RESET, PAGE_READWRITE);
VTable.Assert(result == (void *) startAddr,
"VirtualAlloc MEM_RESET failed");
}
internal static unsafe bool QueryMemory(UIntPtr queryAddr,
out UIntPtr regionAddr,
out UIntPtr regionSize)
{
VTable.Assert(PageTable.PageAligned(queryAddr));
MEMORY_BASIC_INFORMATION memInfo;
UIntPtr size = (UIntPtr) sizeof(MEMORY_BASIC_INFORMATION);
UIntPtr data = VirtualQuery((void *) queryAddr, out memInfo, size);
Trace.Log(Trace.Area.Page,
"VirtualQuery {0}: base={1} size={2}",
__arglist(queryAddr, memInfo.AllocationBase,
memInfo.RegionSize));
if (data == 0) {
// queryAddr is a kernel-mode pointer
regionAddr = queryAddr;
regionSize = (UIntPtr) sizeof(int);
return false;
} else {
VTable.Assert(data == size &&
memInfo.BaseAddress == queryAddr);
regionAddr = memInfo.AllocationBase;
regionSize = (queryAddr - regionAddr) + memInfo.RegionSize;
return (memInfo.State != MEM_FREE);
}
}
#endif
internal static UIntPtr OperatingSystemCommitSize {
get {
return allocationGranularity;
}
}
// We are just guessing here
internal static UIntPtr MemorySize {
get { return totalMemorySize; }
}
// We are just making an educated guess here
internal static UIntPtr OperatingSystemSize {
get { return operatingSystemSize; }
}
}
}