@@ -14,6 +14,7 @@ module Unison.Runtime.Interface
1414 , runStandalone
1515 , StoredCache
1616 , decodeStandalone
17+ , RuntimeHost (.. )
1718 ) where
1819
1920import GHC.Stack (HasCallStack )
@@ -35,6 +36,7 @@ import Data.IORef
3536import Data.Foldable
3637import Data.Set as Set
3738 (Set , (\\) , singleton , map , notMember , filter , fromList )
39+ import qualified Data.Set as Set
3840import Data.Map.Strict (Map )
3941import Data.Traversable (for )
4042import Data.Text (Text , isPrefixOf , pack )
@@ -76,6 +78,7 @@ import Unison.Runtime.Machine
7678 ( apply0 , eval0
7779 , CCache (.. ), cacheAdd , cacheAdd0 , baseCCache
7880 , refNumTm , refNumsTm , refNumsTy , refLookup
81+ , ActiveThreads
7982 )
8083import Unison.Runtime.MCode
8184 ( Combs , combDeps , combTypes , Args (.. ), Section (.. ), Instr (.. )
@@ -86,6 +89,8 @@ import Unison.Runtime.Serialize as SER
8689import Unison.Runtime.Stack
8790import qualified Unison.Hashing.V2.Convert as Hashing
8891import qualified Unison.ConstructorReference as RF
92+ import qualified UnliftIO.Concurrent as UnliftIO
93+ import qualified UnliftIO
8994
9095type Term v = Tm. Term v ()
9196
@@ -326,9 +331,10 @@ backReferenceTm ws rs c i = do
326331evalInContext
327332 :: PrettyPrintEnv
328333 -> EvalCtx
334+ -> ActiveThreads
329335 -> Word64
330336 -> IO (Either Error (Term Symbol ))
331- evalInContext ppe ctx w = do
337+ evalInContext ppe ctx activeThreads w = do
332338 r <- newIORef BlackHole
333339 crs <- readTVarIO (combRefs $ ccache ctx)
334340 let hook = watchHook r
@@ -348,15 +354,15 @@ evalInContext ppe ctx w = do
348354
349355 result <- traverse (const $ readIORef r)
350356 . first prettyError
351- <=< try $ apply0 (Just hook) ((ccache ctx) { tracer = tr }) w
357+ <=< try $ apply0 (Just hook) ((ccache ctx) { tracer = tr }) activeThreads w
352358 pure $ decom =<< result
353359
354360executeMainComb
355361 :: Word64
356362 -> CCache
357363 -> IO ()
358364executeMainComb init cc
359- = eval0 cc
365+ = eval0 cc Nothing
360366 . Ins (Pack RF. unitRef 0 ZArgs )
361367 $ Call True init (BArg1 0 )
362368
@@ -421,9 +427,26 @@ decodeStandalone b = bimap thd thd $ runGetOrFail g b
421427 <*> getNat
422428 <*> getStoredCache
423429
424- startRuntime :: String -> IO (Runtime Symbol )
425- startRuntime version = do
430+ -- | Whether the runtime is hosted within a UCM session or as a standalone process.
431+ data RuntimeHost
432+ = Standalone
433+ | UCM
434+
435+ startRuntime :: RuntimeHost -> String -> IO (Runtime Symbol )
436+ startRuntime runtimeHost version = do
426437 ctxVar <- newIORef =<< baseContext
438+ (activeThreads, cleanupThreads) <- case runtimeHost of
439+ -- Don't bother tracking open threads when running standalone, they'll all be cleaned up
440+ -- when the process itself exits.
441+ Standalone -> pure (Nothing , pure () )
442+ -- Track all forked threads so that they can be killed when the main process returns,
443+ -- otherwise they'll be orphaned and left running.
444+ UCM -> do
445+ activeThreads <- newIORef Set. empty
446+ let cleanupThreads = do
447+ threads <- readIORef activeThreads
448+ foldMap UnliftIO. killThread threads
449+ pure (Just activeThreads, cleanupThreads)
427450 pure $ Runtime
428451 { terminate = pure ()
429452 , evaluate = \ cl ppe tm -> catchInternalErrors $ do
@@ -432,7 +455,7 @@ startRuntime version = do
432455 ctx <- loadDeps cl ppe ctx tyrs tmrs
433456 (ctx, init ) <- prepareEvaluation ppe tm ctx
434457 writeIORef ctxVar ctx
435- evalInContext ppe ctx init
458+ evalInContext ppe ctx activeThreads init `UnliftIO.finally` cleanupThreads
436459 , compileTo = \ cl ppe rf path -> tryM $ do
437460 ctx <- readIORef ctxVar
438461 (tyrs, tmrs) <- collectRefDeps cl rf
0 commit comments