1
1
{-# LANGUAGE DisambiguateRecordFields #-}
2
2
{-# LANGUAGE OverloadedStrings #-}
3
3
{-# LANGUAGE PatternSynonyms #-}
4
+ {-# LANGUAGE ScopedTypeVariables #-}
4
5
{-# LANGUAGE ViewPatterns #-}
5
6
6
7
-- | A module providing a backend that launches solvers as external processes.
7
8
module SMTLIB.Backends.Process
8
9
( Config (.. ),
9
10
Handle (.. ),
10
11
new ,
11
- wait ,
12
+ write ,
12
13
close ,
14
+ kill ,
13
15
with ,
14
16
toBackend ,
15
17
)
@@ -28,7 +30,7 @@ import qualified Data.ByteString.Char8 as BS
28
30
import qualified Data.ByteString.Lazy.Char8 as LBS
29
31
import Data.Default (Default , def )
30
32
import SMTLIB.Backends (Backend (.. ))
31
- import System.Exit (ExitCode )
33
+ import System.Exit (ExitCode ( ExitFailure ) )
32
34
import qualified System.IO as IO
33
35
import System.Process.Typed
34
36
( Process ,
@@ -106,23 +108,37 @@ new config = do
106
108
)
107
109
reportError' = reportError config . LBS. fromStrict
108
110
109
- -- | Wait for the process to exit and cleanup its resources .
110
- wait :: Handle -> IO ExitCode
111
- wait handle = do
112
- cancel $ errorReader handle
113
- waitExitCode $ process handle
111
+ -- | Send a command to the process without reading its response .
112
+ write :: Handle -> Builder -> IO ()
113
+ write handle cmd = do
114
+ hPutBuilder (getStdin $ process handle) $ cmd <> " \n "
115
+ IO. hFlush $ getStdin $ process handle
114
116
115
- -- | Terminate the process, wait for it to actually exit and cleanup its resources.
116
- -- Don't use this if you're manually stopping the solver process by sending an
117
- -- @(exit)@ command. Use `wait` instead.
118
- close :: Handle -> IO ()
117
+ -- | Cleanup the process' resources.
118
+ cleanup :: Handle -> IO ()
119
+ cleanup = cancel . errorReader
120
+
121
+ -- | Cleanup the process' resources, send it an @(exit)@ command and wait for it
122
+ -- to exit.
123
+ close :: Handle -> IO ExitCode
119
124
close handle = do
120
- cancel $ errorReader handle
125
+ cleanup handle
126
+ let p = process handle
127
+ ( do
128
+ write handle " (exit)"
129
+ waitExitCode p
130
+ )
131
+ `X.catch` \ (_ :: X. IOException ) -> do
132
+ stopProcess p
133
+ return $ ExitFailure 1
134
+
135
+ -- | Cleanup the process' resources and kill it immediately.
136
+ kill :: Handle -> IO ()
137
+ kill handle = do
138
+ cleanup handle
121
139
stopProcess $ process handle
122
140
123
- -- | Create a solver process, use it to make a computation and stop it.
124
- -- Don't use this if you're manually stopping the solver process by sending an
125
- -- @(exit)@ command. Use @\\config -> `System.IO.bracket` (`new` config) `wait`@ instead.
141
+ -- | Create a solver process, use it to make a computation and close it.
126
142
with ::
127
143
-- | The solver process' configuration.
128
144
Config ->
@@ -140,8 +156,7 @@ pattern c :< rest <- (BS.uncons -> Just (c, rest))
140
156
toBackend :: Handle -> Backend
141
157
toBackend handle =
142
158
Backend $ \ cmd -> do
143
- hPutBuilder (getStdin $ process handle) $ cmd <> " \n "
144
- IO. hFlush $ getStdin $ process handle
159
+ write handle cmd
145
160
toLazyByteString <$> continueNextLine (scanParen 0 ) mempty
146
161
where
147
162
-- scanParen read lines from the handle's output channel until it has detected
0 commit comments