Skip to content
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

Ctrl-C kills cabal repl on windows #8820

Closed
ambroslins opened this issue Feb 28, 2023 · 29 comments
Closed

Ctrl-C kills cabal repl on windows #8820

ambroslins opened this issue Feb 28, 2023 · 29 comments

Comments

@ambroslins
Copy link

Describe the bug
Using Ctrl + C inside cabal repl (to clear the line or interrupt a long running command) closes the repl on windows.

To Reproduce

$ cabal v2-repl
  • Press Ctrl + C

Expected behavior
I expect the repl to stay open after using Ctrl + C, as ghci and cabal repl on WSL stay open after the interrupt.

System information

  • Operating system: Windows 10
  • cabal version 3.6.2.0 (installed with ghcup)

Additional context
I think this is the same as #1448, but I opened as new issue as this is windows specific.

I also tested the commit 9e417fa built with ghc 8.10.7 and 9.2.5, but it has the same problem.

@ulysses4ever
Copy link
Collaborator

Thanks for the report and a lot of datapoints!

Thoughts. Looks relevant: #8208, where they say that the bug may have been introduced in #7921

Suggestions:

  • Since you have ghcup handy, could you try:
    • cabal 3.8.1.0
    • cabal 3.9.0.0 pre-release (for that you'll need the prerelease channel)
    • a newer, "internal" prerelease via:
    ghcup --no-cache install cabal -u 'https://gitlab.haskell.org/haskell/cabal/-/jobs/1387822/artifacts/raw/out/cabal-install-3.10.1.0-x86_64-linux-alpine.tar.xz' internal-third-prerelease-of-3.10
    
  • Could you try to build cabal just prior Handle SIGTERM properly, on unix systems #7921 and right after it and check that this is the culprit?

@robx
Copy link
Collaborator

robx commented Feb 28, 2023

As far as I understand, #7921 didn't make it into cabal 3.6 (or even cabal 3.8), so it's unlikely to be the cause here. It should be in that 3.9 pre-release though, and might even help here judging by the PR description, since I claim it improves Ctrl-C behaviour on Windows 😅.

I think #7929 is the only one of my somewhat related changes that is in Cabal 3.8, but that shouldn't interfere.

It would be interesting to know if/when this ever worked on Windows -- the thing that's involved on the Unix side is System.Process's delegate_ctlc setting, which is documented to have no effect on Windows. One thing that might be worth playing around with is enable_process_jobs.

@ambroslins
Copy link
Author

Sure, here are my results.

cabal 3.8.1.0 and pre-release cabal 3.9.0.0:

Same problem, but if I run cabal repl in my home directory (no cabal project) it does exit with:

λ> C:\Users\Ambros\AppData\Local\Temp\cabal-repl.-20236: removeDirectoryRecursive:removeContentsRecursive:RemoveDirectory "\\\\?\\C:\\Users\\Ambros\\AppData\\Local\\Temp\\cabal-repl.-20236": permission denied (The process cannot access the file because it is being used by another process.)

Inside a project it does exit the repl but without printing anything.

The provided command for the "internal" prerelease does not work for me, I also tried to replace linux-alpine.tar.xz with windows.zip but that does not exists.

cabal 3.7.0.0 55e036a

Same as 3.8.1.0 and 3.9.0.0

cabal 3.7.0.0 0184445

Same as 3.8.1.0 and 3.9.0.0

@ambroslins
Copy link
Author

@robx

It would be interesting to know if/when this ever worked on Windows -- the thing that's involved on the Unix side is System.Process's delegate_ctlc setting, which is documented to have no effect on Windows. One thing that might be worth playing around with is enable_process_jobs.

I just tested the following cabal version with ghcup and all of them exit the repl:

  • 2.4.1.0
  • 3.0.0.0
  • 3.2.0.0
  • 3.4.1.0
  • 3.6.0.0

@robx
Copy link
Collaborator

robx commented Feb 28, 2023

Awesome, thank you! Not a regression then, "just" one of the bugs around Windows subprocess handling.

Here's one instance where this was fixed for ghci itself: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8692. It appears that the parent process should be calling FreeConsole(). As far as I can tell, there are no Haskell bindings to that function (neither in the package Win32 nor is it used within process).

I was going to suggest extending process to add a call to FreeConsole(), but ran across the flags detach_console and create_new_console -- I hope these might have a similar effect. If you/anyone wants to try this, it could be set here:

proc path args = enableProcessJobs (Process.proc path args) { Process.delegate_ctlc = True }
(or possibly at a narrower scope higher up in the callstack if setting it for all subprocesses causes issues).

@jneira
Copy link
Member

jneira commented Feb 28, 2023

hmm thanks for the report, i just tried to reproduce and it worked for me;

PS D:\dev\ws\haskell\cabal-test> cabal repl
Build profile: -w ghc-8.10.7 -O1
In order, the following will be built (use -v for more details):
 - cabal-test-0.1.0.0 (lib) (first run)
Preprocessing library for cabal-test-0.1.0.0..

GHCi, version 8.10.7: https://www.haskell.org/ghc/  :? for help
......

*Lib> Control.Monad.forever (putStrLn "x")
x
....
x
xInterrupted.
*Lib> putStrLn "Still in the repl"
Still in the repl

I tried cabal-3.9.0.0, not sure which commit but a recent one and cabal-3.8.1.0, using powershell on windows 10

@ambroslins
Copy link
Author

@jneira

hmm thanks for the report, i just tried to reproduce and it worked for me;
Hmm okey, I also tested this on my laptop with Windows 11 and there the repl also closes on the interrupt.
I also checked Windows PowerShell, PowerShell Core and cmd and all behave the same.
This is my output:

Ambros@Ambros-PC in ~\Dev\cabal-interrupt-test
> cabal repl
Build profile: -w ghc-9.2.5 -O1
In order, the following will be built (use -v for more details):
 - cabal-interrupt-test-0.1.0.0 (lib) (ephemeral targets)
Preprocessing library for cabal-interrupt-test-0.1.0.0..
GHCi, version 9.2.5: https://www.haskell.org/ghc/  :? for help
Ok, one module loaded.
ghci> getLine
*** ExceptioInterrupted.
ghci>
Ambros@Ambros-PC in ~\Dev\cabal-interrupt-test
>

@robx

I was going to suggest extending process to add a call to FreeConsole(), but ran across the flags detach_console and create_new_console -- I hope these might have a similar effect. If you/anyone wants to try this, it could be set here:

proc path args = enableProcessJobs (Process.proc path args) { Process.delegate_ctlc = True }

I tried this and using create_new_console = True the repl does behave as expected, so stays open after the interrupt, but this opens a new terminal window so I don't think this is desired. With detach_console I get the following error:

> C:\Users\Ambros\AppData\Local\ghcup\cabal\bin\cabal.exe repl
Warning: cannot determine version of
C:\Users\Ambros\AppData\Local\ghcup\ghcup\bin\ghc.exe :
""
Error: cabal.exe: The program 'ghc' version >=7.0.1 is required but the
version of C:\Users\Ambros\AppData\Local\ghcup\ghcup\bin\ghc.exe could not be
determined.

Ambros@Ambros-PC in ~\Dev\cabal-interrupt-test
> C:\Users\Ambros\AppData\Local\ghcup\ghcup\bin\ghc.exe --version
The Glorious Glasgow Haskell Compilation System, version 9.2.5

@ulysses4ever
Copy link
Collaborator

ulysses4ever commented Mar 1, 2023

@ambroslins sorry about the internal release link mixip: I should have known better than suggesting to get an alpine binary on Windows. Could you try this url instead:

https://gitlab.haskell.org/haskell/cabal/-/jobs/1387827/artifacts/raw/out/cabal-install-3.10.1.0-x86_64-windows.zip

@robx
Copy link
Collaborator

robx commented Mar 1, 2023

It might be worth trying across different ghc(i) versions, too. The following seems to work for that (with ghcup-installed ghc versions):

$ cabal repl -w ghc-8.10

I'm also curious whether there's a difference between cabal repl and cabal exec ghci.

@ambroslins
Copy link
Author

@ambroslins sorry about the internal release link mixip: I should have known better than suggesting to get an alpine binary on Windows. Could you try this url instead:

https://gitlab.haskell.org/haskell/cabal/-/jobs/1387827/artifacts/raw/out/cabal-install-3.10.1.0-x86_64-windows.zip

Same issue.

It might be worth trying across different ghc(i) versions, too. The following seems to work for that (with ghcup-installed ghc versions):

$ cabal repl -w ghc-8.10

I'm also curious whether there's a difference between cabal repl and cabal exec ghci.

I tested with ghc 8.8.4, 8.10.7, 9.0.2, 9.2.5, 9.4.4 and all of them have this issue.
cabal exec ghci and ghci do not have this issue. They stay open after pressing ctrl + c. However I noticed that if I double press ctrl + c very fast it sometimes exits, not sure if this is intentional.

@Mistuke
Copy link
Collaborator

Mistuke commented Mar 5, 2023

I think the difference is your terminals..

@jneira is trying using a native windows terminal, looks like Powershell.

@ambroslins is using what looks like an msys terminal? e.g. bash, sh, zsh etc. If this is the case then it's expected behaviour.

Cabal is a Windows program so it throws Windows signals, which are different from Unix signals and the emulated ones in msys processes. We cannot handle both at the same time.

you can use winpty cabal ... in an msys2 process to get native Windows behavior. (cabal is not the only program with this issue, e.g. running a native gdb in an msys2 terminal also behaves the same, that's why there are emulated posix ones too)

@jneira
Copy link
Member

jneira commented Mar 5, 2023

hmm it is somewhat hidden in the quote but

I also checked Windows PowerShell, PowerShell Core and cmd and all behave the same.

@ambroslins
Copy link
Author

@Mistuke
I also use a native windows terminal (PowerShell Core), but I also tried using the Windows PowerShell and cmd.exe and I am having this issue on all of them.
Also give that the repl stays open using ghci and behaves the same on two different machines, I dont't think this is a "works fine on my my machine" (or in this case does not work). However I will try to setup a clean Windows VM and test it there.

@Mikolaj
Copy link
Member

Mikolaj commented Mar 6, 2023

Please also see (and verify) #8208 (comment)

@Mistuke
Copy link
Collaborator

Mistuke commented Mar 6, 2023

That won't fix it. Sorry finally caught up with the thread. @robx is right that this is a cabal bug. Cabal is handling the signal and not letting the child process do it.

@jneira
Copy link
Member

jneira commented Mar 6, 2023

well it seems to work on my machine but definitely is not a fresh installation,
@Mistuke did you have the chance of reproducing it?

@ambroslins
Copy link
Author

I tried it in a Windows 10 VM and having the same issue there.

Knowing that it works fine for @jneira I still struggle to find what's wrong on my setup(s). I tried on two different machines (Windows 10 with AMD CPU and Windows 11 with Intel) and a VM with a new ghcup install. Is there some other angle I can try to troubleshoot this?

@ulysses4ever
Copy link
Collaborator

Could it depend on the exact moment you hit Ctrl-C? E.g. let's at least agree on which code we try it with. How about just getLine?

@ambroslins
Copy link
Author

Could it depend on the exact moment you hit Ctrl-C? E.g. let's at least agree on which code we try it with. How about just getLine?

I don't think so, I tried a bunch of different functions (including the forever (putStrLn "x")) and all of them exit the repl. It also exits when no code is running, so just on the prompt.

@Mistuke
Copy link
Collaborator

Mistuke commented Mar 8, 2023

well it seems to work on my machine but definitely is not a fresh installation, @Mistuke did you have the chance of reproducing it?

yes, I can reproduce it trivially. The behavior is consistent with cabal handling the signal rather than ghci.
i.e. cabal's default behavior for ctrl+c is to terminate the child program. This makes sense for non-interactive programs. But as stated above, for interactive programs it needs to give up control of signals and re-acquire them after the return.

i.e. call FreeConsole and AttachConsole when done. It does however make sense to implement this in process under delegate_ctlc which is what it's supposed to do. I'll implement it this weekend in process but someone else has to make the cabal change.

@robx
Copy link
Collaborator

robx commented Mar 8, 2023

yes, I can reproduce it trivially. The behavior is consistent with cabal handling the signal rather than ghci. i.e. cabal's default behavior for ctrl+c is to terminate the child program. This makes sense for non-interactive programs. But as stated above, for interactive programs it needs to give up control of signals and re-acquire them after the return.

i.e. call FreeConsole and AttachConsole when done. It does however make sense to implement this in process under delegate_ctlc which is what it's supposed to do. I'll implement it this weekend in process but someone else has to make the cabal change.

Note that Cabal already sets delegate_ctlc:

proc path args = enableProcessJobs (Process.proc path args) { Process.delegate_ctlc = True }
So by my understanding the process change to make delegate_ctlc cross-platform should be all that's needed.

(There's one path that unsets delegate_ctlc:

-- | Is the task we are going to run an interactive foreground task,
-- I don't think that applies here, though.)

(Still confused by how @jneira sees different behavior.)

@jneira
Copy link
Member

jneira commented Mar 8, 2023

I wonder if my reproduction of the hang issue with ctrl c is related, in that case Mistuke does not reproduce it either

@Mistuke
Copy link
Collaborator

Mistuke commented Mar 11, 2023

I wonder if my reproduction of the hang issue with ctrl c is related, in that case Mistuke does not reproduce it either

could very well be.

I put up a PR for this haskell/process#278 can someone try using this? After the removal of sandboxes from cabal I'm not really sure what's the easiest way to test such changes. The knobs change a lot :)

@ambroslins
Copy link
Author

I put up a PR for this haskell/process#278 can someone try using this? After the removal of sandboxes from cabal I'm not really sure what's the easiest way to test such changes. The knobs change a lot :)

I built cabal-install with the latest commit (0ed1218) and your PR and it does fix this issue. Thank you!

However, I noticed that if ctrl + c has been used once in the repl session, the repl will exit with a non zero exit code on :q.

Ok, one module loaded.
λ> last [1..] :: Integer
Interrupted.
λ> :q
Leaving GHCi.
Error: cabal.exe: repl failed for cabal-interrupt-test-0.1.0.0. The build
process terminated with exit code -1073741510

A repl without an interrupt does exit normally on :q.

Also when double pressing ctrl + c the repl still exits, however I think this is another issue as ghci does the same.

@Mistuke
Copy link
Collaborator

Mistuke commented Mar 12, 2023

@ambroslins awesome, thanks for confirming!

Hmm that's interesting.. I don't propagate exit codes in the ctrl+c masking. So it's not process passing it on.

@ulysses4ever
Copy link
Collaborator

The exit code issue — is it Windows-specific too?

@Mistuke
Copy link
Collaborator

Mistuke commented Mar 13, 2023

Process support has been committed

@ambroslins
Copy link
Author

The exit code issue — is it Windows-specific too?

I think it is, I built the same version as before in WSL and there the exit code is 0.

@jasagredo
Copy link
Collaborator

I think we can close this issue as it seems to be working properly with recent GHCs:

❯ cabal repl
Build profile: -w ghc-9.6.6 -O1
In order, the following will be built (use -v for more details):
 - bb-0.1.0.0 (interactive) (lib) (first run)
Preprocessing library for bb-0.1.0.0...
GHCi, version 9.6.6: https://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from C:\Users\Javier\.ghci
[1 of 1] Compiling MyLib            ( src\MyLib.hs, interpreted )
Ok, one module loaded.
λ: This is a very long line I interrupt with Ctrl+c
λ:
λ: It works
λ:
λ:
λ:
λ:
λ:
λ:
λ:
λ:
λ:
λ: I can press it many times

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants