// 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. // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // a set of lightweight static helpers for lazy initialization. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System.Diagnostics; namespace System.Threading { /// /// Provides lazy initialization routines. /// /// /// These routines avoid needing to allocate a dedicated, lazy-initialization instance, instead using /// references to ensure targets have been initialized as they are accessed. /// public static class LazyInitializer { /// /// Initializes a target reference type with the type's default constructor if the target has not /// already been initialized. /// /// The reference type of the reference to be initialized. /// A reference of type to initialize if it has not /// already been initialized. /// The initialized reference of type . /// Type does not have a default /// constructor. /// /// Permissions to access the constructor of type were missing. /// /// /// /// This method may only be used on reference types. To ensure initialization of value /// types, see other overloads of EnsureInitialized. /// /// /// This method may be used concurrently by multiple threads to initialize . /// In the event that multiple threads access this method concurrently, multiple instances of /// may be created, but only one will be stored into . In such an occurrence, this method will not dispose of the /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine /// if an object was not used and to then dispose of the object appropriately. /// /// public static T EnsureInitialized(ref T target) where T : class => Volatile.Read(ref target) ?? EnsureInitializedCore(ref target); /// /// Initializes a target reference type with the type's default constructor (slow path) /// /// The reference type of the reference to be initialized. /// The variable that need to be initialized /// The initialized variable private static T EnsureInitializedCore(ref T target) where T : class { try { Interlocked.CompareExchange(ref target, Activator.CreateInstance(), null); } catch (MissingMethodException) { throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); } Debug.Assert(target != null); return target; } /// /// Initializes a target reference type using the specified function if it has not already been /// initialized. /// /// The reference type of the reference to be initialized. /// The reference of type to initialize if it has not /// already been initialized. /// The invoked to initialize the /// reference. /// The initialized reference of type . /// Type does not have a /// default constructor. /// returned /// null. /// /// /// This method may only be used on reference types, and may /// not return a null reference (Nothing in Visual Basic). To ensure initialization of value types or /// to allow null reference types, see other overloads of EnsureInitialized. /// /// /// This method may be used concurrently by multiple threads to initialize . /// In the event that multiple threads access this method concurrently, multiple instances of /// may be created, but only one will be stored into . In such an occurrence, this method will not dispose of the /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine /// if an object was not used and to then dispose of the object appropriately. /// /// public static T EnsureInitialized(ref T target, Func valueFactory) where T : class => Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, valueFactory); /// /// Initialize the target using the given delegate (slow path). /// /// The reference type of the reference to be initialized. /// The variable that need to be initialized /// The delegate that will be executed to initialize the target /// The initialized variable private static T EnsureInitializedCore(ref T target, Func valueFactory) where T : class { T value = valueFactory(); if (value == null) { throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); } Interlocked.CompareExchange(ref target, value, null); Debug.Assert(target != null); return target; } /// /// Initializes a target reference or value type with its default constructor if it has not already /// been initialized. /// /// The type of the reference to be initialized. /// A reference or value of type to initialize if it /// has not already been initialized. /// A reference to a boolean that determines whether the target has already /// been initialized. /// A reference to an object used as the mutually exclusive lock for initializing /// . If is null, a new object will be instantiated. /// The initialized value of type . public static T EnsureInitialized(ref T target, ref bool initialized, ref object syncLock) { // Fast path. if (Volatile.Read(ref initialized)) { return target; } return EnsureInitializedCore(ref target, ref initialized, ref syncLock); } /// /// Ensure the target is initialized and return the value (slow path). This overload permits nulls /// and also works for value type targets. Uses the type's default constructor to create the value. /// /// The type of target. /// A reference to the target to be initialized. /// A reference to a location tracking whether the target has been initialized. /// A reference to a location containing a mutual exclusive lock. If is null, /// a new object will be instantiated. /// /// The initialized object. private static T EnsureInitializedCore(ref T target, ref bool initialized, ref object syncLock) { // Lazily initialize the lock if necessary and then double check if initialization is still required. lock (EnsureLockInitialized(ref syncLock)) { if (!Volatile.Read(ref initialized)) { try { target = Activator.CreateInstance(); } catch (MissingMethodException) { throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT); } Volatile.Write(ref initialized, true); } } return target; } /// /// Initializes a target reference or value type with a specified function if it has not already been /// initialized. /// /// The type of the reference to be initialized. /// A reference or value of type to initialize if it /// has not already been initialized. /// A reference to a boolean that determines whether the target has already /// been initialized. /// A reference to an object used as the mutually exclusive lock for initializing /// . If is null, a new object will be instantiated. /// The invoked to initialize the /// reference or value. /// The initialized value of type . public static T EnsureInitialized(ref T target, ref bool initialized, ref object syncLock, Func valueFactory) { // Fast path. if (Volatile.Read(ref initialized)) { return target; } return EnsureInitializedCore(ref target, ref initialized, ref syncLock, valueFactory); } /// /// Ensure the target is initialized and return the value (slow path). This overload permits nulls /// and also works for value type targets. Uses the supplied function to create the value. /// /// The type of target. /// A reference to the target to be initialized. /// A reference to a location tracking whether the target has been initialized. /// A reference to a location containing a mutual exclusive lock. If is null, /// a new object will be instantiated. /// /// The to invoke in order to produce the lazily-initialized value. /// /// The initialized object. private static T EnsureInitializedCore(ref T target, ref bool initialized, ref object syncLock, Func valueFactory) { // Lazily initialize the lock if necessary and then double check if initialization is still required. lock (EnsureLockInitialized(ref syncLock)) { if (!Volatile.Read(ref initialized)) { target = valueFactory(); Volatile.Write(ref initialized, true); } } return target; } /// /// Initializes a target reference type with a specified function if it has not already been initialized. /// /// The type of the reference to be initialized. Has to be reference type. /// A reference of type to initialize if it has not already been initialized. /// A reference to an object used as the mutually exclusive lock for initializing /// . If is null, a new object will be instantiated. /// The invoked to initialize the reference. /// The initialized value of type . public static T EnsureInitialized(ref T target, ref object syncLock, Func valueFactory) where T : class => Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, ref syncLock, valueFactory); /// /// Ensure the target is initialized and return the value (slow path). This overload works only for reference type targets. /// Uses the supplied function to create the value. /// /// The type of target. Has to be reference type. /// A reference to the target to be initialized. /// A reference to a location containing a mutual exclusive lock. If is null, /// a new object will be instantiated. /// /// The to invoke in order to produce the lazily-initialized value. /// /// The initialized object. private static T EnsureInitializedCore(ref T target, ref object syncLock, Func valueFactory) where T : class { // Lazily initialize the lock if necessary and then double check if initialization is still required. lock (EnsureLockInitialized(ref syncLock)) { if (Volatile.Read(ref target) == null) { Volatile.Write(ref target, valueFactory()); if (target == null) { throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); } } } return target; } /// /// Ensure the lock object is initialized. /// /// A reference to a location containing a mutual exclusive lock. If is null, /// a new object will be instantiated. /// Initialized lock object. private static object EnsureLockInitialized(ref object syncLock) => syncLock ?? Interlocked.CompareExchange(ref syncLock, new object(), null) ?? syncLock; } }