Skip to content

Commit 3021727

Browse files
authored
Hotfix bound threads for workers unsafe calls for llvm (#4081)
Fixes a problem with recently-introduced thread-local storage in the LLVM backend. We have to make sure that all foreign calls relating to an LLVM-based term evaluation use the same OS thread, which can only be achieved [via bound threads](https://downloads.haskell.org/ghc/latest/docs/libraries/base-4.20.0.0-1f57/Control-Concurrent.html#g:8) . This PR * adds a flag to the generic RPC server in `kore-rpc-types` to run request worker threads in bound threads (using `forkOS`) * `kore-rpc-booster` uses this flag for bound threads when an LLVM backend library is used * declares the LLVM backend calls `unsafe` to make executing OS threads block instead of having new OS threads created for concurrent Haskell execution (they won't read HS heap data and never call back into Haskell). This _should_ protect us against problems related to using thread-local storage in the LLVM backend. Needs to be thoroughly tested before merging, because issues only materialised in proofs with substantial workload and parallel exploration. PR #4080 uses `runInBoundThread` on the individual request processing calls instead of running the whole worker thread in the server as a bound thread. This was not solving the problem.
1 parent 64ad870 commit 3021727

File tree

6 files changed

+17
-8
lines changed

6 files changed

+17
-8
lines changed

booster/library/Booster/LLVM/TH.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ foreignImport name' ty' = do
8989
libHandle <- TH.newName "libHandle"
9090

9191
pure
92-
[ -- foreign import ccall "dynamic" <camel_name>Unwrap :: FunPtr <ty> -> <ty>
92+
[ -- foreign import ccall unsafe "dynamic" <camel_name>Unwrap :: FunPtr <ty> -> <ty>
9393
TH.ForeignD $
94-
TH.ImportF TH.CCall TH.Safe "dynamic" nameUnwrap $
94+
TH.ImportF TH.CCall TH.Unsafe "dynamic" nameUnwrap $
9595
TH.AppT (TH.AppT TH.ArrowT $ TH.AppT (TH.ConT ''FunPtr) ty) ty
9696
, -- <camel_name>FunPtr :: ReaderT DL IO (FunPtr <ty>)
9797
TH.SigD

booster/tools/booster/Server.hs

+1
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ main = do
334334
server =
335335
jsonRpcServer
336336
srvSettings
337+
(isJust mLlvmLibrary) -- run with bound threads if LLVM API in use
337338
( \rawReq req ->
338339
let reqId = getReqId rawReq
339340
in runBoosterLogger $ do

dev-tools/booster-dev/Server.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Control.Monad.Trans.Reader (runReaderT)
1717
import Data.Conduit.Network (serverSettings)
1818
import Data.Map (Map)
1919
import Data.Map.Strict qualified as Map
20-
import Data.Maybe (fromMaybe, isNothing)
20+
import Data.Maybe (fromMaybe, isJust, isNothing)
2121
import Data.Text (Text, unpack)
2222
import Data.Text.Encoding qualified as Text
2323
import Options.Applicative
@@ -163,6 +163,7 @@ runServer port definitions defaultMain mLlvmLibrary rewriteOpts logFile mSMTOpti
163163
}
164164
jsonRpcServer
165165
(serverSettings port "*")
166+
(isJust mLlvmLibrary) -- run in bound threads if LLVM library in use
166167
( \rawReq req ->
167168
flip runReaderT (filteredBoosterContextLogger, toModifiersRep prettyPrintOptions)
168169
. Booster.Log.unLoggerT

dev-tools/kore-rpc-dev/Server.hs

+1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ main = do
240240
server =
241241
jsonRpcServer
242242
srvSettings
243+
False -- no bound threads
243244
(\rawReq -> runBoosterLogger . respond (koreRespond $ getReqId rawReq))
244245
[Kore.handleDecidePredicateUnknown, handleErrorCall, handleSomeException]
245246
interruptHandler _ = do

kore-rpc-types/src/Kore/JsonRpc/Server.hs

+10-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module Kore.JsonRpc.Server (
1414
JsonRpcHandler (..),
1515
) where
1616

17-
import Control.Concurrent (forkIO, throwTo)
17+
import Control.Concurrent (forkIO, forkOS, throwTo)
1818
import Control.Concurrent.STM.TChan (newTChan, readTChan, writeTChan)
1919
import Control.Exception (Exception (fromException), catch, mask, throw)
2020
import Control.Monad (forever)
@@ -78,11 +78,14 @@ jsonRpcServer ::
7878
(MonadUnliftIO m, FromRequestCancellable q, ToJSON r) =>
7979
-- | Connection settings
8080
ServerSettings ->
81+
-- | run workers in bound threads (required if worker below uses
82+
-- foreign calls with thread-local state)
83+
Bool ->
8184
-- | Action to perform on connecting client thread
8285
(Request -> Respond q IO r) ->
8386
[JsonRpcHandler] ->
8487
m a
85-
jsonRpcServer serverSettings respond handlers =
88+
jsonRpcServer serverSettings runBound respond handlers =
8689
runGeneralTCPServer serverSettings $ \cl ->
8790
Log.runNoLoggingT $
8891
runJSONRPCT
@@ -93,17 +96,18 @@ jsonRpcServer serverSettings respond handlers =
9396
False
9497
(appSink cl)
9598
(appSource cl)
96-
(srv respond handlers)
99+
(srv runBound respond handlers)
97100

98101
data JsonRpcHandler = forall e. Exception e => JsonRpcHandler (e -> IO ErrorObj)
99102

100103
srv ::
101104
forall m q r.
102105
(MonadLoggerIO m, FromRequestCancellable q, ToJSON r) =>
106+
Bool ->
103107
(Request -> Respond q IO r) ->
104108
[JsonRpcHandler] ->
105109
JSONRPCT m ()
106-
srv respond handlers = do
110+
srv runBound respond handlers = do
107111
reqQueue <- liftIO $ atomically newTChan
108112
let mainLoop tid =
109113
let loop =
@@ -170,7 +174,8 @@ srv respond handlers = do
170174
restore (thing a) `catch` catchesHandler a
171175

172176
liftIO $
173-
forkIO $
177+
-- workers should run in bound threads (to secure foreign calls) when flagged
178+
(if runBound then forkOS else forkIO) $
174179
forever $
175180
bracketOnReqException
176181
(atomically $ readTChan reqQueue)

kore/src/Kore/JsonRpc.hs

+1
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ runServer port serverState mainModule runSMT Log.LoggerEnv{logAction} = do
731731
flip runLoggingT logFun $
732732
jsonRpcServer
733733
srvSettings
734+
False -- no bound threads
734735
( \req parsed ->
735736
log (InfoJsonRpcProcessRequest (getReqId req) parsed)
736737
>> respond (fromId $ getReqId req) serverState mainModule runSMT parsed

0 commit comments

Comments
 (0)