@@ -14,7 +14,7 @@ import Control.Concurrent.Spawn (parMapIO, pool)
1414import Control.Concurrent.STM (writeTChan , newTChan , TChan , readTChan , atomically , isEmptyTChan , STM )
1515import Control.Concurrent.STM.TVar (TVar , newTVarIO , modifyTVar , readTVar , writeTVar )
1616import Control.Concurrent.STM.TMVar (TMVar , putTMVar , takeTMVar , newEmptyTMVarIO )
17- import Control.Monad (when , forM_ , forM , forever )
17+ import Control.Monad (when , unless , forM_ , forM , forever , void )
1818import Control.Monad.Loops (whileM )
1919import Control.Monad.IO.Unlift
2020import Control.Monad.Operational qualified as Operational
@@ -330,6 +330,7 @@ data InterpTask m a = InterpTask
330330 , numTasks :: TVar Natural
331331 , stepper :: Stepper Symbolic (Expr End )
332332 , handler :: Expr End -> m a
333+ , shouldAbort :: TVar Bool
333334 }
334335
335336data Process m a = Process
@@ -341,10 +342,11 @@ interpret :: forall m a . App m
341342 => Fetch. Fetcher Symbolic m
342343 -> IterConfig
343344 -> VM Symbolic
345+ -> TVar Bool
344346 -> Stepper Symbolic (Expr End )
345347 -> (Expr End -> m a )
346348 -> m [a ]
347- interpret fetcher iterConf vm stepper handler = do
349+ interpret fetcher iterConf vm shouldAbort stepper handler = do
348350 conf <- readConfig
349351 taskQ <- liftIO newChan
350352 processQ <- liftIO newChan
@@ -382,6 +384,7 @@ interpret fetcher iterConf vm stepper handler = do
382384 , numTasks = numTasks
383385 , stepper = stepper
384386 , handler = handler
387+ , shouldAbort = shouldAbort
385388 }
386389 liftIO $ writeChan taskQ interpTask
387390
@@ -398,29 +401,37 @@ interpret fetcher iterConf vm stepper handler = do
398401 taskOrchestrate :: App m
399402 => Chan (InterpTask m a )
400403 -> Chan () -> Chan (Process m a )
401- -> TVar Natural -> TVar Natural -> m b
404+ -> TVar Natural -> TVar Natural -> m ()
402405 taskOrchestrate taskQ avail processQ numTasks numProcs = forever $ do
403406 _ <- liftIO $ readChan avail
404407 task <- liftIO $ readChan taskQ
405- runTask' <- toIO $ getOneExpr task avail processQ numTasks numProcs
406- liftIO $ forkIO runTask'
408+ abortFlag <- liftIO $ atomically $ readTVar shouldAbort
409+ if abortFlag
410+ then liftIO $ writeChan avail ()
411+ else do
412+ runTask' <- toIO $ getOneExpr task avail processQ numTasks numProcs
413+ void $ liftIO $ forkIO runTask'
407414
408415 -- processing orchestrator loop
409- processOrchestrate :: App m => Chan (Process m a ) -> Chan () -> TChan a -> TVar Natural -> TVar Natural -> TMVar () -> m b
410- processOrchestrate processQ availProcessors resChan numProcs numTasks allProcessDone = forever $ do
411- _ <- liftIO $ readChan availProcessors
416+ processOrchestrate :: App m => Chan (Process m a ) -> Chan () -> TChan a -> TVar Natural -> TVar Natural -> TMVar () -> m ()
417+ processOrchestrate processQ avail resChan numProcs numTasks allProcessDone = forever $ do
418+ _ <- liftIO $ readChan avail
412419 proc <- liftIO $ readChan processQ
413- runProcess' <- toIO $ processOne proc availProcessors resChan numProcs numTasks allProcessDone
414- liftIO $ forkIO runProcess'
420+ abortFlag <- liftIO $ atomically $ readTVar shouldAbort
421+ if abortFlag
422+ then liftIO $ writeChan avail ()
423+ else do
424+ runProcess' <- toIO $ processOne proc avail resChan numProcs numTasks allProcessDone
425+ void $ liftIO $ forkIO runProcess'
415426
416427 -- process one task
417428 processOne :: App m => Process m a -> Chan () -> TChan a -> TVar Natural -> TVar Natural -> TMVar () -> m ()
418- processOne task availProcs resChan numProcs numTasks allProcessDone = do
429+ processOne task avail resChan numProcs numTasks allProcessDone = do
419430 processed <- task. handler task. result
420431 liftIO . atomically $ writeTChan resChan processed
421432
422433 -- Return instance to pool immediately after processing
423- liftIO $ writeChan availProcs ()
434+ liftIO $ writeChan avail ()
424435
425436 -- Decrement and check if all done
426437 liftIO $ atomically $ do
@@ -486,19 +497,25 @@ interpretInternal t@InterpTask{..} = eval (Operational.view stepper)
486497 runOne frozen newDepth (v: rest) = do
487498 conf <- readConfig
488499 (ra, vma) <- liftIO $ stToIO $ runStateT (continue v) frozen { result = Nothing , exploreDepth = newDepth }
489- liftIO $ atomically $ modifyTVar numTasks (+ 1 )
490- when (conf. debug && conf. verb >= 2 ) $ liftIO $ putStrLn $ " Queuing new task for ForkMany at depth " <> show newDepth
491- liftIO $ writeChan taskQ t { vm = vma, stepper = (k ra) }
500+ -- Check abort flag before queuing new task
501+ abortFlag <- liftIO $ atomically $ readTVar shouldAbort
502+ unless (conf. earlyAbort && abortFlag) $ do
503+ liftIO $ atomically $ modifyTVar numTasks (+ 1 )
504+ when (conf. debug && conf. verb >= 2 ) $ liftIO $ putStrLn $ " Queuing new task for ForkMany at depth " <> show newDepth
505+ liftIO $ writeChan taskQ t { vm = vma, stepper = (k ra) }
492506 runOne frozen newDepth rest
493507 runOne _ _ [] = internalError " unreachable"
494508 Stepper. Fork (PleaseRunBoth continue) -> do
495509 conf <- readConfig
496510 frozen <- liftIO $ stToIO $ freezeVM vm
497511 let newDepth = vm. exploreDepth+ 1
498512 (ra, vma) <- liftIO $ stToIO $ runStateT (continue True ) frozen { result = Nothing , exploreDepth = newDepth }
499- liftIO $ atomically $ modifyTVar numTasks (+ 1 )
500- liftIO $ writeChan taskQ $ t { vm = vma, stepper = (k ra) }
501- when (conf. debug && conf. verb >= 2 ) $ liftIO $ putStrLn $ " Queued new task for Fork at depth " <> show newDepth
513+ -- Check abort flag before queuing new task
514+ abortFlag <- liftIO $ atomically $ readTVar shouldAbort
515+ unless (conf. earlyAbort && abortFlag) $ do
516+ liftIO $ atomically $ modifyTVar numTasks (+ 1 )
517+ liftIO $ writeChan taskQ $ t { vm = vma, stepper = (k ra) }
518+ when (conf. debug && conf. verb >= 2 ) $ liftIO $ putStrLn $ " Queued new task for Fork at depth " <> show newDepth
502519
503520 (rb, vmb) <- liftIO $ stToIO $ runStateT (continue False ) frozen { result = Nothing , exploreDepth = newDepth }
504521 when (conf. debug && conf. verb >= 2 ) $ liftIO $ putStrLn $ " Continuing task for Fork at depth " <> show newDepth
@@ -613,7 +630,8 @@ getExprEmptyStore solvers c signature' concreteArgs opts = do
613630 conf <- readConfig
614631 calldata <- mkCalldata signature' concreteArgs
615632 preState <- liftIO $ stToIO $ loadEmptySymVM (RuntimeCode (ConcreteRuntimeCode c)) (Lit 0 ) calldata
616- paths <- interpret (Fetch. oracle solvers Nothing opts. rpcInfo) opts. iterConf preState runExpr pure
633+ shouldAbort <- liftIO $ newTVarIO False
634+ paths <- interpret (Fetch. oracle solvers Nothing opts. rpcInfo) opts. iterConf preState shouldAbort runExpr pure
617635 if conf. simp then (pure $ map Expr. simplify paths) else pure paths
618636
619637-- Used only in testing
@@ -629,7 +647,8 @@ getExpr solvers c signature' concreteArgs opts = do
629647 conf <- readConfig
630648 calldata <- mkCalldata signature' concreteArgs
631649 preState <- liftIO $ stToIO $ abstractVM calldata c Nothing False
632- paths <- interpret (Fetch. oracle solvers Nothing opts. rpcInfo) opts. iterConf preState runExpr pure
650+ shouldAbort <- liftIO $ newTVarIO False
651+ paths <- interpret (Fetch. oracle solvers Nothing opts. rpcInfo) opts. iterConf preState shouldAbort runExpr pure
633652 if conf. simp then (pure $ map Expr. simplify paths) else pure paths
634653
635654{- | Checks if an assertion violation has been encountered
@@ -714,7 +733,8 @@ exploreContract solvers theCode signature' concreteArgs opts maybepre = do
714733 calldata <- mkCalldata signature' concreteArgs
715734 preState <- liftIO $ stToIO $ abstractVM calldata theCode maybepre False
716735 let fetcher = Fetch. oracle solvers Nothing opts. rpcInfo
717- executeVM fetcher opts. iterConf preState pure
736+ shouldAbort <- liftIO $ newTVarIO False
737+ executeVM fetcher opts. iterConf preState shouldAbort pure
718738
719739-- | Stepper that parses the result of Stepper.runFully into an Expr End
720740runExpr :: Stepper. Stepper Symbolic (Expr End )
@@ -779,8 +799,8 @@ getPartials = mapMaybe go
779799
780800
781801-- | Symbolically execute the VM and return the representention of the execution
782- executeVM :: forall m a . App m => Fetch. Fetcher Symbolic m -> IterConfig -> VM Symbolic -> (Expr End -> m a ) -> m [a ]
783- executeVM fetcher iterConfig preState handlePath = interpret fetcher iterConfig preState runExpr handlePath
802+ executeVM :: forall m a . App m => Fetch. Fetcher Symbolic m -> IterConfig -> VM Symbolic -> TVar Bool -> (Expr End -> m a ) -> m [a ]
803+ executeVM fetcher iterConfig preState shouldAbort handlePath = interpret fetcher iterConfig preState shouldAbort runExpr handlePath
784804
785805-- | Symbolically execute the VM and check all endstates against the
786806-- postcondition, if available.
@@ -824,7 +844,8 @@ verifyInputsWithHandler solvers opts fetcher preState post cexHandler = do
824844 putStrLn $ " Keccak preimages in state: " <> (show $ length preState. keccakPreImgs)
825845 putStrLn $ " Exploring call " <> call
826846
827- results <- executeVM fetcher opts. iterConf preState $ \ leaf -> do
847+ shouldAbort <- liftIO $ newTVarIO False
848+ results <- executeVM fetcher opts. iterConf preState shouldAbort $ \ leaf -> do
828849 -- Extract partial if applicable
829850 let mPartial = case leaf of
830851 Partial _ _ p -> Just (p, leaf)
@@ -838,7 +859,10 @@ verifyInputsWithHandler solvers opts fetcher preState post cexHandler = do
838859 when (conf. debug && conf. verb >= 2 ) $ liftIO $ putStrLn $ " Checking leaf with props: " <> show props <> " SMT result: " <> show res
839860 -- Call custom handler if provided (for immediate Cex processing/validation/printing)
840861 case (cexHandler, res) of
841- (Just handler, cex@ (Cex _)) -> handler preState cex leaf
862+ (Just handler, cex@ (Cex _)) -> do
863+ handler preState cex leaf
864+ when conf. earlyAbort $ liftIO $ atomically $ writeTVar shouldAbort True
865+ (_, (Cex _)) -> when conf. earlyAbort $ liftIO $ atomically $ writeTVar shouldAbort True
842866 _ -> pure ()
843867 pure (res, leaf)
844868 else pure (Qed , leaf)
@@ -936,7 +960,8 @@ equivalenceCheck solvers sess bytecodeA bytecodeB opts calldata create = do
936960 getBranches bs = do
937961 let bytecode = if BS. null bs then BS. pack [0 ] else bs
938962 prestate <- liftIO $ stToIO $ abstractVM calldata bytecode Nothing create
939- interpret (Fetch. oracle solvers sess Fetch. noRpc) opts. iterConf prestate runExpr pure
963+ shouldAbort <- liftIO $ newTVarIO False
964+ interpret (Fetch. oracle solvers sess Fetch. noRpc) opts. iterConf prestate shouldAbort runExpr pure
940965 filterQeds (EqIssues res partials) = EqIssues (filter (\ (r, _) -> not . isQed $ r) res) partials
941966
942967rewriteFresh :: Text -> [Expr a ] -> [Expr a ]
0 commit comments