You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
7.2 KiB
233 lines
7.2 KiB
5 years ago
|
#if FAT
|
||
|
|
||
|
using System;
|
||
|
using System.Threading;
|
||
|
|
||
|
namespace LinqInternal.Threading
|
||
|
{
|
||
|
internal sealed partial class NoReentrantReadWriteLock : IReadWriteLock
|
||
|
{
|
||
|
private int _edge;
|
||
|
private ManualResetEventSlim _freeToRead = new ManualResetEventSlim(false); // Disposed
|
||
|
private ManualResetEventSlim _freeToWrite = new ManualResetEventSlim(false); // Disposed
|
||
|
private int _master;
|
||
|
private Thread _ownerThread;
|
||
|
private int _readCount;
|
||
|
private int _writeCount;
|
||
|
|
||
|
public bool HasReader
|
||
|
{
|
||
|
get { return _readCount > 0; }
|
||
|
}
|
||
|
|
||
|
public bool HasWriter
|
||
|
{
|
||
|
get { return _ownerThread != null; }
|
||
|
}
|
||
|
|
||
|
public bool IsCurrentThreadReader
|
||
|
{
|
||
|
get { throw new NotSupportedException("Only a ReentratReadWriteLock keeps tracks of which thread is a reader."); }
|
||
|
}
|
||
|
|
||
|
public bool IsCurrentThreadWriter
|
||
|
{
|
||
|
get { return Thread.CurrentThread == _ownerThread; }
|
||
|
}
|
||
|
|
||
|
public IDisposable EnterRead()
|
||
|
{
|
||
|
WaitCanRead();
|
||
|
return DisposableAkin.Create(DoneRead);
|
||
|
}
|
||
|
|
||
|
public IDisposable EnterWrite()
|
||
|
{
|
||
|
WaitCanWrite();
|
||
|
return DisposableAkin.Create(DoneWrite);
|
||
|
}
|
||
|
|
||
|
public bool TryEnterRead(out IDisposable engagement)
|
||
|
{
|
||
|
engagement = null;
|
||
|
if (!CanRead())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
engagement = DisposableAkin.Create(DoneRead);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public bool TryEnterWrite(out IDisposable engagement)
|
||
|
{
|
||
|
engagement = null;
|
||
|
if (!CanWrite())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
engagement = DisposableAkin.Create(DoneWrite);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private bool CanRead()
|
||
|
{
|
||
|
if (Thread.CurrentThread == Volatile.Read(ref _ownerThread))
|
||
|
{
|
||
|
Interlocked.Increment(ref _readCount);
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (Interlocked.CompareExchange(ref _master, 1, 0) >= 0)
|
||
|
{
|
||
|
_freeToWrite.Reset();
|
||
|
Interlocked.Increment(ref _readCount);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private bool CanWrite()
|
||
|
{
|
||
|
if (Thread.CurrentThread == Volatile.Read(ref _ownerThread))
|
||
|
{
|
||
|
Interlocked.Increment(ref _writeCount);
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (Interlocked.CompareExchange(ref _master, -1, 0) == 0)
|
||
|
{
|
||
|
_freeToRead.Reset();
|
||
|
if (Interlocked.CompareExchange(ref _ownerThread, Thread.CurrentThread, null) == null)
|
||
|
{
|
||
|
// Success
|
||
|
Interlocked.Increment(ref _writeCount);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DoneRead()
|
||
|
{
|
||
|
if (Thread.CurrentThread == Volatile.Read(ref _ownerThread))
|
||
|
{
|
||
|
Interlocked.Decrement(ref _readCount);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (Volatile.Read(ref _master) < 0)
|
||
|
{
|
||
|
if (Interlocked.Decrement(ref _readCount) <= Volatile.Read(ref _edge))
|
||
|
{
|
||
|
Volatile.Write(ref _master, 0);
|
||
|
_freeToWrite.Set();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Interlocked.Decrement(ref _readCount);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DoneWrite()
|
||
|
{
|
||
|
if (Interlocked.Decrement(ref _writeCount) == 0)
|
||
|
{
|
||
|
Volatile.Write(ref _master, 0);
|
||
|
Volatile.Write(ref _ownerThread, null);
|
||
|
_freeToRead.Set();
|
||
|
_freeToWrite.Set();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void WaitCanRead()
|
||
|
{
|
||
|
if (Thread.CurrentThread != Volatile.Read(ref _ownerThread))
|
||
|
{
|
||
|
var check = Interlocked.CompareExchange(ref _master, 1, 0);
|
||
|
while (true)
|
||
|
{
|
||
|
switch (check)
|
||
|
{
|
||
|
case -2:
|
||
|
// Write mode already requested
|
||
|
case -1:
|
||
|
// There is a writer
|
||
|
// Go to wait
|
||
|
_freeToRead.Wait();
|
||
|
check = Interlocked.CompareExchange(ref _master, 1, 0);
|
||
|
break;
|
||
|
|
||
|
case 0:
|
||
|
// Free to proceed
|
||
|
// GO!
|
||
|
_freeToWrite.Reset();
|
||
|
goto case 1;
|
||
|
|
||
|
case 1:
|
||
|
// There are readers currently
|
||
|
// GO!
|
||
|
Interlocked.Increment(ref _readCount);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void WaitCanWrite()
|
||
|
{
|
||
|
if (Thread.CurrentThread != Volatile.Read(ref _ownerThread))
|
||
|
{
|
||
|
var check = Interlocked.CompareExchange(ref _master, -1, 0);
|
||
|
while (true)
|
||
|
{
|
||
|
switch (check)
|
||
|
{
|
||
|
case -2:
|
||
|
// Write mode already requested
|
||
|
case -1:
|
||
|
// There is another writer
|
||
|
// Go to wait
|
||
|
_freeToWrite.Wait();
|
||
|
check = Interlocked.CompareExchange(ref _master, -1, 0);
|
||
|
break;
|
||
|
|
||
|
case 0:
|
||
|
// Free to proceed
|
||
|
// GO!
|
||
|
_freeToRead.Reset();
|
||
|
if (Interlocked.CompareExchange(ref _ownerThread, Thread.CurrentThread, null) == null)
|
||
|
{
|
||
|
// Success
|
||
|
Interlocked.Increment(ref _writeCount);
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// It was reserved by another thread
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 1:
|
||
|
// There are readers currently
|
||
|
// Requesting write mode
|
||
|
check = Interlocked.CompareExchange(ref _master, -2, 1);
|
||
|
if (check == 1)
|
||
|
{
|
||
|
_freeToRead.Reset();
|
||
|
check = -2;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|