Alternative System.Lazy implementation (dotnet/coreclr#8963)
authorPaul Westcott <p.westcott@gmail.com>
Sat, 4 Mar 2017 03:14:02 +0000 (14:14 +1100)
committerJan Kotas <jkotas@microsoft.com>
Sat, 4 Mar 2017 03:14:02 +0000 (19:14 -0800)
* Remove use of lock for PublicationOnly

This involves a little bit of trickery. In avoiding the lock we need to
go from m_implementation as PublicationOnly to null in a single step. We
can't do this though without another object, so we reuse out Lazy
object, which basically just spins its wheels until the value is ready.
This shouldn't be a very common scenerio (i.e. hitting that code path)
but it is possible.

* Minimize additional object creation

Storing factory in Lazy object, and passing Lazy as a parameter to
ILazyItem functions. This means that None & PublicationOnly now need no
creation of secondary object.

* Remove Func<T> for default constructor invoking

Fixing startup performance concerns

* Moved non-generic functionality out of Lazy

...and into a helper class

* Expression-bodied functions

Commit migrated from https://github.com/dotnet/coreclr/commit/2453adf1afba45dec79c6c643623b656702a17de

src/coreclr/src/mscorlib/src/System/Lazy.cs

index 446ec30..58fdd4a 100644 (file)
@@ -26,84 +26,190 @@ using System.Runtime.ExceptionServices;
 
 namespace System
 {
-    // Lazy<T> 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
+    internal enum LazyState
     {
-        // Dummy object used as the value of m_threadSafeObj if in PublicationOnly mode.
-        internal static readonly object PUBLICATION_ONLY_SENTINEL = new object();
+        NoneViaConstructor = 0,
+        NoneViaFactory     = 1,
+        NoneException      = 2,
+
+        PublicationOnlyViaConstructor = 3,
+        PublicationOnlyViaFactory     = 4,
+        PublicationOnlyWait           = 5,
+        PublicationOnlyException      = 6,
+
+        ExecutionAndPublicationViaConstructor = 7,
+        ExecutionAndPublicationViaFactory     = 8,
+        ExecutionAndPublicationException      = 9,
     }
 
     /// <summary>
-    /// Provides support for lazy initialization.
+    /// LazyHelper serves multiples purposes
+    /// - minimizing code size of Lazy&lt;T&gt; by implementing as much of the code that is not generic
+    ///   this reduces generic code bloat, making faster class initialization
+    /// - contains singleton objects that are used to handle threading primitives for PublicationOnly mode
+    /// - allows for instantiation for ExecutionAndPublication so as to create an object for locking on
+    /// - holds exception information.
     /// </summary>
-    /// <typeparam name="T">Specifies the type of element being lazily initialized.</typeparam>
-    /// <remarks>
-    /// <para>
-    /// By default, all public and protected members of <see cref="Lazy{T}"/> 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.
-    /// </para>
-    /// </remarks>
-    [Serializable]
-    [DebuggerTypeProxy(typeof(System_LazyDebugView<>))]
-    [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")]
-    public class Lazy<T>
+    internal class LazyHelper
     {
-        #region Inner classes
+        internal readonly static LazyHelper NoneViaConstructor            = new LazyHelper(LazyState.NoneViaConstructor);
+        internal readonly static LazyHelper NoneViaFactory                = new LazyHelper(LazyState.NoneViaFactory);
+        internal readonly static LazyHelper PublicationOnlyViaConstructor = new LazyHelper(LazyState.PublicationOnlyViaConstructor);
+        internal readonly static LazyHelper PublicationOnlyViaFactory     = new LazyHelper(LazyState.PublicationOnlyViaFactory);
+        internal readonly static LazyHelper PublicationOnlyWaitForOtherThreadToPublish       = new LazyHelper(LazyState.PublicationOnlyWait);
+
+        internal LazyState State { get; }
+
+        private readonly ExceptionDispatchInfo _exceptionDispatch;
+
         /// <summary>
-        /// 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
+        /// Constructor that defines the state
         /// </summary>
-        [Serializable]
-        private class Boxed
+        internal LazyHelper(LazyState state)
         {
-            internal Boxed(T value)
+            State = state;
+        }
+
+        /// <summary>
+        /// Constructor used for exceptions
+        /// </summary>
+        internal LazyHelper(LazyThreadSafetyMode mode, Exception exception)
+        {
+            switch(mode)
             {
-                m_value = value;
+                case LazyThreadSafetyMode.ExecutionAndPublication:
+                    State = LazyState.ExecutionAndPublicationException;
+                    break;
+
+                case LazyThreadSafetyMode.None:
+                    State = LazyState.NoneException;
+                    break;
+
+                case LazyThreadSafetyMode.PublicationOnly:
+                    State = LazyState.PublicationOnlyException;
+                    break;
+
+                default:
+                    Debug.Assert(false, "internal constructor, this should never occur");
+                    break;
             }
-            internal T m_value;
+
+            _exceptionDispatch = ExceptionDispatchInfo.Capture(exception);
         }
 
+        internal void ThrowException()
+        {
+            Debug.Assert(_exceptionDispatch != null, "execution path is invalid");
 
-        /// <summary>
-        /// Wrapper class to wrap the excpetion thrown by the value factory
-        /// </summary>
-        private class LazyInternalExceptionHolder
+            _exceptionDispatch.Throw();
+        }
+
+        private LazyThreadSafetyMode GetMode()
         {
-            internal ExceptionDispatchInfo m_edi;
-            internal LazyInternalExceptionHolder(Exception ex)
+            switch (State)
             {
-                m_edi = ExceptionDispatchInfo.Capture(ex);
+                case LazyState.NoneViaConstructor:
+                case LazyState.NoneViaFactory:
+                case LazyState.NoneException:
+                    return LazyThreadSafetyMode.None;
+
+                case LazyState.PublicationOnlyViaConstructor:
+                case LazyState.PublicationOnlyViaFactory:
+                case LazyState.PublicationOnlyWait:
+                case LazyState.PublicationOnlyException:
+                    return LazyThreadSafetyMode.PublicationOnly;
+
+                case LazyState.ExecutionAndPublicationViaConstructor:
+                case LazyState.ExecutionAndPublicationViaFactory:
+                case LazyState.ExecutionAndPublicationException:
+                    return LazyThreadSafetyMode.ExecutionAndPublication;
+
+                default:
+                    Debug.Assert(false, "Invalid logic; State should always have a valid value");
+                    return default(LazyThreadSafetyMode);
             }
         }
-        #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
-        private static readonly Func<T> ALREADY_INVOKED_SENTINEL = delegate
+        internal static LazyThreadSafetyMode? GetMode(LazyHelper state)
         {
-            Debug.Assert(false, "ALREADY_INVOKED_SENTINEL should never be invoked.");
-            return default(T);
-        };
+            if (state == null)
+                return null; // we don't know the mode anymore
+            return state.GetMode();
+        }
+
+        internal static bool GetIsValueFaulted(LazyHelper state) => state?._exceptionDispatch != null;
 
-        //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;
+        internal static LazyHelper Create(LazyThreadSafetyMode mode, bool useDefaultConstructor)
+        {
+            switch (mode)
+            {
+                case LazyThreadSafetyMode.None:
+                    return useDefaultConstructor ? NoneViaConstructor : NoneViaFactory;
+
+                case LazyThreadSafetyMode.PublicationOnly:
+                    return useDefaultConstructor ? PublicationOnlyViaConstructor : PublicationOnlyViaFactory;
+
+                case LazyThreadSafetyMode.ExecutionAndPublication:
+                    // we need to create an object for ExecutionAndPublication because we use Monitor-based locking
+                    var state = useDefaultConstructor ? LazyState.ExecutionAndPublicationViaConstructor : LazyState.ExecutionAndPublicationViaFactory;
+                    return new LazyHelper(state);
+
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(mode), Environment.GetResourceString("Lazy_ctor_ModeInvalid"));
+            }
+        }
 
-        // 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
+        internal static object CreateViaDefaultConstructor(Type type)
+        {
+            try
+            {
+                return Activator.CreateInstance(type);
+            }
+            catch (MissingMethodException)
+            {
+                throw new MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT"));
+            }
+        }
+
+        internal static LazyThreadSafetyMode GetModeFromIsThreadSafe(bool isThreadSafe)
+        {
+            return isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None;
+        }
+    }
+
+    /// <summary>
+    /// Provides support for lazy initialization.
+    /// </summary>
+    /// <typeparam name="T">Specifies the type of element being lazily initialized.</typeparam>
+    /// <remarks>
+    /// <para>
+    /// By default, all public and protected members of <see cref="Lazy{T}"/> 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.
+    /// </para>
+    /// </remarks>
+    [Serializable]
+    [ComVisible(false)]
+    [DebuggerTypeProxy(typeof(System_LazyDebugView<>))]
+    [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")]
+    public class Lazy<T>
+    {
+        private static T CreateViaDefaultConstructor()
+        {
+            return (T)LazyHelper.CreateViaDefaultConstructor(typeof(T));
+        }
+
+        // _state, a volatile reference, is set to null after m_value has been set
         [NonSerialized]
-        private Func<T> m_valueFactory;
+        private volatile LazyHelper _state;
 
-        // 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)
+        // we ensure that _factory when finished is set to null to allow garbage collector to clean up
+        // any referenced items
         [NonSerialized]
-        private object m_threadSafeObj;
+        private Func<T> _factory;
 
+        // m_value eventually stores the lazily created value. It is valid when _state = null.
+        private T _value;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that 
@@ -113,7 +219,7 @@ namespace System
         /// An instance created with this constructor may be used concurrently from multiple threads.
         /// </remarks>
         public Lazy()
-            : this(LazyThreadSafetyMode.ExecutionAndPublication)
+            : this(null, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor:true)
         {
         }
 
@@ -127,7 +233,7 @@ namespace System
         /// </remarks>
         public Lazy(T value)
         {
-            m_boxed = new Boxed(value);
+            _value = value;
         }
 
         /// <summary>
@@ -144,7 +250,7 @@ namespace System
         /// An instance created with this constructor may be used concurrently from multiple threads.
         /// </remarks>
         public Lazy(Func<T> valueFactory)
-            : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)
+            : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor:false)
         {
         }
 
@@ -155,7 +261,7 @@ namespace System
         /// <param name="isThreadSafe">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.
         /// </param>
         public Lazy(bool isThreadSafe) :
-            this(isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
+            this(null, LazyHelper.GetModeFromIsThreadSafe(isThreadSafe), useDefaultConstructor:true)
         {
         }
 
@@ -165,12 +271,11 @@ namespace System
         /// </summary>
         /// <param name="mode">The lazy thread-safety mode mode</param>
         /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid valuee</exception>
-        public Lazy(LazyThreadSafetyMode mode)
+        public Lazy(LazyThreadSafetyMode mode) :
+            this(null, mode, useDefaultConstructor:true)
         {
-            m_threadSafeObj = GetObjectFromMode(mode);
         }
 
-
         /// <summary>
         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class
         /// that uses a specified initialization function and a specified thread-safety mode.
@@ -182,8 +287,8 @@ namespace System
         /// </param>
         /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is
         /// a null reference (Nothing in Visual Basic).</exception>
-        public Lazy(Func<T> valueFactory, bool isThreadSafe)
-            : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
+        public Lazy(Func<T> valueFactory, bool isThreadSafe) :
+            this(valueFactory, LazyHelper.GetModeFromIsThreadSafe(isThreadSafe), useDefaultConstructor:false)
         {
         }
 
@@ -199,27 +304,147 @@ namespace System
         /// a null reference (Nothing in Visual Basic).</exception>
         /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid value.</exception>
         public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode)
+            : this(valueFactory, mode, useDefaultConstructor:false)
         {
-            if (valueFactory == null)
+        }
+
+        private Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode, bool useDefaultConstructor)
+        {
+            if (valueFactory == null && !useDefaultConstructor)
                 throw new ArgumentNullException(nameof(valueFactory));
 
-            m_threadSafeObj = GetObjectFromMode(mode);
-            m_valueFactory = valueFactory;
+            _factory = valueFactory;
+            _state = LazyHelper.Create(mode, useDefaultConstructor);
         }
 
-        /// <summary>
-        /// Static helper function that returns an object based on the given mode. it also throws an exception if the mode is invalid
-        /// </summary>
-        private static object GetObjectFromMode(LazyThreadSafetyMode mode)
+        private void ViaConstructor()
+        {
+            _value = CreateViaDefaultConstructor();
+            _state = null; // volatile write, must occur after setting _value
+        }
+
+        private void ViaFactory(LazyThreadSafetyMode mode)
+        {
+            try
+            {
+                Func<T> factory = _factory;
+                if (factory == null)
+                    throw new InvalidOperationException(Environment.GetResourceString("Lazy_Value_RecursiveCallsToValue"));
+                _factory = null;
+
+                _value = factory();
+                _state = null; // volatile write, must occur after setting _value
+            }
+            catch (Exception exception)
+            {
+                _state = new LazyHelper(mode, exception);
+                throw;
+            }
+        }
+
+        private void ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor)
+        {
+            lock (executionAndPublication)
+            {
+                // it's possible for multiple calls to have piled up behind the lock, so we need to check
+                // to see if the ExecutionAndPublication object is still the current implementation.
+                if (ReferenceEquals(_state, executionAndPublication))
+                {
+                    if (useDefaultConstructor)
+                    {
+                        ViaConstructor();
+                    }
+                    else
+                    {
+                        ViaFactory(LazyThreadSafetyMode.ExecutionAndPublication);
+                    }
+                }
+            }
+        }
+
+        private void PublicationOnly(LazyHelper publicationOnly, T possibleValue)
+        {
+            LazyHelper previous = Interlocked.CompareExchange(ref _state, LazyHelper.PublicationOnlyWaitForOtherThreadToPublish, publicationOnly);
+            if (previous == publicationOnly)
+            {
+                _factory = null;
+                _value = possibleValue;
+                _state = null; // volatile write, must occur after setting _value
+            }
+        }
+
+        private void PublicationOnlyViaConstructor(LazyHelper initializer)
         {
-            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(nameof(mode), Environment.GetResourceString("Lazy_ctor_ModeInvalid"));
+            PublicationOnly(initializer, CreateViaDefaultConstructor());
+        }
 
-            return null; // None mode
+        private void PublicationOnlyViaFactory(LazyHelper initializer)
+        {
+            Func<T> factory = _factory;
+            if (factory == null)
+            {
+                PublicationOnlyWaitForOtherThreadToPublish();
+            }
+            else
+            {
+                PublicationOnly(initializer, factory());
+            }
+        }
+
+        private void PublicationOnlyWaitForOtherThreadToPublish()
+        {
+            var spinWait = new SpinWait();
+            while (!ReferenceEquals(_state, null))
+            {
+                // We get here when PublicationOnly temporarily sets _state to LazyHelper.PublicationOnlyWaitForOtherThreadToPublish.
+                // This temporary state should be quickly followed by _state being set to null.
+                spinWait.SpinOnce();
+            }
+        }
+
+        private T CreateValue()
+        {
+            // we have to create a copy of state here, and use the copy exclusively from here on in
+            // so as to ensure thread safety.
+            var state = _state;
+            if (state != null) 
+            {
+                switch (state.State)
+                {
+                    case LazyState.NoneViaConstructor:
+                        ViaConstructor();
+                        break;
+
+                    case LazyState.NoneViaFactory:
+                        ViaFactory(LazyThreadSafetyMode.None);
+                        break;
+
+                    case LazyState.PublicationOnlyViaConstructor:
+                        PublicationOnlyViaConstructor(state);
+                        break;
+
+                    case LazyState.PublicationOnlyViaFactory:
+                        PublicationOnlyViaFactory(state);
+                        break;
+
+                    case LazyState.PublicationOnlyWait:
+                        PublicationOnlyWaitForOtherThreadToPublish();
+                        break;
+
+                    case LazyState.ExecutionAndPublicationViaConstructor:
+                        ExecutionAndPublication(state, useDefaultConstructor:true);
+                        break;
+
+                    case LazyState.ExecutionAndPublicationViaFactory:
+                        ExecutionAndPublication(state, useDefaultConstructor:false);
+                        break;
+
+                    default:
+                        state.ThrowException();
+                        break;
+                }
+            }
+            return Value;
         }
 
         /// <summary>Forces initialization during serialization.</summary>
@@ -251,30 +476,19 @@ namespace System
                 {
                     return default(T);
                 }
-                return ((Boxed)m_boxed).m_value;
+                return _value;
             }
         }
 
         /// <summary>
         /// Gets a value indicating whether this instance may be used concurrently from multiple threads.
         /// </summary>
-        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;
-            }
-        }
+        internal LazyThreadSafetyMode? Mode => LazyHelper.GetMode(_state);
 
         /// <summary>
         /// Gets whether the value creation is faulted or not
         /// </summary>
-        internal bool IsValueFaulted
-        {
-            get { return m_boxed is LazyInternalExceptionHolder; }
-        }
+        internal bool IsValueFaulted => LazyHelper.GetIsValueFaulted(_state);
 
         /// <summary>Gets a value indicating whether the <see cref="T:System.Lazy{T}"/> has been initialized.
         /// </summary>
@@ -285,13 +499,7 @@ namespace System
         /// a value being produced or an exception being thrown.  If an exception goes unhandled during initialization, 
         /// <see cref="IsValueCreated"/> will return false.
         /// </remarks>
-        public bool IsValueCreated
-        {
-            get
-            {
-                return m_boxed != null && m_boxed is Boxed;
-            }
-        }
+        public bool IsValueCreated => _state == null;
 
         /// <summary>Gets the lazily initialized value of the current <see
         /// cref="T:System.Threading.Lazy{T}"/>.</summary>
@@ -315,149 +523,7 @@ namespace System
         /// from initialization delegate.
         /// </remarks>
         [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;
-                    Debug.Assert(exc != null);
-                    exc.m_edi.Throw();
-                }
-
-                // Fall through to the slow path.
-                return LazyInitValue();
-            }
-        }
-
-        /// <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();
-                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
-                        Debug.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;
-                            Debug.Assert(exHolder != null);
-                            exHolder.m_edi.Throw();
-                        }
-                    }
-                }
-                finally
-                {
-                    if (lockTaken)
-                        Monitor.Exit(threadSafeObj);
-                }
-            }
-            Debug.Assert(boxed != null);
-            return boxed.m_value;
-        }
-
-        /// <summary>Creates an instance of T using m_valueFactory in case its not null or use reflection to create a new T()</summary>
-        /// <returns>An instance of Boxed.</returns>
-        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<T> 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;
-        }
+        public T Value => _state == null ? _value : CreateValue();
     }
 
     /// <summary>A debugger view of the Lazy&lt;T&gt; to surface additional debugging properties and 
@@ -488,7 +554,7 @@ namespace System
         }
 
         /// <summary>Returns the execution mode of the Lazy object</summary>
-        public LazyThreadSafetyMode Mode
+        public LazyThreadSafetyMode? Mode
         {
             get { return m_lazy.Mode; }
         }