Je vois un comportement très étrange où la bracketfonction de Haskell se comporte différemment selon qu'elle est utilisée stack runou non stack test.
Considérez le code suivant, où deux crochets imbriqués sont utilisés pour créer et nettoyer les conteneurs 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
                     )
          )
Lorsque j'exécute cela avec stack runet que j'interromps avec Ctrl+C, j'obtiens la sortie attendue:
Inside both brackets, sleeping!
^CInner release
container2
Done with inner release
Outer release
container1
Done with outer releaseEt je peux vérifier que les deux conteneurs Docker sont créés puis supprimés.
Cependant, si je colle exactement ce même code dans un test et que j'exécute stack test, seul (une partie de) le premier nettoyage se produit:
Inside both brackets, sleeping!
^CInner release
container2Il en résulte un conteneur Docker laissé en cours d'exécution sur ma machine. Que se passe-t-il?
- J'ai fait en sorte que les mêmes informations ghc-optionssoient transmises aux deux.
- Repo de démonstration complet ici: https://github.com/thomasjm/bracket-issue
.stack-worket l'exécute directement, le problème ne se produit pas. Cela ne se produit que lorsque vous courez sous stack test.
                stack testdémarre les threads de travail pour gérer les tests. 2) le gestionnaire SIGINT tue le fil principal. 3) Les programmes Haskell se terminent lorsque le thread principal le fait, ignorant tous les threads supplémentaires. 2 est le comportement par défaut sur SIGINT pour les programmes compilés par GHC. 3 est la façon dont les threads fonctionnent dans Haskell. 1 est une supposition complète.