Skip to content

Commit 82a1ad5

Browse files
Refactor/Improve InvokeAsync to address disposing issues of the cancellation token.
1 parent ee675d3 commit 82a1ad5

File tree

2 files changed

+41
-18
lines changed

2 files changed

+41
-18
lines changed

src/System.Windows.Forms/GlobalSuppressions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
// Publicly shipped API
5+
6+
57
[assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Public API", Scope = "member", Target = "~M:System.Windows.Forms.ButtonBase.OnKeyDown(System.Windows.Forms.KeyEventArgs)")]
68
[assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Public API", Scope = "member", Target = "~M:System.Windows.Forms.ButtonBase.OnKeyUp(System.Windows.Forms.KeyEventArgs)")]
79
[assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration", Justification = "Public API", Scope = "member", Target = "~M:System.Windows.Forms.ButtonBase.OnMouseDown(System.Windows.Forms.MouseEventArgs)")]
@@ -268,3 +270,5 @@
268270
[assembly: SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Analyzer wrongly complains for new APIs - known issue.", Scope = "member", Target = "~M:System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.TaskDialogPage,System.Windows.Forms.TaskDialogStartupLocation)~System.Threading.Tasks.Task{System.Windows.Forms.TaskDialogButton}")]
269271
[assembly: SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Analyzer wrongly complains for new APIs - known issue.", Scope = "member", Target = "~M:System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window,System.Windows.Forms.TaskDialogPage,System.Windows.Forms.TaskDialogStartupLocation)~System.Threading.Tasks.Task{System.Windows.Forms.TaskDialogButton}")]
270272
[assembly: SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Analyzer wrongly complains for new APIs - known issue.", Scope = "member", Target = "~M:System.Windows.Forms.TaskDialog.ShowDialogAsync(System.IntPtr,System.Windows.Forms.TaskDialogPage,System.Windows.Forms.TaskDialogStartupLocation)~System.Threading.Tasks.Task{System.Windows.Forms.TaskDialogButton}")]
273+
[assembly: SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "<Pending>", Scope = "member", Target = "~M:System.Windows.Forms.Control.InvokeAsync(System.Func{System.Threading.CancellationToken,System.Threading.Tasks.ValueTask},System.Threading.CancellationToken)~System.Threading.Tasks.Task")]
274+
[assembly: SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "<Pending>", Scope = "member", Target = "~M:System.Windows.Forms.Control.InvokeAsync``1(System.Func{System.Threading.CancellationToken,System.Threading.Tasks.ValueTask{``0}},System.Threading.CancellationToken)~System.Threading.Tasks.Task{``0}")]

src/System.Windows.Forms/System/Windows/Forms/Control_InvokeAsync.cs

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ void WrappedCallback()
142142
/// A task representing the operation.
143143
/// </returns>
144144
/// <exception cref="InvalidOperationException">Thrown if the control's handle is not yet created.</exception>
145+
/// <exception cref="ArgumentNullException">Thrown if the callback is null.</exception>
145146
/// <remarks>
146147
/// <para>
147148
/// <b>Note:</b> The callback will be marshalled to the thread that owns the control's handle,
@@ -162,31 +163,37 @@ public async Task InvokeAsync(Func<CancellationToken, ValueTask> callback, Cance
162163
{
163164
ArgumentNullException.ThrowIfNull(callback);
164165

166+
if (!IsHandleCreated)
167+
{
168+
throw new InvalidOperationException(SR.ErrorNoMarshalingThread);
169+
}
170+
165171
if (cancellationToken.IsCancellationRequested)
166172
{
167173
return;
168174
}
169175

170176
TaskCompletionSource completion = new(TaskCreationOptions.RunContinuationsAsynchronously);
177+
CancellationTokenRegistration registration = cancellationToken.Register(completion.SetCanceled, useSynchronizationContext: false);
171178

172-
using (cancellationToken.Register(completion.SetCanceled, useSynchronizationContext: false))
173-
{
174-
BeginInvoke(async () => await WrappedCallbackAsync().ConfigureAwait(false));
175-
await completion.Task.ConfigureAwait(false);
176-
}
179+
BeginInvoke(async () => await WrappedCallbackAsync());
180+
await completion.Task.ConfigureAwait(false);
177181

178182
async Task WrappedCallbackAsync()
179183
{
180184
try
181185
{
182-
if (cancellationToken.IsCancellationRequested)
186+
using (registration)
183187
{
184-
completion.TrySetCanceled(cancellationToken);
185-
return;
186-
}
188+
if (cancellationToken.IsCancellationRequested)
189+
{
190+
completion.TrySetCanceled(cancellationToken);
191+
return;
192+
}
187193

188-
await callback(cancellationToken).ConfigureAwait(false);
189-
completion.TrySetResult();
194+
await callback(cancellationToken).ConfigureAwait(false);
195+
completion.TrySetResult();
196+
}
190197
}
191198
catch (Exception ex)
192199
{
@@ -226,25 +233,37 @@ public async Task<T> InvokeAsync<T>(Func<CancellationToken, ValueTask<T>> callba
226233
{
227234
ArgumentNullException.ThrowIfNull(callback);
228235

236+
if (!IsHandleCreated)
237+
{
238+
throw new InvalidOperationException(SR.ErrorNoMarshalingThread);
239+
}
240+
229241
if (cancellationToken.IsCancellationRequested)
230242
{
231243
return default!;
232244
}
233245

234246
TaskCompletionSource<T> completion = new(TaskCreationOptions.RunContinuationsAsynchronously);
247+
CancellationTokenRegistration registration = cancellationToken.Register(completion.SetCanceled, useSynchronizationContext: false);
235248

236-
using (cancellationToken.Register(completion.SetCanceled, useSynchronizationContext: false))
237-
{
238-
BeginInvoke(async () => await WrappedCallbackAsync().ConfigureAwait(false));
239-
return await completion.Task.ConfigureAwait(false);
240-
}
249+
BeginInvoke(async () => await WrappedCallbackAsync());
250+
return await completion.Task.ConfigureAwait(false);
241251

242252
async Task WrappedCallbackAsync()
243253
{
244254
try
245255
{
246-
var returnValue = await callback(cancellationToken).ConfigureAwait(false);
247-
completion.TrySetResult(returnValue);
256+
using (registration)
257+
{
258+
if (cancellationToken.IsCancellationRequested)
259+
{
260+
completion.TrySetCanceled(cancellationToken);
261+
return;
262+
}
263+
264+
T returnValue = await callback(cancellationToken).ConfigureAwait(false);
265+
completion.TrySetResult(returnValue);
266+
}
248267
}
249268
catch (Exception ex)
250269
{

0 commit comments

Comments
 (0)