Skip to content

Commit 02b60fa

Browse files
committed
Merge branch 'develop'
2 parents 924c487 + b427ec3 commit 02b60fa

9 files changed

+208
-63
lines changed

.github/workflows/build.yaml

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
#-----------------------------------------------------------------------
1212
# Checkout
1313

14-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v3
1515
with:
1616
fetch-depth: 0
1717
# lfs: true
@@ -29,26 +29,16 @@ jobs:
2929
#-----------------------------------------------------------------------
3030
# Setup environments
3131

32-
- name: Setup .NET Core 2.2
33-
uses: actions/setup-dotnet@v1
32+
- name: Setup .NET SDKs
33+
uses: actions/setup-dotnet@v4
3434
with:
35-
dotnet-version: 2.2.*
36-
- name: Setup .NET Core 3.1
37-
uses: actions/setup-dotnet@v1
38-
with:
39-
dotnet-version: 3.1.*
40-
- name: Setup .NET 5
41-
uses: actions/setup-dotnet@v1
42-
with:
43-
dotnet-version: 5.0.*
44-
- name: Setup .NET 6
45-
uses: actions/setup-dotnet@v1
46-
with:
47-
dotnet-version: 6.0.*
48-
- name: Setup .NET 7
49-
uses: actions/setup-dotnet@v1
50-
with:
51-
dotnet-version: 7.0.*
35+
dotnet-version: |
36+
2.2.x
37+
3.1.x
38+
5.0.x
39+
6.0.x
40+
7.0.x
41+
8.0.x
5242
5343
#- name: Setup NuGet package reference
5444
# run: |

Directory.Build.props

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<DebugSymbols>true</DebugSymbols>
99
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1010
<CheckEolTargetFramework>false</CheckEolTargetFramework>
11+
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
1112
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1213
<PublishRepositoryUrl>true</PublishRepositoryUrl>
1314
<RepositoryType>git</RepositoryType>
@@ -31,7 +32,7 @@
3132
<PackageTags>synchronization;context;threading;apartment;affinity</PackageTags>
3233
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
3334
<IsPackable>false</IsPackable>
34-
<NoWarn>$(NoWarn);NU1605;NU1701;NU1803</NoWarn>
35+
<NoWarn>$(NoWarn);NU1605;NU1701;NU1803;NU1902;NU1903</NoWarn>
3536
</PropertyGroup>
3637

3738
<PropertyGroup Condition="'$(Configuration)' != 'Release'">
@@ -46,10 +47,12 @@
4647
<Deterministic>true</Deterministic>
4748
<EmbedUntrackedSources>true</EmbedUntrackedSources>
4849
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
50+
<RepoRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\'))</RepoRoot>
51+
<PathMap>$(RepoRoot)=.</PathMap>
4952
</PropertyGroup>
5053

5154
<ItemGroup>
52-
<PackageReference Include="RelaxVersioner" Version="2.14.0" PrivateAssets="All" />
55+
<PackageReference Include="RelaxVersioner" Version="3.4.0" PrivateAssets="All" />
5356
</ItemGroup>
5457

5558
<ItemGroup Condition="'$(Configuration)' == 'Release'">

Lepracaun.Tests/InvokingTest.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ public void RunExceptionTest1()
9696

9797
Exception? ex = null;
9898
app.UnhandledException += (s, e) =>
99+
{
99100
ex = e.Exception;
101+
e.Handled = true;
102+
};
100103

101104
app.Run(() =>
102105
{
@@ -117,7 +120,10 @@ public void RunExceptionTest2()
117120

118121
Exception? ex = null;
119122
app.UnhandledException += (s, e) =>
123+
{
120124
ex = e.Exception;
125+
e.Handled = true;
126+
};
121127

122128
app.Run(async () =>
123129
{
@@ -137,7 +143,10 @@ public void RunExceptionTest3()
137143

138144
Exception? ex = null;
139145
app.UnhandledException += (s, e) =>
146+
{
140147
ex = e.Exception;
148+
e.Handled = true;
149+
};
141150

142151
app.Run(async () =>
143152
{
@@ -153,4 +162,76 @@ static async Task Child()
153162

154163
IsTrue(ex is ApplicationException aex && aex.Message == "ABC");
155164
}
165+
166+
[Test]
167+
public void RunExceptionTest4()
168+
{
169+
IsNull(SynchronizationContext.Current);
170+
171+
using var app = new Application();
172+
173+
Exception? ex = null;
174+
try
175+
{
176+
app.Run(() =>
177+
{
178+
throw new ApplicationException("ABC");
179+
});
180+
}
181+
catch (Exception ex2)
182+
{
183+
ex = ex2;
184+
}
185+
186+
IsTrue(ex is ApplicationException aex && aex.Message == "ABC");
187+
}
188+
189+
[Test]
190+
public void RunExceptionTest5()
191+
{
192+
IsNull(SynchronizationContext.Current);
193+
194+
using var app = new Application();
195+
196+
Exception? ex = null;
197+
try
198+
{
199+
app.Run(() =>
200+
{
201+
var tcs = new TaskCompletionSource<int>();
202+
tcs.SetException(new ApplicationException("ABC"));
203+
return tcs.Task;
204+
});
205+
}
206+
catch (Exception ex2)
207+
{
208+
ex = ex2;
209+
}
210+
211+
IsTrue(ex is ApplicationException aex && aex.Message == "ABC");
212+
}
213+
214+
[Test]
215+
public void RunExceptionTest6()
216+
{
217+
IsNull(SynchronizationContext.Current);
218+
219+
using var app = new Application();
220+
221+
Exception? ex = null;
222+
try
223+
{
224+
app.Run(async() =>
225+
{
226+
await Task.Delay(1);
227+
throw new ApplicationException("ABC");
228+
});
229+
}
230+
catch (Exception ex2)
231+
{
232+
ex = ex2;
233+
}
234+
235+
IsTrue(ex is ApplicationException aex && aex.Message == "ABC");
236+
}
156237
}

Lepracaun/Lepracaun.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net35;net40;net45;net461;net48;netstandard1.3;netstandard1.6;netstandard2.0;netstandard2.1;netcoreapp2.0;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
4+
<TargetFrameworks>net35;net40;net45;net461;net48;net481;netstandard1.3;netstandard1.6;netstandard2.0;netstandard2.1;netcoreapp2.0;netcoreapp2.2;netcoreapp3.0;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
55
<IsPackable>true</IsPackable>
66
</PropertyGroup>
77

Lepracaun/ManagedThreadSynchronizationContext.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77
//
88
/////////////////////////////////////////////////////////////////////////////////////
99

10+
using System;
1011
using Lepracaun.Internal;
1112
using System.Collections.Generic;
13+
using System.Reflection;
1214
using System.Threading;
1315

16+
#if !NET35 && !NET40
17+
using System.Runtime.ExceptionServices;
18+
#endif
19+
1420
namespace Lepracaun;
1521

1622
/// <summary>
@@ -34,6 +40,11 @@ public abstract class ManagedThreadSynchronizationContext :
3440
/// </summary>
3541
private bool completeAdding;
3642

43+
/// <summary>
44+
/// Finalized with an exception.
45+
/// </summary>
46+
private Exception? completeWithException;
47+
3748
/// <summary>
3849
/// Constructor.
3950
/// </summary>
@@ -91,11 +102,22 @@ protected override sealed void OnRun(
91102
entry.Continuation,
92103
entry.State);
93104
}
105+
106+
if (this.completeWithException is { } ex)
107+
{
108+
#if NET35 || NET40
109+
throw new TargetInvocationException(ex);
110+
#else
111+
var edi = ExceptionDispatchInfo.Capture(ex);
112+
edi.Throw();
113+
#endif
114+
}
94115
}
95116

96117
protected override sealed void OnShutdown(
97-
int targetThreadId)
118+
int targetThreadId, Exception? ex)
98119
{
120+
this.completeWithException = ex;
99121
this.completeAdding = true;
100122
this.available.Set();
101123
}

Lepracaun/ThreadBoundSynchronizationContext.cs

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
using System.Runtime.ExceptionServices;
1818
#endif
1919

20+
#pragma warning disable CS0618
21+
2022
namespace Lepracaun;
2123

2224
/// <summary>
@@ -94,27 +96,31 @@ protected abstract void OnRun(
9496
/// Shutdown requested.
9597
/// </summary>
9698
/// <param name="targetThreadId">Target thread identity.</param>
99+
[Obsolete("Use OnShutdown with exception overload instead.")]
100+
protected virtual void OnShutdown(
101+
int targetThreadId) =>
102+
this.OnShutdown(targetThreadId, null);
103+
104+
/// <summary>
105+
/// Shutdown requested.
106+
/// </summary>
107+
/// <param name="targetThreadId">Target thread identity.</param>
108+
/// <param name="ex">Exception when shutdown reason</param>
97109
protected abstract void OnShutdown(
98-
int targetThreadId);
110+
int targetThreadId, Exception? ex);
99111

100-
private bool InvokeUnhandledException(Exception ex)
101-
{
102-
UnhandledExceptionEventArgs e;
103-
if (ex is AggregateException aex &&
104-
aex.InnerExceptions.Count == 1)
105-
{
106-
e = new(aex.InnerExceptions[0]);
107-
}
108-
else if (ex is TargetInvocationException tex &&
109-
tex.InnerException != null)
112+
private Exception UnwrapIfRequired(Exception ex) =>
113+
ex switch
110114
{
111-
e = new(tex.InnerException);
112-
}
113-
else
114-
{
115-
e = new(ex);
116-
}
115+
AggregateException aex when aex.InnerExceptions.Count == 1 => aex.InnerExceptions[0],
116+
TargetInvocationException tex when tex.InnerException != null => tex.InnerException,
117+
_ => ex,
118+
};
117119

120+
private bool InvokeUnhandledException(Exception ex)
121+
{
122+
var e = new UnhandledExceptionEventArgs(ex);
123+
118124
try
119125
{
120126
this.UnhandledException?.Invoke(this, e);
@@ -135,7 +141,7 @@ protected void OnInvoke(SendOrPostCallback continuation, object? state)
135141
}
136142
catch (Exception ex)
137143
{
138-
if (!this.InvokeUnhandledException(ex))
144+
if (!this.InvokeUnhandledException(UnwrapIfRequired(ex)))
139145
{
140146
throw;
141147
}
@@ -236,6 +242,34 @@ public override void Post(SendOrPostCallback continuation, object? state)
236242

237243
this.OnPost(this.boundThreadId, continuation, state);
238244
}
245+
246+
/// <summary>
247+
/// Schedule task final completion.
248+
/// </summary>
249+
/// <param name="task">Task</param>
250+
protected void HookTaskFinalizer(Task? task) =>
251+
task?.ContinueWith(t =>
252+
{
253+
if (t.IsCanceled)
254+
{
255+
var ex = UnwrapIfRequired(t.Exception!);
256+
if (!this.InvokeUnhandledException(ex))
257+
{
258+
this.OnShutdown(this.boundThreadId, ex);
259+
return;
260+
}
261+
}
262+
else if (t.IsFaulted)
263+
{
264+
var ex = UnwrapIfRequired(t.Exception!);
265+
if (!this.InvokeUnhandledException(ex))
266+
{
267+
this.OnShutdown(this.boundThreadId, ex);
268+
return;
269+
}
270+
}
271+
this.OnShutdown(this.boundThreadId, null);
272+
});
239273

240274
/// <summary>
241275
/// Execute message queue.
@@ -251,21 +285,7 @@ public virtual void Run(Task task)
251285
$"Thread mismatch between created and running: Created={this.boundThreadId}, Running={currentThreadId}");
252286
}
253287

254-
// Schedule task completion.
255-
task?.ContinueWith(t =>
256-
{
257-
if (t.IsCanceled)
258-
{
259-
this.InvokeUnhandledException(t.Exception!);
260-
}
261-
else if (t.IsFaulted)
262-
{
263-
this.InvokeUnhandledException(t.Exception!);
264-
}
265-
266-
this.OnShutdown(this.boundThreadId);
267-
});
268-
288+
this.HookTaskFinalizer(task);
269289
this.OnRun(this.boundThreadId);
270290
}
271291

0 commit comments

Comments
 (0)