#if FAT using System; using System.Threading; using LinqInternal.Core; namespace LinqInternal.Threading.Needles { [Serializable] [System.Diagnostics.DebuggerNonUserCode] internal class CacheNeedle : WeakNeedle, ICacheNeedle, IEquatable>, IWaitablePromise where T : class { [NonSerialized] private Thread _initializerThread; private Func _valueFactory; // Can be null private StructNeedle _waitHandle; public CacheNeedle(Func valueFactory) : base(default(T)) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } _valueFactory = valueFactory; _waitHandle = new StructNeedle(new ManualResetEventSlim(false)); } public CacheNeedle(Func valueFactory, T target) : base(target) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } _valueFactory = valueFactory; _waitHandle = new StructNeedle(new ManualResetEventSlim(false)); } public CacheNeedle(Func valueFactory, T target, bool cacheExceptions) : base(target) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } _valueFactory = valueFactory; if (cacheExceptions) { _valueFactory = () => { try { return valueFactory.Invoke(); } catch (Exception exc) { _valueFactory = FuncHelper.GetThrowFunc(exc); throw; } }; } _waitHandle = new StructNeedle(new ManualResetEventSlim(false)); } public CacheNeedle(T target) : base(target) { _valueFactory = null; _waitHandle = new StructNeedle(null); } public CacheNeedle() : base(default(T)) { _valueFactory = null; _waitHandle = new StructNeedle(null); } public T CachedTarget { get { return base.Value; } } Exception IPromise.Exception { get { return Exception; } } bool IPromise.IsCanceled { get { return false; } } public bool IsCompleted { get { return !_waitHandle.IsAlive; } } bool IPromise.IsFaulted { get { return IsFaulted; } } public override T Value { get { Initialize(); return base.Value; } set { SetTargetValue(value); ReleaseWaitHandle(); } } protected INeedle WaitHandle { get { return _waitHandle; } } public bool Equals(CacheNeedle other) { return !ReferenceEquals(other, null) && base.Equals(other); } public virtual void Initialize() { InitializeExtracted(); } public void ReleaseValueFactory() { Volatile.Write(ref _valueFactory, null); } public override bool TryGetValue(out T value) { value = default(T); return IsCompleted && base.TryGetValue(out value); } public void Wait() { if (_initializerThread == Thread.CurrentThread) { throw new InvalidOperationException(); } var waitHandle = _waitHandle.Value; if (waitHandle != null) { try { waitHandle.Wait(); } catch (ObjectDisposedException exception) { // Came late to the party, initialization was done GC.KeepAlive(exception); } } } [System.Diagnostics.DebuggerNonUserCode] protected override void Dispose(bool disposeManagedResources) { if (TakeDisposalExecution()) { try { if (disposeManagedResources) { //Empty } } finally { try { ReleaseWaitHandle(); } finally { _valueFactory = null; } base.Dispose(disposeManagedResources); } } } protected virtual void Initialize(Action beforeInitialize) { if (beforeInitialize == null) { throw new ArgumentNullException("beforeInitialize"); } if (Volatile.Read(ref _valueFactory) == null) { // If unable to initialize do nothing // This happens if // - initialization is done // - ReleaseValueFactory was called // Even if ReleaseValueFactory was called before initialization, // _target can still be set by SetTargetValue or the Value property return; } try { beforeInitialize.Invoke(); } finally { InitializeExtracted(); } } private void InitializeExtracted() { back: var valueFactory = Interlocked.Exchange(ref _valueFactory, null); if (valueFactory == null) { // Many threads may enter here // Prevent reentry if (_initializerThread == Thread.CurrentThread) { throw new InvalidOperationException(); } var waitHandle = _waitHandle.Value; // While _waitHandle.Value is not null it means that we have to wait initialization to complete if (waitHandle != null) { try { // Another thread may have called ReleaseWaitHandle just before the next instruction waitHandle.Wait(); // Finished waiting... if (Volatile.Read(ref _valueFactory) != null) { // There was an error in the initialization, go back goto back; } ReleaseWaitHandle(); } catch (ObjectDisposedException exception) { // Came late to the party, initialization is done GC.KeepAlive(exception); } } } else { // Only one thread enters here _initializerThread = Thread.CurrentThread; try { // Initialize from the value factory SetTargetValue(valueFactory.Invoke()); // Initialization done, let any wating thread go ReleaseWaitHandle(); } catch (Exception exception) { // There was an error during initialization // Set error state SetTargetError(exception); // Restore the valueFactory Interlocked.CompareExchange(ref _valueFactory, valueFactory, null); // Let any waiting threads go, but don't get rid of the wait handle _waitHandle.Value.Set(); throw; } finally { _initializerThread = null; } } } private void ReleaseWaitHandle() { var waitHandle = _waitHandle.Value; if (!ReferenceEquals(waitHandle, null)) { // If another thread is currently waiting, awake it waitHandle.Set(); // If another thread is about to wait // Or if another thread started waiting just after the last instruction // let it throw waitHandle.Dispose(); } _waitHandle.Value = null; } } } #endif