Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 57 additions & 33 deletions src/System.Private.CoreLib/src/System/Threading/LazyInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,8 @@ public static class LazyInitializer
/// if an object was not used and to then dispose of the object appropriately.
/// </para>
/// </remarks>
public static T EnsureInitialized<T>(ref T target) where T : class
{
// Fast path.
if (Volatile.Read<T>(ref target) != null)
{
return target;
}

return EnsureInitializedCore<T>(ref target, LazyHelpers<T>.ActivatorFactorySelectorFunc);
}
public static T EnsureInitialized<T>(ref T target) where T : class =>
Volatile.Read<T>(ref target) ?? EnsureInitializedCore<T>(ref target, LazyHelpers<T>.ActivatorFactorySelectorFunc);

/// <summary>
/// Initializes a target reference type using the specified function if it has not already been
Expand Down Expand Up @@ -86,17 +78,8 @@ public static T EnsureInitialized<T>(ref T target) where T : class
/// if an object was not used and to then dispose of the object appropriately.
/// </para>
/// </remarks>
public static T EnsureInitialized<T>(ref T target, Func<T> 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<T> 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<T>(ref target, valueFactory);
}
public static T EnsureInitialized<T>(ref T target, Func<T> valueFactory) where T : class =>
Volatile.Read(ref target) ?? EnsureInitializedCore<T>(ref target, valueFactory);

/// <summary>
/// Initialize the target using the given delegate (slow path).
Expand Down Expand Up @@ -168,6 +151,29 @@ public static T EnsureInitialized<T>(ref T target, ref bool initialized, ref obj
return EnsureInitializedCore<T>(ref target, ref initialized, ref syncLock, valueFactory);
}

/// <summary>
/// Ensure the lock object is intialized.
/// </summary>
/// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
/// a new object will be instantiated.</param>
/// <returns>Initialized lock object.</returns>
private static object EnsureLockInitialized(ref object syncLock) =>
syncLock ??
Interlocked.CompareExchange(ref syncLock, new object(), null) ??
syncLock;

/// <summary>
/// Initializes a target reference type with a specified function if it has not already been initialized.
/// </summary>
/// <typeparam name="T">The type of the reference to be initialized. Has to be reference type.</typeparam>
/// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not already been initialized.</param>
/// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
/// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
/// <param name="valueFactory">The <see cref="T:System.Func{T}"/> invoked to initialize the reference.</param>
/// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
public static T EnsureInitialized<T>(ref T target, ref object syncLock, Func<T> valueFactory) where T : class =>
Volatile.Read(ref target) ?? EnsureInitializedCore<T>(ref target, ref syncLock, valueFactory);

/// <summary>
/// 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.
Expand All @@ -183,25 +189,43 @@ public static T EnsureInitialized<T>(ref T target, ref bool initialized, ref obj
/// <returns>The initialized object.</returns>
private static T EnsureInitializedCore<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> 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;
}

/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T">The type of target. Has to be reference type.</typeparam>
/// <param name="target">A reference to the target to be initialized.</param>
/// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
/// a new object will be instantiated.</param>
/// <param name="valueFactory">
/// The <see cref="T:System.Func{T}"/> to invoke in order to produce the lazily-initialized value.
/// </param>
/// <returns>The initialized object.</returns>
private static T EnsureInitializedCore<T>(ref T target, ref object syncLock, Func<T> 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);
}
}
}

Expand Down