//////////////////////////////////////////////////////////////////////////////// // // 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 { /// /// /// Implementation of Kernel Spinlocks /// /// [NoCCtor] [CLSCompliant(false)] [AccessedByRuntime("referenced from halidt.asm")] public struct SpinLock { /// /// /// 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 /// /// internal enum Ranks : short { /// Placeholder. eventually all spinlocks should be ranked NoRank = 0x0, /// /// Used by flag pages (low level memory management). This is nested /// within several spin locks with Dispatcher rank, including /// PageManager (GC) and linked stack. /// FlatPages = 0x1, /// /// Use by Hal. This can be nested within other /// spin locks with Dispatcher rank. /// Hal = 0x2, /// /// Used by scheduler and dispatcher code, and other code run with /// interrupt disabled. /// Dispatcher = 0x4, /// /// 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. /// Passive = 0x8, /// /// Used by services implemented in kernel /// Service = 0x10, }; /// /// /// 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 /// /// 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 }; /// /// /// Static initializer /// /// static public void StaticInitialize() { } /// /// /// Init spinlock /// /// Type of SpinLock /// public SpinLock(Types type) { baseLock = new SpinLockBase((int)type); } /// /// /// SpinLock Rank /// /// internal int Rank { [NoHeapAllocation] get { return baseLock.Type >> RankShift; } } /// /// /// SpinLock Type /// /// internal int Type { [NoHeapAllocation] get { return baseLock.Type & TypeMask; } } /// /// /// Acquire spinlock /// /// [NoHeapAllocation] [Inline] public void Acquire() { Thread thread = Thread.CurrentThread; int threadId = (thread==null)?InitialThreadId:thread.GetThreadId(); AcquireInternal(thread, threadId); } /// /// /// Acquire spinlock /// /// /// Thread's Id acquiring spinlock /// [NoHeapAllocation] [Inline] public void Acquire(int threadId) { AcquireInternal(Thread.GetThreadFromThreadId(threadId), threadId); } /// /// /// Acquire spinlock /// /// /// Thread acquiring spinlock /// [NoHeapAllocation] [Inline] public void Acquire(Thread thread) { int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); AcquireInternal(thread, threadId); } /// /// /// Release spinlock /// /// [NoHeapAllocation] [Inline] public void Release() { Thread thread = Thread.CurrentThread; int threadId = (thread == null)?InitialThreadId: thread.GetThreadId(); // Release spinlock ReleaseInternal(thread, threadId); } /// /// /// Release spinlock /// /// /// Thread's Id releasing spinlock /// [NoHeapAllocation] [Inline] public void Release(int threadId) { // Release spinlock ReleaseInternal(Thread.GetThreadFromThreadId(threadId), threadId); } /// /// /// Release spinlock /// /// /// Thread releasing spinlock /// [NoHeapAllocation] [Inline] public void Release(Thread thread) { int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); // Release spinlock ReleaseInternal(thread, threadId); } /// /// /// Try to acquire the spin lock. Always return immediately. /// /// true if the spin lock is acquired. /// [NoHeapAllocation] [Inline] public bool TryAcquire() { Thread thread = Thread.CurrentThread; int threadId =(thread == null)?InitialThreadId:thread.GetThreadId(); return TryAcquireInternal(thread, threadId); } /// /// /// Try to acquire the spin lock. Always return immediately. /// /// /// true if the spin lock is acquired. /// /// Thread acquiring spinlock /// [NoHeapAllocation] [Inline] public bool TryAcquire(Thread thread) { int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); return TryAcquireInternal(thread, thread.GetThreadId()); } /// /// /// Method to find out if spinlock is held by specified thread /// /// true if the spin lock is acquired. /// /// Thread to verify possible spinlock's ownership /// [NoHeapAllocation] public bool IsHeldBy(Thread thread) { int threadId = (thread == null)?InitialThreadId:thread.GetThreadId(); return baseLock.IsHeldBy(threadId + 1); } /// /// /// Method to find out if spinlock is held by specified thread /// /// true if the spin lock is acquired. /// /// Thread's Id to verify possible spinlock's ownership /// [NoHeapAllocation] public bool IsHeldBy(int threadId) { return baseLock.IsHeldBy(threadId + 1); } /// /// /// Assert thatf spinlock is held by specified thread /// /// /// Thread to verify possible spinlock's ownership /// [System.Diagnostics.Conditional("DEBUG")] [NoHeapAllocation] public void AssertHeldBy(Thread thread) { VTable.Assert(IsHeldBy(thread)); } /// /// /// Assert thatf spinlock is held by specified thread /// /// /// Thread's Id to verify possible spinlock's ownership /// [System.Diagnostics.Conditional("DEBUG")] [NoHeapAllocation] public void AssertHeldBy(int threadId) { VTable.Assert(IsHeldBy(threadId)); } /// /// /// Given integer : derive type of a lock /// /// /// Parameter from which we can derive actual type of spinlock /// [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); } /// /// /// Given integer : derive type of a lock /// /// /// Parameter from which we can derive actual rank of spinlock /// [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); } /// /// /// Try to acquire the spin lock. Always return immediately. /// /// true if the spin lock is acquired. /// /// Thread acquiring spinlock /// Thread's Id acquiring spinlock /// [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; } /// /// /// Acquire the spin lock. /// /// /// Thread acquiring spinlock /// Thread's Id acquiring spinlock /// [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); } /// /// /// Release the spin lock. /// /// /// Thread releasing spinlock /// Thread's Id releasing spinlock /// [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); } } /// Constant defines shift of the rank internal const int RankShift = 0x10; /// Constant defines mask for getting type of gompound spinlock type internal const int TypeMask = 0xFFFF; /// Id of a initial thread private const int InitialThreadId = -2; /// Actual mechanism implementing spinlock private SpinLockBase baseLock; } /// Attribute to mark methods that stop lock rank verification [Layer(0)] public class IgnoreLockRankAttribute : Attribute { public IgnoreLockRankAttribute() {} } /// Attribute to mark classes / methods that hold FlatPages locks [Layer(1)] public class FlatPagesLockAttribute : Attribute { public FlatPagesLockAttribute() {} } /// Attribute to mark classes / methods that hold Hal locks [Layer(2)] public class HalLockAttribute : Attribute { public HalLockAttribute() {} } /// Attribute to mark classes / methods that hold Dispatcher locks [Layer(3)] public class DispatcherLockAttribute : Attribute { public DispatcherLockAttribute() {} } /// Attribute to mark classes / methods that hold Passive locks [Layer(4)] public class PassiveLockAttribute : Attribute { public PassiveLockAttribute() {} } /// Attribute to mark classes / methods that hold Service locks [Layer(5)] public class ServiceLockAttribute : Attribute { public ServiceLockAttribute() {} } }