Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / Lazy.cs
1 #pragma warning disable 0420
2 // ==++==
3 //
4 //   Copyright (c) Microsoft Corporation.  All rights reserved.
5 // 
6 // ==--==
7 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
8 //
9 // Lazy.cs
10 //
11 // <OWNER>[....]</OWNER>
12 //
13 // --------------------------------------------------------------------------------------
14 //
15 // A class that provides a simple, lightweight implementation of lazy initialization, 
16 // obviating the need for a developer to implement a custom, thread-safe lazy initialization 
17 // solution.
18 //
19 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
20
21 using System.Runtime;
22 using System.Runtime.InteropServices;
23 using System.Security;
24 using System.Security.Permissions;
25 using System.Diagnostics;
26 using System.Runtime.Serialization;
27 using System.Threading;
28 using System.Diagnostics.Contracts;
29 using System.Runtime.ExceptionServices;
30
31 namespace System
32 {
33     // Lazy<T> is generic, but not all of its state needs to be generic.  Avoid creating duplicate
34     // objects per instantiation by putting them here.
35     internal static class LazyHelpers
36     {
37         // Dummy object used as the value of m_threadSafeObj if in PublicationOnly mode.
38         internal static readonly object PUBLICATION_ONLY_SENTINEL = new object();
39     }
40
41     /// <summary>
42     /// Provides support for lazy initialization.
43     /// </summary>
44     /// <typeparam name="T">Specifies the type of element being lazily initialized.</typeparam>
45     /// <remarks>
46     /// <para>
47     /// By default, all public and protected members of <see cref="Lazy{T}"/> are thread-safe and may be used
48     /// concurrently from multiple threads.  These thread-safety guarantees may be removed optionally and per instance
49     /// using parameters to the type's constructors.
50     /// </para>
51     /// </remarks>
52     [Serializable]
53     [ComVisible(false)]
54 #if !FEATURE_CORECLR
55     [HostProtection(Synchronization = true, ExternalThreading = true)]
56 #endif
57     [DebuggerTypeProxy(typeof(System_LazyDebugView<>))]
58     [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")]
59     public class Lazy<T>
60     {
61
62         #region Inner classes
63         /// <summary>
64         /// wrapper class to box the initialized value, this is mainly created to avoid boxing/unboxing the value each time the value is called in case T is 
65         /// a value type
66         /// </summary>
67         [Serializable]
68         class Boxed
69         {
70             internal Boxed(T value)
71             {
72                 m_value = value;
73             }
74             internal T m_value;
75         }
76
77
78         /// <summary>
79         /// Wrapper class to wrap the excpetion thrown by the value factory
80         /// </summary>
81         class LazyInternalExceptionHolder
82         {
83             internal ExceptionDispatchInfo m_edi;
84             internal LazyInternalExceptionHolder(Exception ex)
85             {
86                 m_edi = ExceptionDispatchInfo.Capture(ex);
87             }
88         }
89         #endregion
90
91         // A dummy delegate used as a  :
92         // 1- Flag to avoid recursive call to Value in None and ExecutionAndPublication modes in m_valueFactory
93         // 2- Flag to m_threadSafeObj if ExecutionAndPublication mode and the value is known to be initialized
94         static readonly Func<T> ALREADY_INVOKED_SENTINEL = delegate 
95         {
96             Contract.Assert(false, "ALREADY_INVOKED_SENTINEL should never be invoked.");
97             return default(T);
98         };
99
100         //null --> value is not created
101         //m_value is Boxed --> the value is created, and m_value holds the value
102         //m_value is LazyExceptionHolder --> it holds an exception
103         private object m_boxed;
104
105         // The factory delegate that returns the value.
106         // In None and ExecutionAndPublication modes, this will be set to ALREADY_INVOKED_SENTINEL as a flag to avoid recursive calls
107         [NonSerialized]
108         private Func<T> m_valueFactory;
109
110         // null if it is not thread safe mode
111         // LazyHelpers.PUBLICATION_ONLY_SENTINEL if PublicationOnly mode
112         // object if ExecutionAndPublication mode (may be ALREADY_INVOKED_SENTINEL if the value is already initialized)
113         [NonSerialized]
114         private object m_threadSafeObj;
115
116
117         /// <summary>
118         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that 
119         /// uses <typeparamref name="T"/>'s default constructor for lazy initialization.
120         /// </summary>
121         /// <remarks>
122         /// An instance created with this constructor may be used concurrently from multiple threads.
123         /// </remarks>
124         public Lazy()
125             : this(LazyThreadSafetyMode.ExecutionAndPublication)
126         {
127         }
128
129         /// <summary>
130         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that uses a
131         /// specified initialization function.
132         /// </summary>
133         /// <param name="valueFactory">
134         /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is
135         /// needed.
136         /// </param>
137         /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is a null
138         /// reference (Nothing in Visual Basic).</exception>
139         /// <remarks>
140         /// An instance created with this constructor may be used concurrently from multiple threads.
141         /// </remarks>
142         public Lazy(Func<T> valueFactory)
143             : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)
144         {
145         }
146
147         /// <summary>
148         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
149         /// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
150         /// </summary>
151         /// <param name="isThreadSafe">true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time.
152         /// </param>
153         public Lazy(bool isThreadSafe) : 
154             this(isThreadSafe? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
155         {
156         }
157
158         /// <summary>
159         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
160         /// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
161         /// </summary>
162         /// <param name="mode">The lazy thread-safety mode mode</param>
163         /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid valuee</exception>
164         public Lazy(LazyThreadSafetyMode mode)
165         {
166             m_threadSafeObj = GetObjectFromMode(mode);
167         }
168
169
170         /// <summary>
171         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class
172         /// that uses a specified initialization function and a specified thread-safety mode.
173         /// </summary>
174         /// <param name="valueFactory">
175         /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
176         /// </param>
177         /// <param name="isThreadSafe">true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time.
178         /// </param>
179         /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is
180         /// a null reference (Nothing in Visual Basic).</exception>
181         public Lazy(Func<T> valueFactory, bool isThreadSafe)
182             : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
183         {
184         }
185
186         /// <summary>
187         /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class
188         /// that uses a specified initialization function and a specified thread-safety mode.
189         /// </summary>
190         /// <param name="valueFactory">
191         /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
192         /// </param>
193         /// <param name="mode">The lazy thread-safety mode.</param>
194         /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is
195         /// a null reference (Nothing in Visual Basic).</exception>
196         /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid value.</exception>
197         public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode)
198         {
199             if (valueFactory == null)
200                 throw new ArgumentNullException("valueFactory");
201
202             m_threadSafeObj = GetObjectFromMode(mode);
203             m_valueFactory = valueFactory;
204         }
205
206         /// <summary>
207         /// Static helper function that returns an object based on the given mode. it also throws an exception if the mode is invalid
208         /// </summary>
209         private static object GetObjectFromMode(LazyThreadSafetyMode mode)
210         {
211             if (mode == LazyThreadSafetyMode.ExecutionAndPublication)
212                 return new object();
213             else if (mode == LazyThreadSafetyMode.PublicationOnly)
214                 return LazyHelpers.PUBLICATION_ONLY_SENTINEL;
215             else if (mode != LazyThreadSafetyMode.None)
216                 throw new ArgumentOutOfRangeException("mode", Environment.GetResourceString("Lazy_ctor_ModeInvalid"));
217             
218             return null; // None mode
219         }
220
221         /// <summary>Forces initialization during serialization.</summary>
222         /// <param name="context">The StreamingContext for the serialization operation.</param>
223         [OnSerializing]
224         private void OnSerializing(StreamingContext context)
225         {
226             // Force initialization
227             T dummy = Value;
228         }
229
230         /// <summary>Creates and returns a string representation of this instance.</summary>
231         /// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see
232         /// cref="Value"/>.</returns>
233         /// <exception cref="T:System.NullReferenceException">
234         /// The <see cref="Value"/> is null.
235         /// </exception>
236         public override string ToString()
237         {
238             return IsValueCreated ? Value.ToString() : Environment.GetResourceString("Lazy_ToString_ValueNotCreated");
239         }
240
241         /// <summary>Gets the value of the Lazy&lt;T&gt; for debugging display purposes.</summary>
242         internal T ValueForDebugDisplay
243         {
244             get
245             {
246                 if (!IsValueCreated)
247                 {
248                     return default(T);
249                 }
250                 return ((Boxed)m_boxed).m_value;
251             }
252         }
253
254         /// <summary>
255         /// Gets a value indicating whether this instance may be used concurrently from multiple threads.
256         /// </summary>
257         internal LazyThreadSafetyMode Mode
258         {
259             get
260             {
261                 if (m_threadSafeObj == null) return LazyThreadSafetyMode.None;
262                 if (m_threadSafeObj == (object)LazyHelpers.PUBLICATION_ONLY_SENTINEL) return LazyThreadSafetyMode.PublicationOnly;
263                 return LazyThreadSafetyMode.ExecutionAndPublication;
264             }
265         }
266
267         /// <summary>
268         /// Gets whether the value creation is faulted or not
269         /// </summary>
270         internal bool IsValueFaulted
271         {
272             get { return m_boxed is LazyInternalExceptionHolder; }
273         }
274
275         /// <summary>Gets a value indicating whether the <see cref="T:System.Lazy{T}"/> has been initialized.
276         /// </summary>
277         /// <value>true if the <see cref="T:System.Lazy{T}"/> instance has been initialized;
278         /// otherwise, false.</value>
279         /// <remarks>
280         /// The initialization of a <see cref="T:System.Lazy{T}"/> instance may result in either
281         /// a value being produced or an exception being thrown.  If an exception goes unhandled during initialization, 
282         /// <see cref="IsValueCreated"/> will return false.
283         /// </remarks>
284         public bool IsValueCreated
285         {
286             get
287             {
288                 return m_boxed != null && m_boxed is Boxed;
289             }
290         }
291
292         /// <summary>Gets the lazily initialized value of the current <see
293         /// cref="T:System.Threading.Lazy{T}"/>.</summary>
294         /// <value>The lazily initialized value of the current <see
295         /// cref="T:System.Threading.Lazy{T}"/>.</value>
296         /// <exception cref="T:System.MissingMemberException">
297         /// The <see cref="T:System.Threading.Lazy{T}"/> was initialized to use the default constructor 
298         /// of the type being lazily initialized, and that type does not have a public, parameterless constructor.
299         /// </exception>
300         /// <exception cref="T:System.MemberAccessException">
301         /// The <see cref="T:System.Threading.Lazy{T}"/> was initialized to use the default constructor 
302         /// of the type being lazily initialized, and permissions to access the constructor were missing.
303         /// </exception>
304         /// <exception cref="T:System.InvalidOperationException">
305         /// The <see cref="T:System.Threading.Lazy{T}"/> was constructed with the <see cref="T:System.Threading.LazyThreadSafetyMode.ExecutionAndPublication"/> or
306         /// <see cref="T:System.Threading.LazyThreadSafetyMode.None"/>  and the initialization function attempted to access <see cref="Value"/> on this instance.
307         /// </exception>
308         /// <remarks>
309         /// If <see cref="IsValueCreated"/> is false, accessing <see cref="Value"/> will force initialization.
310         /// Please <see cref="System.Threading.LazyThreadSafetyMode"> for more information on how <see cref="T:System.Threading.Lazy{T}"/> will behave if an exception is thrown
311         /// from initialization delegate.
312         /// </remarks>
313         [DebuggerBrowsable(DebuggerBrowsableState.Never)]
314         public T Value
315         {
316             get
317             {
318                 Boxed boxed = null;
319                 if (m_boxed != null )
320                 {
321                     // Do a quick check up front for the fast path.
322                     boxed = m_boxed as Boxed;
323                     if (boxed != null)
324                     {
325                         return boxed.m_value;
326                     }
327
328                     LazyInternalExceptionHolder exc = m_boxed as LazyInternalExceptionHolder;
329                     Contract.Assert(m_boxed != null);
330                     exc.m_edi.Throw();
331                 }
332
333                 // Fall through to the slow path.
334 #if !FEATURE_CORECLR
335                 // We call NOCTD to abort attempts by the debugger to funceval this property (e.g. on mouseover)
336                 //   (the debugger proxy is the correct way to look at state/value of this object)
337                 Debugger.NotifyOfCrossThreadDependency(); 
338 #endif
339                 return LazyInitValue();
340                
341             }
342         }
343
344         /// <summary>
345         /// local helper method to initialize the value 
346         /// </summary>
347         /// <returns>The inititialized T value</returns>
348         private T LazyInitValue()
349         {
350             Boxed boxed = null;
351             LazyThreadSafetyMode mode = Mode;
352             if (mode == LazyThreadSafetyMode.None)
353             {
354                 boxed = CreateValue();
355                 m_boxed = boxed;
356             }
357             else if (mode == LazyThreadSafetyMode.PublicationOnly)
358             {
359                 boxed = CreateValue();
360                 if (boxed == null ||
361                     Interlocked.CompareExchange(ref m_boxed, boxed, null) != null)
362                 {
363                     // If CreateValue returns null, it means another thread successfully invoked the value factory
364                     // and stored the result, so we should just take what was stored.  If CreateValue returns non-null
365                     // but we lose the ---- to store the single value, again we should just take what was stored.
366                     boxed = (Boxed)m_boxed;
367                 }
368                 else
369                 {
370                     // We successfully created and stored the value.  At this point, the value factory delegate is
371                     // no longer needed, and we don't want to hold onto its resources.
372                     m_valueFactory = ALREADY_INVOKED_SENTINEL;
373                 }
374             }
375             else
376             {
377                 object threadSafeObj = Volatile.Read(ref m_threadSafeObj);
378                 bool lockTaken = false;
379                 try
380                 {
381                     if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL)
382                         Monitor.Enter(threadSafeObj, ref lockTaken);
383                     else
384                         Contract.Assert(m_boxed != null);
385
386                     if (m_boxed == null)
387                     {
388                         boxed = CreateValue();
389                         m_boxed = boxed;
390                         Volatile.Write(ref m_threadSafeObj, ALREADY_INVOKED_SENTINEL);
391                     }
392                     else // got the lock but the value is not null anymore, check if it is created by another thread or faulted and throw if so
393                     {
394                         boxed = m_boxed as Boxed;
395                         if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder
396                         {
397                             LazyInternalExceptionHolder exHolder = m_boxed as LazyInternalExceptionHolder;
398                             Contract.Assert(exHolder != null);
399                             exHolder.m_edi.Throw();
400                         }
401                     }
402                 }
403                 finally
404                 {
405                     if (lockTaken)
406                         Monitor.Exit(threadSafeObj);
407                 }
408             }
409             Contract.Assert(boxed != null);
410             return boxed.m_value;
411         }
412
413         /// <summary>Creates an instance of T using m_valueFactory in case its not null or use reflection to create a new T()</summary>
414         /// <returns>An instance of Boxed.</returns>
415         private Boxed CreateValue()
416         {
417             Boxed boxed = null;
418             LazyThreadSafetyMode mode = Mode;
419             if (m_valueFactory != null)
420             {
421                 try
422                 {
423                     // check for recursion
424                     if (mode != LazyThreadSafetyMode.PublicationOnly && m_valueFactory == ALREADY_INVOKED_SENTINEL)
425                         throw new InvalidOperationException(Environment.GetResourceString("Lazy_Value_RecursiveCallsToValue"));
426
427                     Func<T> factory = m_valueFactory;
428                     if (mode != LazyThreadSafetyMode.PublicationOnly) // only detect recursion on None and ExecutionAndPublication modes
429                     {
430                         m_valueFactory = ALREADY_INVOKED_SENTINEL;
431                     }
432                     else if (factory == ALREADY_INVOKED_SENTINEL)
433                     {
434                         // Another thread ----d with us and beat us to successfully invoke the factory.
435                         return null;
436                     }
437                     boxed = new Boxed(factory());
438                 }
439                 catch (Exception ex)
440                 {
441                     if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode
442                         m_boxed = new LazyInternalExceptionHolder(ex);
443                     throw;
444                 }
445             }
446             else
447             {
448                 try
449                 {
450                     boxed = new Boxed((T)Activator.CreateInstance(typeof(T)));
451
452                 }
453                 catch (System.MissingMethodException)
454                 {
455                     Exception ex = new System.MissingMemberException(Environment.GetResourceString("Lazy_CreateValue_NoParameterlessCtorForT"));
456                     if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode
457                         m_boxed = new LazyInternalExceptionHolder(ex);
458                     throw ex;
459                 }
460             }
461
462             return boxed;
463         }
464
465     }
466
467     /// <summary>A debugger view of the Lazy&lt;T&gt; to surface additional debugging properties and 
468     /// to ensure that the Lazy&lt;T&gt; does not become initialized if it was not already.</summary>
469     internal sealed class System_LazyDebugView<T>
470     {
471         //The Lazy object being viewed.
472         private readonly Lazy<T> m_lazy;
473
474         /// <summary>Constructs a new debugger view object for the provided Lazy object.</summary>
475         /// <param name="lazy">A Lazy object to browse in the debugger.</param>
476         public System_LazyDebugView(Lazy<T> lazy)
477         {
478             m_lazy = lazy;
479         }
480
481         /// <summary>Returns whether the Lazy object is initialized or not.</summary>
482         public bool IsValueCreated
483         {
484             get { return m_lazy.IsValueCreated; }
485         }
486
487         /// <summary>Returns the value of the Lazy object.</summary>
488         public T Value
489         {
490             get
491             { return m_lazy.ValueForDebugDisplay; }
492         }
493
494         /// <summary>Returns the execution mode of the Lazy object</summary>
495         public LazyThreadSafetyMode Mode
496         {
497             get { return m_lazy.Mode; }
498         }
499
500         /// <summary>Returns the execution mode of the Lazy object</summary>
501         public bool IsValueFaulted
502         {
503             get { return m_lazy.IsValueFaulted; }
504         }
505
506     }
507 }