Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / LazyInitializer.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
7 //
8 // LazyInitializer.cs
9 //
10 // <OWNER>[....]</OWNER>
11 //
12 // a set of lightweight static helpers for lazy initialization.
13 //
14 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
15
16
17 using System.Security.Permissions;
18 using System.Diagnostics.Contracts;
19 namespace System.Threading
20 {
21
22     /// <summary>
23     /// Specifies how a <see cref="T:System.Threading.Lazy{T}"/> instance should synchronize access among multiple threads.
24     /// </summary>
25     public enum LazyThreadSafetyMode
26     {
27         /// <summary>
28         /// This mode makes no guarantees around the thread-safety of the <see cref="T:System.Threading.Lazy{T}"/> instance.  If used from multiple threads, the behavior of the <see cref="T:System.Threading.Lazy{T}"/> is undefined.
29         /// This mode should be used when a <see cref="T:System.Threading.Lazy{T}"/> is guaranteed to never be initialized from more than one thread simultaneously and high performance is crucial. 
30         /// If valueFactory throws an exception when the <see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on subsequent accesses to Value. Also, if valueFactory recursively
31         /// accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown.
32         /// </summary>
33         None,
34
35         /// <summary>
36         /// When multiple threads attempt to simultaneously initialize a <see cref="T:System.Threading.Lazy{T}"/> instance, this mode allows each thread to execute the
37         /// valueFactory but only the first thread to complete initialization will be allowed to set the final value of the  <see cref="T:System.Threading.Lazy{T}"/>.
38         /// 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
39         /// 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
40         /// 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
41         /// the valueFactory delegate re-executing.  Also, if valueFactory recursively accesses Value on this  <see cref="T:System.Threading.Lazy{T}"/> instance, an exception will NOT be thrown.
42         /// </summary>
43         PublicationOnly,
44
45         /// <summary>
46         /// This mode uses locks to ensure that only a single thread can initialize a <see cref="T:System.Threading.Lazy{T}"/> instance in a thread-safe manner.  In general,
47         /// taken if this mode is used in conjunction with a <see cref="T:System.Threading.Lazy{T}"/> valueFactory delegate that uses locks internally, a deadlock can occur if not
48         /// handled carefully.  If valueFactory throws an exception when the<see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on
49         /// subsequent accesses to Value. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a  <see cref="T:System.InvalidOperationException"/> will be thrown.
50         /// </summary>
51         ExecutionAndPublication
52     }
53     /// <summary>
54     /// Provides lazy initialization routines.
55     /// </summary>
56     /// <remarks>
57     /// These routines avoid needing to allocate a dedicated, lazy-initialization instance, instead using
58     /// references to ensure targets have been initialized as they are accessed.
59     /// </remarks>
60     [HostProtection(Synchronization = true, ExternalThreading = true)]
61     public static class LazyInitializer
62     {
63         /// <summary>
64         /// Initializes a target reference type with the type's default constructor if the target has not
65         /// already been initialized.
66         /// </summary>
67         /// <typeparam name="T">The refence type of the reference to be initialized.</typeparam>
68         /// <param name="target">A reference of type <typeparamref name="T"/> to initialize if it has not
69         /// already been initialized.</param>
70         /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
71         /// <exception cref="T:System.MissingMemberException">Type <typeparamref name="T"/> does not have a default
72         /// constructor.</exception>
73         /// <exception cref="T:System.MemberAccessException">
74         /// Permissions to access the constructor of type <typeparamref name="T"/> were missing.
75         /// </exception>
76         /// <remarks>
77         /// <para>
78         /// This method may only be used on reference types. To ensure initialization of value
79         /// types, see other overloads of EnsureInitialized.
80         /// </para>
81         /// <para>
82         /// This method may be used concurrently by multiple threads to initialize <paramref name="target"/>.  
83         /// In the event that multiple threads access this method concurrently, multiple instances of <typeparamref name="T"/>
84         /// may be created, but only one will be stored into <paramref name="target"/>. In such an occurrence, this method will not dispose of the
85         /// objects that were not stored.  If such objects must be disposed, it is up to the caller to determine 
86         /// if an object was not used and to then dispose of the object appropriately.
87         /// </para>
88         /// </remarks>
89         public static T EnsureInitialized<T>(ref T target) where T : class
90         {
91             // Fast path.
92             if (Volatile.Read<T>(ref target) != null)
93             {
94                 return target;
95             }
96
97             return EnsureInitializedCore<T>(ref target, LazyHelpers<T>.s_activatorFactorySelector);
98         }
99
100         /// <summary>
101         /// Initializes a target reference type using the specified function if it has not already been
102         /// initialized.
103         /// </summary>
104         /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
105         /// <param name="target">The reference of type <typeparamref name="T"/> to initialize if it has not
106         /// already been initialized.</param>
107         /// <param name="valueFactory">The <see cref="T:System.Func{T}"/> invoked to initialize the
108         /// reference.</param>
109         /// <returns>The initialized reference of type <typeparamref name="T"/>.</returns>
110         /// <exception cref="T:System.MissingMemberException">Type <typeparamref name="T"/> does not have a
111         /// default constructor.</exception>
112         /// <exception cref="T:System.InvalidOperationException"><paramref name="valueFactory"/> returned
113         /// null.</exception>
114         /// <remarks>
115         /// <para>
116         /// This method may only be used on reference types, and <paramref name="valueFactory"/> may
117         /// not return a null reference (Nothing in Visual Basic). To ensure initialization of value types or
118         /// to allow null reference types, see other overloads of EnsureInitialized.
119         /// </para>
120         /// <para>
121         /// This method may be used concurrently by multiple threads to initialize <paramref name="target"/>.  
122         /// In the event that multiple threads access this method concurrently, multiple instances of <typeparamref name="T"/>
123         /// may be created, but only one will be stored into <paramref name="target"/>. In such an occurrence, this method will not dispose of the
124         /// objects that were not stored.  If such objects must be disposed, it is up to the caller to determine 
125         /// if an object was not used and to then dispose of the object appropriately.
126         /// </para>
127         /// </remarks>
128         public static T EnsureInitialized<T>(ref T target, Func<T> valueFactory) where T : class
129         {
130             // Fast path.
131             if (Volatile.Read<T>(ref target) != null)
132             {
133                 return target;
134             }
135
136             return EnsureInitializedCore<T>(ref target, valueFactory);
137         }
138
139         /// <summary>
140         /// Initialize the target using the given delegate (slow path).
141         /// </summary>
142         /// <typeparam name="T">The reference type of the reference to be initialized.</typeparam>
143         /// <param name="target">The variable that need to be initialized</param>
144         /// <param name="valueFactory">The delegate that will be executed to initialize the target</param>
145         /// <returns>The initialized variable</returns>
146         private static T EnsureInitializedCore<T>(ref T target, Func<T> valueFactory) where T : class
147         {
148             T value = valueFactory();
149             if (value == null)
150             {
151                 throw new InvalidOperationException(Environment.GetResourceString("Lazy_StaticInit_InvalidOperation"));
152             }
153
154             Interlocked.CompareExchange(ref target, value, null);
155             Contract.Assert(target != null);
156             return target;
157         }
158
159
160         /// <summary>
161         /// Initializes a target reference or value type with its default constructor if it has not already
162         /// been initialized.
163         /// </summary>
164         /// <typeparam name="T">The type of the reference to be initialized.</typeparam>
165         /// <param name="target">A reference or value of type <typeparamref name="T"/> to initialize if it
166         /// has not already been initialized.</param>
167         /// <param name="initialized">A reference to a boolean that determines whether the target has already
168         /// been initialized.</param>
169         /// <param name="syncLock">A reference to an object used as the mutually exclusive lock for initializing
170         /// <paramref name="target"/>. If <paramref name="syncLock"/> is null, a new object will be instantiated.</param>
171         /// <returns>The initialized value of type <typeparamref name="T"/>.</returns>
172         public static T EnsureInitialized<T>(ref T target, ref bool initialized, ref object syncLock)
173         {
174             // Fast path.
175             if (Volatile.Read(ref initialized))
176             {
177                 return target;
178             }
179
180             return EnsureInitializedCore<T>(ref target, ref initialized, ref syncLock, LazyHelpers<T>.s_activatorFactorySelector);
181         }
182
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="T: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>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
198         {
199             // Fast path.
200             if (Volatile.Read(ref initialized))
201             {
202                 return target;
203             }
204
205
206             return EnsureInitializedCore<T>(ref target, ref initialized, ref syncLock, valueFactory);
207         }
208
209         /// <summary>
210         /// Ensure the target is initialized and return the value (slow path). This overload permits nulls
211         /// and also works for value type targets. Uses the supplied function to create the value.
212         /// </summary>
213         /// <typeparam name="T">The type of target.</typeparam>
214         /// <param name="target">A reference to the target to be initialized.</param>
215         /// <param name="initialized">A reference to a location tracking whether the target has been initialized.</param>
216         /// <param name="syncLock">A reference to a location containing a mutual exclusive lock. If <paramref name="syncLock"/> is null, 
217         /// a new object will be instantiated.</param>
218         /// <param name="valueFactory">
219         /// The <see cref="T:System.Func{T}"/> to invoke in order to produce the lazily-initialized value.
220         /// </param>
221         /// <returns>The initialized object.</returns>
222         private static T EnsureInitializedCore<T>(ref T target, ref bool initialized, ref object syncLock, Func<T> valueFactory)
223         {
224             // Lazily initialize the lock if necessary.
225             object slock = syncLock;
226             if (slock == null)
227             {
228                 object newLock = new object();
229                 slock = Interlocked.CompareExchange(ref syncLock, newLock, null);
230                 if (slock == null)
231                 {
232                     slock = newLock;
233                 }
234             }
235
236             // Now double check that initialization is still required.
237             lock (slock)
238             {
239                 if (!Volatile.Read(ref initialized))
240                 {
241                     target = valueFactory();
242                     Volatile.Write(ref initialized, true);
243                 }
244             }
245
246             return target;
247         }
248
249     }
250
251     // Caches the activation selector function to avoid delegate allocations.
252     static class LazyHelpers<T>
253     {
254         internal static Func<T> s_activatorFactorySelector = new Func<T>(ActivatorFactorySelector);
255
256         private static T ActivatorFactorySelector()
257         {
258             try
259             {
260                 return (T)Activator.CreateInstance(typeof(T));
261             }
262             catch (MissingMethodException)
263             {
264                 throw new MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT"));
265             }
266         }
267     }
268 }