2016-08-03 10:59:49 +00:00
#pragma warning disable 0420
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// Lazy.cs
//
2017-08-21 15:34:15 +00:00
// <OWNER>Microsoft</OWNER>
2016-08-03 10:59:49 +00:00
//
// --------------------------------------------------------------------------------------
//
// 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<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
{
// Dummy object used as the value of m_threadSafeObj if in PublicationOnly mode.
internal static readonly object PUBLICATION_ONLY_SENTINEL = new object ( ) ;
}
/// <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)]
#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 < T >
{
#region Inner classes
/// <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
/// </summary>
[Serializable]
class Boxed
{
internal Boxed ( T value )
{
m_value = value ;
}
internal T m_value ;
}
/// <summary>
/// Wrapper class to wrap the excpetion thrown by the value factory
/// </summary>
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 < T > 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 < T > 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 ;
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that
/// uses <typeparamref name="T"/>'s default constructor for lazy initialization.
/// </summary>
/// <remarks>
/// An instance created with this constructor may be used concurrently from multiple threads.
/// </remarks>
public Lazy ( )
: this ( LazyThreadSafetyMode . ExecutionAndPublication )
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that uses a
/// specified initialization function.
/// </summary>
/// <param name="valueFactory">
/// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is
/// needed.
/// </param>
/// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is a null
/// reference (Nothing in Visual Basic).</exception>
/// <remarks>
/// An instance created with this constructor may be used concurrently from multiple threads.
/// </remarks>
public Lazy ( Func < T > valueFactory )
: this ( valueFactory , LazyThreadSafetyMode . ExecutionAndPublication )
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
/// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
/// </summary>
/// <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 )
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
/// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
/// </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 )
{
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.
/// </summary>
/// <param name="valueFactory">
/// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
/// </param>
/// <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>
/// <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 )
{
}
/// <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.
/// </summary>
/// <param name="valueFactory">
/// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
/// </param>
/// <param name="mode">The lazy thread-safety mode.</param>
/// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is
/// 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 )
{
if ( valueFactory = = null )
throw new ArgumentNullException ( "valueFactory" ) ;
m_threadSafeObj = GetObjectFromMode ( mode ) ;
m_valueFactory = valueFactory ;
}
/// <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 )
{
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
}
/// <summary>Forces initialization during serialization.</summary>
/// <param name="context">The StreamingContext for the serialization operation.</param>
[OnSerializing]
private void OnSerializing ( StreamingContext context )
{
// Force initialization
T dummy = Value ;
}
/// <summary>Creates and returns a string representation of this instance.</summary>
/// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see
/// cref="Value"/>.</returns>
/// <exception cref="T:System.NullReferenceException">
/// The <see cref="Value"/> is null.
/// </exception>
public override string ToString ( )
{
return IsValueCreated ? Value . ToString ( ) : Environment . GetResourceString ( "Lazy_ToString_ValueNotCreated" ) ;
}
/// <summary>Gets the value of the Lazy<T> for debugging display purposes.</summary>
internal T ValueForDebugDisplay
{
get
{
if ( ! IsValueCreated )
{
return default ( T ) ;
}
return ( ( Boxed ) m_boxed ) . m_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 ;
}
}
/// <summary>
/// Gets whether the value creation is faulted or not
/// </summary>
internal bool IsValueFaulted
{
get { return m_boxed is LazyInternalExceptionHolder ; }
}
/// <summary>Gets a value indicating whether the <see cref="T:System.Lazy{T}"/> has been initialized.
/// </summary>
/// <value>true if the <see cref="T:System.Lazy{T}"/> instance has been initialized;
/// otherwise, false.</value>
/// <remarks>
/// The initialization of a <see cref="T:System.Lazy{T}"/> instance may result in either
/// 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 ;
}
}
/// <summary>Gets the lazily initialized value of the current <see
/// cref="T:System.Threading.Lazy{T}"/>.</summary>
/// <value>The lazily initialized value of the current <see
/// cref="T:System.Threading.Lazy{T}"/>.</value>
/// <exception cref="T:System.MissingMemberException">
/// The <see cref="T:System.Threading.Lazy{T}"/> was initialized to use the default constructor
/// of the type being lazily initialized, and that type does not have a public, parameterless constructor.
/// </exception>
/// <exception cref="T:System.MemberAccessException">
/// The <see cref="T:System.Threading.Lazy{T}"/> was initialized to use the default constructor
/// of the type being lazily initialized, and permissions to access the constructor were missing.
/// </exception>
/// <exception cref="T:System.InvalidOperationException">
/// The <see cref="T:System.Threading.Lazy{T}"/> was constructed with the <see cref="T:System.Threading.LazyThreadSafetyMode.ExecutionAndPublication"/> or
/// <see cref="T:System.Threading.LazyThreadSafetyMode.None"/> and the initialization function attempted to access <see cref="Value"/> on this instance.
/// </exception>
/// <remarks>
/// If <see cref="IsValueCreated"/> is false, accessing <see cref="Value"/> will force initialization.
/// Please <see cref="System.Threading.LazyThreadSafetyMode"> for more information on how <see cref="T:System.Threading.Lazy{T}"/> will behave if an exception is thrown
/// 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 ;
Contract . Assert ( m_boxed ! = 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 ( ) ;
}
}
/// <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 we lose the ---- to store the single value, again 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 ;
}
/// <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 )
{
2017-08-21 15:34:15 +00:00
// Another thread raced with us and beat us to successfully invoke the factory.
2016-08-03 10:59:49 +00:00
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 ;
}
}
/// <summary>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.</summary>
internal sealed class System_LazyDebugView < T >
{
//The Lazy object being viewed.
private readonly Lazy < T > m_lazy ;
/// <summary>Constructs a new debugger view object for the provided Lazy object.</summary>
/// <param name="lazy">A Lazy object to browse in the debugger.</param>
public System_LazyDebugView ( Lazy < T > lazy )
{
m_lazy = lazy ;
}
/// <summary>Returns whether the Lazy object is initialized or not.</summary>
public bool IsValueCreated
{
get { return m_lazy . IsValueCreated ; }
}
/// <summary>Returns the value of the Lazy object.</summary>
public T Value
{
get
{ return m_lazy . ValueForDebugDisplay ; }
}
/// <summary>Returns the execution mode of the Lazy object</summary>
public LazyThreadSafetyMode Mode
{
get { return m_lazy . Mode ; }
}
/// <summary>Returns the execution mode of the Lazy object</summary>
public bool IsValueFaulted
{
get { return m_lazy . IsValueFaulted ; }
}
}
}