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.
117 lines
3.6 KiB
117 lines
3.6 KiB
5 years ago
|
#if FAT
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Threading;
|
||
|
|
||
|
namespace LinqInternal.Threading.Needles
|
||
|
{
|
||
|
internal sealed class LockableNeedle<T> : Needle<T>
|
||
|
{
|
||
|
private readonly LockableContext _context;
|
||
|
private readonly NeedleLock<Thread> _needleLock;
|
||
|
|
||
|
public LockableNeedle(T value, LockableContext context)
|
||
|
: base(value)
|
||
|
{
|
||
|
if (ReferenceEquals(context, null))
|
||
|
{
|
||
|
throw new ArgumentNullException("context");
|
||
|
}
|
||
|
_context = context;
|
||
|
_needleLock = new NeedleLock<Thread>(_context.Context);
|
||
|
}
|
||
|
|
||
|
public override T Value
|
||
|
{
|
||
|
get { return base.Value; }
|
||
|
|
||
|
set
|
||
|
{
|
||
|
LockableSlot slot;
|
||
|
if (_context.TryGetSlot(out slot))
|
||
|
{
|
||
|
CaptureAndWait(slot);
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
base.Value = value;
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw new InvalidOperationException("The current thread has not entered the LockableContext of this LockableNeedle.");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void CaptureAndWait()
|
||
|
{
|
||
|
LockableSlot slot;
|
||
|
if (!_context.TryGetSlot(out slot))
|
||
|
{
|
||
|
throw new InvalidOperationException("The current thread has not entered the LockableContext of this LockableNeedle.");
|
||
|
}
|
||
|
CaptureAndWait(slot);
|
||
|
}
|
||
|
|
||
|
public bool TryUpdate(T newValue, T expectedValue)
|
||
|
{
|
||
|
CaptureAndWait();
|
||
|
if (!EqualityComparer<T>.Default.Equals(base.Value, expectedValue))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
base.Value = newValue;
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public bool TryUpdate(T newValue, T expectedValue, IEqualityComparer<T> comparer)
|
||
|
{
|
||
|
CaptureAndWait();
|
||
|
if (!comparer.Equals(base.Value, expectedValue))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
base.Value = newValue;
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public T Update(Func<T, T> updateValueFactory)
|
||
|
{
|
||
|
if (updateValueFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("updateValueFactory");
|
||
|
}
|
||
|
CaptureAndWait();
|
||
|
var result = updateValueFactory(base.Value);
|
||
|
base.Value = result;
|
||
|
ThreadingHelper.MemoryBarrier();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private void Capture(LockableSlot slot)
|
||
|
{
|
||
|
var lockslot = slot.LockSlot;
|
||
|
if (ReferenceEquals(lockslot, null))
|
||
|
{
|
||
|
throw new InvalidOperationException("The current thread has not entered the LockableContext of this LockableNeedle.");
|
||
|
}
|
||
|
_needleLock.Capture(lockslot);
|
||
|
slot.Add(_needleLock);
|
||
|
}
|
||
|
|
||
|
private void CaptureAndWait(LockableSlot slot)
|
||
|
{
|
||
|
Capture(slot);
|
||
|
var thread = Thread.CurrentThread;
|
||
|
// The reason while I cannot make an smarter wait function:
|
||
|
// If another thread changed _needleLock.Value after the check but before the starting to wait, the wait will not finish.
|
||
|
ThreadingHelper.SpinWaitUntil(() => _needleLock.Value == thread);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|