Exemple #1
0
        /// <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);
        }
Exemple #2
0
        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);
            }
        }