////////////////////////////////////////////////////////////////////////////////
//
// 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;
}
}