From ffb31b72696727192685e4669df592b5ec9cbd7f Mon Sep 17 00:00:00 2001 From: WinCPP Date: Thu, 2 Mar 2017 23:29:03 +0530 Subject: [PATCH] LazyInitializer.EnsureInitialized overload for reference types that does not take boolean for tracking initialization status of the reference type object. Null check on the object parameter is sufficient to whether or not it has been initialized. --- .../src/System/Threading/LazyInitializer.cs | 90 ++++++++++++------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs b/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs index 5b1d6251187..5853353661e 100644 --- a/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs +++ b/src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs @@ -47,16 +47,8 @@ public static class LazyInitializer /// 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.ActivatorFactorySelectorFunc); - } + public static T EnsureInitialized(ref T target) where T : class => + Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, LazyHelpers.ActivatorFactorySelectorFunc); /// /// Initializes a target reference type using the specified function if it has not already been @@ -86,17 +78,8 @@ public static T EnsureInitialized(ref T target) where T : class /// 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. - // According to the abstract ECMA CLI memory model, this read should be volatile, due to possible reorderings with - // subsequent reads from fields of "target." However, as of today (7/12/12), Volatile.Read is quite slow, and in practice - // this read does *not* need to be volatile, because our memory model preserves ordering of data-dependent reads. - if (target != null) - return target; - - return EnsureInitializedCore(ref target, valueFactory); - } + 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). @@ -168,6 +151,29 @@ public static T EnsureInitialized(ref T target, ref bool initialized, ref obj return EnsureInitializedCore(ref target, ref initialized, ref syncLock, valueFactory); } + /// + /// Ensure the lock object is intialized. + /// + /// 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; + + /// + /// 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 permits nulls /// and also works for value type targets. Uses the supplied function to create the value. @@ -183,25 +189,43 @@ public static T EnsureInitialized(ref T target, ref bool initialized, ref obj /// 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) + // Lazily initialize the lock if necessary and, then double check if initialization is still required. + lock (EnsureLockInitialized(ref syncLock)) { - object newLock = new object(); - slock = Interlocked.CompareExchange(ref syncLock, newLock, null); - if (slock == null) + if (!Volatile.Read(ref initialized)) { - slock = newLock; + target = valueFactory(); + Volatile.Write(ref initialized, true); } } - // Now double check that initialization is still required. - lock (slock) + return target; + } + + /// + /// 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 initialized)) + if (Volatile.Read(ref target) == null) { - target = valueFactory(); - Volatile.Write(ref initialized, true); + Volatile.Write(ref target, valueFactory()); + if (target == null) + { + throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation); + } } }