/// <summary> /// local helper method to initialize the value /// </summary> /// <returns>The inititialized T value</returns> private T LazyInitValue() { Boxed boxed = null; LazyThreadSafetyMode mode = Mode; if (mode == LazyThreadSafetyMode.None) { boxed = CreateValue(); _boxed = boxed; } else if (mode == LazyThreadSafetyMode.PublicationOnly) { boxed = CreateValue(); if (boxed == null || Interlocked.CompareExchange(ref _boxed, boxed, null) != null) { // If CreateValue returns null, it means another thread successfully invoked the value factory // and stored the result, so we should just take what was stored. If CreateValue returns non-null // but we lose the race to store the single value, again we should just take what was stored. boxed = (Boxed)_boxed; } else { // We successfully created and stored the value. At this point, the value factory delegate is // no longer needed, and we don't want to hold onto its resources. _valueFactory = s_ALREADY_INVOKED_SENTINEL; } } else { object threadSafeObj = Volatile.Read(ref _threadSafeObj); bool lockTaken = false; try { if (threadSafeObj != (object)s_ALREADY_INVOKED_SENTINEL) { Monitor.Enter(threadSafeObj, ref lockTaken); } else { Contract.Assert(_boxed != null); } if (_boxed == null) { boxed = CreateValue(); _boxed = boxed; Volatile.Write(ref _threadSafeObj, s_ALREADY_INVOKED_SENTINEL); } else // got the lock but the value is not null anymore, check if it is created by another thread or faulted and throw if so { boxed = _boxed as Boxed; if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder { LazyInternalExceptionHolder exHolder = _boxed as LazyInternalExceptionHolder; Contract.Assert(exHolder != null); exHolder.m_edi.Throw(); } } } finally { if (lockTaken) { Monitor.Exit(threadSafeObj); } } } Contract.Assert(boxed != null); return(boxed.m_value); }
private Lazy(Func <T> valueFactory, LazyThreadSafetyMode mode, bool cacheExceptions) { if (valueFactory == null) { throw new ArgumentNullException(nameof(valueFactory)); } switch (mode) { case LazyThreadSafetyMode.None: { if (cacheExceptions) { var threads = new HashSet <Thread>(); _valueFactory = () => CachingNoneMode(threads); } else { var threads = new HashSet <Thread>(); _valueFactory = () => NoneMode(threads); } } break; case LazyThreadSafetyMode.PublicationOnly: { _valueFactory = PublicationOnlyMode; } break; default: /*LazyThreadSafetyMode.ExecutionAndPublication*/ { if (cacheExceptions) { Thread thread = null; var waitHandle = new ManualResetEvent(false); _valueFactory = () => CachingFullMode(valueFactory, waitHandle, ref thread); } else { Thread thread = null; var waitHandle = new ManualResetEvent(false); var preIsValueCreated = 0; _valueFactory = () => FullMode(valueFactory, waitHandle, ref thread, ref preIsValueCreated); } } break; } T CachingNoneMode(HashSet <Thread> threads) { if (Volatile.Read(ref _isValueCreated) != 0) { return(_valueFactory.Invoke()); } try { // lock (threads) // This is meant to not be thread-safe { var currentThread = Thread.CurrentThread; if (threads.Contains(currentThread)) { throw new InvalidOperationException(); } threads.Add(currentThread); } ValueForDebugDisplay = valueFactory(); _valueFactory = FuncHelper.GetReturnFunc(ValueForDebugDisplay); Volatile.Write(ref _isValueCreated, 1); return(ValueForDebugDisplay); } catch (Exception exception) { _valueFactory = FuncHelper.GetThrowFunc <T>(exception); throw; } finally { // lock (threads) // This is meant to not be thread-safe { threads.Remove(Thread.CurrentThread); } } } T NoneMode(HashSet <Thread> threads) { if (Volatile.Read(ref _isValueCreated) != 0) { return(_valueFactory.Invoke()); } try { // lock (threads) // This is meant to not be thread-safe { var currentThread = Thread.CurrentThread; if (threads.Contains(currentThread)) { throw new InvalidOperationException(); } threads.Add(currentThread); } ValueForDebugDisplay = valueFactory(); _valueFactory = FuncHelper.GetReturnFunc(ValueForDebugDisplay); Volatile.Write(ref _isValueCreated, 1); return(ValueForDebugDisplay); } catch (Exception) { Volatile.Write(ref _isValueCreated, 0); throw; } finally { // lock (threads) // This is meant to not be thread-safe { threads.Remove(Thread.CurrentThread); } } } T PublicationOnlyMode() { ValueForDebugDisplay = valueFactory(); if (Interlocked.CompareExchange(ref _isValueCreated, 1, 0) == 0) { _valueFactory = FuncHelper.GetReturnFunc(ValueForDebugDisplay); } return(ValueForDebugDisplay); } }
/// <summary> /// Asynchronously invokes all members of a delegate's invokation list. /// </summary> /// <typeparam name="TDelegate">The type of delegate.</typeparam> /// <param name="delegate">The delegate that defines the invokation list.</param> /// <param name="invocation">Defines the invocation of the delegate.</param> /// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="invocation"/> is null.</exception> /// <remarks> /// Other than invoking the delegate directly, this ensures that each invokation list member is invoked /// regardless of other members throwing exceptions. If one invokation list member throws an exception, /// this exception is rethrown, if multiple list members throw exceptions, an aggregate exception is thrown. /// </remarks> public static async ValueTask InvokeAllAsync <TDelegate>(this TDelegate @delegate, Func <TDelegate, ValueTask> invocation) where TDelegate : Delegate { if (invocation == null) { throw new ArgumentNullException(nameof(invocation)); } #pragma warning disable CA1062 var invocationList = @delegate.GetInvocationList(); #pragma warning restore CA1062 List <Exception>?capturedExceptions = null; List <Exception> GetCapturedExceptions() { var result = Volatile.Read(ref capturedExceptions); if (result == null) { result = new List <Exception>(); var current = Interlocked.CompareExchange(ref capturedExceptions, result, null); if (current != null) { result = current; } } return(result); } async ValueTask InvokeCoreAsync(TDelegate singlecastDelegate) { try { await invocation(singlecastDelegate); } #pragma warning disable CA1031 catch (Exception exc) #pragma warning restore CA1031 { var exceptions = GetCapturedExceptions(); lock (exceptions) { exceptions.Add(exc); } } } await invocationList.Select(p => InvokeCoreAsync((TDelegate)p)).WhenAll(); if (capturedExceptions == null || !capturedExceptions.Any()) { return; } if (capturedExceptions.Count == 1) { throw capturedExceptions.First(); } else { throw new AggregateException(capturedExceptions); } }