Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Canceling the PosixSignal.SIGTSTP does not seem to be handled correctly #78302

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

Open
bmitc opened this issue Nov 14, 2022 · 9 comments
Open

Canceling the PosixSignal.SIGTSTP does not seem to be handled correctly #78302

bmitc opened this issue Nov 14, 2022 · 9 comments

Comments

@bmitc
Copy link

bmitc commented Nov 14, 2022

Description

I am writing a console application in F# using .NET 7. I am wanting to intercept and prevent console exit signals, such as PosixSignal.SIGTSTP and PosixSignal.SIGINT. These enums are defined here. To ignore them, I create and register a handler using PosixSignalRegistration.Create and then write true to the Cancel property inside the handler. This should cancel the signal, to my understanding, as it does for PosixSignal.SIGINT. However, that doesn't seem to be the case for PosixSignal.SIGTSTP.

Reproduction Steps

These steps were executed on Ubuntu 20 running in WSL2 on Windows 11.

  1. Save the following code into an F# script named posix.fsx.

    // posix.fsx
    open System
    open System.Runtime.InteropServices
    
    PosixSignalRegistration.Create(PosixSignal.SIGINT,
        fun context ->
            printfn "Prevented Posix SIGINT"
            context.Cancel <- true)
    |> ignore
    
    if System.OperatingSystem.IsLinux() then
        printfn "System is Linux"
        PosixSignalRegistration.Create(PosixSignal.SIGTSTP,
            fun context ->
                printfn "Prevented Posix SIGTSTP"
                context.Cancel <- true)
        |> ignore
    
    let mutable cont = true
    
    while cont do
        let key = Console.ReadKey(intercept = true)
        let keyChar = key.KeyChar
        let keyModifiers = key.Modifiers
        printfn "key: %A, modifiers: %A" keyChar keyModifiers
        if keyChar = 'q' then
            cont <- false
  2. Run the script with dotnet fsi posix.fsx

  3. Type any character except 'q'. The key information will be printed out.

  4. Enter Ctrl + C and note that "Prevented Posix SIGINT" is printed out to the screen but that the process is still running, which can be verified by entering in other characters (again, besides 'q').

  5. Enter Ctrl + Z and note that:

    • [3]+ Stopped dotnet fsi posix.fsx is printed to the screen
    • The process is stopped, but then "Prevented Posix SIGTSTP" is printed out afterwards, after the new prompt is shown.
    • However, the process seems to be restarted or still going, as it will respond to a key entry but usually only for one, and the process will end when Enter is entered. This behavior is a little inconsistent.

Expected behavior

When Ctrl + Z is entered, "Prevented Posix SIGTSTP" should be printed to the console without the program being stopped.

Actual behavior

$ dotnet fsi posix.fsx
System is Linux
key: 'a', modifiers: 0
key: '\013', modifiers: 0
Prevented Posix SIGINT
key: 'a', modifiers: 0

[2]+  Stopped                 dotnet fsi posix.fsx
$ Prevented Posix SIGTSTP
key: 'a', modifiers: 0

$ 

Regression?

Not sure.

Known Workarounds

None that I know of.

Configuration

$ dotnet --version
7.0.100

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
Codename:       focal

The architecture is x64.

Other information

No response

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Nov 14, 2022
@ghost
Copy link

ghost commented Nov 14, 2022

Tagging subscribers to this area: @dotnet/interop-contrib
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

I am writing a console application in F# using .NET 7. I am wanting to intercept and prevent console exit signals, such as PosixSignal.SIGTSTP and PosixSignal.SIGINT. These enums are defined here. To ignore them, I create and register a handler using PosixSignalRegistration.Create and then write true to the Cancel property inside the handler. This should cancel the signal, to my understanding, as it does for PosixSignal.SIGINT. However, that doesn't seem to be the case for PosixSignal.SIGTSTP.

Reproduction Steps

These steps were executed on Ubuntu 20 running in WSL2 on Windows 11.

  1. Save the following code into an F# script named posix.fsx.

    // posix.fsx
    open System
    open System.Runtime.InteropServices
    
    PosixSignalRegistration.Create(PosixSignal.SIGINT,
        fun context ->
            printfn "Prevented Posix SIGINT"
            context.Cancel <- true)
    |> ignore
    
    if System.OperatingSystem.IsLinux() then
        printfn "System is Linux"
        PosixSignalRegistration.Create(PosixSignal.SIGTSTP,
            fun context ->
                printfn "Prevented Posix SIGTSTP"
                context.Cancel <- true)
        |> ignore
    
    let mutable cont = true
    
    while cont do
        let key = Console.ReadKey(intercept = true)
        let keyChar = key.KeyChar
        let keyModifiers = key.Modifiers
        printfn "key: %A, modifiers: %A" keyChar keyModifiers
        if keyChar = 'q' then
            cont <- false
  2. Run the script with dotnet fsi posix.fsx

  3. Type any character except 'q'. The key information will be printed out.

  4. Enter Ctrl + C and note that "Prevented Posix SIGINT" is printed out to the screen but that the process is still running, which can be verified by entering in other characters (again, besides 'q').

  5. Enter Ctrl + Z and note that:

    • [3]+ Stopped dotnet fsi posix.fsx is printed to the screen
    • The process is stopped, but then "Prevented Posix SIGTSTP" is printed out afterwards, after the new prompt is shown.
    • However, the process seems to be restarted or still going, as it will respond to a key entry but usually only for one, and the process will end when Enter is entered. This behavior is a little inconsistent.

Expected behavior

When Ctrl + Z is entered, "Prevented Posix SIGTSTP" should be printed to the console without the program being stopped.

Actual behavior

$ dotnet fsi posix.fsx
System is Linux
key: 'a', modifiers: 0
key: '\013', modifiers: 0
Prevented Posix SIGINT
key: 'a', modifiers: 0

[2]+  Stopped                 dotnet fsi posix.fsx
$ Prevented Posix SIGTSTP
key: 'a', modifiers: 0

$ 

Regression?

Not sure.

Known Workarounds

None that I know of.

Configuration

$ dotnet --version
7.0.100

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
Codename:       focal

The architecture is x64.

Other information

No response

Author: bmitc
Assignees: -
Labels:

area-System.Runtime.InteropServices, untriaged

Milestone: -

@tmds
Copy link
Member

tmds commented Nov 14, 2022

This should cancel the signal

The semantics of Cancel depend on the signal and what use-case we want to enable.

SIGTSTP was added on request of @alexrp (#50527 (comment)).
We didn't diverge much into his specific use-case when including it.

We should look at the use-case and see what is the meaningful behavior when Cancel is true/false.

Looking at the code, I think they currently both do the same thing: prevent SIGTSTP from stopping the app.

@alexrp
Copy link
Contributor

alexrp commented Nov 14, 2022

My understanding (and reason for bringing it up back then) is that the common use case for handling SIGTSTP is to save any critical program state before it's suspended. I would expect a program to do that in the event handler, so no need to set Cancel = true.

I can't really think of why one would want to completely block SIGTSTP. Seeing as SIGSTOP can't be blocked anyway, there's no way to 'opt out' of suspension altogether. In any case, I would expect Cancel to do the intuitive thing: true blocks the signal, false lets it go through (so the program gets suspended).

@tmds
Copy link
Member

tmds commented Nov 14, 2022

I would expect Cancel to do the intuitive thing: true blocks the signal, false lets it go through (so the program gets suspended).

We can change it to do that, which is a breaking change, but probably the desired behavior.

When at its default value of Cancel = false, the process will then be stopped. (by sending SIGSTOP to it after the handler ran).
When set to Cancel = true, the process will keep running.

@bmitc how does that sound to you?

@AaronRobinsonMSFT AaronRobinsonMSFT removed the untriaged New issue has not been triaged by the area owner label Dec 31, 2022
@AaronRobinsonMSFT AaronRobinsonMSFT added this to the 8.0.0 milestone Dec 31, 2022
@bmitc
Copy link
Author

bmitc commented Jan 1, 2023

I apologize for getting back to this so late. This is a side project that got pushed aside. Thank you @tmds and @alexrp for responding so quickly to this, and I'm again sorry for the delay.

My use case was using pure F# to implement the modified Kilo text editor described in Build Your Own Text Editor. Specifically, it was in implementing the section Turn off Ctrl-C and Ctrl-Z signals where I came across this issue. The tutorial uses the C library termios to accomplish disabling SIGINT and SIGTSTP. For reference (also in the above link):

By default, Ctrl-C sends a SIGINT signal to the current process which causes it to terminate, and Ctrl-Z sends a SIGTSTP signal to the current process which causes it to suspend. Let’s turn off the sending of both of these signals.

But to do this behavior in pure F#/.NET, I was needing the PosixSignal.SIGINT and PosixSignal.SIGTSTP signals actually canceled when doing context.Cancel <- true. I think a terminal text editor is probably a legitimate use case for canceling these, although admittedly is probably a rare one.

However, I am not remotely close to an expert on terminal implementations, which is one reason why I was working through this tutorial, so it's possible I misunderstand something here.

@tmds
Copy link
Member

tmds commented Jan 11, 2023

Specifically, it was in implementing the section Turn off Ctrl-C and Ctrl-Z signals where I came across this issue. The tutorial uses the C library termios to accomplish disabling SIGINT and SIGTSTP. For reference (also in the above link):

Besides adding the PosixSignalRegistration, you should also set Console.TreatControlCAsInput to true.

Then you'll be able to read the Ctrl-C and Ctrl-Z combinations.

KeyChar will be the raw control value as mentioned in the article (Now Ctrl-C can be read as a 3 byte and Ctrl-Z can be read as a 26 byte.). ConsoleKey will be set based on that.

@jkoritzinsky
Copy link
Member

Moving this to .NET 9 as the likely fix is a breaking change and we're pretty late into .NET 8 for breaking changes.

@jkoritzinsky jkoritzinsky modified the milestones: 8.0.0, 9.0.0 Jul 26, 2023
@jkoritzinsky
Copy link
Member

Sadly we didn't get to this in .NET 9 and it is likely still a breaking change, so moving to .NET 10.

@jkoritzinsky jkoritzinsky modified the milestones: 9.0.0, 10.0.0 Jul 24, 2024
@agocke agocke modified the milestones: 10.0.0, Future May 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

7 participants