using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
namespace NCindy.Util
{
public static class LockHelper
{
public static ValueTypeWrapper<LockCookie> SafeAcquireWriterLock(ReaderWriterLock readerWriterlock,
int timeout)
{
ValueTypeWrapper<LockCookie> lockCookie = ValueTypeWrapper<LockCookie>.Empty;
if (readerWriterlock.IsReaderLockHeld && !readerWriterlock.IsWriterLockHeld)
{
lockCookie = new ValueTypeWrapper<LockCookie>(readerWriterlock.UpgradeToWriterLock(timeout));
}
else
{
readerWriterlock.AcquireWriterLock(timeout);
}
return lockCookie;
}
public static void SafeReleaseWriterLock(ReaderWriterLock readerWriterlock,
ValueTypeWrapper<LockCookie> lockCookie)
{
if (!readerWriterlock.IsWriterLockHeld)
{
return;
}
if (ValueTypeWrapper<LockCookie>.Empty == lockCookie)
{
readerWriterlock.ReleaseWriterLock();
}
else
{
LockCookie cookie = lockCookie.Value;
readerWriterlock.DowngradeFromWriterLock(ref cookie);
}
}
}
public static class InterlockedHelper
{
#region Interlocked Helpers
public static Int32 And(ref Int32 target, Int32 with)
{
Int32 i, j = target;
do
{
i = j;
j = Interlocked.CompareExchange(ref target, i & with, i);
} while (i != j);
return j;
}
public static Int32 Or(ref Int32 target, Int32 with)
{
Int32 i, j = target;
do
{
i = j;
j = Interlocked.CompareExchange(ref target, i | with, i);
} while (i != j);
return j;
}
public static bool CompareAndExchange(ref Int32 value,
Int32 ifThisEqualsToValue,
Int32 thenExchangeValueWithThis)
{
return (Interlocked.CompareExchange(ref value, thenExchangeValueWithThis, ifThisEqualsToValue) == ifThisEqualsToValue);
}
public static bool CompareAndExchange(ref Int32 value,
Int32 ifThisEqualsToValue,
Int32 thenExchangeValueWithThis,
out Int32 originalValue)
{
originalValue = Interlocked.CompareExchange(ref value,
thenExchangeValueWithThis,
ifThisEqualsToValue);
return (originalValue == ifThisEqualsToValue);
}
#endregion
}
#region SpinWaitLock
// NOTE: This is a value type so it works very efficiently when used as
// a field in a class. Avoid boxing this or you will lose thread safety!
public struct SpinWaitLock
{
private static readonly Boolean IsSingleCpuMachine =
(Environment.ProcessorCount == 1);
private const Int32 LockIsFree = 0;
private const Int32 LockIsOwned = 1;
private Int32 lockState; // Defaults to 0=c_lsFree
public void Enter()
{
Thread.BeginCriticalRegion();
while (true)
{
// If resource available, set it to in-use and return
if (Interlocked.Exchange(
ref lockState, LockIsOwned) == LockIsFree)
{
return;
}
// Efficiently spin, until the resource looks like it might
// be free. NOTE: Just reading here (as compared to repeatedly
// calling Exchange) improves performance because writing
// forces all CPUs to update this value
while (Thread.VolatileRead(ref lockState) == LockIsOwned)
{
StallThread();
}
}
}
public void Exit()
{
// Mark the resource as available
Interlocked.Exchange(ref lockState, LockIsFree);
Thread.EndCriticalRegion();
}
private static void StallThread()
{
// On a single-CPU system, spinning does no good
if (IsSingleCpuMachine)
{
SwitchToThread();
}
else
{
// Multi-CPU system might be hyper-threaded, let other thread run
Thread.SpinWait(1);
}
}
[DllImport("kernel32", ExactSpelling = true)]
private static extern void SwitchToThread();
}
#endregion
public sealed class LightLock : IDisposable
{
// Bit 0: 0=Lock is free, 1=Lock is owned
// Bits 1-31: Number of waiters
private const Int32 LockIsFree = 0x00000000;//h000
private const Int32 LockIsOwned = 0x00000001;//h001
private const Int32 WaitersCountBase = 0x00000002;//h010
private Int32 lockState = LockIsFree;
private readonly Semaphore waiterLock = new Semaphore(0, Int32.MaxValue);
private bool isDisposed;
public LightLock()
{
isDisposed = false;
}
public void Dispose()
{
CheckDisposed();
try
{
waiterLock.Close();
}
finally
{
this.isDisposed = true;
}
}
private void CheckDisposed()
{
if (this.isDisposed)
{
throw new ObjectDisposedException(null);
}
}
public void Enter()
{
this.Enter(Timeout.Infinite);
}
public void Enter(int timeout)
{
CheckDisposed();
Thread.BeginCriticalRegion();
while (true)
{
// Turn on the "owned" bit
Int32 ls = InterlockedHelper.Or(ref lockState, LockIsOwned);
// If lock was free, this thread got it, return
if ((ls & LockIsOwned) == LockIsFree)
{
return;
}
// Another thread owned the lock, add 1 waiter
if (InterlockedHelper.CompareAndExchange(ref lockState, ls, ls + WaitersCountBase))
{
// If successfully added 1, wait for lock
waiterLock.WaitOne(timeout, false);
}
// We weren't able to add 1 waiter or waiter woke, attempt to get the lock
}
}
public void Exit()
{
CheckDisposed();
// Pre-condition: Lock's state must be Owned
// Post-condition: Lock's state must become Free (the lock is never passed)
// Free the lock, Turn off the owned bit.
Int32 ls = InterlockedHelper.And(ref lockState, ~LockIsOwned);
if (ls == LockIsOwned)
{
// If no waiters, nothing to do, we can just return
}
else
{
// Possibly wake waiters
// If lock is free, try to subtract 1 from the number of waiters
ls &= ~LockIsOwned;
if (InterlockedHelper.CompareAndExchange(ref lockState, ls & ~LockIsOwned, ls - WaitersCountBase))
{
// We sucessfully subtracted 1, wake 1 waiter
waiterLock.Release(1);
}
else
{
// Lock's state changed by other thread, other thread will deal with it
}
}
Thread.EndCriticalRegion();
}
}
}