Widzę bardzo dziwne zachowanie, w którym bracketfunkcja Haskella zachowuje się różnie, w zależności od tego, stack runczy stack testjest 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 runi 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-optionsjest przekazywane do obu. - Pełne repozytorium demonstracyjne tutaj: https://github.com/thomasjm/bracket-issue
.stack-worki uruchomię go bezpośrednio, problem się nie pojawi. Zdarza się to tylko podczas biegania pod stack test.
stack testuruchamia 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.