Fix pragma warning restore (dotnet/coreclr#26389)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Threading / LazyInitializer.cs
blobb8b05a77473790341f2e4f82c581a19ccaa5b7ad
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6 //
7 // a set of lightweight static helpers for lazy initialization.
8 //
9 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
11 using System.Diagnostics;
12 using System.Diagnostics.CodeAnalysis;
14 namespace System.Threading
16 /// <summary>
17 /// Provides lazy initialization routines.
18 /// </summary>
19 /// <remarks>
20 /// These routines avoid needing to allocate a dedicated, lazy-initialization instance, instead using
21 /// references to ensure targets have been initialized as they are accessed.
22 /// </remarks>
23 public static class LazyInitializer
25 /// <summary>
26 /// Initializes a target reference type with the type's default constructor if the target has not
27 /// already been initialized.
28 /// </summary>
29 /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
30 /// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not
31 /// already been initialized.</param>
32 /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
33 /// <exception cref="System.MissingMemberException">Type <typeparamref name="T"/> does not have a default
34 /// constructor.</exception>
35 /// <exception cref="System.MemberAccessException">
36 /// Permissions to access the constructor of type <typeparamref name="T"/> were missing.
37 /// </exception>
38 /// <remarks>
39 /// <para>
40 /// This method may only be used on reference types. To ensure initialization of value
41 /// types, see other overloads of EnsureInitialized.
42 /// </para>
43 /// <para>
44 /// This method may be used concurrently by multiple threads to initialize <paramref name="target"/>.
45 /// In the event that multiple threads access this method concurrently, multiple instances of <typeparamref name="T"/>
46 /// may be created, but only one will be stored into <paramref name="target"/>. In such an occurrence, this method will not dispose of the
47 /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine
48 /// if an object was not used and to then dispose of the object appropriately.
49 /// </para>
50 /// </remarks>
51 public static T EnsureInitialized<T>([NotNull] ref T? target) where T : class =>
52 Volatile.Read(ref target) ?? EnsureInitializedCore(ref target);
54 /// <summary>
55 /// Initializes a target reference type with the type's default constructor (slow path)
56 /// </summary>
57 /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
58 /// <param name="target">The variable that need to be initialized</param>
59 /// <returns>The initialized variable</returns>
60 private static T EnsureInitializedCore<T>([NotNull] ref T? target) where T : class
62 try
64 Interlocked.CompareExchange(ref target, Activator.CreateInstance<T>(), null!);
66 catch (MissingMethodException)
68 throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT);
71 Debug.Assert(target != null);
72 return target;
75 /// <summary>
76 /// Initializes a target reference type using the specified function if it has not already been
77 /// initialized.
78 /// </summary>
79 /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
80 /// <param name="target">The reference of type <typeparamref name="T"/> to initialize if it has not
81 /// already been initialized.</param>
82 /// <param name="valueFactory">The <see cref="System.Func{T}"/> invoked to initialize the
83 /// reference.</param>
84 /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
85 /// <exception cref="System.MissingMemberException">Type <typeparamref name="T"/> does not have a
86 /// default constructor.</exception>
87 /// <exception cref="System.InvalidOperationException"><paramref name="valueFactory"/> returned
88 /// null.</exception>
89 /// <remarks>
90 /// <para>
91 /// This method may only be used on reference types, and <paramref name="valueFactory"/> may
92 /// not return a null reference (Nothing in Visual Basic). To ensure initialization of value types or
93 /// to allow null reference types, see other overloads of EnsureInitialized.
94 /// </para>
95 /// <para>
96 /// This method may be used concurrently by multiple threads to initialize <paramref name="target"/>.
97 /// In the event that multiple threads access this method concurrently, multiple instances of <typeparamref name="T"/>
98 /// may be created, but only one will be stored into <paramref name="target"/>. In such an occurrence, this method will not dispose of the
99 /// objects that were not stored. If such objects must be disposed, it is up to the caller to determine
100 /// if an object was not used and to then dispose of the object appropriately.
101 /// </para>
102 /// </remarks>
103 public static T EnsureInitialized<T>([NotNull] ref T? target, Func<T> valueFactory) where T : class =>
104 Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, valueFactory);
106 /// <summary>
107 /// Initialize the target using the given delegate (slow path).
108 /// </summary>
109 /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
110 /// <param name="target">The variable that need to be initialized</param>
111 /// <param name="valueFactory">The delegate that will be executed to initialize the target</param>
112 /// <returns>The initialized variable</returns>
113 private static T EnsureInitializedCore<T>([NotNull] ref T? target, Func<T> valueFactory) where T : class
115 T value = valueFactory();
116 if (value == null)
118 throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation);
121 Interlocked.CompareExchange(ref target, value, null!);
122 Debug.Assert(target != null);
123 return target;
126 /// <summary>
127 /// Initializes a target reference or value type with its default constructor if it has not already
128 /// been initialized.
129 /// </summary>
130 /// <typeparam name="T">The type of the reference to be initialized.</typeparam>
131 /// <param name="target">A reference or value of type <typeparamref name="T"/> to initialize if it
132 /// has not already been initialized.</param>
133 /// <param name="initialized">A reference to a boolean that determines whether the target has already
134 /// been initialized.</param>
135 /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
136 /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
137 /// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
138 public static T EnsureInitialized<T>([AllowNull] ref T target, ref bool initialized, [NotNull] ref object? syncLock)
140 // Fast path.
141 if (Volatile.Read(ref initialized))
143 return target;
146 return EnsureInitializedCore(ref target, ref initialized, ref syncLock);
149 /// <summary>
150 /// Ensure the target is initialized and return the value (slow path). This overload permits nulls
151 /// and also works for value type targets. Uses the type's default constructor to create the value.
152 /// </summary>
153 /// <typeparam name="T">The type of target.</typeparam>
154 /// <param name="target">A reference to the target to be initialized.</param>
155 /// <param name="initialized">A reference to a location tracking whether the target has been initialized.</param>
156 /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
157 /// a new object will be instantiated.
158 /// </param>
159 /// <returns>The initialized object.</returns>
160 private static T EnsureInitializedCore<T>([AllowNull] ref T target, ref bool initialized, [NotNull] ref object? syncLock)
162 // Lazily initialize the lock if necessary and then double check if initialization is still required.
163 lock (EnsureLockInitialized(ref syncLock))
165 if (!Volatile.Read(ref initialized))
169 target = Activator.CreateInstance<T>();
171 catch (MissingMethodException)
173 throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT);
176 Volatile.Write(ref initialized, true);
180 return target;
183 /// <summary>
184 /// Initializes a target reference or value type with a specified function if it has not already been
185 /// initialized.
186 /// </summary>
187 /// <typeparam name="T">The type of the reference to be initialized.</typeparam>
188 /// <param name="target">A reference or value of type <typeparamref name="T"/> to initialize if it
189 /// has not already been initialized.</param>
190 /// <param name="initialized">A reference to a boolean that determines whether the target has already
191 /// been initialized.</param>
192 /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
193 /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
194 /// <param name="valueFactory">The <see cref="System.Func{T}"/> invoked to initialize the
195 /// reference or value.</param>
196 /// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
197 public static T EnsureInitialized<T>([AllowNull] ref T target, ref bool initialized, [NotNull] ref object? syncLock, Func<T> valueFactory)
199 // Fast path.
200 if (Volatile.Read(ref initialized))
202 return target;
205 return EnsureInitializedCore(ref target, ref initialized, ref syncLock, valueFactory);
208 /// <summary>
209 /// Ensure the target is initialized and return the value (slow path). This overload permits nulls
210 /// and also works for value type targets. Uses the supplied function to create the value.
211 /// </summary>
212 /// <typeparam name="T">The type of target.</typeparam>
213 /// <param name="target">A reference to the target to be initialized.</param>
214 /// <param name="initialized">A reference to a location tracking whether the target has been initialized.</param>
215 /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
216 /// a new object will be instantiated.</param>
217 /// <param name="valueFactory">
218 /// The <see cref="System.Func{T}"/> to invoke in order to produce the lazily-initialized value.
219 /// </param>
220 /// <returns>The initialized object.</returns>
221 private static T EnsureInitializedCore<T>([AllowNull] ref T target, ref bool initialized, [NotNull] ref object? syncLock, Func<T> valueFactory)
223 // Lazily initialize the lock if necessary and then double check if initialization is still required.
224 lock (EnsureLockInitialized(ref syncLock))
226 if (!Volatile.Read(ref initialized))
228 target = valueFactory();
229 Volatile.Write(ref initialized, true);
233 return target;
236 /// <summary>
237 /// Initializes a target reference type with a specified function if it has not already been initialized.
238 /// </summary>
239 /// <typeparam name="T">The type of the reference to be initialized. Has to be reference type.</typeparam>
240 /// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not already been initialized.</param>
241 /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
242 /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
243 /// <param name="valueFactory">The <see cref="System.Func{T}"/> invoked to initialize the reference.</param>
244 /// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
245 public static T EnsureInitialized<T>([NotNull] ref T? target, [NotNull] ref object? syncLock, Func<T> valueFactory) where T : class =>
246 Volatile.Read(ref target) ?? EnsureInitializedCore(ref target, ref syncLock, valueFactory);
248 /// <summary>
249 /// Ensure the target is initialized and return the value (slow path). This overload works only for reference type targets.
250 /// Uses the supplied function to create the value.
251 /// </summary>
252 /// <typeparam name="T">The type of target. Has to be reference type.</typeparam>
253 /// <param name="target">A reference to the target to be initialized.</param>
254 /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
255 /// a new object will be instantiated.</param>
256 /// <param name="valueFactory">
257 /// The <see cref="System.Func{T}"/> to invoke in order to produce the lazily-initialized value.
258 /// </param>
259 /// <returns>The initialized object.</returns>
260 private static T EnsureInitializedCore<T>([NotNull] ref T? target, [NotNull] ref object? syncLock, Func<T> valueFactory) where T : class
262 // Lazily initialize the lock if necessary and then double check if initialization is still required.
263 lock (EnsureLockInitialized(ref syncLock))
265 if (Volatile.Read(ref target) == null)
267 Volatile.Write(ref target, valueFactory());
268 if (target == null)
270 throw new InvalidOperationException(SR.Lazy_StaticInit_InvalidOperation);
275 return target!; // TODO-NULLABLE: Compiler can't infer target's non-nullness (https://github.com/dotnet/roslyn/issues/37300)
278 /// <summary>
279 /// Ensure the lock object is initialized.
280 /// </summary>
281 /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null,
282 /// a new object will be instantiated.</param>
283 /// <returns>Initialized lock object.</returns>
284 private static object EnsureLockInitialized([NotNull] ref object? syncLock) =>
285 syncLock ??
286 Interlocked.CompareExchange(ref syncLock, new object(), null) ??
287 syncLock;