Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Threading / Tasks / TaskExceptionHolder.cs / 1305376 / TaskExceptionHolder.cs
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// TaskExceptionHolder.cs
//
// [....]
//
// An abstraction for holding and aggregating exceptions.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Disable the "reference to volatile field not treated as volatile" error.
#pragma warning disable 0420
namespace System.Threading.Tasks
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
///
/// An exception holder manages a list of exceptions for one particular task.
/// It offers the ability to aggregate, but more importantly, also offers intrinsic
/// support for propagating unhandled exceptions that are never observed. It does
/// this by aggregating and throwing if the holder is ever GC'd without the holder's
/// contents ever having been requested (e.g. by a Task.Wait, Task.get_Exception, etc).
///
internal class TaskExceptionHolder
{
private List m_exceptions; // One or more exceptions to be aggregated.
private bool m_isHandled; // Whether the holder was observed.
private Task m_task; // The task to which these exceptions belong.
///
/// Creates a new holder; it will be registered for finalization.
///
/// The task this holder belongs to.
internal TaskExceptionHolder(Task task)
{
Contract.Assert(task != null, "Expected a non-null task.");
EnsureADUnloadCallbackRegistered();
m_exceptions = new List(1);
m_task = task;
}
private static volatile bool s_domainUnloadStarted;
private static volatile EventHandler s_adUnloadEventHandler;
private static void EnsureADUnloadCallbackRegistered()
{
if (s_adUnloadEventHandler == null &&
Interlocked.CompareExchange( ref s_adUnloadEventHandler,
new EventHandler(AppDomainUnloadCallback),
null) == null)
{
AppDomain.CurrentDomain.DomainUnload += s_adUnloadEventHandler;
}
}
private static void AppDomainUnloadCallback(object sender, EventArgs e)
{
s_domainUnloadStarted = true;
}
///
/// A finalizer that repropagates unhandled exceptions.
///
~TaskExceptionHolder()
{
// Raise unhandled exceptions only when we know that neither the process or nor the appdomain is being torn down.
// We need to do this filtering because all TaskExceptionHolders will be finalized during shutdown or unload
// regardles of reachability of the task (i.e. even if the user code was about to observe the task's exception),
// which can otherwise lead to spurious crashes during shutdown.
if (!m_isHandled && !Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload() && !s_domainUnloadStarted)
{
// We don't want to crash the finalizer thread if any ThreadAbortExceptions
// occur in the list or in any nested AggregateExceptions.
// (Don't rethrow ThreadAbortExceptions.)
foreach (Exception exp in m_exceptions)
{
AggregateException aggExp = exp as AggregateException;
if (aggExp != null)
{
AggregateException flattenedAggExp = aggExp.Flatten();
foreach (Exception innerExp in flattenedAggExp.InnerExceptions)
{
if (innerExp is ThreadAbortException)
return;
}
}
else if (exp is ThreadAbortException)
{
return;
}
}
// We will only propagate if this is truly unhandled. The reason this could
// ever occur is somewhat subtle: if a Task's exceptions are observed in some
// other finalizer, and the Task was finalized before the holder, the holder
// will have been marked as handled before even getting here.
// Give users a chance to keep this exception from crashing the process
// First, publish the unobserved exception and allow users to observe it
AggregateException exceptionToThrow = new AggregateException(
Environment.GetResourceString("TaskExceptionHolder_UnhandledException"),
m_exceptions);
UnobservedTaskExceptionEventArgs ueea = new UnobservedTaskExceptionEventArgs(exceptionToThrow);
TaskScheduler.PublishUnobservedTaskException(m_task, ueea);
// Now, if we are still unobserved, throw the exception
if (!ueea.m_observed)
{
throw exceptionToThrow;
}
}
}
///
/// Add an exception to the internal list. This will ensure the holder is
/// in the proper state (handled/unhandled) depending on the list's contents.
///
/// An exception object (either an Exception or an
/// IEnumerable{Exception}) to add to the list.
internal void Add(object exceptionObject)
{
Contract.Assert(exceptionObject != null);
Contract.Assert(m_exceptions != null);
Contract.Assert(exceptionObject is Exception || exceptionObject is IEnumerable,
"TaskExceptionHolder.Add(): Expected Exception or IEnumerable");
Exception exception = exceptionObject as Exception;
if (exception != null) m_exceptions.Add(exception);
else
{
IEnumerable exColl = exceptionObject as IEnumerable;
if (exColl != null) m_exceptions.AddRange(exColl);
else
{
throw new ArgumentException(Environment.GetResourceString("TaskExceptionHolder_UnknownExceptionType"), "exceptionObject");
}
}
// If all of the exceptions are ThreadAbortExceptions and/or
// AppDomainUnloadExceptions, we do not want the finalization
// probe to propagate them, so we consider the holder to be
// handled. If a subsequent exception comes in of a different
// kind, we will reactivate the holder.
for (int i = 0; i < m_exceptions.Count; i++)
{
if (m_exceptions[i].GetType() != typeof(ThreadAbortException) &&
m_exceptions[i].GetType() != typeof(AppDomainUnloadedException))
{
MarkAsUnhandled();
break;
}
else if (i == m_exceptions.Count - 1)
{
MarkAsHandled(false);
}
}
}
///
/// A private helper method that ensures the holder is considered
/// unhandled, i.e. it is registered for finalization.
///
private void MarkAsUnhandled()
{
// If a thread partially observed this thread's exceptions, we
// should revert back to "not handled" so that subsequent exceptions
// must also be seen. Otherwise, some could go missing. We also need
// to reregister for finalization.
if (m_isHandled)
{
GC.ReRegisterForFinalize(this);
m_isHandled = false;
}
}
///
/// A private helper method that ensures the holder is considered
/// handled, i.e. it is not registered for finalization.
///
/// Whether this is called from the finalizer thread.
internal void MarkAsHandled(bool calledFromFinalizer)
{
if (!m_isHandled)
{
if (!calledFromFinalizer)
{
GC.SuppressFinalize(this);
}
m_isHandled = true;
}
}
///
/// Allocates a new aggregate exception and adds the contents of the list to
/// it. By calling this method, the holder assumes exceptions to have been
/// "observed", such that the finalization check will be subsequently skipped.
///
/// Whether this is being called from a finalizer.
/// An extra exception to be included (optionally).
/// The aggregate exception to throw.
internal AggregateException CreateExceptionObject(bool calledFromFinalizer, Exception includeThisException)
{
Contract.Assert(m_exceptions.Count > 0, "Expected at least one exception.");
// Mark as handled and aggregate the exceptions.
MarkAsHandled(calledFromFinalizer);
List exceptions = m_exceptions;
// If the caller wants a specific exception to be included, add it now.
if (includeThisException != null)
{
exceptions = new List(exceptions);
exceptions.Add(includeThisException);
}
// Manufacture the aggregate.
return new AggregateException(exceptions);
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// TaskExceptionHolder.cs
//
// [....]
//
// An abstraction for holding and aggregating exceptions.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Disable the "reference to volatile field not treated as volatile" error.
#pragma warning disable 0420
namespace System.Threading.Tasks
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
///
/// An exception holder manages a list of exceptions for one particular task.
/// It offers the ability to aggregate, but more importantly, also offers intrinsic
/// support for propagating unhandled exceptions that are never observed. It does
/// this by aggregating and throwing if the holder is ever GC'd without the holder's
/// contents ever having been requested (e.g. by a Task.Wait, Task.get_Exception, etc).
///
internal class TaskExceptionHolder
{
private List m_exceptions; // One or more exceptions to be aggregated.
private bool m_isHandled; // Whether the holder was observed.
private Task m_task; // The task to which these exceptions belong.
///
/// Creates a new holder; it will be registered for finalization.
///
/// The task this holder belongs to.
internal TaskExceptionHolder(Task task)
{
Contract.Assert(task != null, "Expected a non-null task.");
EnsureADUnloadCallbackRegistered();
m_exceptions = new List(1);
m_task = task;
}
private static volatile bool s_domainUnloadStarted;
private static volatile EventHandler s_adUnloadEventHandler;
private static void EnsureADUnloadCallbackRegistered()
{
if (s_adUnloadEventHandler == null &&
Interlocked.CompareExchange( ref s_adUnloadEventHandler,
new EventHandler(AppDomainUnloadCallback),
null) == null)
{
AppDomain.CurrentDomain.DomainUnload += s_adUnloadEventHandler;
}
}
private static void AppDomainUnloadCallback(object sender, EventArgs e)
{
s_domainUnloadStarted = true;
}
///
/// A finalizer that repropagates unhandled exceptions.
///
~TaskExceptionHolder()
{
// Raise unhandled exceptions only when we know that neither the process or nor the appdomain is being torn down.
// We need to do this filtering because all TaskExceptionHolders will be finalized during shutdown or unload
// regardles of reachability of the task (i.e. even if the user code was about to observe the task's exception),
// which can otherwise lead to spurious crashes during shutdown.
if (!m_isHandled && !Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload() && !s_domainUnloadStarted)
{
// We don't want to crash the finalizer thread if any ThreadAbortExceptions
// occur in the list or in any nested AggregateExceptions.
// (Don't rethrow ThreadAbortExceptions.)
foreach (Exception exp in m_exceptions)
{
AggregateException aggExp = exp as AggregateException;
if (aggExp != null)
{
AggregateException flattenedAggExp = aggExp.Flatten();
foreach (Exception innerExp in flattenedAggExp.InnerExceptions)
{
if (innerExp is ThreadAbortException)
return;
}
}
else if (exp is ThreadAbortException)
{
return;
}
}
// We will only propagate if this is truly unhandled. The reason this could
// ever occur is somewhat subtle: if a Task's exceptions are observed in some
// other finalizer, and the Task was finalized before the holder, the holder
// will have been marked as handled before even getting here.
// Give users a chance to keep this exception from crashing the process
// First, publish the unobserved exception and allow users to observe it
AggregateException exceptionToThrow = new AggregateException(
Environment.GetResourceString("TaskExceptionHolder_UnhandledException"),
m_exceptions);
UnobservedTaskExceptionEventArgs ueea = new UnobservedTaskExceptionEventArgs(exceptionToThrow);
TaskScheduler.PublishUnobservedTaskException(m_task, ueea);
// Now, if we are still unobserved, throw the exception
if (!ueea.m_observed)
{
throw exceptionToThrow;
}
}
}
///
/// Add an exception to the internal list. This will ensure the holder is
/// in the proper state (handled/unhandled) depending on the list's contents.
///
/// An exception object (either an Exception or an
/// IEnumerable{Exception}) to add to the list.
internal void Add(object exceptionObject)
{
Contract.Assert(exceptionObject != null);
Contract.Assert(m_exceptions != null);
Contract.Assert(exceptionObject is Exception || exceptionObject is IEnumerable,
"TaskExceptionHolder.Add(): Expected Exception or IEnumerable");
Exception exception = exceptionObject as Exception;
if (exception != null) m_exceptions.Add(exception);
else
{
IEnumerable exColl = exceptionObject as IEnumerable;
if (exColl != null) m_exceptions.AddRange(exColl);
else
{
throw new ArgumentException(Environment.GetResourceString("TaskExceptionHolder_UnknownExceptionType"), "exceptionObject");
}
}
// If all of the exceptions are ThreadAbortExceptions and/or
// AppDomainUnloadExceptions, we do not want the finalization
// probe to propagate them, so we consider the holder to be
// handled. If a subsequent exception comes in of a different
// kind, we will reactivate the holder.
for (int i = 0; i < m_exceptions.Count; i++)
{
if (m_exceptions[i].GetType() != typeof(ThreadAbortException) &&
m_exceptions[i].GetType() != typeof(AppDomainUnloadedException))
{
MarkAsUnhandled();
break;
}
else if (i == m_exceptions.Count - 1)
{
MarkAsHandled(false);
}
}
}
///
/// A private helper method that ensures the holder is considered
/// unhandled, i.e. it is registered for finalization.
///
private void MarkAsUnhandled()
{
// If a thread partially observed this thread's exceptions, we
// should revert back to "not handled" so that subsequent exceptions
// must also be seen. Otherwise, some could go missing. We also need
// to reregister for finalization.
if (m_isHandled)
{
GC.ReRegisterForFinalize(this);
m_isHandled = false;
}
}
///
/// A private helper method that ensures the holder is considered
/// handled, i.e. it is not registered for finalization.
///
/// Whether this is called from the finalizer thread.
internal void MarkAsHandled(bool calledFromFinalizer)
{
if (!m_isHandled)
{
if (!calledFromFinalizer)
{
GC.SuppressFinalize(this);
}
m_isHandled = true;
}
}
///
/// Allocates a new aggregate exception and adds the contents of the list to
/// it. By calling this method, the holder assumes exceptions to have been
/// "observed", such that the finalization check will be subsequently skipped.
///
/// Whether this is being called from a finalizer.
/// An extra exception to be included (optionally).
/// The aggregate exception to throw.
internal AggregateException CreateExceptionObject(bool calledFromFinalizer, Exception includeThisException)
{
Contract.Assert(m_exceptions.Count > 0, "Expected at least one exception.");
// Mark as handled and aggregate the exceptions.
MarkAsHandled(calledFromFinalizer);
List exceptions = m_exceptions;
// If the caller wants a specific exception to be included, add it now.
if (includeThisException != null)
{
exceptions = new List(exceptions);
exceptions.Add(includeThisException);
}
// Manufacture the aggregate.
return new AggregateException(exceptions);
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WebSysDescriptionAttribute.cs
- VariableAction.cs
- DispatcherTimer.cs
- SQLGuidStorage.cs
- Brush.cs
- TdsValueSetter.cs
- ListDataHelper.cs
- MetafileHeaderWmf.cs
- X509ScopedServiceCertificateElementCollection.cs
- controlskin.cs
- BinaryReader.cs
- LocalizabilityAttribute.cs
- BuildManager.cs
- DataKeyArray.cs
- XmlnsPrefixAttribute.cs
- Socket.cs
- SingleAnimationBase.cs
- DragDropManager.cs
- Parser.cs
- NetworkInformationException.cs
- PriorityQueue.cs
- DictionaryBase.cs
- _emptywebproxy.cs
- IntellisenseTextBox.designer.cs
- ThicknessKeyFrameCollection.cs
- OutputCacheProfile.cs
- EventSinkHelperWriter.cs
- AnimatedTypeHelpers.cs
- MetadataFile.cs
- UserControlParser.cs
- ModelTreeEnumerator.cs
- ChangeProcessor.cs
- Marshal.cs
- EncodedStreamFactory.cs
- OracleFactory.cs
- WorkflowServiceBehavior.cs
- CqlParserHelpers.cs
- ToolStripProgressBar.cs
- DataGridItemCollection.cs
- PointLightBase.cs
- ipaddressinformationcollection.cs
- LexicalChunk.cs
- JsonCollectionDataContract.cs
- WebPartVerb.cs
- HttpCacheVaryByContentEncodings.cs
- NamedObject.cs
- HttpProfileGroupBase.cs
- UIntPtr.cs
- UserNamePasswordValidator.cs
- Blend.cs
- PropertyEntry.cs
- DependencyObjectProvider.cs
- EndPoint.cs
- ChangeProcessor.cs
- AssertSection.cs
- Compiler.cs
- ImplicitInputBrush.cs
- BitmapMetadataEnumerator.cs
- TreeSet.cs
- HttpResponseInternalWrapper.cs
- SafeNativeMethods.cs
- PhonemeEventArgs.cs
- DirectionalLight.cs
- ArrayConverter.cs
- LinqDataSourceInsertEventArgs.cs
- Accessors.cs
- DataGridViewRowCollection.cs
- ResolveCriteriaApril2005.cs
- RoleGroup.cs
- Queue.cs
- SqlDataAdapter.cs
- ResourceLoader.cs
- AppModelKnownContentFactory.cs
- TreeViewDataItemAutomationPeer.cs
- WebScriptMetadataMessage.cs
- FileStream.cs
- DataGridViewToolTip.cs
- EntityCommandDefinition.cs
- MethodImplAttribute.cs
- SqlWorkflowInstanceStore.cs
- Expr.cs
- UnknownWrapper.cs
- Closure.cs
- DocumentOrderComparer.cs
- SimpleBitVector32.cs
- DeleteWorkflowOwnerCommand.cs
- AuthenticateEventArgs.cs
- ContainerSelectorActiveEvent.cs
- ShaderEffect.cs
- infer.cs
- NamespaceDisplayAutomationPeer.cs
- SimpleWebHandlerParser.cs
- IgnoreFileBuildProvider.cs
- Decorator.cs
- HtmlInputRadioButton.cs
- connectionpool.cs
- PseudoWebRequest.cs
- SqlCaseSimplifier.cs
- FixedHyperLink.cs
- Error.cs