summaryrefslogtreecommitdiff
path: root/source/Provers.hs
diff options
context:
space:
mode:
Diffstat (limited to 'source/Provers.hs')
-rw-r--r--source/Provers.hs1940
1 files changed, 0 insertions, 1940 deletions
diff --git a/source/Provers.hs b/source/Provers.hs
deleted file mode 100644
index 7a22e12..0000000
--- a/source/Provers.hs
+++ /dev/null
@@ -1,1940 +0,0 @@
-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards #-}
-
-module Provers
- ( Vampire
- , vampire
- , VampireTaskMode(..)
- , VampireStatus(..)
- , CanonicalAtpOutcome(..)
- , CanonicalAtpRejection(..)
- , VampireProtocolError(..)
- , classifyVampireProtocol
- , vampireStatusParser
- , TimeLimit(..)
- , MemoryLimit(..)
- , defaultTimeLimit
- , defaultMemoryLimit
- , ProverAnswer
- ( CounterSatisfiable
- , ContradictoryAxioms
- , Uncertain
- , Error
- )
- , pattern Yes
- , ProverStream(..)
- , ProverOutputStream(..)
- , ProverProcessError(..)
- , VerificationDialect(..)
- , PreparedVerificationRequest
- , preparedVerificationDialect
- , preparedVerificationText
- , preparedVerificationBytes
- , preparedVerificationByteCount
- , preparedVerificationRequestId
- , PreparedTypedProverTask
- , prepareTypedProverTask
- , preparedTypedProverLogicalProblem
- , preparedTypedProverRequest
- , AcceptedVampireRun
- , acceptedVampireRequestId
- , provedVampireRun
- , runPreparedTypedProver
- , runPreparedTypedProverWithObserver
- , EffectiveJobs
- , effectiveJobs
- , effectiveJobsValue
- , JobsSelection(..)
- , selectEffectiveJobs
- , WorkPosition
- , workPosition
- , workPositionModuleOrdinal
- , workPositionLocalRequestOrdinal
- , VampireExecutor
- , VampireExecutorFault
- , VampireExecutorObservation(..)
- , VampireRequestOwner
- , VampireHandle
- , VampireTerminal(..)
- , renderVampireTerminalDiagnostic
- , VampireCompletion(..)
- , withVampireExecutor
- , withVampireRequestOwner
- , submitVampireRequest
- , awaitVampireRequest
- , awaitPreparedVampireRequest
- , cancelVampireRequest
- , runPreparedTypedProverWithExecutor
- , vampireExecutorObservation
- ) where
-
-import Base
-import Checking.Authority qualified as Authority
-import Checking.Backend.Problem
-import Checking.Backend.Tptp
-
-import Control.Concurrent.STM
- ( STM
- , TBQueue
- , TMVar
- , TVar
- , atomically
- , check
- , modifyTVar'
- , newEmptyTMVarIO
- , newTBQueueIO
- , newTVarIO
- , orElse
- , readTMVar
- , readTBQueue
- , readTVar
- , readTVarIO
- , throwSTM
- , tryPutTMVar
- , tryReadTMVar
- , writeTBQueue
- , writeTVar
- )
-import Control.Exception
- ( AsyncException
- , IOException
- , SomeException
- , displayException
- , fromException
- )
-import Control.Exception qualified as Exception
-import Control.Monad (replicateM, unless, when)
-import Control.Monad.Logger
-import Data.ByteString qualified as ByteString
-import Data.IntMap.Strict qualified as IntMap
-import Data.IORef
- ( IORef
- , atomicModifyIORef'
- , newIORef
- , readIORef
- , writeIORef
- )
-import Data.Set qualified as Set
-import Data.Text qualified as Text
-import Data.Text.Encoding qualified as TextEncoding
-import Data.Text.Encoding.Error qualified as TextEncodingError
-import Data.Time
-import Numeric.Natural (Natural)
-import System.Exit (ExitCode(..))
-import System.Posix.Signals (sigKILL, signalProcessGroup)
-import System.Posix.Types (ProcessGroupID)
-import System.Process
- ( CreateProcess(..)
- , ProcessHandle
- , StdStream(CreatePipe)
- , getPid
- , proc
- , waitForProcess
- , withCreateProcess
- )
-import System.Timeout qualified as Timeout
-import Text.Megaparsec
-import Text.Megaparsec.Char qualified as Char
-import TextBuilder
-import UnliftIO.Async
- ( async
- , cancel
- , concurrently
- , waitCatch
- , waitCatchSTM
- )
-
-data Vampire = Vampire
- { vampireExecutable :: FilePath
- , vampireTimeLimit :: TimeLimit
- , vampireMemoryLimit :: MemoryLimit
- }
- deriving (Show, Eq)
-
-newtype TimeLimit = Seconds Word64 deriving (Show, Eq, Num)
-newtype MemoryLimit = Megabytes Word64 deriving (Show, Eq)
-
-defaultTimeLimit :: TimeLimit
-defaultTimeLimit = Seconds 10
-
-defaultMemoryLimit :: MemoryLimit
-defaultMemoryLimit = Megabytes 5000
-
-vampire :: FilePath -> TimeLimit -> MemoryLimit -> Vampire
-vampire = Vampire
-
-vampireArguments :: Vampire -> [String]
-vampireArguments Vampire{..} =
- [ "--input_syntax", "tptp"
- , "--mode", "casc"
- , "--time_limit", toSeconds vampireTimeLimit
- , "--memory_limit", toMegabytes vampireMemoryLimit
- , "--cores", "2"
- ]
-
--- | A strictly positive invocation-local bound for module pipelines and
--- top-level Vampire invocations.
-newtype EffectiveJobs = EffectiveJobs Int
- deriving (Show, Eq, Ord)
-
-effectiveJobs :: Int -> Maybe EffectiveJobs
-effectiveJobs amount
- | amount > 0 = Just (EffectiveJobs amount)
- | otherwise = Nothing
-
-effectiveJobsValue :: EffectiveJobs -> Int
-effectiveJobsValue (EffectiveJobs amount) = amount
-
--- | The effective worker policy selected for one invocation. Processor
--- discovery is operational evidence only and enters no durable identity.
-data JobsSelection = JobsSelection
- { jobsSelectionDetectedProcessors :: !(Maybe Int)
- , jobsSelectionEffectiveJobs :: !EffectiveJobs
- , jobsSelectionWasOverridden :: !Bool
- }
- deriving (Show, Eq)
-
--- | Select the worker bound, with processor discovery injected for focused
--- testing. Asynchronous cancellation is never mistaken for failed discovery.
-selectEffectiveJobs
- :: Maybe EffectiveJobs
- -> IO Int
- -> IO JobsSelection
-selectEffectiveJobs override detectProcessors =
- case override of
- Just selected ->
- pure
- JobsSelection
- { jobsSelectionDetectedProcessors = Nothing
- , jobsSelectionEffectiveJobs = selected
- , jobsSelectionWasOverridden = True
- }
- Nothing -> do
- detectedResult <- Exception.try detectProcessors
- case detectedResult of
- Left detectionFailure
- | Just asynchronous <-
- (fromException detectionFailure
- :: Maybe AsyncException) ->
- Exception.throwIO asynchronous
- | otherwise ->
- fallback
- Right detectedRaw ->
- let detected = max 1 detectedRaw
- -- Two portfolio workers plus one module pipeline
- -- nominally consume about three logical CPUs.
- automaticJobs = max 1 ((detected + 1) `div` 3)
- in pure
- JobsSelection
- { jobsSelectionDetectedProcessors =
- Just detected
- , jobsSelectionEffectiveJobs =
- EffectiveJobs automaticJobs
- , jobsSelectionWasOverridden = False
- }
- where
- fallback =
- pure
- JobsSelection
- { jobsSelectionDetectedProcessors = Nothing
- , jobsSelectionEffectiveJobs = EffectiveJobs 1
- , jobsSelectionWasOverridden = False
- }
-
--- | Stable runtime diagnostic position. It is deliberately separate from
--- request, validation, cache, and mathematical identities.
-data WorkPosition = WorkPosition !Natural !Natural
- deriving (Show, Eq, Ord)
-
-workPosition :: Natural -> Natural -> WorkPosition
-workPosition = WorkPosition
-
-workPositionModuleOrdinal :: WorkPosition -> Natural
-workPositionModuleOrdinal (WorkPosition moduleOrdinal _localOrdinal) =
- moduleOrdinal
-
-workPositionLocalRequestOrdinal :: WorkPosition -> Natural
-workPositionLocalRequestOrdinal (WorkPosition _moduleOrdinal localOrdinal) =
- localOrdinal
-
-toSeconds :: TimeLimit -> String
-toSeconds (Seconds secs) = show secs
-
-toMegabytes :: MemoryLimit -> String
-toMegabytes (Megabytes mbs) = show mbs
-
-data VampireTaskMode
- = DirectTask
- | IndirectTask
- deriving (Show, Eq)
-
-data VampireStatus
- = StatusTheorem
- | StatusCounterSatisfiable
- | StatusContradictoryAxioms
- | StatusTimeout
- | StatusResourceOut
- | StatusGaveUp
- | StatusUnknown
- | UnsupportedStatus Text
- deriving (Show, Eq)
-
-data CanonicalAtpOutcome
- = Proved
- | Counterexample
- | ContradictoryInput
- | Indeterminate
- deriving (Show, Eq, Ord)
-
--- | The closed non-accepting subset of canonical ATP outcomes. Protocol
--- disagreement is represented separately from a semantic non-proof, while an
--- accepting result is unrepresentable here.
-data CanonicalAtpRejection
- = RejectedCounterexample
- | RejectedContradictoryInput
- | RejectedIndeterminate
- deriving (Show, Eq)
-
-data VampireProtocolError
- = UnsuccessfulVampireExit ExitCode
- | UnsupportedVampireStatuses (Set Text)
- | MalformedVampireStatusLines (Set Text)
- | MissingVampireOutcome
- | ConflictingTerminalOutcomes (Set CanonicalAtpOutcome)
- deriving (Show, Eq)
-
-data CompleteCapturedStreams = CompleteCapturedStreams
- { completedStdout :: !Text
- , completedStderr :: !Text
- }
- deriving (Show, Eq)
-
-data CompletedTranscript = CompletedTranscript
- { completedExitCode :: !ExitCode
- , completedStreams :: !CompleteCapturedStreams
- }
- deriving (Show, Eq)
-
-data PartialCapturedStreams = PartialCapturedStreams
- { partialStdout :: !ByteString.ByteString
- , partialStderr :: !ByteString.ByteString
- }
- deriving (Eq)
-
-instance Show PartialCapturedStreams where
- showsPrec precedence PartialCapturedStreams{..} =
- showParen (precedence > applicationPrecedence)
- ( showString "PartialCapturedStreams "
- . shows
- ( ByteString.length partialStdout
- , ByteString.length partialStderr
- )
- )
-
-data BoundedBytes = BoundedBytes
- { boundedBytesOriginalCount :: !Int
- , boundedBytesRetained :: !ByteString.ByteString
- , boundedBytesTruncated :: !Bool
- }
- deriving (Eq)
-
-instance Show BoundedBytes where
- showsPrec precedence BoundedBytes{..} =
- showParen (precedence > applicationPrecedence)
- ( showString "BoundedBytes "
- . shows
- ( boundedBytesOriginalCount
- , ByteString.length boundedBytesRetained
- , boundedBytesTruncated
- )
- )
-
-data BoundedCapturedStreams = BoundedCapturedStreams
- { boundedStdout :: !BoundedBytes
- , boundedStderr :: !BoundedBytes
- }
- deriving (Show, Eq)
-
-data BoundedDiagnostic = BoundedDiagnostic
- { boundedDiagnosticSummary :: !Text
- , boundedDiagnosticStdout :: !BoundedBytes
- , boundedDiagnosticStderr :: !BoundedBytes
- }
- deriving (Show, Eq)
-
--- | Classify parsed Vampire statuses without depending on stream or line order.
-classifyVampireProtocol
- :: VampireTaskMode
- -> ExitCode
- -> [VampireStatus]
- -> Either VampireProtocolError CanonicalAtpOutcome
-classifyVampireProtocol mode exitCode statuses =
- case exitCode of
- ExitFailure _ ->
- Left (UnsuccessfulVampireExit exitCode)
- ExitSuccess
- | not (Set.null unsupported) ->
- Left (UnsupportedVampireStatuses unsupported)
- | otherwise ->
- classifyOutcomes
- (Set.fromList
- [ outcome
- | status <- statuses
- , Just outcome <- [vampireStatusOutcome mode status]
- ])
- where
- unsupported =
- Set.fromList
- [ status
- | UnsupportedStatus status <- statuses
- ]
-
-classifyOutcomes
- :: Set CanonicalAtpOutcome
- -> Either VampireProtocolError CanonicalAtpOutcome
-classifyOutcomes outcomes =
- case Set.toList terminalOutcomes of
- [] ->
- if Indeterminate `Set.member` outcomes
- then Right Indeterminate
- else Left MissingVampireOutcome
- [outcome] ->
- Right outcome
- _ ->
- Left (ConflictingTerminalOutcomes terminalOutcomes)
- where
- terminalOutcomes = Set.delete Indeterminate outcomes
-
-vampireStatusOutcome
- :: VampireTaskMode
- -> VampireStatus
- -> Maybe CanonicalAtpOutcome
-vampireStatusOutcome mode = \case
- StatusTheorem ->
- Just Proved
- StatusCounterSatisfiable ->
- Just Counterexample
- StatusContradictoryAxioms ->
- Just case mode of
- DirectTask -> ContradictoryInput
- IndirectTask -> Proved
- StatusTimeout ->
- Just Indeterminate
- StatusResourceOut ->
- Just Indeterminate
- StatusGaveUp ->
- Just Indeterminate
- StatusUnknown ->
- Just Indeterminate
- UnsupportedStatus{} ->
- Nothing
-
-data VerificationDialect
- = VerificationFof
- | VerificationTh0
- deriving (Show, Eq)
-
-data PreparedVerificationRequest = PreparedVerificationRequest
- { preparedVerificationDialect :: !VerificationDialect
- , preparedVerificationMode :: !VampireTaskMode
- , preparedVerificationInput :: !ByteString.ByteString
- , preparedVerificationIdentity :: !Authority.PreparedRequestId
- }
- deriving (Eq)
-
--- | Decode the one canonical serialized request only at a textual boundary.
--- Prepared requests deliberately do not retain an equivalent 'Text' copy.
-preparedVerificationText :: PreparedVerificationRequest -> Text
-preparedVerificationText request =
- let encoded = preparedVerificationInput request
- withoutFinalNewline =
- case ByteString.unsnoc encoded of
- Just (body, 10) -> body
- _ -> encoded
- in TextEncoding.decodeUtf8 withoutFinalNewline
-
-preparedVerificationByteCount
- :: PreparedVerificationRequest
- -> Int
-preparedVerificationByteCount =
- ByteString.length . preparedVerificationInput
-
-preparedVerificationBytes
- :: PreparedVerificationRequest
- -> ByteString.ByteString
-preparedVerificationBytes =
- preparedVerificationInput
-
-preparedVerificationRequestId
- :: PreparedVerificationRequest
- -> Authority.PreparedRequestId
-preparedVerificationRequestId =
- preparedVerificationIdentity
-
-data PreparedTypedProverTask ref local origin global =
- PreparedTypedProverTask
- !(TypedProblem ref local origin global)
- !PreparedVerificationRequest
- !Text
-
-prepareTypedProverTask
- :: (Ord local, Ord global)
- => VampireTaskMode
- -> TypedProblem ref local origin global
- -> Either
- (TypedTptpPreparationError local global)
- (PreparedTypedProverTask ref local origin global)
-prepareTypedProverTask mode problem = do
- prepared <-
- prepareTypedTptpProblem problem
- let dialect =
- case preparedTypedTptpRoute prepared of
- RouteFof -> VerificationFof
- RouteTh0 -> VerificationTh0
- bytes =
- TextEncoding.encodeUtf8
- (preparedTypedTptpTextNewline prepared)
- identity =
- Authority.preparedRequestId
- (case dialect of
- VerificationFof -> Authority.PreparedRequestFof
- VerificationTh0 -> Authority.PreparedRequestTh0)
- (case mode of
- DirectTask -> Authority.PreparedRequestDirect
- IndirectTask -> Authority.PreparedRequestIndirect)
- bytes
- preparedRequest =
- PreparedVerificationRequest
- { preparedVerificationDialect = dialect
- , preparedVerificationMode = mode
- , preparedVerificationInput = bytes
- , preparedVerificationIdentity = identity
- }
- pure
- (PreparedTypedProverTask
- problem
- preparedRequest
- (preparedTypedTptpConjectureText prepared))
-
-preparedTypedProverLogicalProblem
- :: PreparedTypedProverTask ref local origin global
- -> TypedProblem ref local origin global
-preparedTypedProverLogicalProblem
- (PreparedTypedProverTask
- problem
- _request
- _conjecture) =
- problem
-
-preparedTypedProverRequest
- :: PreparedTypedProverTask ref local origin global
- -> PreparedVerificationRequest
-preparedTypedProverRequest
- (PreparedTypedProverTask
- _problem
- request
- _conjecture) =
- request
-
-data AcceptedVampireRun = AcceptedVampireRun
- { acceptedVampireRequestId :: !Authority.PreparedRequestId }
- deriving (Eq)
-
-data ProverAnswer
- = ProvedAnswer AcceptedVampireRun
- | CounterSatisfiable Text
- | ContradictoryAxioms Text
- | Uncertain Text
- | Error Text Text
- deriving (Eq)
-
-pattern Yes :: ProverAnswer
-pattern Yes <- ProvedAnswer _
-
-{-# COMPLETE Yes, CounterSatisfiable, ContradictoryAxioms, Uncertain, Error #-}
-
-instance Show ProverAnswer where
- showsPrec _ (ProvedAnswer _) =
- showString "Yes"
- showsPrec precedence (CounterSatisfiable task) =
- showParen (precedence > applicationPrecedence)
- (showString "CounterSatisfiable "
- . showsPrec (applicationPrecedence + 1) task)
- showsPrec precedence (ContradictoryAxioms task) =
- showParen (precedence > applicationPrecedence)
- (showString "ContradictoryAxioms "
- . showsPrec (applicationPrecedence + 1) task)
- showsPrec precedence (Uncertain task) =
- showParen (precedence > applicationPrecedence)
- (showString "Uncertain "
- . showsPrec (applicationPrecedence + 1) task)
- showsPrec precedence (Error errorLabel message) =
- showParen (precedence > applicationPrecedence)
- (showString "Error "
- . showsPrec (applicationPrecedence + 1) errorLabel
- . showChar ' '
- . showsPrec (applicationPrecedence + 1) message)
-
-provedVampireRun :: ProverAnswer -> Maybe AcceptedVampireRun
-provedVampireRun = \case
- ProvedAnswer accepted ->
- Just accepted
- CounterSatisfiable{} ->
- Nothing
- ContradictoryAxioms{} ->
- Nothing
- Uncertain{} ->
- Nothing
- Error{} ->
- Nothing
-
-applicationPrecedence :: Int
-applicationPrecedence = 10
-
-data ProverStream
- = ProverStdin
- | ProverStdout
- | ProverStderr
- deriving (Show, Eq)
-
-data ProverOutputStream
- = ProverOutputStdout
- | ProverOutputStderr
- deriving (Show, Eq)
-
-data ProverProcessError
- = ProverLaunchFailed !FilePath !Text
- | ProverCommunicationFailed !FilePath !ProverStream !Text
- | ProverOutputMalformedUtf8 !FilePath !ProverOutputStream !Text
- | ProverTimedOut
- !FilePath
- !TimeLimit
- !BoundedCapturedStreams
- | ProverOutputLimitExceeded
- !FilePath
- !ProverOutputStream
- !BoundedCapturedStreams
- | ProverTerminatedBySignal
- !FilePath
- !Int
- !BoundedCapturedStreams
- | ProverLifecycleFailed !FilePath !Text
- deriving (Show, Eq)
-
-data SupervisorAbort
- = SupervisorCommunicationFailed !ProverStream !Text
- | SupervisorOutputLimitExceeded !ProverOutputStream
- | SupervisorLifecycleFailed !Text
- deriving (Show)
-
-instance Exception.Exception SupervisorAbort
-
-data CaptureBuffer = CaptureBuffer
- { captureByteCount :: !Int
- , captureChunksReversed :: ![ByteString.ByteString]
- }
-
-vampireOutputByteLimit :: Int
-vampireOutputByteLimit =
- 16 * 1024 * 1024
-
-vampireWallGraceSeconds :: Integer
-vampireWallGraceSeconds =
- 2
-
-captureChunkSize :: Int
-captureChunkSize =
- 32 * 1024
-
-runPreparedTypedProver
- :: (MonadIO io, MonadLogger io)
- => Vampire
- -> PreparedTypedProverTask ref local origin global
- -> io (Either ProverProcessError ProverAnswer)
-runPreparedTypedProver
- vampireCommand =
- runPreparedTypedProverWithObserver
- (\_request -> pure ())
- vampireCommand
-
-runPreparedTypedProverWithObserver
- :: (MonadIO io, MonadLogger io)
- => (PreparedVerificationRequest -> IO ())
- -> Vampire
- -> PreparedTypedProverTask ref local origin global
- -> io (Either ProverProcessError ProverAnswer)
-runPreparedTypedProverWithObserver
- observer
- vampireCommand
- (PreparedTypedProverTask
- _problem
- preparedRequest
- conjecture) = do
- startTime <- liftIO getCurrentTime
- terminal <- liftIO
- (runPreparedVerificationTerminal
- observer
- vampireCommand
- preparedRequest)
- answer <- liftIO
- (completionProverResult
- preparedRequest
- (VampireCompletion terminal))
- endTime <- liftIO getCurrentTime
- let duration =
- timeDifferenceToText
- startTime
- endTime
- dialect =
- case preparedVerificationDialect preparedRequest of
- VerificationFof ->
- "FOF"
- VerificationTh0 ->
- "TH0"
- logInfoN
- (duration
- <> " "
- <> conjecture
- <> " [typed "
- <> dialect
- <> "]")
- pure answer
-
--- | Invocation-local bounded owner of Vampire subprocesses. The finite queue
--- carries only immutable prepared bytes plus runtime diagnostic position;
--- checker builders and typed tasks never cross this boundary.
-data VampireExecutor = VampireExecutor
- { executorQueue :: !(TBQueue ExecutorJob)
- , executorState :: !(TVar VampireExecutorState)
- , executorNextHandle :: !(TVar Int)
- , executorCommand :: !Vampire
- , executorRequestObserver
- :: !(WorkPosition -> PreparedVerificationRequest -> IO ())
- , executorRuntime :: !(TVar VampireExecutorRuntime)
- }
-
-data ExecutorJob = ExecutorJob
- { executorJobPosition :: !WorkPosition
- , executorJobRequest :: !PreparedVerificationRequest
- , executorJobCancelled :: !(TVar Bool)
- , executorJobStarted :: !(TVar Bool)
- , executorJobCompletion
- :: !(TMVar VampireCompletion)
- }
-
-data VampireExecutorState
- = ExecutorRunning
- | ExecutorClosing
- | ExecutorFaulted !VampireExecutorFault
-
--- | A global executor fault is distinct from one request's declared process
--- or protocol outcome. Its bounded message carries no process transcript.
-newtype VampireExecutorFault = VampireExecutorFault Text
- deriving (Show)
-
-instance Exception.Exception VampireExecutorFault
-
-data VampireRequestOwner = VampireRequestOwner
- { requestOwnerExecutor :: !VampireExecutor
- , requestOwnerState :: !(TVar VampireRequestOwnerState)
- }
-
-data VampireRequestOwnerState
- = RequestOwnerOpen !(IntMap VampireHandle)
- | RequestOwnerClosed
-
--- | An opaque, owner-scoped completion handle. It deliberately retains no
--- prepared request, command, transcript, builder, or parser state.
-data VampireHandle = VampireHandle
- { vampireHandleOrdinal :: !Int
- , vampireHandleOwner :: !VampireRequestOwner
- , vampireHandleCancelled :: !(TVar Bool)
- , vampireHandleStarted :: !(TVar Bool)
- , vampireHandleCompletion :: !(TMVar VampireCompletion)
- }
-
-data VampireTerminal
- = VampireAccepted !Authority.PreparedRequestId
- | VampireRejected !CanonicalAtpRejection !BoundedDiagnostic
- | VampireProtocolFailed !BoundedDiagnostic
- | VampireProcessFailed !ProverProcessError
- | VampireCancelled
- deriving (Show, Eq)
-
-newtype VampireCompletion = VampireCompletion
- { vampireCompletionTerminal :: VampireTerminal }
- deriving (Show, Eq)
-
-renderVampireTerminalDiagnostic :: VampireTerminal -> Maybe Text
-renderVampireTerminalDiagnostic = \case
- VampireAccepted{} -> Nothing
- VampireRejected _rejection diagnostic ->
- Just (renderBoundedDiagnostic diagnostic)
- VampireProtocolFailed diagnostic ->
- Just (renderBoundedDiagnostic diagnostic)
- VampireProcessFailed processFailure ->
- Just (Text.pack (show processFailure))
- VampireCancelled -> Nothing
-
-data VampireExecutorRuntime = VampireExecutorRuntime
- { runtimeSubmittedCount :: !Int
- , runtimeRunCount :: !Int
- , runtimeLiveCount :: !Int
- , runtimeMaximumLiveCount :: !Int
- , runtimeFirstStartNanoseconds :: !(Maybe Word64)
- , runtimeFinalSubmissionNanoseconds :: !(Maybe Word64)
- , runtimeFinalCompletionNanoseconds :: !(Maybe Word64)
- , runtimeExecutionNanoseconds :: !Word64
- , runtimeLongestExecutionNanoseconds :: !Word64
- }
-
-data VampireExecutorObservation = VampireExecutorObservation
- { vampireExecutorSubmittedCount :: !Int
- , vampireExecutorRunCount :: !Int
- , vampireExecutorMaximumLiveCount :: !Int
- , vampireExecutorFirstStartNanoseconds :: !(Maybe Word64)
- , vampireExecutorFinalSubmissionNanoseconds :: !(Maybe Word64)
- , vampireExecutorFinalCompletionNanoseconds :: !(Maybe Word64)
- , vampireExecutorExecutionNanoseconds :: !Word64
- , vampireExecutorLongestExecutionNanoseconds :: !Word64
- }
- deriving (Show, Eq)
-
-data VampireExecutorClosed = VampireExecutorClosed
- deriving (Show)
-
-instance Exception.Exception VampireExecutorClosed
-
-data VampireRequestOwnerClosed = VampireRequestOwnerClosed
- deriving (Show)
-
-instance Exception.Exception VampireRequestOwnerClosed
-
-data RequestObserverFailure = RequestObserverFailure SomeException
-
-instance Show RequestObserverFailure where
- show (RequestObserverFailure observerError) =
- "request observer failed: " <> displayException observerError
-
-instance Exception.Exception RequestObserverFailure
-
-initialVampireExecutorRuntime :: VampireExecutorRuntime
-initialVampireExecutorRuntime =
- VampireExecutorRuntime
- { runtimeSubmittedCount = 0
- , runtimeRunCount = 0
- , runtimeLiveCount = 0
- , runtimeMaximumLiveCount = 0
- , runtimeFirstStartNanoseconds = Nothing
- , runtimeFinalSubmissionNanoseconds = Nothing
- , runtimeFinalCompletionNanoseconds = Nothing
- , runtimeExecutionNanoseconds = 0
- , runtimeLongestExecutionNanoseconds = 0
- }
-
--- | Bracket exactly the selected number of workers. Cancelling the bracket
--- cancels each worker; a worker owning a subprocess in turn terminates and
--- reaps that process group through the ordinary supervisor boundary.
-withVampireExecutor
- :: EffectiveJobs
- -> Vampire
- -> (WorkPosition -> PreparedVerificationRequest -> IO ())
- -> (VampireExecutor -> IO value)
- -> IO value
-withVampireExecutor selected command observer action =
- Exception.bracket acquire release (action . fst)
- where
- workerCount = effectiveJobsValue selected
-
- acquire = do
- queue <- newTBQueueIO
- (fromIntegral workerCount * 2)
- state <- newTVarIO ExecutorRunning
- nextHandle <- newTVarIO 0
- runtime <- newTVarIO initialVampireExecutorRuntime
- let executor =
- VampireExecutor
- { executorQueue = queue
- , executorState = state
- , executorNextHandle = nextHandle
- , executorCommand = command
- , executorRequestObserver = observer
- , executorRuntime = runtime
- }
- workers <- replicateM workerCount
- (async (superviseExecutorWorker executor))
- pure (executor, workers)
-
- release (executor, workers) = do
- atomically do
- state <- readTVar (executorState executor)
- case state of
- ExecutorRunning ->
- writeTVar (executorState executor) ExecutorClosing
- ExecutorClosing ->
- pure ()
- ExecutorFaulted{} ->
- pure ()
- traverse_ cancel workers
- traverse_ waitCatch workers
-
--- | Bracket the complete set of handles submitted by one module pipeline.
--- Normal early return, failure, and asynchronous cancellation all cancel and
--- reap the remaining owned work before the scope is left.
-withVampireRequestOwner
- :: VampireExecutor
- -> (VampireRequestOwner -> IO value)
- -> IO value
-withVampireRequestOwner executor =
- Exception.bracket acquire release
- where
- acquire =
- VampireRequestOwner executor
- <$> newTVarIO (RequestOwnerOpen IntMap.empty)
-
- release owner = Exception.mask_ do
- handles <- atomically do
- state <- readTVar (requestOwnerState owner)
- case state of
- RequestOwnerClosed ->
- pure []
- RequestOwnerOpen owned -> do
- writeTVar (requestOwnerState owner) RequestOwnerClosed
- pure (IntMap.elems owned)
- traverse_ signalVampireCancellation handles
- traverse_ awaitVampireCancellation handles
- traverse_ deregisterVampireHandle handles
-
-runPreparedTypedProverWithExecutor
- :: (MonadIO io, MonadLogger io)
- => VampireRequestOwner
- -> WorkPosition
- -> PreparedTypedProverTask ref local origin global
- -> io (Either ProverProcessError ProverAnswer)
-runPreparedTypedProverWithExecutor
- owner
- position
- (PreparedTypedProverTask
- _problem
- preparedRequest
- conjecture) = do
- startTime <- liftIO getCurrentTime
- answer <- liftIO do
- handle <- submitVampireRequest owner position preparedRequest
- completion <-
- awaitVampireRequest handle
- `Exception.onException` cancelVampireRequest handle
- completionProverResult preparedRequest completion
- endTime <- liftIO getCurrentTime
- let dialect =
- case preparedVerificationDialect preparedRequest of
- VerificationFof -> "FOF"
- VerificationTh0 -> "TH0"
- logInfoN
- (timeDifferenceToText startTime endTime
- <> " "
- <> conjecture
- <> " [typed "
- <> dialect
- <> "]")
- pure answer
-
-vampireExecutorObservation
- :: VampireExecutor
- -> IO VampireExecutorObservation
-vampireExecutorObservation executor = do
- runtime <- readTVarIO (executorRuntime executor)
- pure
- VampireExecutorObservation
- { vampireExecutorSubmittedCount = runtimeSubmittedCount runtime
- , vampireExecutorRunCount = runtimeRunCount runtime
- , vampireExecutorMaximumLiveCount =
- runtimeMaximumLiveCount runtime
- , vampireExecutorFirstStartNanoseconds =
- runtimeFirstStartNanoseconds runtime
- , vampireExecutorFinalSubmissionNanoseconds =
- runtimeFinalSubmissionNanoseconds runtime
- , vampireExecutorFinalCompletionNanoseconds =
- runtimeFinalCompletionNanoseconds runtime
- , vampireExecutorExecutionNanoseconds =
- runtimeExecutionNanoseconds runtime
- , vampireExecutorLongestExecutionNanoseconds =
- runtimeLongestExecutionNanoseconds runtime
- }
-
-submitVampireRequest
- :: VampireRequestOwner
- -> WorkPosition
- -> PreparedVerificationRequest
- -> IO VampireHandle
-submitVampireRequest owner position request = do
- -- Force the compact queue payload before masked ownership acquisition.
- _ <- Exception.evaluate (preparedVerificationByteCount request)
- _ <- Exception.evaluate (preparedVerificationRequestId request)
- Exception.mask_ do
- cancelled <- newTVarIO False
- started <- newTVarIO False
- completion <- newEmptyTMVarIO
- let executor = requestOwnerExecutor owner
- handle <- atomically do
- executorStatus <- readTVar (executorState executor)
- case executorStatus of
- ExecutorClosing -> throwSTM VampireExecutorClosed
- ExecutorFaulted fault -> throwSTM fault
- ExecutorRunning -> pure ()
- ownerStatus <- readTVar (requestOwnerState owner)
- owned <- case ownerStatus of
- RequestOwnerClosed -> throwSTM VampireRequestOwnerClosed
- RequestOwnerOpen current -> pure current
- ordinal <- readTVar (executorNextHandle executor)
- writeTVar (executorNextHandle executor) (ordinal + 1)
- let acquired =
- VampireHandle
- { vampireHandleOrdinal = ordinal
- , vampireHandleOwner = owner
- , vampireHandleCancelled = cancelled
- , vampireHandleStarted = started
- , vampireHandleCompletion = completion
- }
- job =
- ExecutorJob
- { executorJobPosition = position
- , executorJobRequest = request
- , executorJobCancelled = cancelled
- , executorJobStarted = started
- , executorJobCompletion = completion
- }
- writeTBQueue (executorQueue executor) job
- writeTVar
- (requestOwnerState owner)
- (RequestOwnerOpen (IntMap.insert ordinal acquired owned))
- modifyTVar'
- (executorRuntime executor)
- (\runtime ->
- runtime
- { runtimeSubmittedCount =
- runtimeSubmittedCount runtime + 1
- })
- pure acquired
- submitted <- getMonotonicTimeNSec
- atomically
- (modifyTVar'
- (executorRuntime executor)
- (\runtime ->
- runtime
- { runtimeFinalSubmissionNanoseconds =
- Just
- (maybe submitted
- (max submitted)
- (runtimeFinalSubmissionNanoseconds
- runtime))
- }))
- pure handle
-
-awaitVampireRequest :: VampireHandle -> IO VampireCompletion
-awaitVampireRequest handle = Exception.mask \restore -> do
- let executor = requestOwnerExecutor (vampireHandleOwner handle)
- completion <- restore (atomically do
- executorStatus <- readTVar (executorState executor)
- case executorStatus of
- ExecutorFaulted fault -> throwSTM fault
- ExecutorRunning -> readTMVar (vampireHandleCompletion handle)
- ExecutorClosing ->
- (readTMVar (vampireHandleCompletion handle))
- `orElse` throwSTM VampireExecutorClosed)
- deregisterVampireHandle handle
- pure completion
-
--- | Await one already submitted request and recover the ordinary typed
--- prover result. The supplied immutable request is checked against an
--- accepting terminal before any caller may treat the result as evidence.
-awaitPreparedVampireRequest
- :: PreparedVerificationRequest
- -> VampireHandle
- -> IO (Either ProverProcessError ProverAnswer)
-awaitPreparedVampireRequest request handle =
- awaitVampireRequest handle >>= completionProverResult request
-
-cancelVampireRequest :: VampireHandle -> IO ()
-cancelVampireRequest handle = Exception.mask_ do
- signalVampireCancellation handle
- awaitVampireCancellation handle
- deregisterVampireHandle handle
-
-signalVampireCancellation :: VampireHandle -> IO ()
-signalVampireCancellation handle = do
- let executor = requestOwnerExecutor (vampireHandleOwner handle)
- completion = VampireCompletion VampireCancelled
- _ <- Exception.evaluate completion
- published <- getMonotonicTimeNSec
- atomically do
- writeTVar (vampireHandleCancelled handle) True
- started <- readTVar (vampireHandleStarted handle)
- unless started
- (void
- (publishVampireTerminalSTM
- executor
- (vampireHandleCompletion handle)
- published
- completion))
-
-awaitVampireCancellation :: VampireHandle -> IO ()
-awaitVampireCancellation handle =
- -- A running worker acknowledges ordinary cancellation only after
- -- terminating and reaping its owned process group. After a global fault,
- -- the worker lifecycle flag is the acknowledgement because the terminal
- -- cell deliberately remains subordinate to that fault.
- atomically do
- executorStatus <- readTVar
- (executorState (requestOwnerExecutor (vampireHandleOwner handle)))
- case executorStatus of
- ExecutorFaulted{} -> do
- stillRunning <- readTVar (vampireHandleStarted handle)
- check (not stillRunning)
- _ -> void (readTMVar (vampireHandleCompletion handle))
-
-deregisterVampireHandle :: VampireHandle -> IO ()
-deregisterVampireHandle handle = atomically do
- let owner = vampireHandleOwner handle
- state <- readTVar (requestOwnerState owner)
- case state of
- RequestOwnerClosed -> pure ()
- RequestOwnerOpen owned ->
- writeTVar
- (requestOwnerState owner)
- (RequestOwnerOpen
- (IntMap.delete (vampireHandleOrdinal handle) owned))
-
-superviseExecutorWorker :: VampireExecutor -> IO ()
-superviseExecutorWorker executor = do
- result <- Exception.try (executorWorker executor)
- case result of
- Right () -> pure ()
- Left workerFailure -> do
- state <- readTVarIO (executorState executor)
- case state of
- ExecutorRunning ->
- recordExecutorFault executor workerFailure
- ExecutorClosing ->
- pure ()
- ExecutorFaulted{} ->
- pure ()
-
-recordExecutorFault :: VampireExecutor -> SomeException -> IO ()
-recordExecutorFault executor workerFailure = atomically do
- state <- readTVar (executorState executor)
- case state of
- ExecutorRunning ->
- writeTVar
- (executorState executor)
- (ExecutorFaulted
- (VampireExecutorFault
- (boundedText
- (Text.pack (displayException workerFailure)))))
- ExecutorClosing -> pure ()
- ExecutorFaulted{} -> pure ()
-
-executorWorker :: VampireExecutor -> IO ()
-executorWorker executor = do
- next <- atomically do
- state <- readTVar (executorState executor)
- case state of
- ExecutorRunning -> Just <$> readTBQueue (executorQueue executor)
- ExecutorClosing -> pure Nothing
- ExecutorFaulted{} -> pure Nothing
- case next of
- Nothing -> pure ()
- Just job -> do
- shouldRun <- atomically do
- completed <- tryReadTMVar (executorJobCompletion job)
- case completed of
- Just _ -> pure False
- Nothing -> do
- writeTVar (executorJobStarted job) True
- pure True
- when shouldRun
- (executeJob executor job
- `Exception.finally`
- atomically (writeTVar (executorJobStarted job) False))
- executorWorker executor
-
-data JobWait value
- = JobFinished !(Either SomeException value)
- | JobCancelled
- | JobExecutorStopped
-
-executeJob :: VampireExecutor -> ExecutorJob -> IO ()
-executeJob executor job =
- Exception.mask \restore -> do
- running <- async
- (restore
- (timedExecutorRun executor job))
- waited <- restore
- (atomically
- (do
- state <- readTVar (executorState executor)
- case state of
- ExecutorRunning ->
- (JobFinished <$> waitCatchSTM running)
- `orElse`
- (do
- cancelled <- readTVar
- (executorJobCancelled job)
- check cancelled
- pure JobCancelled)
- ExecutorClosing -> pure JobExecutorStopped
- ExecutorFaulted{} -> pure JobExecutorStopped))
- `Exception.onException`
- (cancel running >> void (waitCatch running))
- case waited of
- JobFinished result -> case result of
- Left workerFailure -> Exception.throwIO workerFailure
- Right terminal ->
- publishVampireTerminal
- executor
- (executorJobCompletion job)
- terminal
- JobCancelled -> do
- cancel running
- void (waitCatch running)
- publishVampireTerminal
- executor
- (executorJobCompletion job)
- VampireCancelled
- JobExecutorStopped -> do
- cancel running
- void (waitCatch running)
-
-timedExecutorRun
- :: VampireExecutor
- -> ExecutorJob
- -> IO VampireTerminal
-timedExecutorRun executor job = Exception.mask \restore -> do
- started <- getMonotonicTimeNSec
- atomically
- (modifyTVar'
- (executorRuntime executor)
- (\runtime ->
- let live = runtimeLiveCount runtime + 1
- in runtime
- { runtimeRunCount = runtimeRunCount runtime + 1
- , runtimeLiveCount = live
- , runtimeMaximumLiveCount =
- max live (runtimeMaximumLiveCount runtime)
- , runtimeFirstStartNanoseconds =
- runtimeFirstStartNanoseconds runtime <|> Just started
- }))
- terminal <- restore
- (runPreparedVerificationTerminal
- (executorRequestObserver executor (executorJobPosition job))
- (executorCommand executor)
- (executorJobRequest job))
- `Exception.onException` finishExecutorRun executor started
- finishExecutorRun executor started
- pure terminal
-
-finishExecutorRun :: VampireExecutor -> Word64 -> IO ()
-finishExecutorRun executor started = do
- finished <- getMonotonicTimeNSec
- let elapsed = finished - started
- atomically
- (modifyTVar'
- (executorRuntime executor)
- (\runtime ->
- runtime
- { runtimeLiveCount = runtimeLiveCount runtime - 1
- , runtimeExecutionNanoseconds =
- runtimeExecutionNanoseconds runtime + elapsed
- , runtimeLongestExecutionNanoseconds =
- max elapsed (runtimeLongestExecutionNanoseconds runtime)
- }))
-
-publishVampireTerminal
- :: VampireExecutor
- -> TMVar VampireCompletion
- -> VampireTerminal
- -> IO ()
-publishVampireTerminal executor completionCell terminal = do
- let completion = VampireCompletion terminal
- _ <- Exception.evaluate completion
- published <- getMonotonicTimeNSec
- void
- (atomically
- (publishVampireTerminalSTM
- executor completionCell published completion))
-
-publishVampireTerminalSTM
- :: VampireExecutor
- -> TMVar VampireCompletion
- -> Word64
- -> VampireCompletion
- -> STM Bool
-publishVampireTerminalSTM executor completionCell published completion = do
- inserted <- tryPutTMVar completionCell completion
- when inserted
- (modifyTVar'
- (executorRuntime executor)
- (\runtime ->
- runtime
- { runtimeFinalCompletionNanoseconds =
- Just
- (maybe published
- (max published)
- (runtimeFinalCompletionNanoseconds runtime))
- }))
- pure inserted
-
-runPreparedVerificationTerminal
- :: (PreparedVerificationRequest -> IO ())
- -> Vampire
- -> PreparedVerificationRequest
- -> IO VampireTerminal
-runPreparedVerificationTerminal observer command request = do
- transcriptResult <-
- runPreparedVampireProcessWithObserver observer command request
- pure case transcriptResult of
- Left processFailure ->
- VampireProcessFailed processFailure
- Right transcript ->
- case classifyVampireCompleted
- (preparedVerificationMode request)
- transcript of
- Left protocolFailure ->
- VampireProtocolFailed
- (boundedTranscriptDiagnostic
- (Text.pack (show protocolFailure))
- transcript)
- Right Proved ->
- VampireAccepted (preparedVerificationRequestId request)
- Right Counterexample ->
- VampireRejected
- RejectedCounterexample
- (boundedTranscriptDiagnostic
- "Vampire found a countermodel"
- transcript)
- Right ContradictoryInput ->
- VampireRejected
- RejectedContradictoryInput
- (boundedTranscriptDiagnostic
- "Vampire reported contradictory input"
- transcript)
- Right Indeterminate ->
- VampireRejected
- RejectedIndeterminate
- (boundedTranscriptDiagnostic
- "Vampire did not establish the obligation"
- transcript)
-
-completionProverResult
- :: PreparedVerificationRequest
- -> VampireCompletion
- -> IO (Either ProverProcessError ProverAnswer)
-completionProverResult request completion =
- case vampireCompletionTerminal completion of
- VampireAccepted acceptedId
- | acceptedId == preparedVerificationRequestId request ->
- pure
- (Right
- (ProvedAnswer
- (AcceptedVampireRun acceptedId)))
- | otherwise ->
- Exception.throwIO
- (VampireExecutorFault
- "accepted Vampire result has the wrong request id")
- VampireRejected rejection _diagnostic ->
- pure
- (Right
- (case rejection of
- RejectedCounterexample ->
- CounterSatisfiable task
- RejectedContradictoryInput ->
- ContradictoryAxioms task
- RejectedIndeterminate ->
- Uncertain task))
- VampireProtocolFailed diagnostic ->
- pure
- (Right
- (Error
- "typed obligation"
- (renderBoundedDiagnostic diagnostic)))
- VampireProcessFailed processFailure ->
- pure (Left processFailure)
- VampireCancelled ->
- Exception.throwIO
- (VampireExecutorFault
- "active admission encountered a cancelled Vampire request")
- where
- task = preparedVerificationText request
-
-runPreparedVampireProcessWithObserver
- :: (PreparedVerificationRequest -> IO ())
- -> Vampire
- -> PreparedVerificationRequest
- -> IO (Either ProverProcessError CompletedTranscript)
-runPreparedVampireProcessWithObserver
- observer
- vampireCommand@Vampire{vampireExecutable = executable}
- preparedRequest = do
- callbackStarted <- newIORef False
- processResult <- Exception.try
- (withCreateProcess
- (proc executable (vampireArguments vampireCommand))
- { std_in = CreatePipe
- , std_out = CreatePipe
- , std_err = CreatePipe
- , create_group = True
- }
- \mIn mOut mErr processHandle ->
- Exception.mask \restore -> do
- writeIORef callbackStarted True
- case (mIn, mOut, mErr) of
- (Just inputHandle, Just outputHandle, Just errorHandle) -> do
- maybePid <- getPid processHandle
- processGroup <- case maybePid of
- Just pid ->
- pure (fromIntegral pid)
- Nothing ->
- impossible
- "Vampire supervisor: missing process-group id"
- restore
- (do
- runRequestObserver
- observer
- preparedRequest
- superviseVampireProcess
- executable
- (vampireTimeLimit vampireCommand)
- preparedRequest
- inputHandle
- outputHandle
- errorHandle
- processGroup
- processHandle)
- `Exception.onException`
- forceTerminateProcessGroup
- processGroup
- processHandle
- _ ->
- impossible
- "Vampire supervisor: expected CreatePipe handles")
- :: IO
- (Either
- SomeException
- (Either ProverProcessError CompletedTranscript))
- case processResult of
- Right result ->
- pure result
- Left err
- | Just (RequestObserverFailure observerFailure) <-
- fromException err ->
- Exception.throwIO observerFailure
- | Just asynchronous <-
- (fromException err :: Maybe AsyncException) ->
- Exception.throwIO asynchronous
- | Just ioFailure <-
- (fromException err :: Maybe IOException) -> do
- started <- readIORef callbackStarted
- pure
- (Left
- (if started
- then
- ProverLifecycleFailed
- executable
- (boundedText
- (exceptionText ioFailure))
- else
- ProverLaunchFailed
- executable
- (boundedText
- (exceptionText ioFailure))))
- | otherwise ->
- Exception.throwIO err
-
-runRequestObserver
- :: (PreparedVerificationRequest -> IO ())
- -> PreparedVerificationRequest
- -> IO ()
-runRequestObserver observer request =
- observer request `Exception.catch` \observerError ->
- case fromException observerError :: Maybe AsyncException of
- Just asynchronous ->
- Exception.throwIO asynchronous
- Nothing ->
- Exception.throwIO
- (RequestObserverFailure observerError)
-
-superviseVampireProcess
- :: FilePath
- -> TimeLimit
- -> PreparedVerificationRequest
- -> Handle
- -> Handle
- -> Handle
- -> ProcessGroupID
- -> ProcessHandle
- -> IO (Either ProverProcessError CompletedTranscript)
-superviseVampireProcess
- executable
- timeLimit
- preparedRequest
- inputHandle
- outputHandle
- errorHandle
- processGroup
- processHandle = do
- stdoutCapture <- newIORef emptyCaptureBuffer
- stderrCapture <- newIORef emptyCaptureBuffer
- let terminateOwnedProcess =
- forceTerminateProcessGroup
- processGroup
- processHandle
- communicate =
- communicateWithVampire
- preparedRequest
- inputHandle
- outputHandle
- errorHandle
- stdoutCapture
- stderrCapture
- processHandle
- supervised <-
- Timeout.timeout
- (wallTimeoutMicroseconds timeLimit)
- (Exception.try communicate
- :: IO (Either SupervisorAbort ExitCode))
- case supervised of
- Nothing -> do
- terminateOwnedProcess
- partial <-
- capturedStreams
- stdoutCapture
- stderrCapture
- pure
- (Left
- (ProverTimedOut
- executable
- timeLimit
- (boundedCapturedStreams partial)))
- Just (Left abort) -> do
- terminateOwnedProcess
- partial <-
- capturedStreams
- stdoutCapture
- stderrCapture
- pure
- (Left
- (supervisorAbortError
- executable
- partial
- abort))
- Just (Right exitCode) -> do
- terminateRemainingProcessGroup processGroup
- partial <-
- capturedStreams
- stdoutCapture
- stderrCapture
- case exitCode of
- ExitFailure signalCode
- | signalCode < 0 ->
- pure
- (Left
- (ProverTerminatedBySignal
- executable
- (negate signalCode)
- (boundedCapturedStreams partial)))
- _ ->
- pure do
- stdout <- decodeOutput
- executable
- ProverOutputStdout
- (partialStdout partial)
- stderr <- decodeOutput
- executable
- ProverOutputStderr
- (partialStderr partial)
- Right
- CompletedTranscript
- { completedExitCode = exitCode
- , completedStreams =
- CompleteCapturedStreams
- { completedStdout = stdout
- , completedStderr = stderr
- }
- }
-
-communicateWithVampire
- :: PreparedVerificationRequest
- -> Handle
- -> Handle
- -> Handle
- -> IORef CaptureBuffer
- -> IORef CaptureBuffer
- -> ProcessHandle
- -> IO ExitCode
-communicateWithVampire
- preparedRequest
- inputHandle
- outputHandle
- errorHandle
- stdoutCapture
- stderrCapture
- processHandle = do
- _ <-
- concurrently
- (concurrently
- (captureCommunication ProverStdin
- (ByteString.hPut
- inputHandle
- (preparedVerificationInput preparedRequest)
- `Exception.finally` hClose inputHandle))
- (captureOutput
- ProverOutputStdout
- outputHandle
- stdoutCapture))
- (captureOutput
- ProverOutputStderr
- errorHandle
- stderrCapture)
- captureLifecycle (waitForProcess processHandle)
-
-captureCommunication
- :: ProverStream
- -> IO a
- -> IO a
-captureCommunication stream action =
- action `Exception.catch` \(err :: IOException) ->
- throwIO
- (SupervisorCommunicationFailed
- stream
- (exceptionText err))
-
-captureOutput
- :: ProverOutputStream
- -> Handle
- -> IORef CaptureBuffer
- -> IO ()
-captureOutput stream handle capture =
- captureCommunication (outputStream stream) go
- where
- go = do
- bytes <- ByteString.hGetSome handle captureChunkSize
- if ByteString.null bytes
- then pure ()
- else do
- exceeded <- appendCapturedChunk capture bytes
- if exceeded
- then
- throwIO
- (SupervisorOutputLimitExceeded stream)
- else go
-
-outputStream :: ProverOutputStream -> ProverStream
-outputStream = \case
- ProverOutputStdout ->
- ProverStdout
- ProverOutputStderr ->
- ProverStderr
-
-captureLifecycle :: IO a -> IO a
-captureLifecycle action =
- action `Exception.catch` \(err :: IOException) ->
- throwIO (SupervisorLifecycleFailed (exceptionText err))
-
-emptyCaptureBuffer :: CaptureBuffer
-emptyCaptureBuffer =
- CaptureBuffer
- { captureByteCount = 0
- , captureChunksReversed = []
- }
-
-appendCapturedChunk
- :: IORef CaptureBuffer
- -> ByteString.ByteString
- -> IO Bool
-appendCapturedChunk capture bytes =
- atomicModifyIORef' capture \buffer@CaptureBuffer{..} ->
- let remaining =
- vampireOutputByteLimit - captureByteCount
- retained =
- ByteString.take remaining bytes
- retainedLength =
- ByteString.length retained
- buffer' =
- buffer
- { captureByteCount =
- captureByteCount + retainedLength
- , captureChunksReversed =
- if ByteString.null retained
- then captureChunksReversed
- else retained : captureChunksReversed
- }
- in (buffer', ByteString.length bytes > remaining)
-
-capturedStreams
- :: IORef CaptureBuffer
- -> IORef CaptureBuffer
- -> IO PartialCapturedStreams
-capturedStreams stdoutCapture stderrCapture =
- PartialCapturedStreams
- <$> capturedBytes stdoutCapture
- <*> capturedBytes stderrCapture
-
-capturedBytes :: IORef CaptureBuffer -> IO ByteString.ByteString
-capturedBytes capture =
- ByteString.concat
- . reverse
- . captureChunksReversed
- <$> readIORef capture
-
-boundedTranscriptBytes :: Int
-boundedTranscriptBytes = 32 * 1024
-
-boundedTranscriptHalf :: Int
-boundedTranscriptHalf = boundedTranscriptBytes `div` 2
-
-boundedBytes :: ByteString.ByteString -> BoundedBytes
-boundedBytes bytes =
- let original = ByteString.length bytes
- in if original <= boundedTranscriptBytes
- then
- BoundedBytes
- { boundedBytesOriginalCount = original
- , boundedBytesRetained = bytes
- , boundedBytesTruncated = False
- }
- else
- BoundedBytes
- { boundedBytesOriginalCount = original
- , boundedBytesRetained =
- ByteString.take boundedTranscriptHalf bytes
- <> ByteString.drop
- (original - boundedTranscriptHalf)
- bytes
- , boundedBytesTruncated = True
- }
-
-boundedText :: Text -> Text
-boundedText =
- decodeBoundedBytes . boundedBytes . TextEncoding.encodeUtf8
-
-decodeBoundedBytes :: BoundedBytes -> Text
-decodeBoundedBytes =
- TextEncoding.decodeUtf8With TextEncodingError.lenientDecode
- . boundedBytesRetained
-
-boundedCapturedStreams
- :: PartialCapturedStreams
- -> BoundedCapturedStreams
-boundedCapturedStreams PartialCapturedStreams{..} =
- BoundedCapturedStreams
- { boundedStdout = boundedBytes partialStdout
- , boundedStderr = boundedBytes partialStderr
- }
-
-boundedTranscriptDiagnostic
- :: Text
- -> CompletedTranscript
- -> BoundedDiagnostic
-boundedTranscriptDiagnostic summary CompletedTranscript{completedStreams = streams} =
- BoundedDiagnostic
- { boundedDiagnosticSummary = boundedText summary
- , boundedDiagnosticStdout =
- boundedBytes
- (TextEncoding.encodeUtf8 (completedStdout streams))
- , boundedDiagnosticStderr =
- boundedBytes
- (TextEncoding.encodeUtf8 (completedStderr streams))
- }
-
-renderBoundedDiagnostic :: BoundedDiagnostic -> Text
-renderBoundedDiagnostic BoundedDiagnostic{..} =
- Text.unlines
- [ boundedDiagnosticSummary
- , renderBoundedStream "stdout" boundedDiagnosticStdout
- , renderBoundedStream "stderr" boundedDiagnosticStderr
- ]
-
-renderBoundedStream :: Text -> BoundedBytes -> Text
-renderBoundedStream streamLabel bytes =
- streamLabel
- <> (if boundedBytesTruncated bytes
- then
- " (retained first and last 16 KiB of "
- <> Text.pack (show (boundedBytesOriginalCount bytes))
- <> " bytes)"
- else "")
- <> ":\n"
- <> decodeBoundedBytes bytes
-
-supervisorAbortError
- :: FilePath
- -> PartialCapturedStreams
- -> SupervisorAbort
- -> ProverProcessError
-supervisorAbortError executable partial = \case
- SupervisorCommunicationFailed stream message ->
- ProverCommunicationFailed executable stream (boundedText message)
- SupervisorOutputLimitExceeded stream ->
- ProverOutputLimitExceeded
- executable
- stream
- (boundedCapturedStreams partial)
- SupervisorLifecycleFailed message ->
- ProverLifecycleFailed executable (boundedText message)
-
-wallTimeoutMicroseconds :: TimeLimit -> Int
-wallTimeoutMicroseconds (Seconds seconds) =
- fromInteger
- (min
- (toInteger (maxBound :: Int))
- ((toInteger seconds + vampireWallGraceSeconds)
- * 1000000))
-
-forceTerminateProcessGroup
- :: ProcessGroupID
- -> ProcessHandle
- -> IO ()
-forceTerminateProcessGroup processGroup processHandle =
- Exception.uninterruptibleMask_ do
- terminateRemainingProcessGroup processGroup
- _ <- waitForProcess processHandle
- pure ()
-
-terminateRemainingProcessGroup :: ProcessGroupID -> IO ()
-terminateRemainingProcessGroup processGroup =
- ignoreMissingProcess
- (signalProcessGroup sigKILL processGroup)
-
-ignoreMissingProcess :: IO () -> IO ()
-ignoreMissingProcess action =
- action `Exception.catch` \(err :: IOException) ->
- if isDoesNotExistError err
- then pure ()
- else throwIO err
-
-decodeOutput
- :: FilePath
- -> ProverOutputStream
- -> ByteString.ByteString
- -> Either ProverProcessError Text
-decodeOutput executable stream bytes =
- case TextEncoding.decodeUtf8' bytes of
- Left err ->
- Left
- (ProverOutputMalformedUtf8
- executable
- stream
- (boundedText (Text.pack (show err))))
- Right output ->
- Right output
-
-exceptionText :: IOException -> Text
-exceptionText =
- Text.pack . displayException
-
-classifyVampireCompleted
- :: VampireTaskMode
- -> CompletedTranscript
- -> Either VampireProtocolError CanonicalAtpOutcome
-classifyVampireCompleted mode CompletedTranscript{..} =
- case completedExitCode of
- ExitFailure _ ->
- classifyVampireProtocol mode completedExitCode []
- ExitSuccess -> do
- statuses <- statusesFromCompleteStreams completedStreams
- classifyVampireProtocol mode completedExitCode statuses
-
-statusesFromCompleteStreams
- :: CompleteCapturedStreams
- -> Either VampireProtocolError [VampireStatus]
-statusesFromCompleteStreams CompleteCapturedStreams{..}
- | Set.null malformed =
- Right statuses
- | otherwise =
- Left (MalformedVampireStatusLines malformed)
- where
- (malformed, statuses) =
- foldMap classifyLine
- (Text.lines completedStdout <> Text.lines completedStderr)
-
- classifyLine line =
- case parseMaybe vampireStatusParser line of
- Just status ->
- (mempty, [status])
- Nothing
- | "SZS status" `Text.isInfixOf` line ->
- (Set.singleton line, [])
- | otherwise ->
- mempty
-
-vampireStatusFromText :: Text -> VampireStatus
-vampireStatusFromText = \case
- "Theorem" -> StatusTheorem
- "CounterSatisfiable" -> StatusCounterSatisfiable
- "ContradictoryAxioms" -> StatusContradictoryAxioms
- "Timeout" -> StatusTimeout
- "ResourceOut" -> StatusResourceOut
- "GaveUp" -> StatusGaveUp
- "Unknown" -> StatusUnknown
- status -> UnsupportedStatus status
-
--- | Parse a Vampire SZS status line.
---
--- Recognizes both standard lines like:
--- % SZS status Timeout for 123
--- and lines prefixed by worker ids (seen with portfolio output), e.g.:
--- % (2581105)SZS status Timeout for
-vampireStatusParser :: Parsec Void Text VampireStatus
-vampireStatusParser = do
- _ <- Char.char '%'
- Char.hspace
- optional do
- _ <- Char.char '('
- _ <- some Char.digitChar
- _ <- Char.char ')'
- Char.hspace
- _ <- chunk "SZS"
- Char.hspace1
- _ <- chunk "status"
- Char.hspace1
- status <-
- takeWhile1P
- (Just "SZS status")
- (\character -> character /= ' ' && character /= '\t')
- _ <- takeRest
- pure (vampireStatusFromText status)
-
-nominalDiffTimeToText :: NominalDiffTime -> Text
-nominalDiffTimeToText delta =
- toText (nominalDiffTimeToTextBuilder delta)
-
-nominalDiffTimeToTextBuilder :: NominalDiffTime -> TextBuilder
-nominalDiffTimeToTextBuilder delta =
- case hours of
- 0 ->
- padded minutes
- <> ":"
- <> padded restSeconds
- <> "."
- <> padded restCentis
- _ ->
- padded hours
- <> ":"
- <> padded restMinutes
- <> ":"
- <> padded restSeconds
- where
- padded n =
- if n < 10
- then char '0' <> decimal n
- else decimal n
- centiseconds =
- truncate (100 * nominalDiffTimeToSeconds delta) :: Int
- (seconds, restCentis) = divMod centiseconds 100
- (minutes, restSeconds) = divMod seconds 60
- (hours, restMinutes) = divMod minutes 60
-
-timeDifferenceToText :: UTCTime -> UTCTime -> Text
-timeDifferenceToText startTime endTime =
- nominalDiffTimeToText (diffUTCTime endTime startTime)