// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #pragma warning disable 0420 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // // // -------------------------------------------------------------------------------------- // // A class that provides a simple, lightweight implementation of lazy initialization, // obviating the need for a developer to implement a custom, thread-safe lazy initialization // solution. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Runtime; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Diagnostics; using System.Runtime.Serialization; using System.Threading; using System.Diagnostics.Contracts; using System.Runtime.ExceptionServices; namespace System { // Lazy is generic, but not all of its state needs to be generic. Avoid creating duplicate // objects per instantiation by putting them here. internal static class LazyHelpers { // Dummy object used as the value of m_threadSafeObj if in PublicationOnly mode. internal static readonly object PUBLICATION_ONLY_SENTINEL = new object(); } /// /// Provides support for lazy initialization. /// /// Specifies the type of element being lazily initialized. /// /// /// By default, all public and protected members of are thread-safe and may be used /// concurrently from multiple threads. These thread-safety guarantees may be removed optionally and per instance /// using parameters to the type's constructors. /// /// [Serializable] [ComVisible(false)] #if !FEATURE_CORECLR [HostProtection(Synchronization = true, ExternalThreading = true)] #endif [DebuggerTypeProxy(typeof(System_LazyDebugView<>))] [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")] public class Lazy { #region Inner classes /// /// wrapper class to box the initialized value, this is mainly created to avoid boxing/unboxing the value each time the value is called in case T is /// a value type /// [Serializable] class Boxed { internal Boxed(T value) { m_value = value; } internal T m_value; } /// /// Wrapper class to wrap the excpetion thrown by the value factory /// class LazyInternalExceptionHolder { internal ExceptionDispatchInfo m_edi; internal LazyInternalExceptionHolder(Exception ex) { m_edi = ExceptionDispatchInfo.Capture(ex); } } #endregion // A dummy delegate used as a : // 1- Flag to avoid recursive call to Value in None and ExecutionAndPublication modes in m_valueFactory // 2- Flag to m_threadSafeObj if ExecutionAndPublication mode and the value is known to be initialized static readonly Func ALREADY_INVOKED_SENTINEL = delegate { Contract.Assert(false, "ALREADY_INVOKED_SENTINEL should never be invoked."); return default(T); }; //null --> value is not created //m_value is Boxed --> the value is created, and m_value holds the value //m_value is LazyExceptionHolder --> it holds an exception private object m_boxed; // The factory delegate that returns the value. // In None and ExecutionAndPublication modes, this will be set to ALREADY_INVOKED_SENTINEL as a flag to avoid recursive calls [NonSerialized] private Func m_valueFactory; // null if it is not thread safe mode // LazyHelpers.PUBLICATION_ONLY_SENTINEL if PublicationOnly mode // object if ExecutionAndPublication mode (may be ALREADY_INVOKED_SENTINEL if the value is already initialized) [NonSerialized] private object m_threadSafeObj; /// /// Initializes a new instance of the class that /// uses 's default constructor for lazy initialization. /// /// /// An instance created with this constructor may be used concurrently from multiple threads. /// public Lazy() : this(LazyThreadSafetyMode.ExecutionAndPublication) { } /// /// Initializes a new instance of the class that uses a /// specified initialization function. /// /// /// The invoked to produce the lazily-initialized value when it is /// needed. /// /// is a null /// reference (Nothing in Visual Basic). /// /// An instance created with this constructor may be used concurrently from multiple threads. /// public Lazy(Func valueFactory) : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication) { } /// /// Initializes a new instance of the /// class that uses 's default constructor and a specified thread-safety mode. /// /// true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time. /// public Lazy(bool isThreadSafe) : this(isThreadSafe? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) { } /// /// Initializes a new instance of the /// class that uses 's default constructor and a specified thread-safety mode. /// /// The lazy thread-safety mode mode /// mode contains an invalid valuee public Lazy(LazyThreadSafetyMode mode) { m_threadSafeObj = GetObjectFromMode(mode); } /// /// Initializes a new instance of the class /// that uses a specified initialization function and a specified thread-safety mode. /// /// /// The invoked to produce the lazily-initialized value when it is needed. /// /// true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time. /// /// is /// a null reference (Nothing in Visual Basic). public Lazy(Func valueFactory, bool isThreadSafe) : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) { } /// /// Initializes a new instance of the class /// that uses a specified initialization function and a specified thread-safety mode. /// /// /// The invoked to produce the lazily-initialized value when it is needed. /// /// The lazy thread-safety mode. /// is /// a null reference (Nothing in Visual Basic). /// mode contains an invalid value. public Lazy(Func valueFactory, LazyThreadSafetyMode mode) { if (valueFactory == null) throw new ArgumentNullException("valueFactory"); m_threadSafeObj = GetObjectFromMode(mode); m_valueFactory = valueFactory; } /// /// Static helper function that returns an object based on the given mode. it also throws an exception if the mode is invalid /// private static object GetObjectFromMode(LazyThreadSafetyMode mode) { if (mode == LazyThreadSafetyMode.ExecutionAndPublication) return new object(); else if (mode == LazyThreadSafetyMode.PublicationOnly) return LazyHelpers.PUBLICATION_ONLY_SENTINEL; else if (mode != LazyThreadSafetyMode.None) throw new ArgumentOutOfRangeException("mode", Environment.GetResourceString("Lazy_ctor_ModeInvalid")); return null; // None mode } /// Forces initialization during serialization. /// The StreamingContext for the serialization operation. [OnSerializing] private void OnSerializing(StreamingContext context) { // Force initialization T dummy = Value; } /// Creates and returns a string representation of this instance. /// The result of calling on the . /// /// The is null. /// public override string ToString() { return IsValueCreated ? Value.ToString() : Environment.GetResourceString("Lazy_ToString_ValueNotCreated"); } /// Gets the value of the Lazy<T> for debugging display purposes. internal T ValueForDebugDisplay { get { if (!IsValueCreated) { return default(T); } return ((Boxed)m_boxed).m_value; } } /// /// Gets a value indicating whether this instance may be used concurrently from multiple threads. /// internal LazyThreadSafetyMode Mode { get { if (m_threadSafeObj == null) return LazyThreadSafetyMode.None; if (m_threadSafeObj == (object)LazyHelpers.PUBLICATION_ONLY_SENTINEL) return LazyThreadSafetyMode.PublicationOnly; return LazyThreadSafetyMode.ExecutionAndPublication; } } /// /// Gets whether the value creation is faulted or not /// internal bool IsValueFaulted { get { return m_boxed is LazyInternalExceptionHolder; } } /// Gets a value indicating whether the has been initialized. /// /// true if the instance has been initialized; /// otherwise, false. /// /// The initialization of a instance may result in either /// a value being produced or an exception being thrown. If an exception goes unhandled during initialization, /// will return false. /// public bool IsValueCreated { get { return m_boxed != null && m_boxed is Boxed; } } /// Gets the lazily initialized value of the current . /// The lazily initialized value of the current . /// /// The was initialized to use the default constructor /// of the type being lazily initialized, and that type does not have a public, parameterless constructor. /// /// /// The was initialized to use the default constructor /// of the type being lazily initialized, and permissions to access the constructor were missing. /// /// /// The was constructed with the or /// and the initialization function attempted to access on this instance. /// /// /// If is false, accessing will force initialization. /// Please for more information on how will behave if an exception is thrown /// from initialization delegate. /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] public T Value { get { Boxed boxed = null; if (m_boxed != null ) { // Do a quick check up front for the fast path. boxed = m_boxed as Boxed; if (boxed != null) { return boxed.m_value; } LazyInternalExceptionHolder exc = m_boxed as LazyInternalExceptionHolder; Contract.Assert(exc != null); exc.m_edi.Throw(); } // Fall through to the slow path. #if !FEATURE_CORECLR // We call NOCTD to abort attempts by the debugger to funceval this property (e.g. on mouseover) // (the debugger proxy is the correct way to look at state/value of this object) Debugger.NotifyOfCrossThreadDependency(); #endif return LazyInitValue(); } } /// /// local helper method to initialize the value /// /// The inititialized T value private T LazyInitValue() { Boxed boxed = null; LazyThreadSafetyMode mode = Mode; if (mode == LazyThreadSafetyMode.None) { boxed = CreateValue(); m_boxed = boxed; } else if (mode == LazyThreadSafetyMode.PublicationOnly) { boxed = CreateValue(); if (boxed == null || Interlocked.CompareExchange(ref m_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 another thread set the value we should just take what was stored. boxed = (Boxed)m_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. m_valueFactory = ALREADY_INVOKED_SENTINEL; } } else { object threadSafeObj = Volatile.Read(ref m_threadSafeObj); bool lockTaken = false; try { if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL) Monitor.Enter(threadSafeObj, ref lockTaken); else Contract.Assert(m_boxed != null); if (m_boxed == null) { boxed = CreateValue(); m_boxed = boxed; Volatile.Write(ref m_threadSafeObj, 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 = m_boxed as Boxed; if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder { LazyInternalExceptionHolder exHolder = m_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; } /// Creates an instance of T using m_valueFactory in case its not null or use reflection to create a new T() /// An instance of Boxed. private Boxed CreateValue() { Boxed boxed = null; LazyThreadSafetyMode mode = Mode; if (m_valueFactory != null) { try { // check for recursion if (mode != LazyThreadSafetyMode.PublicationOnly && m_valueFactory == ALREADY_INVOKED_SENTINEL) throw new InvalidOperationException(Environment.GetResourceString("Lazy_Value_RecursiveCallsToValue")); Func factory = m_valueFactory; if (mode != LazyThreadSafetyMode.PublicationOnly) // only detect recursion on None and ExecutionAndPublication modes { m_valueFactory = ALREADY_INVOKED_SENTINEL; } else if (factory == ALREADY_INVOKED_SENTINEL) { // Another thread raced to successfully invoke the factory. return null; } boxed = new Boxed(factory()); } catch (Exception ex) { if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode m_boxed = new LazyInternalExceptionHolder(ex); throw; } } else { try { boxed = new Boxed((T)Activator.CreateInstance(typeof(T))); } catch (System.MissingMethodException) { Exception ex = new System.MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT")); if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode m_boxed = new LazyInternalExceptionHolder(ex); throw ex; } } return boxed; } } /// A debugger view of the Lazy<T> to surface additional debugging properties and /// to ensure that the Lazy<T> does not become initialized if it was not already. internal sealed class System_LazyDebugView { //The Lazy object being viewed. private readonly Lazy m_lazy; /// Constructs a new debugger view object for the provided Lazy object. /// A Lazy object to browse in the debugger. public System_LazyDebugView(Lazy lazy) { m_lazy = lazy; } /// Returns whether the Lazy object is initialized or not. public bool IsValueCreated { get { return m_lazy.IsValueCreated; } } /// Returns the value of the Lazy object. public T Value { get { return m_lazy.ValueForDebugDisplay; } } /// Returns the execution mode of the Lazy object public LazyThreadSafetyMode Mode { get { return m_lazy.Mode; } } /// Returns the execution mode of the Lazy object public bool IsValueFaulted { get { return m_lazy.IsValueFaulted; } } } }