singrdk/base/Kernel/System/Threading/SpinLock.cs

167 lines
5.4 KiB
C#

//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
namespace System.Threading {
using Microsoft.Singularity;
using System.Runtime.CompilerServices;
using System;
[NoCCtor]
[CLSCompliant(false)]
public struct SpinLock
{
[NoHeapAllocation]
public void Acquire()
{
Acquire(Thread.CurrentThread);
}
// Note: any changes to this code must be reflected in
// the Release code in SwitchToThreadContext in halidt.asm.
[NoHeapAllocation]
public void Release()
{
Release(Thread.CurrentThread);
}
[NoHeapAllocation]
public void Acquire(Thread thread)
{
#if TRACE
Tracing.Log(Tracing.Debug, "SpinLock.Acquire({0:x8},{1})",
VTable.addressOf(thread),
(UIntPtr)unchecked((uint)(thread != null ? thread.threadIndex : -1)));
#endif
AcquireInternal(thread.GetThreadId() + 1);
}
// Note: any changes to this code must be reflected in
// the Release code in SwitchToThreadContext in halidt.asm.
[NoHeapAllocation]
public void Release(Thread thread)
{
#if TRACE
Tracing.Log(Tracing.Debug, "SpinLock.Release({0:x8},{1})",
VTable.addressOf(thread),
(UIntPtr)unchecked((uint)(thread != null ? thread.threadIndex : -1)));
#endif
ReleaseInternal(thread.GetThreadId() + 1);
}
[System.Diagnostics.Conditional("DEBUG")]
[NoHeapAllocation]
public void AssertHeldBy(Thread thread)
{
VTable.Assert(IsHeldBy(thread));
}
[NoHeapAllocation]
public bool IsHeldBy(Thread thread)
{
#if TRACE
Tracing.Log(Tracing.Debug, "SpinLock.IsHeldBy({0:x8},{1})",
VTable.addressOf(thread),
(UIntPtr)unchecked((uint)(thread != null ? thread.threadIndex : -1)));
#endif
return IsHeldByInternal(thread.GetThreadId() + 1);
}
[NoHeapAllocation]
public void Acquire(int threadId)
{
AcquireInternal(threadId + 1);
}
[NoHeapAllocation]
public void Release(int threadId)
{
ReleaseInternal(threadId + 1);
}
[System.Diagnostics.Conditional("DEBUG")]
[NoHeapAllocation]
public void AssertHeldBy(int threadId)
{
VTable.Assert(IsHeldByInternal(threadId + 1));
}
[NoHeapAllocation]
public bool IsHeldBy(int threadId)
{
return IsHeldByInternal(threadId + 1);
}
[NoHeapAllocation]
private void AcquireInternal(int currentThreadIndexPlusOne)
{
// SpinLocks are *NOT* re-entrant.
VTable.Assert(lockingThreadIndexPlusOne !=
currentThreadIndexPlusOne);
VTable.Assert(Processor.InterruptsDisabled());
int result = Interlocked.Exchange(ref this.lockWord, 1);
if (result != 0) {
// spin for exponentially backed off delay
int count = 31;
while (true) {
#if SINGULARITY_KERNEL
Kernel.Waypoint(888);
#endif // SINGULARITY_KERNEL
Thread.SpinWait(count);
result = Interlocked.Exchange(ref this.lockWord, 1);
if (result == 0) {
break;
}
#if !SINGULARITY_KERNEL
// We yield to allow the thread holding the lock to run
Thread.Yield();
// check the lock word once after yielding
result = Interlocked.Exchange(ref this.lockWord, 1);
if (result == 0) {
break;
}
#endif // !SINGULARITY_KERNEL
count = (count == 0x7ffffffu) ? count : count + count + 1;
} // while
}
VTable.Assert(lockWord == 1 && lockingThreadIndexPlusOne == 0);
this.lockingThreadIndexPlusOne = currentThreadIndexPlusOne;
}
// Note: any changes to this code must be reflected in
// the Release code in SwitchToThreadContext in halidt.asm.
[NoHeapAllocation]
private void ReleaseInternal(int currentThreadIndexPlusOne)
{
VTable.Assert(lockWord == 1 &&
lockingThreadIndexPlusOne == currentThreadIndexPlusOne);
VTable.Assert(Processor.InterruptsDisabled());
this.lockingThreadIndexPlusOne = 0;
this.lockWord = 0;
}
[NoHeapAllocation]
private bool IsHeldInternal()
{
return this.lockWord != 0;
}
[NoHeapAllocation]
private bool IsHeldByInternal(int threadIndexPlusOne)
{
return (this.lockWord != 0 &&
this.lockingThreadIndexPlusOne == threadIndexPlusOne);
}
[AccessedByRuntime("referenced from halidt.asm")]
private int lockWord;
[AccessedByRuntime("referenced from halidt.asm")]
private int lockingThreadIndexPlusOne; // Thread.GetThreadId() + 1
}
}