323 lines
12 KiB
C#
323 lines
12 KiB
C#
/*******************************************************************/
|
|
/* 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. */
|
|
/*******************************************************************/
|
|
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
namespace System.GCs {
|
|
|
|
using System.Threading;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
#if SINGULARITY
|
|
#if SINGULARITY_KERNEL
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Scheduling;
|
|
#else
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.V1.Services;
|
|
#endif
|
|
|
|
[CLSCompliant(false)]
|
|
public enum GarbageCollectorEvent : ushort
|
|
{
|
|
StartStopTheWorld = 1,
|
|
EndStopTheWorld = 2,
|
|
StartCollection = 3,
|
|
EndCollection = 4,
|
|
}
|
|
#endif
|
|
|
|
[NoCCtor]
|
|
internal abstract class StopTheWorldCollector : BaseCollector
|
|
{
|
|
|
|
internal static int collectorThreadIndex;
|
|
|
|
/// <summary>
|
|
/// Identifies the current collection phase
|
|
/// </summary>
|
|
internal enum STWPhase {
|
|
Dummy, // Not used!
|
|
Idle, // No collection is taking place
|
|
Synchronizing, // Attempting to stop the world
|
|
SingleThreaded, // Stop-the-world phase
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current state of the collector.
|
|
/// </summary>
|
|
internal static STWPhase CurrentPhase;
|
|
|
|
[PreInitRefCounts]
|
|
internal static void Initialize() {
|
|
collectorThreadIndex = -1;
|
|
CurrentPhase = STWPhase.Idle;
|
|
}
|
|
|
|
internal abstract void CollectStopped(int currentThreadIndex,
|
|
int generation);
|
|
|
|
// Implementation of GCInterface
|
|
internal override bool IsOnTheFlyCollector {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal override void Collect(int currentThreadIndex,
|
|
int generation)
|
|
{
|
|
int foundIndex =
|
|
Interlocked.CompareExchange(ref collectorThreadIndex,
|
|
currentThreadIndex, -1);
|
|
if (foundIndex < 0) {
|
|
// We are the designated collector thread
|
|
PerformCollection(generation);
|
|
} else {
|
|
Transitions.TakeDormantControlNoGC(currentThreadIndex);
|
|
// The 'foundIndex' thread may have completed its
|
|
// collection and another thread may have started
|
|
// another collection after we read
|
|
// 'collectorThreadIndex'. The collector thread may
|
|
// have decided to wait for us before we entered
|
|
// DormantState, so we have to read
|
|
// 'collectorThreadIndex' again.
|
|
foundIndex = collectorThreadIndex;
|
|
if (foundIndex >= 0) {
|
|
Thread.SignalGCEvent(foundIndex);
|
|
}
|
|
Transitions.TakeMutatorControlNoGC(currentThreadIndex);
|
|
}
|
|
}
|
|
|
|
private void PerformCollection(int generation)
|
|
{
|
|
#if SINGULARITY
|
|
Tracing.Log(Tracing.Debug,"GC start");
|
|
#endif
|
|
CollectorStatistics.Event(GCEvent.StopTheWorld);
|
|
CurrentPhase = STWPhase.Synchronizing;
|
|
StopTheWorld();
|
|
CurrentPhase = STWPhase.SingleThreaded;
|
|
#if SINGULARITY
|
|
long preGcMemoryUsage = GC.GetTotalMemory(false);
|
|
#if SINGULARITY_KERNEL
|
|
#if THREAD_TIME_ACCOUNTING
|
|
TimeSpan ticks = Thread.CurrentThread.ExecutionTime;
|
|
TimeSpan ticks2 = SystemClock.KernelUpTime;
|
|
#else
|
|
TimeSpan ticks = SystemClock.KernelUpTime;
|
|
#endif
|
|
#elif SINGULARITY_PROCESS
|
|
#if THREAD_TIME_ACCOUNTING
|
|
TimeSpan ticks = ProcessService.GetThreadTime();
|
|
TimeSpan ticks2 = ProcessService.GetUpTime();
|
|
#else
|
|
TimeSpan ticks = ProcessService.GetUpTime();
|
|
#endif
|
|
#endif
|
|
#endif //singularity
|
|
#if SINGULARITY_KERNEL
|
|
bool iflag = Processor.DisableInterrupts();
|
|
#endif
|
|
#if SINGULARITY
|
|
ulong beg = Processor.GetCycleCount();
|
|
#endif
|
|
// Preparation
|
|
GC.allocationInhibitGC = true;
|
|
// Verify the heap before GC
|
|
if (VTable.enableGCVerify) {
|
|
this.VerifyHeap(true);
|
|
}
|
|
// Invoke the chosen collector
|
|
#if SINGULARITY
|
|
Monitoring.Log(Monitoring.Provider.GC,
|
|
(ushort)GarbageCollectorEvent.StartCollection);
|
|
#endif
|
|
this.CollectStopped(collectorThreadIndex, generation);
|
|
#if SINGULARITY
|
|
Monitoring.Log(Monitoring.Provider.GC,
|
|
(ushort)GarbageCollectorEvent.EndCollection);
|
|
#endif
|
|
// Verify the heap after GC
|
|
if (VTable.enableGCVerify) {
|
|
this.VerifyHeap(false);
|
|
}
|
|
if (VTable.enableGCAccounting) {
|
|
MemoryAccounting.Report(GC.gcType);
|
|
}
|
|
// Cleanup
|
|
CollectorStatistics.Event(GCEvent.ResumeTheWorld);
|
|
GC.allocationInhibitGC = false;
|
|
CurrentPhase = STWPhase.Idle;
|
|
#if SINGULARITY
|
|
long postGcMemoryUsage = GC.GetTotalMemory(false);
|
|
#endif
|
|
ResumeTheWorld();
|
|
collectorThreadIndex = -1;
|
|
#if SINGULARITY
|
|
Tracing.Log(Tracing.Debug,"GC stop");
|
|
long pagesCollected = preGcMemoryUsage - postGcMemoryUsage;
|
|
#if SINGULARITY_KERNEL
|
|
#if THREAD_TIME_ACCOUNTING
|
|
int procId = Thread.CurrentProcess.ProcessId;
|
|
ticks = Thread.CurrentThread.ExecutionTime - ticks;
|
|
ticks2 = SystemClock.KernelUpTime - ticks2;
|
|
Process.kernelProcess.SetGcPerformanceCounters(ticks, (long) pagesCollected);
|
|
#else
|
|
ticks = SystemClock.KernelUpTime - ticks;
|
|
#endif
|
|
Thread.CurrentProcess.SetGcPerformanceCounters(ticks, (long) pagesCollected);
|
|
#elif SINGULARITY_PROCESS
|
|
#if THREAD_TIME_ACCOUNTING
|
|
ushort procId = ProcessService.GetCurrentProcessId();
|
|
ticks = ProcessService.GetThreadTime() - ticks;
|
|
ticks2 = ProcessService.GetUpTime() - ticks2;
|
|
#else
|
|
ticks = ProcessService.GetUpTime() - ticks;
|
|
#endif
|
|
ProcessService.SetGcPerformanceCounters(ticks, (long) pagesCollected);
|
|
#endif
|
|
|
|
#if DEBUG
|
|
#if THREAD_TIME_ACCOUNTING
|
|
DebugStub.WriteLine("~~~~~ StopTheWorld [collected pages={0:x8}, pid={1:x3}, ms(Thread)={2:d6}, ms(System)={3:d6}, procId={4}, tid={5}]",
|
|
__arglist(pagesCollected,
|
|
PageTable.processTag >> 16,
|
|
ticks.Milliseconds,
|
|
ticks2.Milliseconds,
|
|
procId,
|
|
Thread.GetCurrentThreadIndex()
|
|
));
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if SINGULARITY
|
|
DebugStub.AddToPerfCounter(GC.perfCounter, Processor.GetCycleCount() - beg);
|
|
#endif
|
|
#if SINGULARITY_KERNEL
|
|
#else
|
|
#if false
|
|
DebugStub.WriteLine("GC{0} after {1,11}",
|
|
__arglist(
|
|
ProcessService.GetCurrentProcessId(),
|
|
(UIntPtr)GC.installedGC.TotalMemory));
|
|
if (GC.installedGC.TotalMemory >= VTable.enableGCAccountingThreshold) {
|
|
DebugStub.WriteLine("GC memory {0,16}", __arglist(GC.installedGC.TotalMemory));
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if SINGULARITY_KERNEL
|
|
Processor.RestoreInterrupts(iflag);
|
|
#endif
|
|
}
|
|
|
|
internal override void CheckForNeededGCWork(Thread currentThread) {
|
|
while (CurrentPhase == STWPhase.Synchronizing &&
|
|
currentThread.threadIndex != collectorThreadIndex) {
|
|
GC.InvokeCollection(currentThread);
|
|
}
|
|
}
|
|
|
|
internal override void NewThreadNotification(Thread newThread,
|
|
bool initial)
|
|
{
|
|
base.NewThreadNotification(newThread, initial);
|
|
if (CurrentPhase == STWPhase.Synchronizing) {
|
|
Transitions.MakeGCRequest(newThread.threadIndex);
|
|
}
|
|
}
|
|
|
|
internal override void DeadThreadNotification(Thread deadThread)
|
|
{
|
|
MultiUseWord.CollectFromThread(deadThread);
|
|
base.DeadThreadNotification(deadThread);
|
|
}
|
|
|
|
internal override void ThreadDormantGCNotification(int threadIndex) {
|
|
int ctid = collectorThreadIndex;
|
|
if (ctid >= 0) {
|
|
Thread.SignalGCEvent(ctid);
|
|
}
|
|
}
|
|
|
|
private static void StopTheWorld() {
|
|
#if SINGULARITY
|
|
//DebugStub.WriteLine("~~~~~ StopTheWorld()");
|
|
Monitoring.Log(Monitoring.Provider.GC,
|
|
(ushort)GarbageCollectorEvent.StartStopTheWorld);
|
|
#if SINGULARITY_KERNEL
|
|
TimeSpan ticks = SystemClock.KernelUpTime;
|
|
#elif SINGULARITY_PROCESS
|
|
TimeSpan ticks = ProcessService.GetUpTime();
|
|
#endif
|
|
#endif
|
|
VTable.Assert(Thread.GetCurrentThreadIndex() ==
|
|
collectorThreadIndex);
|
|
Transitions.MakeGCRequests(collectorThreadIndex);
|
|
// Force threads to take allocation slow path.
|
|
for (int i = 0; i < Thread.threadTable.Length; i++) {
|
|
Thread t = Thread.threadTable[i];
|
|
if (t != null) {
|
|
BumpAllocator.Preempt(t);
|
|
}
|
|
}
|
|
for(int i = 0; i < Thread.threadTable.Length; i++) {
|
|
if (Thread.threadTable[i] == null) {
|
|
continue;
|
|
}
|
|
if (i == collectorThreadIndex) {
|
|
continue;
|
|
}
|
|
CollectorStatistics.Event(GCEvent.StopThread, i);
|
|
while (!Transitions.TakeGCControl(i) &&
|
|
!Transitions.UnderGCControl(i) &&
|
|
Transitions.HasGCRequest(i) &&
|
|
Thread.threadTable[i] != null) {
|
|
Thread.WaitForGCEvent(collectorThreadIndex);
|
|
}
|
|
}
|
|
#if SINGULARITY
|
|
#if SINGULARITY_KERNEL
|
|
ticks = SystemClock.KernelUpTime - ticks;
|
|
#elif SINGULARITY_PROCESS
|
|
ticks = ProcessService.GetUpTime() - ticks;
|
|
#endif
|
|
Monitoring.Log(Monitoring.Provider.GC,
|
|
(ushort)GarbageCollectorEvent.EndStopTheWorld);
|
|
#endif
|
|
}
|
|
|
|
private static void ResumeTheWorld() {
|
|
VTable.Assert(Thread.GetCurrentThreadIndex() ==
|
|
collectorThreadIndex);
|
|
for(int i = 0; i < Thread.threadTable.Length; i++) {
|
|
#if SINGULARITY_KERNEL
|
|
if (Scheduler.IsIdleThread(i)) {
|
|
continue;
|
|
}
|
|
#endif
|
|
if (i == collectorThreadIndex) {
|
|
if (Transitions.HasGCRequest(i)) {
|
|
Transitions.ClearGCRequest(i);
|
|
}
|
|
} else if (Transitions.UnderGCControl(i)) {
|
|
Transitions.ReleaseGCControl(i);
|
|
} else if (Thread.threadTable[i] != null) {
|
|
Thread.SignalGCEvent(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|