module GHC.Runtime.Interpreter.Process ( -- * Low-level API callInterpProcess , readInterpProcess , writeInterpProcess -- * Message API , Message(..) , DelayedResponse (..) , sendMessage , sendMessageNoResponse , sendMessageDelayedResponse , sendAnyValue , receiveAnyValue , receiveDelayedResponse , receiveTHMessage ) where import GHC.Prelude import GHC.Runtime.Interpreter.Types import GHCi.Message import GHC.IO (catchException) import GHC.Utils.Panic import GHC.Utils.Exception as Ex import Data.Binary import System.Exit import System.Process data DelayedResponse a = DelayedResponse -- | Send a message to the interpreter process that doesn't expect a response sendMessageNoResponse :: ExtInterpInstance d -> Message () -> IO () sendMessageNoResponse i m = writeInterpProcess (instProcess i) (putMessage m) -- | Send a message to the interpreter that excepts a response sendMessage :: Binary a => ExtInterpInstance d -> Message a -> IO a sendMessage i m = callInterpProcess (instProcess i) m -- | Send a message to the interpreter process whose response is expected later -- -- This is useful to avoid forgetting to receive the value and to ensure that -- the type of the response isn't lost. Use receiveDelayedResponse to read it. sendMessageDelayedResponse :: ExtInterpInstance d -> Message a -> IO (DelayedResponse a) sendMessageDelayedResponse i m = do writeInterpProcess (instProcess i) (putMessage m) pure DelayedResponse -- | Send any value sendAnyValue :: Binary a => ExtInterpInstance d -> a -> IO () sendAnyValue i m = writeInterpProcess (instProcess i) (put m) -- | Expect a value to be received receiveAnyValue :: ExtInterpInstance d -> Get a -> IO a receiveAnyValue i get = readInterpProcess (instProcess i) get -- | Expect a delayed result to be received now receiveDelayedResponse :: Binary a => ExtInterpInstance d -> DelayedResponse a -> IO a receiveDelayedResponse i DelayedResponse = readInterpProcess (instProcess i) get -- | Expect a value to be received receiveTHMessage :: ExtInterpInstance d -> IO THMsg receiveTHMessage i = receiveAnyValue i getTHMessage -- ----------------------------------------------------------------------------- -- Low-level API -- | Send a 'Message' and receive the response from the interpreter process callInterpProcess :: Binary a => InterpProcess -> Message a -> IO a callInterpProcess i msg = remoteCall (interpPipe i) msg `catchException` \(e :: SomeException) -> handleInterpProcessFailure i e -- | Read a value from the interpreter process readInterpProcess :: InterpProcess -> Get a -> IO a readInterpProcess i get = readPipe (interpPipe i) get `catchException` \(e :: SomeException) -> handleInterpProcessFailure i e -- | Send a value to the interpreter process writeInterpProcess :: InterpProcess -> Put -> IO () writeInterpProcess i put = writePipe (interpPipe i) put `catchException` \(e :: SomeException) -> handleInterpProcessFailure i e handleInterpProcessFailure :: InterpProcess -> SomeException -> IO a handleInterpProcessFailure i e = do let hdl = interpHandle i ex <- getProcessExitCode hdl case ex of Just (ExitFailure n) -> throwIO (InstallationError ("External interpreter terminated (" ++ show n ++ ")")) _ -> do terminateProcess hdl _ <- waitForProcess hdl throw e