// 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.Security.Permissions; using System.Diagnostics; using System.Diagnostics.Contracts; namespace System.Threading { /// /// Specifies how a instance should synchronize access among multiple threads. /// public enum LazyThreadSafetyMode { /// /// This mode makes no guarantees around the thread-safety of the instance. If used from multiple threads, the behavior of the is undefined. /// This mode should be used when a is guaranteed to never be initialized from more than one thread simultaneously and high performance is crucial. /// If valueFactory throws an exception when the is initialized, the exception will be cached and returned on subsequent accesses to Value. Also, if valueFactory recursively /// accesses Value on this instance, a will be thrown. /// None, /// /// When multiple threads attempt to simultaneously initialize a instance, this mode allows each thread to execute the /// valueFactory but only the first thread to complete initialization will be allowed to set the final value of the . /// Once initialized successfully, any future calls to Value will return the cached result. If valueFactory throws an exception on any thread, that exception will be /// propagated out of Value. If any thread executes valueFactory without throwing an exception and, therefore, successfully sets the value, that value will be returned on /// subsequent accesses to Value from any thread. If no thread succeeds in setting the value, IsValueCreated will remain false and subsequent accesses to Value will result in /// the valueFactory delegate re-executing. Also, if valueFactory recursively accesses Value on this instance, an exception will NOT be thrown. /// PublicationOnly, /// /// This mode uses locks to ensure that only a single thread can initialize a instance in a thread-safe manner. In general, /// taken if this mode is used in conjunction with a valueFactory delegate that uses locks internally, a deadlock can occur if not /// handled carefully. If valueFactory throws an exception when the is initialized, the exception will be cached and returned on /// subsequent accesses to Value. Also, if valueFactory recursively accesses Value on this instance, a will be thrown. /// ExecutionAndPublication } /// /// 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 refence 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 { // Fast path. if (Volatile.Read(ref target) != null) { return target; } return EnsureInitializedCore(ref target, LazyHelpers.s_activatorFactorySelector); } /// /// 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 { // Fast path. if (Volatile.Read(ref target) != null) { return target; } return 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(Environment.GetResourceString("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, LazyHelpers.s_activatorFactorySelector); } /// /// 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. object slock = syncLock; if (slock == null) { object newLock = new object(); slock = Interlocked.CompareExchange(ref syncLock, newLock, null); if (slock == null) { slock = newLock; } } // Now double check that initialization is still required. lock (slock) { if (!Volatile.Read(ref initialized)) { target = valueFactory(); Volatile.Write(ref initialized, true); } } return target; } } // Caches the activation selector function to avoid delegate allocations. static class LazyHelpers { internal static Func s_activatorFactorySelector = new Func(ActivatorFactorySelector); private static T ActivatorFactorySelector() { try { return (T)Activator.CreateInstance(typeof(T)); } catch (MissingMethodException) { throw new MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT")); } } } }