-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Implement ControlledExecution API #71661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
AntonLapounov
merged 10 commits into
dotnet:main
from
AntonLapounov:ControlledExecution
Jul 28, 2022
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f93ded4
Implement ControlledExecution API
AntonLapounov b3fb148
Fix build breaks
AntonLapounov f57bce7
Address feedback
AntonLapounov 9f8f1eb
Fix ARM64 build break
AntonLapounov 3a5fc3c
Address Jan's feedback
AntonLapounov 0553d52
Missed one change
AntonLapounov b95b2e3
Re-disable tests for Unix
AntonLapounov 702a21a
Make ResetAbortThread a QCall
AntonLapounov a73aae7
Address Stephen's feedback
AntonLapounov b4716dc
Update comments
AntonLapounov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
159 changes: 159 additions & 0 deletions
159
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Runtime.CompilerServices; | ||
| using System.Runtime.ExceptionServices; | ||
| using System.Runtime.InteropServices; | ||
| using System.Threading; | ||
|
|
||
| namespace System.Runtime | ||
| { | ||
| /// <summary> | ||
| /// Allows to run code and abort it asynchronously. | ||
| /// </summary> | ||
| public static partial class ControlledExecution | ||
| { | ||
| [ThreadStatic] | ||
| private static bool t_executing; | ||
|
|
||
| /// <summary> | ||
| /// Runs code that may be aborted asynchronously. | ||
| /// </summary> | ||
| /// <param name="action">The delegate that represents the code to execute.</param> | ||
| /// <param name="cancellationToken">The cancellation token that may be used to abort execution.</param> | ||
| /// <exception cref="System.PlatformNotSupportedException">The method is not supported on this platform.</exception> | ||
| /// <exception cref="System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception> | ||
| /// <exception cref="System.InvalidOperationException"> | ||
| /// The current thread is already running the <see cref="ControlledExecution.Run"/> method. | ||
| /// </exception> | ||
| /// <exception cref="System.OperationCanceledException">The execution was aborted.</exception> | ||
| /// <remarks> | ||
| /// <para>This method enables aborting arbitrary managed code in a non-cooperative manner by throwing an exception | ||
| /// in the thread executing that code. While the exception may be caught by the code, it is re-thrown at the end | ||
| /// of `catch` blocks until the execution flow returns to the `ControlledExecution.Run` method.</para> | ||
| /// <para>Execution of the code is not guaranteed to abort immediately, or at all. This situation can occur, for | ||
| /// example, if a thread is stuck executing unmanaged code or the `catch` and `finally` blocks that are called as | ||
| /// part of the abort procedure, thereby indefinitely delaying the abort. Furthermore, execution may not be | ||
| /// aborted immediately if the thread is currently executing a `catch` or `finally` block.</para> | ||
| /// <para>Aborting code at an unexpected location may corrupt the state of data structures in the process and lead | ||
| /// to unpredictable results. For that reason, this method should not be used in production code and calling it | ||
| /// produces a compile-time warning.</para> | ||
| /// </remarks> | ||
| [Obsolete(Obsoletions.ControlledExecutionRunMessage, DiagnosticId = Obsoletions.ControlledExecutionRunDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] | ||
| public static void Run(Action action, CancellationToken cancellationToken) | ||
| { | ||
| if (!OperatingSystem.IsWindows()) | ||
| { | ||
| throw new PlatformNotSupportedException(); | ||
| } | ||
|
|
||
| ArgumentNullException.ThrowIfNull(action); | ||
|
|
||
| // ControlledExecution.Run does not support nested invocations. If there's one already in flight | ||
| // on this thread, fail. | ||
| if (t_executing) | ||
| { | ||
| throw new InvalidOperationException(SR.InvalidOperation_NestedControlledExecutionRun); | ||
| } | ||
|
|
||
| // Store the current thread so that it may be referenced by the Canceler.Cancel callback if one occurs. | ||
| Canceler canceler = new(Thread.CurrentThread); | ||
|
|
||
| try | ||
| { | ||
| // Mark this thread as now running a ControlledExecution.Run to prevent recursive usage. | ||
| t_executing = true; | ||
|
|
||
| // Register for aborting. From this moment until ctr.Unregister is called, this thread is subject to being | ||
| // interrupted at any moment. This could happen during the call to UnsafeRegister if cancellation has | ||
| // already been requested at the time of the registration. | ||
| CancellationTokenRegistration ctr = cancellationToken.UnsafeRegister(e => ((Canceler)e!).Cancel(), canceler); | ||
| try | ||
| { | ||
| // Invoke the caller's code. | ||
| action(); | ||
| } | ||
| finally | ||
| { | ||
| // This finally block may be cloned by JIT for the non-exceptional code flow. In that case the code | ||
| // below is not guarded against aborting. That is OK as the outer try block will catch the | ||
| // ThreadAbortException and call ResetAbortThread. | ||
|
|
||
| // Unregister the callback. Unlike Dispose, Unregister will not block waiting for an callback in flight | ||
| // to complete, and will instead return false if the callback has already been invoked or is currently | ||
| // in flight. | ||
| if (!ctr.Unregister()) | ||
| { | ||
| // Wait until the callback has completed. Either the callback is already invoked and completed | ||
| // (in which case IsCancelCompleted will be true), or it may still be in flight. If it's in flight, | ||
| // the AbortThread call may be waiting for this thread to exit this finally block to exit, so while | ||
| // spinning waiting for the callback to complete, we also need to call ResetAbortThread in order to | ||
| // reset the flag the AbortThread call is polling in its waiting loop. | ||
| SpinWait sw = default; | ||
| while (!canceler.IsCancelCompleted) | ||
| { | ||
| ResetAbortThread(); | ||
| sw.SpinOnce(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| catch (ThreadAbortException tae) | ||
| { | ||
| // We don't want to leak ThreadAbortExceptions to user code. Instead, translate the exception into | ||
| // an OperationCanceledException, preserving stack trace details from the ThreadAbortException in | ||
| // order to aid in diagnostics and debugging. | ||
| OperationCanceledException e = cancellationToken.IsCancellationRequested ? new(cancellationToken) : new(); | ||
| if (tae.StackTrace is string stackTrace) | ||
| { | ||
| ExceptionDispatchInfo.SetRemoteStackTrace(e, stackTrace); | ||
| } | ||
| throw e; | ||
| } | ||
| finally | ||
| { | ||
| // Unmark this thread for recursion detection. | ||
| t_executing = false; | ||
|
|
||
| if (cancellationToken.IsCancellationRequested) | ||
| { | ||
| // Reset an abort request that may still be pending on this thread. | ||
| ResetAbortThread(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Abort")] | ||
| private static partial void AbortThread(ThreadHandle thread); | ||
|
|
||
| [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ResetAbort")] | ||
| [SuppressGCTransition] | ||
| private static partial void ResetAbortThread(); | ||
|
|
||
| private sealed class Canceler | ||
| { | ||
| private readonly Thread _thread; | ||
| private volatile bool _cancelCompleted; | ||
|
|
||
| public Canceler(Thread thread) | ||
| { | ||
| _thread = thread; | ||
| } | ||
|
|
||
| public bool IsCancelCompleted => _cancelCompleted; | ||
|
|
||
| public void Cancel() | ||
| { | ||
| try | ||
| { | ||
| // Abort the thread executing the action (which may be the current thread). | ||
| AbortThread(_thread.GetNativeHandle()); | ||
| } | ||
| finally | ||
| { | ||
| _cancelCompleted = true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
...eclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ControlledExecution.NativeAot.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.Threading; | ||
|
|
||
| namespace System.Runtime | ||
| { | ||
| public static class ControlledExecution | ||
| { | ||
| [Obsolete(Obsoletions.ControlledExecutionRunMessage, DiagnosticId = Obsoletions.ControlledExecutionRunDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] | ||
| public static void Run(Action action, CancellationToken cancellationToken) | ||
| { | ||
| throw new PlatformNotSupportedException(); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.