@@ -5,6 +5,7 @@
55{-# LANGUAGE BangPatterns #-}
66{-# LANGUAGE GeneralizedNewtypeDeriving #-}
77{-# LANGUAGE RecordWildCards #-}
8+{-# LANGUAGE MagicHash #-}
89
910-- | Local nodes
1011--
@@ -65,6 +66,8 @@ import Control.Exception
6566 , Exception
6667 , throwTo
6768 , uninterruptibleMask_
69+ , getMaskingState
70+ , MaskingState(..)
6871 )
6972import qualified Control.Exception as Exception
7073 ( Handler(..)
@@ -83,6 +86,12 @@ import Control.Distributed.Process.Internal.StrictMVar
8386 , putMVar
8487 , takeMVar
8588 )
89+import Control.Distributed.Process.Internal.ThreadPool
90+ ( newThreadPool
91+ , submitTask
92+ , lookupWorker
93+ , ThreadPool
94+ )
8695import Control.Concurrent.Chan (newChan, writeChan, readChan)
8796import qualified Control.Concurrent.MVar as MVar (newEmptyMVar, takeMVar)
8897import Control.Concurrent.STM
@@ -218,8 +227,18 @@ import qualified Control.Distributed.Process.Internal.StrictContainerAccessors a
218227 ( mapMaybe
219228 , mapDefault
220229 )
230+import GHC.IO (IO(..), unsafeUnmask)
231+import GHC.Base ( maskAsyncExceptions# )
232+
221233import Unsafe.Coerce
222234
235+-- Remove these when this is fixed:
236+-- https://ghc.haskell.org/trac/ghc/ticket/10149
237+block :: IO a -> IO a
238+block (IO io) = IO $ maskAsyncExceptions# io
239+unblock :: IO a -> IO a
240+unblock = unsafeUnmask
241+
223242--------------------------------------------------------------------------------
224243-- Initialization --
225244--------------------------------------------------------------------------------
@@ -249,12 +268,14 @@ createBareLocalNode endPoint rtable = do
249268 , _localConnections = Map.empty
250269 }
251270 ctrlChan <- newChan
271+ sendPool <- newThreadPool
252272 let node = LocalNode { localNodeId = NodeId $ NT.address endPoint
253273 , localEndPoint = endPoint
254274 , localState = state
255275 , localCtrlChan = ctrlChan
256276 , localEventBus = MxEventBusInitialising
257277 , remoteTable = rtable
278+ , localSendPool = sendPool
258279 }
259280 tracedNode <- startMxAgent node
260281
@@ -361,11 +382,14 @@ runProcess node proc = do
361382
362383-- | Spawn a new process on a local node
363384forkProcess :: LocalNode -> Process () -> IO ProcessId
364−forkProcess node proc =
365− modifyMVarMasked (localState node) startProcess
385+forkProcess node proc = do
386+ ms <- getMaskingState
387+ modifyMVarMasked (localState node) (startProcess ms)
366388 where
367− startProcess :: LocalNodeState -> IO (LocalNodeState, ProcessId)
368− startProcess (LocalNodeValid vst) = do
389+ startProcess :: MaskingState
390+ -> LocalNodeState
391+ -> IO (LocalNodeState, ProcessId)
392+ startProcess ms (LocalNodeValid vst) = do
369393 let lpid = LocalProcessId { lpidCounter = vst ^. localPidCounter
370394 , lpidUnique = vst ^. localPidUnique
371395 }
@@ -387,7 +411,13 @@ forkProcess node proc =
387411 , processThread = tid
388412 , processNode = node
389413 }
390− tid' <- uninterruptibleMask_ $ forkIOWithUnmask $ \unmask -> do
414+ -- Rewrite this code when this is fixed:
415+ -- https://ghc.haskell.org/trac/ghc/ticket/10149
416+ let unmask = case ms of
417+ Unmasked -> unblock
418+ MaskedInterruptible -> block
419+ MaskedUninterruptible -> id
420+ tid' <- uninterruptibleMask_ $ forkIO $ do
391421 reason <- Exception.catches
392422 (unmask $ runLocalProcess lproc proc >> return DiedNormal)
393423 [ (Exception.Handler (\ex@(ProcessExitException from msg) -> do
@@ -433,7 +463,7 @@ forkProcess node proc =
433463 $ vst
434464 , pid
435465 )
436− startProcess LocalNodeClosed =
466+ startProcess _ LocalNodeClosed =
437467 throwIO $ NodeClosedException $ localNodeId node
438468
439469 cleanupProcess :: ProcessId
@@ -580,6 +610,8 @@ handleIncomingMessages node = go initConnectionState
580610 , ctrlMsgSignal = Died nid DiedDisconnect
581611 }
582612 let notLost k = not (k `Set.member` (st ^. incomingFrom theirAddr))
613+ liftIO $ lookupWorker (localSendPool node) (NodeId theirAddr)
614+ >>= maybe (return ()) killThread
583615 closeImplicitReconnections node nid
584616 go ( (incomingFrom theirAddr ^= Set.empty)
585617 . (incoming ^: Map.filterWithKey (const . notLost))
@@ -634,6 +666,12 @@ data NCState = NCState
634666 , _registeredOnNodes :: !(Map ProcessId [(NodeId,Int)])
635667 }
636668
669+submitSendPool :: LocalNode -> NodeId -> IO () -> IO ()
670+submitSendPool node nid task = submitTask (localSendPool node) fork nid task
671+ where
672+ fork io =
673+ forkIO $ io `Exception.catch` \(NodeClosedException _) -> return ()
674+
637675newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
638676 deriving ( Applicative
639677 , Functor
@@ -644,11 +682,12 @@ newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
644682 )
645683
646684initNCState :: NCState
647−initNCState = NCState { _links = Map.empty
648− , _monitors = Map.empty
649− , _registeredHere = Map.empty
650− , _registeredOnNodes = Map.empty
651− }
685+initNCState = NCState
686+ { _links = Map.empty
687+ , _monitors = Map.empty
688+ , _registeredHere = Map.empty
689+ , _registeredOnNodes = Map.empty
690+ }
652691
653692-- | Thrown in response to the user invoking 'kill' (see Primitives.hs). This
654693-- type is deliberately not exported so it cannot be caught explicitly.
@@ -666,7 +705,7 @@ ncSendToProcess pid msg = do
666705 node <- ask
667706 if processNodeId pid == localNodeId node
668707 then ncEffectLocalSend node pid msg
669− else liftIO $ sendBinary node
708+ else liftIO $ submitSendPool node (processNodeId pid) $ sendBinary node
670709 (NodeIdentifier $ localNodeId node)
671710 (NodeIdentifier $ processNodeId pid)
672711 WithImplicitReconnect
@@ -679,7 +718,7 @@ ncSendToNode to msg = do
679718 node <- ask
680719 liftIO $ if to == localNodeId node
681720 then writeChan (localCtrlChan node) $! msg
682− else sendBinary node
721+ else submitSendPool node to $ sendBinary node
683722 (NodeIdentifier $ localNodeId node)
684723 (NodeIdentifier to)
685724 WithImplicitReconnect