Infinite loop when running interactive process with native WinIO manager
I recently tried running a build of [Cryptol](https://github.com/GaloisInc/cryptol/) with the new native Windows IO manager enabled, but I ran into an infinite loop that is not present when using the POSIX-based IO manager. To reproduce the infinite loop, do the following on a Windows machine:
1. Download Z3 from https://github.com/Z3Prover/z3/releases/download/z3-4.8.14/z3-4.8.14-x64-win.zip and extract it.
2. Compile the following minimized example:
```hs
module Main (main) where
import System.IO (Handle, hClose, hGetLine, hFlush, hPutStrLn)
import System.Process (CreateProcess(..), ProcessHandle, StdStream(..), createProcess, proc, waitForProcess)
main :: IO ()
main = do
(in_h, out_h, err_h, ph) <- startProcess "z3" ["-smt2", "-in"]
let doCmd cmd = do
hPutStrLn in_h cmd
hFlush in_h
line <- hGetLine out_h
putStrLn line
doCmd "(set-option :print-success true)"
doCmd "(set-option :produce-models true)"
doCmd "(set-option :pp.decimal true)"
doCmd "(set-option :global-declarations true)"
doCmd "(get-info :error-behavior)"
doCmd "(exit)"
hClose in_h
hClose out_h
hClose err_h
print =<< waitForProcess ph
-- | Start a process connected to this one via pipes.
startProcess ::
FilePath {-^ Path to executable -} ->
[String] {-^ Command-line arguments -} ->
IO (Handle, Handle, Handle, ProcessHandle)
{-^ process stdin, process stdout, process stderr, process handle -}
startProcess path args =
do let create_proc
= (proc path args)
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
, create_group = False
, cwd = Nothing
, delegate_ctlc = True
}
createProcess create_proc >>= \x -> case x of
(Just in_h, Just out_h, Just err_h, ph) -> return (in_h, out_h, err_h, ph)
_ -> fail $ unlines $
[ "startProcess"
, "Failed to exec: " ++ show path
, "With the following arguments:"
] ++ args
```
With:
```
$ ghc-9.2 Main.hs -rtsopts
```
(Note that GHC 9.0 is also sufficient to trigger the bug.)
3. First, run the program with the POSIX-based IO manager:
```
$ $env:Path = "$env:USERPROFILE\Software\z3-4.8.14-x64-win\bin;" + $env:Path
$ ./Main.exe +RTS --io-manager=posix -RTS
success
success
success
success
(:error-behavior continued-execution)
success
ExitSuccess
```
This is the expected behavior of the program.
4. Finally, run the program with the native WinIO manager:
```
$ ./Main.exe +RTS --io-manager=native -RTS
success
# infinite loop
```
After printing out a single `success`, the program will infinitely loop. If you Ctrl-C at this point, you'll get some interesting errors depending on where in the program you interrupt. I've seen the following happen:
* ```
Main.exe: schedule: re-entered unsafely.
Perhaps a 'foreign import unsafe' should be 'safe'?
```
* ```
Main.exe: internal error: TSO object (0000000000a33c71) entered!
(GHC version 9.2.2 for x86_64_unknown_mingw32)
Please report this as a GHC bug: https://www.haskell.org/ghc/reportabug
```
* ```
Access violation in generated code when reading 0xffffffffffffffff
Attempting to reconstruct a stack trace...
Frame Code address
* 0x214db30 0x1902cf0 C:\Users\winferno\Documents\Hacking\Haskell\Main.exe+0x1502cf0
* 0x214db38 0x22fedd0
* 0x214db40 0x7fffe3596098 C:\Windows\System32\USER32.dll+0x96098
* 0x214db48 0x22fe450
* 0x214db50 0x22fedd0
* 0x214db58 0x6f0064006e0069
* 0x214db60 0x73005c00730077
* 0x214db68 0x65007400730079
```
* ```
Segmentation fault
```
issue