网上演练
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.

312 lines
9.9 KiB

#if FAT
using System;
using System.Collections.Generic;
using System.Threading;
using LinqInternal.Collections.ThreadSafe;
using LinqInternal.Core;
namespace LinqInternal.Threading.Needles
{
internal sealed partial class Transact
{
public sealed class Needle<T> : Needles.Needle<T>, IResource
{
private readonly ICloner<T> _cloner;
private readonly IEqualityComparer<T> _comparer;
private readonly RuntimeUniqueIdProdiver.UniqueId _id;
private readonly NeedleLock<Thread> _needleLock;
private int _status;
private int _inUse;
public Needle(T value)
: base(value)
{
_cloner = CloneHelper<T>.GetCloner();
if (ReferenceEquals(_cloner, null))
{
throw new InvalidOperationException("Unable to get a cloner for " + typeof(T));
}
_comparer = EqualityComparer<T>.Default;
_needleLock = new NeedleLock<Thread>(Context);
_id = RuntimeUniqueIdProdiver.GetNextId();
}
public Needle(T value, ICloner<T> cloner)
: base(value)
{
if (ReferenceEquals(cloner, null))
{
throw new ArgumentNullException("cloner");
}
_cloner = cloner;
_comparer = EqualityComparer<T>.Default;
_needleLock = new NeedleLock<Thread>(Context);
_id = RuntimeUniqueIdProdiver.GetNextId();
}
public Needle(T value, IEqualityComparer<T> comparer)
: base(value)
{
_cloner = CloneHelper<T>.GetCloner();
if (ReferenceEquals(_cloner, null))
{
throw new InvalidOperationException("Unable to get a cloner for " + typeof(T));
}
_comparer = comparer ?? EqualityComparer<T>.Default;
_needleLock = new NeedleLock<Thread>(Context);
_id = RuntimeUniqueIdProdiver.GetNextId();
}
public Needle(T value, ICloner<T> cloner, IEqualityComparer<T> comparer)
: base(value)
{
if (ReferenceEquals(cloner, null))
{
throw new ArgumentNullException("cloner");
}
_cloner = cloner;
_comparer = comparer ?? EqualityComparer<T>.Default;
_needleLock = new NeedleLock<Thread>(Context);
_id = RuntimeUniqueIdProdiver.GetNextId();
}
[System.Diagnostics.DebuggerNonUserCode]
~Needle()
{
try
{
// Empty
}
finally
{
try
{
Dispose(false);
}
catch (Exception exception)
{
// Fields may be partially collected.
GC.KeepAlive(exception);
}
}
}
public override T Value
{
get
{
var transaction = CurrentTransaction;
return RetrieveClone(transaction);
}
set
{
if (NeedleReservoir.Recycling)
{
if (Volatile.Read(ref _inUse) == 1)
{
throw new InvalidOperationException("The Needle has been used in a transaction.");
}
StoreValue(null, value);
}
else
{
var transaction = CurrentTransaction;
StoreValue(transaction, value);
}
}
}
[System.Diagnostics.DebuggerNonUserCode]
public void Dispose()
{
try
{
Dispose(true);
}
finally
{
GC.SuppressFinalize(this);
}
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override void Free()
{
Value = default(T);
}
public override int GetHashCode()
{
return _id.GetHashCode();
}
bool IResource.Capture()
{
var transaction = CurrentTransaction;
if (transaction == null)
{
return false;
}
var lockSlot = transaction._lockSlot;
if (lockSlot == null)
{
return false;
}
_needleLock.Capture(lockSlot);
return true;
}
bool IResource.CheckCapture()
{
var thread = Thread.CurrentThread;
var check = _needleLock.Value;
return check == thread;
}
bool IResource.CheckValue()
{
Volatile.Write(ref _inUse, 1);
var transaction = CurrentTransaction;
object value;
if (transaction._readLog.TryGetValue(this, out value))
{
var original = RetrieveValue(transaction._parentTransaction);
var found = (T)value;
return _comparer.Equals(original, found);
}
return false;
}
bool IResource.Commit()
{
var transaction = CurrentTransaction;
if (_needleLock.Value == Thread.CurrentThread)
{
object value;
Volatile.Write(ref _inUse, 1);
if (transaction._writeLog.TryGetValue(this, out value))
{
StoreValue(transaction._parentTransaction, (T)value);
}
return true;
}
return false;
}
[System.Diagnostics.DebuggerNonUserCode]
private void Dispose(bool disposeManagedResources)
{
if (TakeDisposalExecution())
{
if (disposeManagedResources)
{
OnDispose();
}
}
}
private void OnDispose()
{
var transaction = CurrentTransaction;
if (transaction != null)
{
if (!ReferenceEquals(transaction._lockSlot, null))
{
_needleLock.Uncapture(transaction._lockSlot);
}
_needleLock.Release();
transaction._readLog.Remove(this);
transaction._writeLog.Remove(this);
}
}
void IResource.Release()
{
// Release is only called on a thread that did capture the Needle
OnDispose();
}
private T RetrieveClone(Transact transaction)
{
if (ReferenceEquals(transaction, null))
{
return base.Value;
}
else
{
object value;
Volatile.Write(ref _inUse, 1);
if (transaction._writeLog.TryGetValue(this, out value))
{
return (T)value;
}
if (transaction._readLog.TryGetValue(this, out value))
{
return (T)value;
}
var original = RetrieveValue(transaction._parentTransaction);
var clone = _cloner.Clone(original);
if (!_comparer.Equals(clone, original))
{
transaction._writeLog.Set(this, clone);
}
transaction._readLog.TryAdd(this, original);
return clone;
}
}
private T RetrieveValue(Transact transaction)
{
if (ReferenceEquals(transaction, null))
{
return base.Value;
}
else
{
object value;
Volatile.Write(ref _inUse, 1);
if (transaction._writeLog.TryGetValue(this, out value))
{
return (T)value;
}
if (transaction._readLog.TryGetValue(this, out value))
{
return (T)value;
}
var original = RetrieveValue(transaction._parentTransaction);
transaction._readLog.TryAdd(this, original);
return original;
}
}
private void StoreValue(Transact transaction, T value)
{
if (!IsAlive || ReferenceEquals(transaction, null))
{
base.Value = value;
}
else
{
Volatile.Write(ref _inUse, 1);
transaction._writeLog.Set(this, value);
}
}
private bool TakeDisposalExecution()
{
return _status != -1 && ThreadingHelper.SpinWaitSetUnless(ref _status, -1, 0, -1);
}
}
}
}
#endif