1 #pragma warning disable 0420
4 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
11 // <OWNER>[....]</OWNER>
13 // --------------------------------------------------------------------------------------
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
19 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
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;
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
37 // Dummy object used as the value of m_threadSafeObj if in PublicationOnly mode.
38 internal static readonly object PUBLICATION_ONLY_SENTINEL = new object();
42 /// Provides support for lazy initialization.
44 /// <typeparam name="T">Specifies the type of element being lazily initialized.</typeparam>
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.
55 [HostProtection(Synchronization = true, ExternalThreading = true)]
57 [DebuggerTypeProxy(typeof(System_LazyDebugView<>))]
58 [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")]
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
70 internal Boxed(T value)
79 /// Wrapper class to wrap the excpetion thrown by the value factory
81 class LazyInternalExceptionHolder
83 internal ExceptionDispatchInfo m_edi;
84 internal LazyInternalExceptionHolder(Exception ex)
86 m_edi = ExceptionDispatchInfo.Capture(ex);
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
96 Contract.Assert(false, "ALREADY_INVOKED_SENTINEL should never be invoked.");
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;
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
108 private Func<T> m_valueFactory;
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)
114 private object m_threadSafeObj;
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.
122 /// An instance created with this constructor may be used concurrently from multiple threads.
125 : this(LazyThreadSafetyMode.ExecutionAndPublication)
130 /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that uses a
131 /// specified initialization function.
133 /// <param name="valueFactory">
134 /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is
137 /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is a null
138 /// reference (Nothing in Visual Basic).</exception>
140 /// An instance created with this constructor may be used concurrently from multiple threads.
142 public Lazy(Func<T> valueFactory)
143 : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication)
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.
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.
153 public Lazy(bool isThreadSafe) :
154 this(isThreadSafe? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None)
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.
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)
166 m_threadSafeObj = GetObjectFromMode(mode);
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.
174 /// <param name="valueFactory">
175 /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
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.
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)
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.
190 /// <param name="valueFactory">
191 /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
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)
199 if (valueFactory == null)
200 throw new ArgumentNullException("valueFactory");
202 m_threadSafeObj = GetObjectFromMode(mode);
203 m_valueFactory = valueFactory;
207 /// Static helper function that returns an object based on the given mode. it also throws an exception if the mode is invalid
209 private static object GetObjectFromMode(LazyThreadSafetyMode mode)
211 if (mode == LazyThreadSafetyMode.ExecutionAndPublication)
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"));
218 return null; // None mode
221 /// <summary>Forces initialization during serialization.</summary>
222 /// <param name="context">The StreamingContext for the serialization operation.</param>
224 private void OnSerializing(StreamingContext context)
226 // Force initialization
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.
236 public override string ToString()
238 return IsValueCreated ? Value.ToString() : Environment.GetResourceString("Lazy_ToString_ValueNotCreated");
241 /// <summary>Gets the value of the Lazy<T> for debugging display purposes.</summary>
242 internal T ValueForDebugDisplay
250 return ((Boxed)m_boxed).m_value;
255 /// Gets a value indicating whether this instance may be used concurrently from multiple threads.
257 internal LazyThreadSafetyMode Mode
261 if (m_threadSafeObj == null) return LazyThreadSafetyMode.None;
262 if (m_threadSafeObj == (object)LazyHelpers.PUBLICATION_ONLY_SENTINEL) return LazyThreadSafetyMode.PublicationOnly;
263 return LazyThreadSafetyMode.ExecutionAndPublication;
268 /// Gets whether the value creation is faulted or not
270 internal bool IsValueFaulted
272 get { return m_boxed is LazyInternalExceptionHolder; }
275 /// <summary>Gets a value indicating whether the <see cref="T:System.Lazy{T}"/> has been initialized.
277 /// <value>true if the <see cref="T:System.Lazy{T}"/> instance has been initialized;
278 /// otherwise, false.</value>
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.
284 public bool IsValueCreated
288 return m_boxed != null && m_boxed is Boxed;
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.
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.
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.
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.
313 [DebuggerBrowsable(DebuggerBrowsableState.Never)]
319 if (m_boxed != null )
321 // Do a quick check up front for the fast path.
322 boxed = m_boxed as Boxed;
325 return boxed.m_value;
328 LazyInternalExceptionHolder exc = m_boxed as LazyInternalExceptionHolder;
329 Contract.Assert(m_boxed != null);
333 // Fall through to the slow path.
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();
339 return LazyInitValue();
345 /// local helper method to initialize the value
347 /// <returns>The inititialized T value</returns>
348 private T LazyInitValue()
351 LazyThreadSafetyMode mode = Mode;
352 if (mode == LazyThreadSafetyMode.None)
354 boxed = CreateValue();
357 else if (mode == LazyThreadSafetyMode.PublicationOnly)
359 boxed = CreateValue();
361 Interlocked.CompareExchange(ref m_boxed, boxed, null) != null)
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;
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;
377 object threadSafeObj = Volatile.Read(ref m_threadSafeObj);
378 bool lockTaken = false;
381 if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL)
382 Monitor.Enter(threadSafeObj, ref lockTaken);
384 Contract.Assert(m_boxed != null);
388 boxed = CreateValue();
390 Volatile.Write(ref m_threadSafeObj, ALREADY_INVOKED_SENTINEL);
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
394 boxed = m_boxed as Boxed;
395 if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder
397 LazyInternalExceptionHolder exHolder = m_boxed as LazyInternalExceptionHolder;
398 Contract.Assert(exHolder != null);
399 exHolder.m_edi.Throw();
406 Monitor.Exit(threadSafeObj);
409 Contract.Assert(boxed != null);
410 return boxed.m_value;
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()
418 LazyThreadSafetyMode mode = Mode;
419 if (m_valueFactory != null)
423 // check for recursion
424 if (mode != LazyThreadSafetyMode.PublicationOnly && m_valueFactory == ALREADY_INVOKED_SENTINEL)
425 throw new InvalidOperationException(Environment.GetResourceString("Lazy_Value_RecursiveCallsToValue"));
427 Func<T> factory = m_valueFactory;
428 if (mode != LazyThreadSafetyMode.PublicationOnly) // only detect recursion on None and ExecutionAndPublication modes
430 m_valueFactory = ALREADY_INVOKED_SENTINEL;
432 else if (factory == ALREADY_INVOKED_SENTINEL)
434 // Another thread ----d with us and beat us to successfully invoke the factory.
437 boxed = new Boxed(factory());
441 if (mode != LazyThreadSafetyMode.PublicationOnly) // don't cache the exception for PublicationOnly mode
442 m_boxed = new LazyInternalExceptionHolder(ex);
450 boxed = new Boxed((T)Activator.CreateInstance(typeof(T)));
453 catch (System.MissingMethodException)
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);
467 /// <summary>A debugger view of the Lazy<T> to surface additional debugging properties and
468 /// to ensure that the Lazy<T> does not become initialized if it was not already.</summary>
469 internal sealed class System_LazyDebugView<T>
471 //The Lazy object being viewed.
472 private readonly Lazy<T> m_lazy;
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)
481 /// <summary>Returns whether the Lazy object is initialized or not.</summary>
482 public bool IsValueCreated
484 get { return m_lazy.IsValueCreated; }
487 /// <summary>Returns the value of the Lazy object.</summary>
491 { return m_lazy.ValueForDebugDisplay; }
494 /// <summary>Returns the execution mode of the Lazy object</summary>
495 public LazyThreadSafetyMode Mode
497 get { return m_lazy.Mode; }
500 /// <summary>Returns the execution mode of the Lazy object</summary>
501 public bool IsValueFaulted
503 get { return m_lazy.IsValueFaulted; }