167 lines
5.4 KiB
C#
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
|
|
}
|
|
}
|