@@ -5,6 +5,7 @@
5{-# LANGUAGE BangPatterns #-}
5{-# LANGUAGE BangPatterns #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7{-# LANGUAGE RecordWildCards #-}
7{-# LANGUAGE RecordWildCards #-}
8+{-# LANGUAGE MagicHash #-}
9-- | Local nodes
10-- | Local nodes
@@ -28,6 +29,7 @@ import System.IO (fixIO, hPutStrLn, stderr)
28import System.Mem.Weak (Weak, deRefWeak)
29import System.Mem.Weak (Weak, deRefWeak)
29import qualified Data.ByteString.Lazy as BSL (fromChunks)
30import qualified Data.ByteString.Lazy as BSL (fromChunks)
30import Data.Binary (decode)
31import Data.Binary (decode)
32+import Data.Function (fix)
31import Data.Map (Map)
33import Data.Map (Map)
32import qualified Data.Map as Map
34import qualified Data.Map as Map
@@ -65,14 +67,24 @@ import Control.Exception
65 , Exception
67 , Exception
67 , uninterruptibleMask_
69 , uninterruptibleMask_
69import qualified Control.Exception as Exception
73import qualified Control.Exception as Exception
70 ( Handler(..)
74 ( Handler(..)
81+import Control.Concurrent
75−import Control.Concurrent (forkIO, forkIOWithUnmask, killThread, myThreadId)
76import Control.Distributed.Process.Internal.StrictMVar
88import Control.Distributed.Process.Internal.StrictMVar
78 , withMVar
90 , withMVar
@@ -83,8 +95,18 @@ import Control.Distributed.Process.Internal.StrictMVar
84 , takeMVar
96 , takeMVar
98+import Control.Distributed.Process.Internal.ThreadPool
86import Control.Concurrent.Chan (newChan, writeChan, readChan)
104import Control.Concurrent.Chan (newChan, writeChan, readChan)
87−import qualified Control.Concurrent.MVar as MVar (newEmptyMVar, takeMVar)
105+import qualified Control.Concurrent.MVar as MVar
88import Control.Concurrent.STM
110import Control.Concurrent.STM
89 ( atomically
111 ( atomically
@@ -218,8 +240,19 @@ import qualified Control.Distributed.Process.Internal.StrictContainerAccessors a
218 ( mapMaybe
240 ( mapMaybe
219 , mapDefault
241 , mapDefault
243+import GHC.IO (IO(..), unsafeUnmask)
244+import GHC.Base ( maskAsyncExceptions# )
246+import System.Timeout
221import Unsafe.Coerce
247import Unsafe.Coerce
249+-- Remove these when this is fixed:
250+-- https://ghc.haskell.org/trac/ghc/ticket/10149
251+block :: IO a -> IO a
252+block (IO io) = IO $ maskAsyncExceptions# io
253+unblock :: IO a -> IO a
254+unblock = unsafeUnmask
223--------------------------------------------------------------------------------
256--------------------------------------------------------------------------------
224-- Initialization --
257-- Initialization --
225--------------------------------------------------------------------------------
258--------------------------------------------------------------------------------
@@ -249,12 +282,14 @@ createBareLocalNode endPoint rtable = do
249 , _localConnections = Map.empty
282 , _localConnections = Map.empty
251 ctrlChan <- newChan
284 ctrlChan <- newChan
285+ sendPool <- newThreadPool
252 let node = LocalNode { localNodeId = NodeId $ NT.address endPoint
286 let node = LocalNode { localNodeId = NodeId $ NT.address endPoint
253 , localEndPoint = endPoint
287 , localEndPoint = endPoint
254 , localState = state
288 , localState = state
255 , localCtrlChan = ctrlChan
289 , localCtrlChan = ctrlChan
256 , localEventBus = MxEventBusInitialising
290 , localEventBus = MxEventBusInitialising
257 , remoteTable = rtable
291 , remoteTable = rtable
292+ , localSendPool = sendPool
259 tracedNode <- startMxAgent node
294 tracedNode <- startMxAgent node
@@ -361,11 +396,14 @@ runProcess node proc = do
362-- | Spawn a new process on a local node
397-- | Spawn a new process on a local node
363forkProcess :: LocalNode -> Process () -> IO ProcessId
398forkProcess :: LocalNode -> Process () -> IO ProcessId
364−forkProcess node proc =
399+forkProcess node proc = do
400+ ms <- getMaskingState
365− modifyMVarMasked (localState node) startProcess
401+ modifyMVarMasked (localState node) (startProcess ms)
403+ startProcess :: MaskingState
367− startProcess :: LocalNodeState -> IO (LocalNodeState, ProcessId)
405+ -> IO (LocalNodeState, ProcessId)
368− startProcess (LocalNodeValid vst) = do
406+ startProcess ms (LocalNodeValid vst) = do
369 let lpid = LocalProcessId { lpidCounter = vst ^. localPidCounter
407 let lpid = LocalProcessId { lpidCounter = vst ^. localPidCounter
370 , lpidUnique = vst ^. localPidUnique
408 , lpidUnique = vst ^. localPidUnique
@@ -387,7 +425,13 @@ forkProcess node proc =
387 , processThread = tid
425 , processThread = tid
388 , processNode = node
426 , processNode = node
428+ -- Rewrite this code when this is fixed:
429+ -- https://ghc.haskell.org/trac/ghc/ticket/10149
430+ let unmask = case ms of
432+ MaskedInterruptible -> block
433+ MaskedUninterruptible -> id
390− tid' <- uninterruptibleMask_ $ forkIOWithUnmask $ \unmask -> do
434+ tid' <- uninterruptibleMask_ $ forkIO $ do
391 reason <- Exception.catches
435 reason <- Exception.catches
392 (unmask $ runLocalProcess lproc proc >> return DiedNormal)
436 (unmask $ runLocalProcess lproc proc >> return DiedNormal)
393 [ (Exception.Handler (\ex@(ProcessExitException from msg) -> do
437 [ (Exception.Handler (\ex@(ProcessExitException from msg) -> do
@@ -433,7 +477,7 @@ forkProcess node proc =
436− startProcess LocalNodeClosed =
480+ startProcess _ LocalNodeClosed =
437 throwIO $ NodeClosedException $ localNodeId node
481 throwIO $ NodeClosedException $ localNodeId node
439 cleanupProcess :: ProcessId
483 cleanupProcess :: ProcessId
@@ -491,24 +535,78 @@ incomingFrom addr = aux >>> DAC.mapDefault Set.empty addr
492 aux = accessor _incomingFrom (\fr st -> st { _incomingFrom = fr })
536 aux = accessor _incomingFrom (\fr st -> st { _incomingFrom = fr })
538+-- | And 'EventMonitor' monitors the handling of events reporting when an event
539+-- is not handled promptly. It monitors events which are handled in sequence.
540+data EventMonitor a = EventMonitor
541+ { -- | Notifies that an event was received. Takes the event as argument.
542+ eventMonitorGotEvent :: a -> IO ()
543+ -- | Notifies that the last received event was handled.
545+ -- Might be called even when there is no event, in which case it will have
547+ , eventMonitorHandledEvent :: IO ()
548+ -- | Stops the event monitor.
549+ , eventMonitorStop :: IO ()
552+-- | `eventMon <- monitorThread t action`
554+-- Creates an event monitor that executes `action` when an event hasn't been
555+-- handled withing `t` microseconds. `action` takes as argument the event that
556+-- wasn't handled timely.
558+monitorThread :: Int -> (a -> IO ()) -> IO (EventMonitor a)
559+monitorThread tmicrosecs action = do
560+ mv <- MVar.newEmptyMVar
561+ tid <- forkIO $ forever' $ do
562+ -- loop until some event is reported
563+ ev <- fix $ \loop -> MVar.takeMVar mv >>= maybe loop return
564+ -- wait until the event is handled
565+ Exception.mask_ (timeout tmicrosecs $ MVar.takeMVar mv) >>=
566+ maybe (do action ev -- report the unhandled event
567+ -- wait for the slow event handler
568+ void $ MVar.takeMVar mv
572+ { eventMonitorGotEvent = MVar.putMVar mv . Just
573+ , eventMonitorHandledEvent = MVar.putMVar mv Nothing
574+ , eventMonitorStop = killThread tid
494handleIncomingMessages :: LocalNode -> IO ()
577handleIncomingMessages :: LocalNode -> IO ()
495−handleIncomingMessages node = go initConnectionState
578+handleIncomingMessages node =
580+ (monitorThread 1000000 $ \ev ->
582+ "handleIncomingMessages blocked" ++ show (localNodeId node) ++
585+ eventMonitorStop $ \eventMon ->
586+ (go eventMon initConnectionState
496− `Exception.catch` \(NodeClosedException _) -> return ()
587+ `Exception.catch` \(NodeClosedException _) -> return ()
588+ ) `Exception.catch` \e -> do
590+ "handleIncomingMessages failed: " ++
591+ show (localNodeId node, e :: SomeException)
498− go :: ConnectionState -> IO ()
594+ go :: EventMonitor NT.Event -> ConnectionState -> IO ()
499− go !st = do
595+ go eventMon !st = do
596+ eventMonitorHandledEvent eventMon
500 event <- NT.receive endpoint
597 event <- NT.receive endpoint
598+ eventMonitorGotEvent eventMon event
501 case event of
599 case event of
502 NT.ConnectionOpened cid rel theirAddr ->
600 NT.ConnectionOpened cid rel theirAddr ->
503 if rel == NT.ReliableOrdered
601 if rel == NT.ReliableOrdered
505 trace node (MxConnected cid theirAddr)
603 trace node (MxConnected cid theirAddr)
506− >> go (
604+ >> go eventMon (
507 (incomingAt cid ^= Just (theirAddr, Uninit))
605 (incomingAt cid ^= Just (theirAddr, Uninit))
508 . (incomingFrom theirAddr ^: Set.insert cid)
606 . (incomingFrom theirAddr ^: Set.insert cid)
511− else invalidRequest cid st
609+ else invalidRequest eventMon cid st
512 NT.Received cid payload ->
610 NT.Received cid payload ->
513 case st ^. incomingAt cid of
611 case st ^. incomingAt cid of
514 Just (_, ToProc pid weakQueue) -> do
612 Just (_, ToProc pid weakQueue) -> do
@@ -519,7 +617,7 @@ handleIncomingMessages node = go initConnectionState
519 let msg = payloadToMessage payload
617 let msg = payloadToMessage payload
520 enqueue queue msg -- 'enqueue' is strict
618 enqueue queue msg -- 'enqueue' is strict
521 trace node (MxReceived pid msg)
619 trace node (MxReceived pid msg)
522− go st
620+ go eventMon st
523 Just (_, ToChan (TypedChannel chan')) -> do
621 Just (_, ToChan (TypedChannel chan')) -> do
524 mChan <- deRefWeak chan'
622 mChan <- deRefWeak chan'
525 -- If mChan is Nothing, the process has given up the read end of
623 -- If mChan is Nothing, the process has given up the read end of
@@ -527,11 +625,11 @@ handleIncomingMessages node = go initConnectionState
527 forM_ mChan $ \chan -> atomically $
625 forM_ mChan $ \chan -> atomically $
528 -- We make sure the message is fully decoded when it is enqueued
626 -- We make sure the message is fully decoded when it is enqueued
529 writeTQueue chan $! decode (BSL.fromChunks payload)
627 writeTQueue chan $! decode (BSL.fromChunks payload)
530− go st
628+ go eventMon st
531 Just (_, ToNode) -> do
629 Just (_, ToNode) -> do
532 let ctrlMsg = decode . BSL.fromChunks $ payload
630 let ctrlMsg = decode . BSL.fromChunks $ payload
533 writeChan ctrlChan $! ctrlMsg
631 writeChan ctrlChan $! ctrlMsg
534− go st
632+ go eventMon st
535 Just (src, Uninit) ->
633 Just (src, Uninit) ->
536 case decode (BSL.fromChunks payload) of
634 case decode (BSL.fromChunks payload) of
537 ProcessIdentifier pid -> do
635 ProcessIdentifier pid -> do
@@ -539,9 +637,9 @@ handleIncomingMessages node = go initConnectionState
539 mProc <- withValidLocalState node $ return . (^. localProcessWithId lpid)
637 mProc <- withValidLocalState node $ return . (^. localProcessWithId lpid)
540 case mProc of
638 case mProc of
541 Just proc ->
639 Just proc ->
542− go (incomingAt cid ^= Just (src, ToProc pid (processWeakQ proc)) $ st)
640+ go eventMon (incomingAt cid ^= Just (src, ToProc pid (processWeakQ proc)) $ st)
543 Nothing ->
641 Nothing ->
544− invalidRequest cid st
642+ invalidRequest eventMon cid st
545 SendPortIdentifier chId -> do
643 SendPortIdentifier chId -> do
546 let lcid = sendPortLocalId chId
644 let lcid = sendPortLocalId chId
547 lpid = processLocalId (sendPortProcessId chId)
645 lpid = processLocalId (sendPortProcessId chId)
@@ -551,24 +649,25 @@ handleIncomingMessages node = go initConnectionState
551 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
649 mChannel <- withMVar (processState proc) $ return . (^. typedChannelWithId lcid)
552 case mChannel of
650 case mChannel of
553 Just channel ->
651 Just channel ->
554− go (incomingAt cid ^= Just (src, ToChan channel) $ st)
653+ (incomingAt cid ^= Just (src, ToChan channel) $ st)
555 Nothing ->
654 Nothing ->
556− invalidRequest cid st
655+ invalidRequest eventMon cid st
557 Nothing ->
656 Nothing ->
558− invalidRequest cid st
657+ invalidRequest eventMon cid st
559 NodeIdentifier nid ->
658 NodeIdentifier nid ->
560 if nid == localNodeId node
659 if nid == localNodeId node
561− then go (incomingAt cid ^= Just (src, ToNode) $ st)
660+ then go eventMon (incomingAt cid ^= Just (src, ToNode) $ st)
562− else invalidRequest cid st
661+ else invalidRequest eventMon cid st
563 Nothing ->
662 Nothing ->
564− invalidRequest cid st
663+ invalidRequest eventMon cid st
565 NT.ConnectionClosed cid ->
664 NT.ConnectionClosed cid ->
566 case st ^. incomingAt cid of
665 case st ^. incomingAt cid of
567 Nothing ->
666 Nothing ->
568− invalidRequest cid st
667+ invalidRequest eventMon cid st
569 Just (src, _) -> do
668 Just (src, _) -> do
570 trace node (MxDisconnected cid src)
669 trace node (MxDisconnected cid src)
571− go ( (incomingAt cid ^= Nothing)
670+ go eventMon ( (incomingAt cid ^= Nothing)
572 . (incomingFrom src ^: Set.delete cid)
671 . (incomingFrom src ^: Set.delete cid)
@@ -580,8 +679,10 @@ handleIncomingMessages node = go initConnectionState
580 , ctrlMsgSignal = Died nid DiedDisconnect
679 , ctrlMsgSignal = Died nid DiedDisconnect
582 let notLost k = not (k `Set.member` (st ^. incomingFrom theirAddr))
681 let notLost k = not (k `Set.member` (st ^. incomingFrom theirAddr))
682+ liftIO $ lookupWorker (localSendPool node) (NodeId theirAddr)
683+ >>= maybe (return ()) killThread
583 closeImplicitReconnections node nid
684 closeImplicitReconnections node nid
584− go ( (incomingFrom theirAddr ^= Set.empty)
685+ go eventMon ( (incomingFrom theirAddr ^= Set.empty)
585 . (incoming ^: Map.filterWithKey (const . notLost))
686 . (incoming ^: Map.filterWithKey (const . notLost))
@@ -596,15 +697,17 @@ handleIncomingMessages node = go initConnectionState
596 -- and we just give up
697 -- and we just give up
597 fail "Cloud Haskell fatal error: received unexpected multicast"
698 fail "Cloud Haskell fatal error: received unexpected multicast"
700+ invalidRequest :: EventMonitor NT.Event
599− invalidRequest :: NT.ConnectionId -> ConnectionState -> IO ()
702+ -> ConnectionState -> IO ()
600− invalidRequest cid st = do
703+ invalidRequest eventMon cid st = do
601 -- TODO: We should treat this as a fatal error on the part of the remote
704 -- TODO: We should treat this as a fatal error on the part of the remote
602 -- node. That is, we should report the remote node as having died, and we
705 -- node. That is, we should report the remote node as having died, and we
603 -- should close incoming connections (this requires a Transport layer
706 -- should close incoming connections (this requires a Transport layer
604 -- extension).
707 -- extension).
605 traceEventFmtIO node "" [(TraceStr " [network] invalid request: "),
708 traceEventFmtIO node "" [(TraceStr " [network] invalid request: "),
606 (Trace cid)]
709 (Trace cid)]
607− go ( incomingAt cid ^= Nothing
710+ go eventMon ( incomingAt cid ^= Nothing
@@ -616,9 +719,21 @@ handleIncomingMessages node = go initConnectionState
616--------------------------------------------------------------------------------
719--------------------------------------------------------------------------------
618runNodeController :: LocalNode -> IO ()
721runNodeController :: LocalNode -> IO ()
619−runNodeController node =
722+runNodeController node = do
724+ (monitorThread 1000000 $ \ev ->
726+ "nodeController blocked" ++ show (localNodeId node) ++
729+ eventMonitorStop $ \eventMon ->
620− runReaderT (evalStateT (unNC nodeController) initNCState) node
730+ (runReaderT (evalStateT (unNC (nodeController eventMon)) initNCState) node
621− `Exception.catch` \(NodeClosedException _) -> return ()
731+ `Exception.catch` \(NodeClosedException _) -> return ()
732+ ) `Exception.catch` \e -> do
734+ "nodeController failed: " ++
735+ show (localNodeId node, e :: SomeException)
623--------------------------------------------------------------------------------
738--------------------------------------------------------------------------------
624-- Internal data types --
739-- Internal data types --
@@ -634,6 +749,12 @@ data NCState = NCState
634 , _registeredOnNodes :: !(Map ProcessId [(NodeId,Int)])
749 , _registeredOnNodes :: !(Map ProcessId [(NodeId,Int)])
752+submitSendPool :: LocalNode -> NodeId -> IO () -> IO ()
753+submitSendPool node nid task = submitTask (localSendPool node) fork nid task
756+ forkIO $ io `Exception.catch` \(NodeClosedException _) -> return ()
637newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
758newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
638 deriving ( Applicative
759 deriving ( Applicative
639 , Functor
760 , Functor
@@ -644,11 +765,12 @@ newtype NC a = NC { unNC :: StateT NCState (ReaderT LocalNode IO) a }
646initNCState :: NCState
767initNCState :: NCState
647−initNCState = NCState { _links = Map.empty
768+initNCState = NCState
769+ { _links = Map.empty
648− , _monitors = Map.empty
770+ , _monitors = Map.empty
649− , _registeredHere = Map.empty
771+ , _registeredHere = Map.empty
650− , _registeredOnNodes = Map.empty
772+ , _registeredOnNodes = Map.empty
653-- | Thrown in response to the user invoking 'kill' (see Primitives.hs). This
775-- | Thrown in response to the user invoking 'kill' (see Primitives.hs). This
654-- type is deliberately not exported so it cannot be caught explicitly.
776-- type is deliberately not exported so it cannot be caught explicitly.
@@ -666,7 +788,7 @@ ncSendToProcess pid msg = do
666 node <- ask
788 node <- ask
667 if processNodeId pid == localNodeId node
789 if processNodeId pid == localNodeId node
668 then ncEffectLocalSend node pid msg
790 then ncEffectLocalSend node pid msg
669− else liftIO $ sendBinary node
791+ else liftIO $ submitSendPool node (processNodeId pid) $ sendBinary node
670 (NodeIdentifier $ localNodeId node)
792 (NodeIdentifier $ localNodeId node)
671 (NodeIdentifier $ processNodeId pid)
793 (NodeIdentifier $ processNodeId pid)
672 WithImplicitReconnect
794 WithImplicitReconnect
@@ -679,7 +801,7 @@ ncSendToNode to msg = do
679 node <- ask
801 node <- ask
680 liftIO $ if to == localNodeId node
802 liftIO $ if to == localNodeId node
681 then writeChan (localCtrlChan node) $! msg
803 then writeChan (localCtrlChan node) $! msg
682− else sendBinary node
804+ else submitSendPool node to $ sendBinary node
683 (NodeIdentifier $ localNodeId node)
805 (NodeIdentifier $ localNodeId node)
684 (NodeIdentifier to)
806 (NodeIdentifier to)
685 WithImplicitReconnect
807 WithImplicitReconnect
@@ -718,11 +840,13 @@ withLocalTracer node act = act (localEventBus node)
718--------------------------------------------------------------------------------
840--------------------------------------------------------------------------------
720-- [Unified: Table 7]
842-- [Unified: Table 7]
721−nodeController :: NC ()
843+nodeController :: EventMonitor NCMsg -> NC ()
722−nodeController = do
844+nodeController eventMon = do
723 node <- ask
845 node <- ask
724 forever' $ do
846 forever' $ do
847+ liftIO $ eventMonitorHandledEvent eventMon
725 msg <- liftIO $ readChan (localCtrlChan node)
848 msg <- liftIO $ readChan (localCtrlChan node)
849+ liftIO $ eventMonitorGotEvent eventMon msg
727 -- [Unified: Table 7, rule nc_forward]
851 -- [Unified: Table 7, rule nc_forward]
728 case destNid (ctrlMsgSignal msg) of
852 case destNid (ctrlMsgSignal msg) of