Skip to content

[API Proposal]: Non-cooperative abortion of code execution #69622

@AntonLapounov

Description

@AntonLapounov

Background and motivation

Update: Replaced TryThrowException with TryAbort as was in the original proposal.

There is a number of execution environments for .NET code that may benefit from having an option to perform non-cooperative abortion of code execution, e.g., C# and F# interactive compilers (csi and fsi) or .NET Interactive Notebooks (see #41291). While the legacy Framework provided the Thread.Abort method, it has never been supported by .NET Core, because aborting code execution at a random point is inherently unsafe and may leave the process in an inconsistent or a corrupted state. Thus, the only available option to interrupt stuck code execution is to terminate the process and lose its state, which is undesirable for interactive scenarios.

Below is the proposed API to allow controlled execution of given code. The Run method starts execution of the delegate and TryAbort method may be used to attempt aborting its execution by throwing an asynchronous ThreadAbortException on that thread. Aborting may be successful only when the code is executing managed code, e.g., running a CPU-intensive loop, on the original thread. This API would not help cases where the thread is stuck in unmanaged code or waiting for some other thread to finish its work.

The implementation will generally use the same mechanism as the one utilized by the debugger to abort threads doing function evaluation (Thread::UserAbort). That includes marking the thread for abortion, suspending it, checking whether it is running managed code and safe to redirect, redirecting the thread to the ThrowControlForThread runtime helper, walking the stack to verify the tread is in a safe spot, and raising the exception for thread abort.

A possible option to make the implementation simpler is to rely only on GC polls / polls for thread abort instead of redirecting the thread on Windows, similarly to what is done on Unixes.

This design was discussed in #66480.

API Proposal

namespace System.Runtime.CompilerServices;

public class ControlledExecution
{
    public ControlledExecution(Action action);

    // Runs the specified action.
    // Throws an InvalidOperationException if the action is already running.
    // Throws a ThreadAbortException if execution was aborted.
    public void Run();

    // Attempts to abort the execution of the action by injecting an asynchronous exception.
    // Returns true on success or false otherwise.
    // Throws an InvalidOperationException if the action is not running.
    public bool TryAbort(TimeSpan timeout);
}

API Usage

s_executor = new ControlledExecution(SomeLengthyAction);
s_executor.Run();

// On another thread:
s_executor.TryAbort(TimeSpan.FromMilliseconds(500));

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions