Veo un comportamiento muy extraño en el que la bracket
función de Haskell se comporta de manera diferente dependiendo de si se usa stack run
o no stack test
.
Considere el siguiente código, donde se usan dos corchetes anidados para crear y limpiar contenedores Docker:
module Main where
import Control.Concurrent
import Control.Exception
import System.Process
main :: IO ()
main = do
bracket (callProcess "docker" ["run", "-d", "--name", "container1", "registry:2"])
(\() -> do
putStrLn "Outer release"
callProcess "docker" ["rm", "-f", "container1"]
putStrLn "Done with outer release"
)
(\() -> do
bracket (callProcess "docker" ["run", "-d", "--name", "container2", "registry:2"])
(\() -> do
putStrLn "Inner release"
callProcess "docker" ["rm", "-f", "container2"]
putStrLn "Done with inner release"
)
(\() -> do
putStrLn "Inside both brackets, sleeping!"
threadDelay 300000000
)
)
Cuando ejecuto esto con stack run
e interrumpo con Ctrl+C
, obtengo el resultado esperado:
Inside both brackets, sleeping!
^CInner release
container2
Done with inner release
Outer release
container1
Done with outer release
Y puedo verificar que ambos contenedores Docker se crean y luego se eliminan.
Sin embargo, si pego exactamente el mismo código en una prueba y ejecuto stack test
, solo (parte de) ocurre la primera limpieza:
Inside both brackets, sleeping!
^CInner release
container2
Esto da como resultado un contenedor Docker que se ejecuta en mi máquina. ¿Que esta pasando?
- Me he asegurado de que se
ghc-options
les pase exactamente lo mismo a ambos. - Repo de demostración completo aquí: https://github.com/thomasjm/bracket-issue
.stack-work
y lo ejecuto directamente, entonces el problema no ocurre. Solo sucede cuando se ejecuta debajo stack test
.
stack test
inicia subprocesos de trabajo para manejar pruebas. 2) el controlador SIGINT mata el hilo principal. 3) Los programas Haskell finalizan cuando el hilo principal lo hace, ignorando cualquier hilo adicional. 2 es el comportamiento predeterminado en SIGINT para programas compilados por GHC. 3 es cómo funcionan los hilos en Haskell. 1 es una suposición completa.