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.
379 lines
11 KiB
379 lines
11 KiB
5 years ago
|
// Needed for Workaround
|
||
|
#if !NET_4_6
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Security.Permissions;
|
||
|
using System.Threading;
|
||
|
using LinqInternal.Core;
|
||
|
|
||
|
namespace LinqInternal.Threading.Needles
|
||
|
{
|
||
|
[System.Diagnostics.DebuggerNonUserCode]
|
||
|
internal partial class WeakNeedle<T> : IEquatable<WeakNeedle<T>>, IRecyclableNeedle<T>, ICacheNeedle<T>
|
||
|
where T : class
|
||
|
{
|
||
|
private readonly int _hashCode;
|
||
|
private readonly bool _trackResurrection;
|
||
|
private volatile bool _faultExpected;
|
||
|
private GCHandle _handle;
|
||
|
private int _managedDisposal;
|
||
|
|
||
|
public WeakNeedle()
|
||
|
: this(false)
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
|
||
|
public WeakNeedle(bool trackResurrection)
|
||
|
{
|
||
|
_trackResurrection = trackResurrection;
|
||
|
_hashCode = base.GetHashCode();
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
public WeakNeedle(T target)
|
||
|
: this(target, false)
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
public WeakNeedle(T target, bool trackResurrection)
|
||
|
{
|
||
|
if (target == null)
|
||
|
{
|
||
|
_hashCode = base.GetHashCode();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetTargetValue(target);
|
||
|
_hashCode = target.GetHashCode();
|
||
|
}
|
||
|
_trackResurrection = trackResurrection;
|
||
|
}
|
||
|
|
||
|
public Exception Exception
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
object target;
|
||
|
if (ReadTarget(out target))
|
||
|
{
|
||
|
var exception = target as Exception;
|
||
|
if (exception != null && _faultExpected)
|
||
|
{
|
||
|
return exception;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool IPromise.IsCanceled
|
||
|
{
|
||
|
get { return false; }
|
||
|
}
|
||
|
|
||
|
bool IPromise.IsCompleted
|
||
|
{
|
||
|
get { return true; }
|
||
|
}
|
||
|
|
||
|
public bool IsAlive
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
object target;
|
||
|
if (ReadTarget(out target))
|
||
|
{
|
||
|
if (target is T && !_faultExpected)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsFaulted
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
object target;
|
||
|
if (ReadTarget(out target))
|
||
|
{
|
||
|
if (target is Exception && _faultExpected)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public virtual bool TrackResurrection
|
||
|
{
|
||
|
get { return _trackResurrection; }
|
||
|
}
|
||
|
|
||
|
public virtual T Value
|
||
|
{
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
get
|
||
|
{
|
||
|
object target;
|
||
|
if (ReadTarget(out target))
|
||
|
{
|
||
|
var inner = target as T;
|
||
|
if (inner != null && !_faultExpected)
|
||
|
{
|
||
|
return inner;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
set { SetTargetValue(value); }
|
||
|
}
|
||
|
|
||
|
public static explicit operator T(WeakNeedle<T> needle)
|
||
|
{
|
||
|
if (needle == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("needle");
|
||
|
}
|
||
|
return needle.Value;
|
||
|
}
|
||
|
|
||
|
public static implicit operator WeakNeedle<T>(T field)
|
||
|
{
|
||
|
return new WeakNeedle<T>(field);
|
||
|
}
|
||
|
|
||
|
public static bool operator !=(WeakNeedle<T> left, WeakNeedle<T> right)
|
||
|
{
|
||
|
return NotEqualsExtracted(left, right);
|
||
|
}
|
||
|
|
||
|
public static bool operator ==(WeakNeedle<T> left, WeakNeedle<T> right)
|
||
|
{
|
||
|
return EqualsExtracted(left, right);
|
||
|
}
|
||
|
|
||
|
public sealed override bool Equals(object obj)
|
||
|
{
|
||
|
var needle = obj as WeakNeedle<T>;
|
||
|
if (needle != null)
|
||
|
{
|
||
|
return EqualsExtractedExtracted(this, needle);
|
||
|
}
|
||
|
var value = obj as T;
|
||
|
if (value != null)
|
||
|
{
|
||
|
var target = Value;
|
||
|
return IsAlive && EqualityComparer<T>.Default.Equals(target, value);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool Equals(WeakNeedle<T> other)
|
||
|
{
|
||
|
return !ReferenceEquals(other, null) && EqualsExtractedExtracted(this, other);
|
||
|
}
|
||
|
|
||
|
public void Free()
|
||
|
{
|
||
|
Dispose();
|
||
|
}
|
||
|
|
||
|
public sealed override int GetHashCode()
|
||
|
{
|
||
|
return _hashCode;
|
||
|
}
|
||
|
|
||
|
public override string ToString()
|
||
|
{
|
||
|
var target = Value;
|
||
|
if (IsAlive)
|
||
|
{
|
||
|
return target.ToString();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return "<Dead Needle>";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
public virtual bool TryGetValue(out T value)
|
||
|
{
|
||
|
value = null;
|
||
|
object target;
|
||
|
if (ReadTarget(out target))
|
||
|
{
|
||
|
var inner = target as T;
|
||
|
if (inner != null)
|
||
|
{
|
||
|
value = inner;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
protected void SetTargetError(Exception error)
|
||
|
{
|
||
|
_faultExpected = true;
|
||
|
WriteTarget(error);
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
protected void SetTargetValue(T value)
|
||
|
{
|
||
|
_faultExpected = false;
|
||
|
WriteTarget(value);
|
||
|
}
|
||
|
|
||
|
private static bool EqualsExtracted(WeakNeedle<T> left, WeakNeedle<T> right)
|
||
|
{
|
||
|
if (ReferenceEquals(left, null))
|
||
|
{
|
||
|
return ReferenceEquals(right, null);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return !ReferenceEquals(right, null) && EqualsExtractedExtracted(left, right);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static bool EqualsExtractedExtracted(WeakNeedle<T> left, WeakNeedle<T> right)
|
||
|
{
|
||
|
var leftValue = left.Value;
|
||
|
if (left.IsAlive)
|
||
|
{
|
||
|
var rightValue = right.Value;
|
||
|
return right.IsAlive && EqualityComparer<T>.Default.Equals(leftValue, rightValue);
|
||
|
}
|
||
|
return !right.IsAlive;
|
||
|
}
|
||
|
|
||
|
private static bool NotEqualsExtracted(WeakNeedle<T> left, WeakNeedle<T> right)
|
||
|
{
|
||
|
if (ReferenceEquals(left, null))
|
||
|
{
|
||
|
return !ReferenceEquals(right, null);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return ReferenceEquals(right, null) || NotEqualsExtractedExtracted(left, right);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static bool NotEqualsExtractedExtracted(WeakNeedle<T> left, WeakNeedle<T> right)
|
||
|
{
|
||
|
var leftValue = left.Value;
|
||
|
if (left.IsAlive)
|
||
|
{
|
||
|
var rightValue = right.Value;
|
||
|
return !right.IsAlive || !EqualityComparer<T>.Default.Equals(leftValue, rightValue);
|
||
|
}
|
||
|
return right.IsAlive;
|
||
|
}
|
||
|
|
||
|
private bool ReadTarget(out object target)
|
||
|
{
|
||
|
target = null;
|
||
|
if (_handle.IsAllocated)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
target = _handle.Target; // Throws InvalidOperationException
|
||
|
}
|
||
|
catch (InvalidOperationException)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||
|
private void ReleaseExtracted()
|
||
|
{
|
||
|
if (_handle.IsAllocated)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
_handle.Free(); // Throws InvalidOperationException
|
||
|
}
|
||
|
catch (InvalidOperationException)
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void ReportManagedDisposal()
|
||
|
{
|
||
|
Volatile.Write(ref _managedDisposal, 1);
|
||
|
}
|
||
|
|
||
|
private void WriteTarget(object target)
|
||
|
{
|
||
|
if (_status == -1 || !ThreadingHelper.SpinWaitRelativeSet(ref _status, 1, -1))
|
||
|
{
|
||
|
ReleaseExtracted();
|
||
|
_handle = GCHandle.Alloc(target, _trackResurrection ? GCHandleType.Weak : GCHandleType.WeakTrackResurrection);
|
||
|
if (Interlocked.CompareExchange(ref _managedDisposal, 0, 1) == 1)
|
||
|
{
|
||
|
GC.ReRegisterForFinalize(this);
|
||
|
}
|
||
|
UnDispose();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
var oldHandle = _handle;
|
||
|
if (oldHandle.IsAllocated)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
oldHandle.Target = target;
|
||
|
return;
|
||
|
}
|
||
|
catch (InvalidOperationException)
|
||
|
{
|
||
|
_handle = GCHandle.Alloc(target, _trackResurrection ? GCHandleType.Weak : GCHandleType.WeakTrackResurrection);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_handle = GCHandle.Alloc(target, _trackResurrection ? GCHandleType.Weak : GCHandleType.WeakTrackResurrection);
|
||
|
}
|
||
|
if (oldHandle.IsAllocated)
|
||
|
{
|
||
|
oldHandle.Free();
|
||
|
try
|
||
|
{
|
||
|
oldHandle.Free();
|
||
|
}
|
||
|
catch (InvalidOperationException)
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
Interlocked.Decrement(ref _status);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|