// 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 : IEquatable>, IRecyclableNeedle, ICacheNeedle 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 needle) { if (needle == null) { throw new ArgumentNullException("needle"); } return needle.Value; } public static implicit operator WeakNeedle(T field) { return new WeakNeedle(field); } public static bool operator !=(WeakNeedle left, WeakNeedle right) { return NotEqualsExtracted(left, right); } public static bool operator ==(WeakNeedle left, WeakNeedle right) { return EqualsExtracted(left, right); } public sealed override bool Equals(object obj) { var needle = obj as WeakNeedle; if (needle != null) { return EqualsExtractedExtracted(this, needle); } var value = obj as T; if (value != null) { var target = Value; return IsAlive && EqualityComparer.Default.Equals(target, value); } return false; } public bool Equals(WeakNeedle 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 ""; } } //[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 left, WeakNeedle right) { if (ReferenceEquals(left, null)) { return ReferenceEquals(right, null); } else { return !ReferenceEquals(right, null) && EqualsExtractedExtracted(left, right); } } private static bool EqualsExtractedExtracted(WeakNeedle left, WeakNeedle right) { var leftValue = left.Value; if (left.IsAlive) { var rightValue = right.Value; return right.IsAlive && EqualityComparer.Default.Equals(leftValue, rightValue); } return !right.IsAlive; } private static bool NotEqualsExtracted(WeakNeedle left, WeakNeedle right) { if (ReferenceEquals(left, null)) { return !ReferenceEquals(right, null); } else { return ReferenceEquals(right, null) || NotEqualsExtractedExtracted(left, right); } } private static bool NotEqualsExtractedExtracted(WeakNeedle left, WeakNeedle right) { var leftValue = left.Value; if (left.IsAlive) { var rightValue = right.Value; return !right.IsAlive || !EqualityComparer.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