// Needed for NET35 (ThreadLocal) #if !NET_4_6 using System; using System.Collections.Generic; using System.Threading; using LinqInternal.Collections; using LinqInternal.Collections.ThreadSafe; using LinqInternal.Threading.Needles; namespace LinqInternal.Threading { [System.Diagnostics.DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}")] internal sealed class TrackingThreadLocal : IThreadLocal, IWaitablePromise, ICacheNeedle, IObserver { private const int _maxProbingHint = 4; private int _disposing; private SafeDictionary> _slots; private Func _valueFactory; public TrackingThreadLocal(Func valueFactory) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } _valueFactory = valueFactory; _slots = new SafeDictionary>(_maxProbingHint); } public bool IsValueCreated { get { if (Volatile.Read(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } INeedle needle; if (_slots.TryGetValue(Thread.CurrentThread, out needle)) { return needle is ReadOnlyStructNeedle; } return false; } } public T Value { get { return GetValue(Thread.CurrentThread); } set { SetValue(Thread.CurrentThread, value); } } public IList Values { get { return _slots.ConvertFiltered(input => input.Value.Value, input => input.Value is ReadOnlyStructNeedle); } } 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; } } [System.Diagnostics.DebuggerNonUserCode] public void Dispose() { if (Interlocked.CompareExchange(ref _disposing, 1, 0) == 0) { _slots = null; _valueFactory = null; } } public void EraseValue() { EraseValue(Thread.CurrentThread); } public override string ToString() { return string.Format(System.Globalization.CultureInfo.InvariantCulture, "[ThreadLocal: IsValueCreated={0}, Value={1}]", IsValueCreated, Value); } public bool TryGetValue(Thread thread, out T target) { if (Volatile.Read(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } INeedle tmp; if (_slots.TryGetValue(thread, out tmp)) { target = tmp.Value; return true; } target = default(T); return false; } public bool TryGetValue(out T value) { return TryGetValue(Thread.CurrentThread, out value); } void IObserver.OnCompleted() { // Empty } void IObserver.OnError(Exception error) { SetError(Thread.CurrentThread, error); } void IObserver.OnNext(T value) { Value = value; } void IWaitablePromise.Wait() { GC.KeepAlive(Value); } private void EraseValue(Thread thread) { if (Volatile.Read(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } _slots.Remove(thread); } private T GetValue(Thread thread) { if (Volatile.Read(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } INeedle needle; if (_slots.TryGetOrAdd(thread, ThreadLocalHelper.RecursionGuardNeedle, out needle)) { try { needle = new ReadOnlyStructNeedle(_valueFactory.Invoke()); } catch (Exception exception) { if (exception != ThreadLocalHelper.RecursionGuardException) { needle = new ExceptionStructNeedle(exception); } } _slots.Set(thread, needle); } return needle.Value; } private void SetError(Thread thread, Exception error) { if (Volatile.Read(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } _slots.Set(thread, new ExceptionStructNeedle(error)); } private void SetValue(Thread thread, T value) { if (Volatile.Read(ref _disposing) == 1) { throw new ObjectDisposedException(GetType().FullName); } _slots.Set(thread, new ReadOnlyStructNeedle(value)); } T IThreadLocal.ValueForDebugDisplay { get { T target; return TryGetValue(Thread.CurrentThread, out target) ? target : default(T); } } } } #endif