541 lines
19 KiB
C#
541 lines
19 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: KernelSpinlock.cs
|
|
//
|
|
// Note:
|
|
// Kernel spinlock functionality
|
|
//
|
|
using System;
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Audit;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
|
|
namespace System.Threading
|
|
{
|
|
///
|
|
/// <summary>
|
|
/// Implementation of Kernel Spinlocks
|
|
/// </summary>
|
|
///
|
|
[NoCCtor]
|
|
[CLSCompliant(false)]
|
|
[AccessedByRuntime("referenced from halidt.asm")]
|
|
public struct SpinLock
|
|
{
|
|
///
|
|
/// <summary>
|
|
/// Spinlock Rank is enumerator used to enumerate spinlock ranks. The rule is:
|
|
/// holders of lower rank spinlocks can't acquire spinlocks with higher ranks.
|
|
/// Note this needs to be a bitmask enum because of the validation logic in Thread.cs
|
|
/// </summary>
|
|
///
|
|
internal enum Ranks : short
|
|
{
|
|
/// <summary> Placeholder. eventually all spinlocks should be ranked </summary>
|
|
NoRank = 0x0,
|
|
|
|
/// <summary>
|
|
/// Used by flag pages (low level memory management). This is nested
|
|
/// within several spin locks with Dispatcher rank, including
|
|
/// PageManager (GC) and linked stack.
|
|
/// </summary>
|
|
FlatPages = 0x1,
|
|
|
|
/// <summary>
|
|
/// Use by Hal. This can be nested within other
|
|
/// spin locks with Dispatcher rank.
|
|
/// </summary>
|
|
Hal = 0x2,
|
|
|
|
/// <summary>
|
|
/// Used by scheduler and dispatcher code, and other code run with
|
|
/// interrupt disabled.
|
|
/// </summary>
|
|
Dispatcher = 0x4,
|
|
|
|
/// <summary>
|
|
/// The lowest rank above Dispatcher. A thread holding a lock with this rank
|
|
/// cannot acquire another spinlock, unless it enters scheduling code through
|
|
/// Yield or blocking. Most code above dispatcher level should use this rank.
|
|
///
|
|
/// Add higher ranks only if the code needs to acquire multiple spinlocks,
|
|
/// which should be avoided unless absolutely necessary.
|
|
/// </summary>
|
|
Passive = 0x8,
|
|
|
|
/// <summary>
|
|
/// Used by services implemented in kernel
|
|
/// </summary>
|
|
Service = 0x10,
|
|
};
|
|
|
|
///
|
|
/// <summary>
|
|
/// Spinlock Type is enumerator used to enumerate spinlock types so that we can keep
|
|
/// proper statistic for each spinlock. Statistic will allow us to identify spinlock problems
|
|
/// </summary>
|
|
///
|
|
public enum Types : int
|
|
{
|
|
NoType = 0,
|
|
InterruptMutex = (Ranks.Dispatcher << RankShift) | 1,
|
|
InterruptAutoResetEvent = (Ranks.Dispatcher << RankShift) | 2,
|
|
AutoResetEvent = (Ranks.Passive << RankShift) | 3,
|
|
Mutex = (Ranks.Passive << RankShift) | 4,
|
|
ManualResetEvent = (Ranks.Passive << RankShift) | 5,
|
|
Timer = (Ranks.Hal << RankShift) | 6,
|
|
IoApic = (Ranks.Hal << RankShift) | 7,
|
|
MpHalClock = (Ranks.Hal << RankShift) | 8,
|
|
RTClock = (Ranks.Hal << RankShift) | 9,
|
|
HalScreen = (Ranks.Hal << RankShift) | 10,
|
|
FlatPages = (Ranks.FlatPages << RankShift) | 11,
|
|
VirtualMemoryRange = (Ranks.Dispatcher << RankShift) | 12,
|
|
VMManager = (Ranks.Dispatcher << RankShift) | 13,
|
|
VMKernelMapping = (Ranks.Dispatcher << RankShift) | 14,
|
|
ProtectionDomainTable = (Ranks.Dispatcher << RankShift) | 15,
|
|
ProtectionDomainInit = (Ranks.Dispatcher << RankShift) | 16,
|
|
ProtectionDomainMapping = (Ranks.Dispatcher << RankShift) | 17,
|
|
SharedHeapAllocationOwner = (Ranks.Service << RankShift) | 18,
|
|
PhysicalHeap = (Ranks.Dispatcher << RankShift) | 19,
|
|
PhysicalPages = (Ranks.Dispatcher << RankShift) | 20,
|
|
PageManager = (Ranks.Dispatcher << RankShift) | 21,
|
|
IoResources = (Ranks.Dispatcher << RankShift) | 22,
|
|
Finalizer = (Ranks.Dispatcher << RankShift) | 23,
|
|
MpExecutionFreeze = (Ranks.Dispatcher << RankShift) | 24,
|
|
MpExecutionMpCall = (Ranks.Dispatcher << RankShift) | 25,
|
|
Scheduler = (Ranks.Dispatcher << RankShift) | 26,
|
|
GCTracing = (Ranks.Dispatcher << RankShift) | 27,
|
|
IoIrq = (Ranks.Dispatcher << RankShift) | 28,
|
|
ServiceQueue = (Ranks.Dispatcher << RankShift) | 29,
|
|
EndpointCore = (Ranks.Dispatcher << RankShift) | 30,
|
|
KernelTestCase = (Ranks.Dispatcher << RankShift) | 31,
|
|
HandleTable = (Ranks.Passive << RankShift) | 32,
|
|
ThreadTable = (Ranks.Passive << RankShift) | 33,
|
|
ProcessTable = (Ranks.Passive << RankShift) | 34,
|
|
Thread = (Ranks.Passive << RankShift) | 35,
|
|
MaxTypeId = (Thread & TypeMask) +1
|
|
};
|
|
|
|
///
|
|
/// <summary>
|
|
/// Static initializer
|
|
/// </summary>
|
|
///
|
|
static public void StaticInitialize()
|
|
{
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Init spinlock
|
|
/// </summary>
|
|
/// <param name="type">Type of SpinLock</param>
|
|
///
|
|
public SpinLock(Types type)
|
|
{
|
|
baseLock = new SpinLockBase((int)type);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// SpinLock Rank
|
|
/// </summary>
|
|
///
|
|
internal int Rank
|
|
{
|
|
[NoHeapAllocation]
|
|
get
|
|
{
|
|
return baseLock.Type >> RankShift;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// SpinLock Type
|
|
/// </summary>
|
|
///
|
|
internal int Type
|
|
{
|
|
[NoHeapAllocation]
|
|
get
|
|
{
|
|
return baseLock.Type & TypeMask;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Acquire spinlock
|
|
/// </summary>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public void Acquire()
|
|
{
|
|
Thread thread = Thread.CurrentThread;
|
|
int threadId = (thread==null)?InitialThreadId:thread.GetThreadId();
|
|
|
|
AcquireInternal(thread, threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Acquire spinlock
|
|
/// </summary>
|
|
///
|
|
/// <param name="threadId">Thread's Id acquiring spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public void Acquire(int threadId)
|
|
{
|
|
AcquireInternal(Thread.GetThreadFromThreadId(threadId), threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Acquire spinlock
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread acquiring spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public void Acquire(Thread thread)
|
|
{
|
|
int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();
|
|
|
|
AcquireInternal(thread, threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Release spinlock
|
|
/// </summary>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public void Release()
|
|
{
|
|
Thread thread = Thread.CurrentThread;
|
|
int threadId = (thread == null)?InitialThreadId:
|
|
thread.GetThreadId();
|
|
// Release spinlock
|
|
ReleaseInternal(thread, threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Release spinlock
|
|
/// </summary>
|
|
///
|
|
/// <param name="threadId">Thread's Id releasing spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public void Release(int threadId)
|
|
{
|
|
// Release spinlock
|
|
ReleaseInternal(Thread.GetThreadFromThreadId(threadId), threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Release spinlock
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread releasing spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public void Release(Thread thread)
|
|
{
|
|
int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();
|
|
|
|
// Release spinlock
|
|
ReleaseInternal(thread, threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Try to acquire the spin lock. Always return immediately.
|
|
/// </summary>
|
|
/// <returns> true if the spin lock is acquired. </returns>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public bool TryAcquire()
|
|
{
|
|
Thread thread = Thread.CurrentThread;
|
|
int threadId =(thread == null)?InitialThreadId:thread.GetThreadId();
|
|
|
|
return TryAcquireInternal(thread, threadId);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Try to acquire the spin lock. Always return immediately.
|
|
/// </summary>
|
|
///
|
|
/// <returns> true if the spin lock is acquired. </returns>
|
|
///
|
|
/// <param name="thread">Thread acquiring spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
public bool TryAcquire(Thread thread)
|
|
{
|
|
int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();
|
|
|
|
return TryAcquireInternal(thread, thread.GetThreadId());
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Method to find out if spinlock is held by specified thread
|
|
/// </summary>
|
|
/// <returns> true if the spin lock is acquired. </returns>
|
|
///
|
|
/// <param name="thread">Thread to verify possible spinlock's ownership</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public bool IsHeldBy(Thread thread)
|
|
{
|
|
int threadId = (thread == null)?InitialThreadId:thread.GetThreadId();
|
|
return baseLock.IsHeldBy(threadId + 1);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Method to find out if spinlock is held by specified thread
|
|
/// </summary>
|
|
/// <returns> true if the spin lock is acquired. </returns>
|
|
///
|
|
/// <param name="threadId">Thread's Id to verify possible spinlock's ownership</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
public bool IsHeldBy(int threadId)
|
|
{
|
|
return baseLock.IsHeldBy(threadId + 1);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Assert thatf spinlock is held by specified thread
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread to verify possible spinlock's ownership</param>
|
|
///
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
[NoHeapAllocation]
|
|
public void AssertHeldBy(Thread thread)
|
|
{
|
|
VTable.Assert(IsHeldBy(thread));
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Assert thatf spinlock is held by specified thread
|
|
/// </summary>
|
|
///
|
|
/// <param name="threadId">Thread's Id to verify possible spinlock's ownership</param>
|
|
///
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
[NoHeapAllocation]
|
|
public void AssertHeldBy(int threadId)
|
|
{
|
|
VTable.Assert(IsHeldBy(threadId));
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Given integer : derive type of a lock
|
|
/// </summary>
|
|
///
|
|
/// <param name="type">Parameter from which we can derive actual type of spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
internal static int DeriveType (int type)
|
|
{
|
|
// Type has to be in correct range
|
|
VTable.Assert((type & TypeMask) >= (int)Types.NoType &&
|
|
(type & TypeMask) < (int)Types.MaxTypeId);
|
|
|
|
return (type & (int)TypeMask);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Given integer : derive type of a lock
|
|
/// </summary>
|
|
///
|
|
/// <param name="type">Parameter from which we can derive actual rank of spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
internal static int DeriveRank (int type)
|
|
{
|
|
// Type has to be in correct range
|
|
VTable.Assert((type & TypeMask) >= (int)Types.NoType &&
|
|
(type & TypeMask) < (int)Types.MaxTypeId);
|
|
|
|
return (type >> RankShift);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Try to acquire the spin lock. Always return immediately.
|
|
/// </summary>
|
|
/// <returns> true if the spin lock is acquired. </returns>
|
|
///
|
|
/// <param name="thread">Thread acquiring spinlock</param>
|
|
/// <param name="threadId">Thread's Id acquiring spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private bool TryAcquireInternal(Thread thread, int threadId)
|
|
{
|
|
bool result;
|
|
|
|
// Assert preconditions:for spinlock with a rank = DisabledInterrupts and below
|
|
// interrupts have to be disabled.
|
|
VTable.Assert(Rank == (int)Ranks.NoRank ||
|
|
Rank > (int)Ranks.Dispatcher ||
|
|
Processor.InterruptsDisabled());
|
|
|
|
|
|
// Notify thread that we are about to acquire spinlock
|
|
if (thread != null) {
|
|
thread.NotifySpinLockAboutToAcquire(this.baseLock.Type);
|
|
}
|
|
|
|
result = baseLock.TryAcquire(threadId + 1);
|
|
|
|
// If we didn't acquire spinlock -we should notify thread about it: Just use release
|
|
// notification
|
|
if (thread != null && !result) {
|
|
thread.NotifySpinLockReleased(this.baseLock.Type);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Acquire the spin lock.
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread acquiring spinlock</param>
|
|
/// <param name="threadId">Thread's Id acquiring spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private void AcquireInternal(Thread thread, int threadId)
|
|
{
|
|
// Assert preconditions:for spinlock with a rank = DisabledInterrupts and below
|
|
// interrupts have to be disabled.
|
|
VTable.Assert(Rank == (int)Ranks.NoRank ||
|
|
Rank > (int)Ranks.Dispatcher ||
|
|
Processor.InterruptsDisabled());
|
|
|
|
|
|
// Thread has to be notified if we are about to acquire spinlock
|
|
if (thread != null) {
|
|
thread.NotifySpinLockAboutToAcquire(this.baseLock.Type);
|
|
}
|
|
|
|
// Get lock
|
|
baseLock.Acquire(threadId + 1);
|
|
}
|
|
|
|
///
|
|
/// <summary>
|
|
/// Release the spin lock.
|
|
/// </summary>
|
|
///
|
|
/// <param name="thread">Thread releasing spinlock</param>
|
|
/// <param name="threadId">Thread's Id releasing spinlock</param>
|
|
///
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private void ReleaseInternal(Thread thread, int threadId)
|
|
{
|
|
// Assert preconditions:for spinlock with a rank = DisabledInterrupts and below
|
|
// interrupts have to be disabled.
|
|
VTable.Assert(Rank == (int)Ranks.NoRank ||
|
|
Rank > (int)Ranks.Dispatcher ||
|
|
Processor.InterruptsDisabled());
|
|
|
|
|
|
// Release spinlock
|
|
baseLock.Release(threadId + 1);
|
|
|
|
if (thread != null) {
|
|
// Don't forget to notify thread that it just released spinlock
|
|
thread.NotifySpinLockReleased(this.baseLock.Type);
|
|
}
|
|
}
|
|
|
|
/// <summary> Constant defines shift of the rank</summary>
|
|
internal const int RankShift = 0x10;
|
|
|
|
/// <summary> Constant defines mask for getting type of gompound spinlock type </summary>
|
|
internal const int TypeMask = 0xFFFF;
|
|
|
|
/// <summary> Id of a initial thread </summary>
|
|
private const int InitialThreadId = -2;
|
|
|
|
/// <summary> Actual mechanism implementing spinlock</summary>
|
|
private SpinLockBase baseLock;
|
|
}
|
|
|
|
/// <summary> Attribute to mark methods that stop lock rank verification </summary>
|
|
[Layer(0)]
|
|
public class IgnoreLockRankAttribute : Attribute
|
|
{
|
|
public IgnoreLockRankAttribute() {}
|
|
}
|
|
|
|
/// <summary> Attribute to mark classes / methods that hold FlatPages locks </summary>
|
|
[Layer(1)]
|
|
public class FlatPagesLockAttribute : Attribute
|
|
{
|
|
public FlatPagesLockAttribute() {}
|
|
}
|
|
|
|
/// <summary> Attribute to mark classes / methods that hold Hal locks </summary>
|
|
[Layer(2)]
|
|
public class HalLockAttribute : Attribute
|
|
{
|
|
public HalLockAttribute() {}
|
|
}
|
|
|
|
/// <summary> Attribute to mark classes / methods that hold Dispatcher locks </summary>
|
|
[Layer(3)]
|
|
public class DispatcherLockAttribute : Attribute
|
|
{
|
|
public DispatcherLockAttribute() {}
|
|
}
|
|
|
|
/// <summary> Attribute to mark classes / methods that hold Passive locks </summary>
|
|
[Layer(4)]
|
|
public class PassiveLockAttribute : Attribute
|
|
{
|
|
public PassiveLockAttribute() {}
|
|
}
|
|
|
|
/// <summary> Attribute to mark classes / methods that hold Service locks </summary>
|
|
[Layer(5)]
|
|
public class ServiceLockAttribute : Attribute
|
|
{
|
|
public ServiceLockAttribute() {}
|
|
}
|
|
}
|