//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: SpinlockBase.cs // // Note: // Base spinlock functionality that is shared between user and kernel level spinlocks // using System; using Microsoft.Singularity; using System.Runtime.CompilerServices; namespace System.Threading { /// /// /// Spinlock Base - actual implementation of spinlock mechanism /// /// [NoCCtor] [CLSCompliant(false)] public struct SpinLockBase { /// /// /// Construct a SpinLockBase and initialize its type /// /// /// Type of the spin lock. See KernelSpinLock.cs for details /// [NoHeapAllocation] public SpinLockBase(int type) { this.type = type; this.ownerId = 0; } /// /// /// Assert if the specified thread actual owner of the spinlock /// /// /// Owner Id that request is using to get spinlock /// [System.Diagnostics.Conditional("DEBUG")] [NoHeapAllocation] public void AssertHeldBy(int ownerId) { VTable.Assert(IsHeldBy(ownerId)); } /// /// /// Internal method to find out if spinlock is held by any thread /// /// true if the spin lock is acquired. /// [NoHeapAllocation] public bool IsHeld() { return this.ownerId != 0; } /// /// /// Method to find out if spinlock is held by specific thread /// /// true if the spin lock is acquired by specific thread. /// /// Owner Id that request is using to check for spinlock ownership /// [NoHeapAllocation] public bool IsHeldBy(int ownerId) { return (this.ownerId == ownerId); } /// /// /// Try to acquire the spin lock. Always return immediately. /// /// true if the spin lock is acquired. /// [Inline] [NoHeapAllocation] public bool TryAcquire(int ownerId) { bool result; // Assert preconditions SpinLocks are *NOT* re-entrant, thread can't hold spinlocks of // lower rank VTable.Assert(ownerId != this.ownerId); // Try to acquire the spin lock result = (Interlocked.CompareExchange(ref this.ownerId, ownerId, 0) == 0); return result; } /// /// /// Acquire a lock /// /// /// Owner Id that request is using to acquire spinlock /// [NoHeapAllocation] [Inline] public void Acquire(int ownerId) { // Thead Id can't be null VTable.Assert(ownerId != 0); // Try to acquire spinlock if (!TryAcquire(ownerId)){ // We failed to acquire just go ahead and dive into deeper spin loop with attempts // to acquire lock SpinToAcquire(ownerId); } } /// /// /// Release a lock /// /// /// Spinlock owner /// [NoHeapAllocation] [Inline] public void Release(int ownerId) { // Assert preconditions: Thread should own spinlock VTable.Assert(this.ownerId == ownerId); Interlocked.Exchange (ref this.ownerId, 0); } /// /// /// Type of a spinlock /// /// internal int Type { [NoHeapAllocation] [Inline] get { return this.type; } } /// /// /// Spin until we can actually acquire spinlock /// /// /// Owner Id that request is using to acquire spinlock /// [NoHeapAllocation] private void SpinToAcquire (int ownerId) { int iSpin; int backoffs = 0; // Assert preconditions: thread's id and passed in id's should be the same VTable.Assert(ownerId != 0); while (true) { // It is assumed this routine is only called after the inline // method has failed the interlocked spinlock test. Therefore we // retry using the safe test only after cheaper, unsafe test // succeeds. for (iSpin = 0; (this.ownerId != 0 && iSpin < MaxSpinLimit); iSpin++) { // Hopefully ownerId will not be enregistered, and this read will // always hit memory, if it does then we are in trouble // Perform HT friendly pause: Thread.NativeNoOp(); } // If we exited the loop prematurely, then try to get the lock if (iSpin < MaxSpinLimit) { // Attempt to grab the spinlock if (TryAcquire(ownerId)) { break; } // If we couldn't get the lock, at least we know someone did, // and the system is making progress; there is no need to // back off. backoffs = 0; continue; } // Increment back off stats backoffs++; } } /// A timer for short back off in ms const int MaxSpinLimit = 10000; /// A counter for short back off in ms const long ShortBackOffLimit = 8; /// A counter for short back off in ms #if DEBUG const long LongBackOffLimit = 1000; #else const long LongBackOffLimit = 10000; #endif /// A timer for short back off in ms const long ShortBackOff = 1; /// A timer for long back off in ms const long LongBackOff = 5000; /// Thread owner private int ownerId; /// Type of a spinlock private readonly int type; } }