Skip to content

Commit 36772b5

Browse files
committed
Handle non-critical exceptions
1 parent b0cab0d commit 36772b5

File tree

5 files changed

+190
-4
lines changed

5 files changed

+190
-4
lines changed

BasicSccProvider.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
<Compile Include="Diff\View\DiffMarginControl.xaml.cs">
117117
<DependentUpon>DiffMarginControl.xaml</DependentUpon>
118118
</Compile>
119+
<Compile Include="ExceptionExtensions.cs" />
119120
<Compile Include="GitToolCommands.cs" />
120121
<Compile Include="GitSccOptions.cs" />
121122
<Compile Include="PendingChangesView.xaml.cs">
@@ -142,6 +143,7 @@
142143
<Compile Include="SccProviderService.cs" />
143144
<Compile Include="PendingChangesToolWindow.cs" />
144145
<Compile Include="Schedulers\QueuedTaskScheduler.cs" />
146+
<Compile Include="TaskExtensions.cs" />
145147
<Compile Include="UI\BranchPicker.xaml.cs">
146148
<DependentUpon>BranchPicker.xaml</DependentUpon>
147149
</Compile>

ExceptionExtensions.cs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* The MIT License
2+
*
3+
* Copyright (c) 2013 Sam Harwell, Tunnel Vision Labs, LLC
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining
6+
* a copy of this software and associated documentation files (the
7+
* "Software"), to deal in the Software without restriction, including
8+
* without limitation the rights to use, copy, modify, merge, publish,
9+
* distribute, sublicense, and/or sell copies of the Software, and to
10+
* permit persons to whom the Software is furnished to do so, subject to
11+
* the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
*/
24+
25+
namespace GitScc
26+
{
27+
using System;
28+
using System.Reflection;
29+
30+
public static class ExceptionExtensions
31+
{
32+
/// <summary>
33+
/// Provides an <see cref="Action{Exception}"/> delegate wrapping the
34+
/// <c>InternalPreserveStackTrace</c> method in the Microsoft .NET Framework.
35+
/// </summary>
36+
/// <remarks>
37+
/// This initializer does not consider other frameworks (e.g. Mono), but
38+
/// since this is part of a Visual Studio extension we know that the
39+
/// Microsoft implementation is the one in use.
40+
/// </remarks>
41+
private static readonly Action<Exception> _internalPreserveStackTrace =
42+
(Action<Exception>)Delegate.CreateDelegate(
43+
typeof(Action<Exception>),
44+
typeof(Exception).GetMethod(
45+
"InternalPreserveStackTrace",
46+
BindingFlags.Instance | BindingFlags.NonPublic));
47+
48+
#pragma warning disable 618 // 'System.ExecutionEngineException' is obsolete
49+
/// <summary>
50+
/// Returns <c>true</c> if <paramref name="e"/> is considered a critical
51+
/// exception, i.e. an exception which is likely to corrupt the process state
52+
/// and unless <em>explicitly</em> handled should result in an application crash.
53+
/// </summary>
54+
/// <param name="e">The exception</param>
55+
/// <returns><c>true</c> if <paramref name="e"/> is a critical exception,
56+
/// otherwise <c>false</c>.</returns>
57+
/// <exception cref="ArgumentNullException">if <paramref name="e"/> in <c>null</c>.</exception>
58+
public static bool IsCritical(this Exception e)
59+
{
60+
if (e == null)
61+
throw new ArgumentNullException("e");
62+
63+
if (e is AccessViolationException
64+
|| e is StackOverflowException
65+
|| e is ExecutionEngineException
66+
|| e is OutOfMemoryException
67+
|| e is BadImageFormatException
68+
|| e is AppDomainUnloadedException)
69+
{
70+
return true;
71+
}
72+
73+
return false;
74+
}
75+
#pragma warning restore 618
76+
77+
/// <summary>
78+
/// This method ensures that the stack trace for <paramref name="e"/> is preserved
79+
/// before a <c>rethrow</c> statement.
80+
/// </summary>
81+
/// <remarks>
82+
/// <para>
83+
/// In the Microsoft .NET Framework, if an exception is thrown, caught, and rethrown
84+
/// all within the same method, by default the stack trace for the rethrown exception
85+
/// will indicate that the exception was initially thrown at the location of the
86+
/// <c>rethrow</c> statement. This method instructs the framework to instead
87+
/// preserve the original stack trace when the exception is rethrown.
88+
/// </para>
89+
///
90+
/// <para>
91+
/// This is the default behavior when an exception is caught and rethrown in a
92+
/// <em>different</em> method from which it was originally thrown.
93+
/// </para>
94+
/// </remarks>
95+
/// <param name="e">The exception</param>
96+
/// <exception cref="ArgumentNullException">if <paramref name="e"/> in <c>null</c>.</exception>
97+
public static void PreserveStackTrace(this Exception e)
98+
{
99+
if (e == null)
100+
throw new ArgumentNullException("e");
101+
102+
_internalPreserveStackTrace(e);
103+
}
104+
}
105+
}

PendingChangesView.xaml.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs
121121
}
122122
};
123123

124-
Task.Factory.StartNew(act, CancellationToken.None, TaskCreationOptions.LongRunning, SccProviderService.TaskScheduler);
124+
Task.Factory.StartNew(act, CancellationToken.None, TaskCreationOptions.LongRunning, SccProviderService.TaskScheduler)
125+
.HandleNonCriticalExceptions();
125126
}
126127

127128
private void dataGrid1_MouseDoubleClick(object sender, MouseButtonEventArgs e)
@@ -317,7 +318,9 @@ internal void Refresh(GitFileStatusTracker tracker)
317318
};
318319

319320
Task.Factory.StartNew(getChangedFiles, CancellationToken.None, TaskCreationOptions.LongRunning, SccProviderService.TaskScheduler)
320-
.ContinueWith(continuationAction, TaskContinuationOptions.ExecuteSynchronously);
321+
.HandleNonCriticalExceptions()
322+
.ContinueWith(continuationAction, TaskContinuationOptions.ExecuteSynchronously)
323+
.HandleNonCriticalExceptions();
321324
}
322325

323326
internal void ClearUI()

SccProviderService.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,8 @@ internal void OpenTracker()
679679
private void HandleFileSystemChanged(object sender, FileSystemEventArgs e)
680680
{
681681
Action action = () => ProcessFileSystemChange(e);
682-
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, SccProviderService.TaskScheduler);
682+
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, SccProviderService.TaskScheduler)
683+
.HandleNonCriticalExceptions();
683684
}
684685

685686
private void ProcessFileSystemChange(FileSystemEventArgs e)
@@ -1092,7 +1093,9 @@ public void UpdateNodesGlyphs()
10921093
};
10931094

10941095
Task.Factory.StartNew(openTrackerAction, CancellationToken.None, TaskCreationOptions.LongRunning, SccProviderService.TaskScheduler)
1095-
.ContinueWith(continuationAction, TaskContinuationOptions.ExecuteSynchronously);
1096+
.HandleNonCriticalExceptions()
1097+
.ContinueWith(continuationAction, TaskContinuationOptions.ExecuteSynchronously)
1098+
.HandleNonCriticalExceptions();
10961099
}
10971100
}
10981101

TaskExtensions.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* The MIT License
2+
*
3+
* Copyright (c) 2013 Sam Harwell, Tunnel Vision Labs, LLC
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining
6+
* a copy of this software and associated documentation files (the
7+
* "Software"), to deal in the Software without restriction, including
8+
* without limitation the rights to use, copy, modify, merge, publish,
9+
* distribute, sublicense, and/or sell copies of the Software, and to
10+
* permit persons to whom the Software is furnished to do so, subject to
11+
* the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23+
*/
24+
25+
namespace GitScc
26+
{
27+
using System;
28+
using System.Linq;
29+
using System.Threading.Tasks;
30+
31+
internal static class TaskExtensions
32+
{
33+
/// <summary>
34+
/// This extension method ensures that any non-critical exception thrown during task
35+
/// execution is handled before the task is cleaned up by the garbage collector.
36+
/// </summary>
37+
/// <typeparam name="T">The task type</typeparam>
38+
/// <param name="task">The task</param>
39+
/// <returns>Returns <paramref name="task"/>.</returns>
40+
/// <exception cref="ArgumentNullException">If <paramref name="task"/> is <code>null</code>.</exception>
41+
public static T HandleNonCriticalExceptions<T>(this T task)
42+
where T : Task
43+
{
44+
if (task == null)
45+
throw new ArgumentNullException("task");
46+
47+
task.ContinueWith(HandleNonCriticalExceptionsCore, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
48+
return task;
49+
}
50+
51+
private static void HandleNonCriticalExceptionsCore(Task task)
52+
{
53+
if (task == null)
54+
throw new ArgumentNullException("task");
55+
56+
AggregateException exception = task.Exception;
57+
if (HasCriticalException(exception))
58+
throw exception;
59+
}
60+
61+
private static bool HasCriticalException(Exception exception)
62+
{
63+
if (exception == null)
64+
throw new ArgumentNullException("exception");
65+
66+
AggregateException aggregate = exception as AggregateException;
67+
if (aggregate != null)
68+
return aggregate.InnerExceptions != null && aggregate.InnerExceptions.Any(HasCriticalException);
69+
70+
return exception.IsCritical();
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)