Widzę bardzo dziwne zachowanie, w którym bracket
funkcja Haskella zachowuje się różnie, w zależności od tego, stack run
czy stack test
jest używana.
Rozważ następujący kod, w którym dwa zagnieżdżone nawiasy klamrowe są używane do tworzenia i czyszczenia kontenerów 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
)
)
Kiedy uruchamiam to stack run
i przerywam Ctrl+C
, otrzymuję oczekiwany wynik:
Inside both brackets, sleeping!
^CInner release
container2
Done with inner release
Outer release
container1
Done with outer release
I mogę zweryfikować, że oba kontenery Docker są tworzone, a następnie usuwane.
Jeśli jednak wkleję ten sam kod do testu i uruchomię stack test
, nastąpi tylko (część) pierwszego czyszczenia:
Inside both brackets, sleeping!
^CInner release
container2
Powoduje to, że na moim komputerze nadal działa kontener Docker. Co się dzieje?
- Upewniłem się, że dokładnie to samo
ghc-options
jest przekazywane do obu. - Pełne repozytorium demonstracyjne tutaj: https://github.com/thomasjm/bracket-issue
.stack-work
i uruchomię go bezpośrednio, problem się nie pojawi. Zdarza się to tylko podczas biegania pod stack test
.
stack test
uruchamia wątki robocze do obsługi testów. 2) moduł obsługi SIGINT zabija główny wątek. 3) Programy Haskell kończą się, gdy robi to główny wątek, ignorując wszelkie dodatkowe wątki. 2 jest domyślnym zachowaniem SIGINT dla programów skompilowanych przez GHC. 3 to, jak działają wątki w Haskell. 1 jest całkowitym domysłem.