// Needed for NET35 (ThreadLocal) #if !NET_4_6 using System; using System.Threading; using LinqInternal.Core; using LinqInternal.Threading.Needles; namespace LinqInternal.Threading { [System.Diagnostics.DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}")] [System.Diagnostics.DebuggerNonUserCode] internal sealed class NoTrackingThreadLocal : IThreadLocal, IWaitablePromise, ICacheNeedle, IObserver { private int _disposing; private LocalDataStoreSlot _slot; private Func _valueFactory; public NoTrackingThreadLocal() : this(TypeHelper.GetCreateOrDefault()) { // Empty } public NoTrackingThreadLocal(Func valueFactory) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } _valueFactory = valueFactory; _slot = Thread.AllocateDataSlot(); } public bool IsValueCreated { get { if (Thread.VolatileRead(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } return Thread.GetData(_slot) is ReadOnlyStructNeedle; } } public T Value { get { if (Thread.VolatileRead(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } var bundle = Thread.GetData(_slot); var needle = bundle as INeedle; if (needle == null) { try { Thread.SetData(_slot, ThreadLocalHelper.RecursionGuardNeedle); var result = _valueFactory.Invoke(); Thread.SetData(_slot, new ReadOnlyStructNeedle(result)); return result; } catch (Exception exception) { if (!ReferenceEquals(exception, ThreadLocalHelper.RecursionGuardException)) { Thread.SetData(_slot, new ExceptionStructNeedle(exception)); } throw; } } return needle.Value; } set { if (Thread.VolatileRead(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } Thread.SetData(_slot, new ReadOnlyStructNeedle(value)); } } Exception IPromise.Exception { get { return null; } } bool IReadOnlyNeedle.IsAlive { get { return IsValueCreated; } } bool IPromise.IsCanceled { get { return false; } } bool IPromise.IsCompleted { get { return IsValueCreated; } } bool IPromise.IsFaulted { get { return false; } } T IThreadLocal.ValueForDebugDisplay { get { return ValueForDebugDisplay; } } System.Collections.Generic.IList IThreadLocal.Values { get { throw new InvalidOperationException(); } } [System.Diagnostics.DebuggerNonUserCode] public void Dispose() { if (Interlocked.CompareExchange(ref _disposing, 1, 0) == 0) { _slot = null; _valueFactory = null; } } public void EraseValue() { if (Thread.VolatileRead(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } Thread.SetData(_slot, null); } public override string ToString() { return string.Format(System.Globalization.CultureInfo.InvariantCulture, "[ThreadLocal: IsValueCreated={0}, Value={1}]", IsValueCreated, Value); } public bool TryGetValue(out T target) { var bundle = Thread.GetData(_slot); var container = bundle as INeedle; if (container == null) { target = default(T); return false; } target = container.Value; return true; } void IObserver.OnCompleted() { GC.KeepAlive(Value); } void IObserver.OnError(Exception error) { if (Thread.VolatileRead(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } Thread.SetData(_slot, new ExceptionStructNeedle(error)); } void IObserver.OnNext(T value) { Value = value; } void IWaitablePromise.Wait() { GC.KeepAlive(Value); } internal T ValueForDebugDisplay { get { T target; return TryGetValue(out target) ? target : default(T); } } } } #endif