git.iko.soy/distributed-process
on master help sign in
Merge branch 'debug-trace'
commit6a84b5986a36af869b74f6260df516412cb30f52 authorTim Watson <tim@rabbitmq.com> dateSat 26 Jan 2013, 17:05 (14 years ago) parent2419547628f496
6 files changed+277−65
distributed-process/distributed-process.cabal+2−1 distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+19 distributed-process/src/Control/Distributed/Process/Internal/Trace.hs+122 distributed-process/src/Control/Distributed/Process/Internal/Types.hs+52−28 distributed-process/src/Control/Distributed/Process/Node.hs+70−24 distributed-process/tests/TestStats.hs+12−12
distributed-process/distributed-process.cabal+2−1
@@ -61,6 +61,7 @@ Library
6161 Control.Distributed.Process.Internal.Primitives,
6262 Control.Distributed.Process.Internal.CQueue,
6363 Control.Distributed.Process.Internal.Types,
64+ Control.Distributed.Process.Internal.Trace,
6465 Control.Distributed.Process.Internal.Closure.BuiltIn,
6566 Control.Distributed.Process.Internal.Messaging,
6667 Control.Distributed.Process.Internal.StrictList,
@@ -145,7 +146,7 @@ Test-Suite TestStats
145146 ScopedTypeVariables,
146147 DeriveDataTypeable,
147148 GeneralizedNewtypeDeriving
148− ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
149+ ghc-options: -Wall -debug -eventlog -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
149150 HS-Source-Dirs: tests
150151
151152
distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+19
@@ -83,6 +83,8 @@ module Control.Distributed.Process.Internal.Primitives
8383 -- * Reconnecting
8484 , reconnect
8585 , reconnectPort
86+ -- * Tracing/Debugging
87+ , trace
8688 ) where
8789
8890#if ! MIN_VERSION_base(4,6,0)
@@ -168,6 +170,7 @@ import Control.Distributed.Process.Internal.Messaging
168170 , sendPayload
169171 , disconnect
170172 )
173+import qualified Control.Distributed.Process.Internal.Trace as Trace
171174import Control.Distributed.Process.Internal.WeakTQueue
172175 ( newTQueueIO
173176 , readTQueue
@@ -892,6 +895,22 @@ reconnectPort them = do
892895 node <- processNode <$> ask
893896 liftIO $ disconnect node (ProcessIdentifier us) (SendPortIdentifier (sendPortId them))
894897
898+--------------------------------------------------------------------------------
899+-- Debugging/Tracing --
900+--------------------------------------------------------------------------------
901+
902+-- | Send a message to the internal (system) trace facility. If tracing is
903+-- enabled, this will create a custom trace event in the event log. Specifically,
904+-- the environment variable @DISTRIBUTED_PROCESS_TRACE_FILE@ is set to a valid
905+-- path, then trace output will be written to that file. Otherwise, if the GHC
906+-- eventlog is enabled (i.e., you've compiled with @-eventlog@ set) and the
907+-- relevant @+RTS@ options given, then the message will be sent to the event log.
908+-- If neither facility is in use then trace messages will be silently dropped.
909+trace :: String -> Process ()
910+trace s = do
911+ node <- processNode <$> ask
912+ liftIO $ Trace.trace (localTracer node) s
913+
895914--------------------------------------------------------------------------------
896915-- Auxiliary functions --
897916--------------------------------------------------------------------------------
distributed-process/src/Control/Distributed/Process/Internal/Trace.hsnew file+122
@@ -0,0 +1,122 @@
1+-- | Simple (internal) system logging/tracing support.
2+module Control.Distributed.Process.Internal.Trace
3+ ( Tracer
4+ , TraceArg(..)
5+ , trace
6+ , traceFormat
7+ , startTracing
8+ , stopTracer
9+ ) where
10+
11+import Control.Concurrent (forkIO)
12+import Control.Concurrent.Chan (writeChan)
13+import Control.Concurrent.STM
14+ ( TQueue
15+ , newTQueueIO
16+ , readTQueue
17+ , writeTQueue
18+ , atomically
19+ )
20+import Control.Distributed.Process.Internal.Types
21+ ( Tracer(..)
22+ , LocalNode(..)
23+ , NCMsg(..)
24+ , Identifier(ProcessIdentifier)
25+ , ProcessSignal(NamedSend)
26+ , forever'
27+ , nullProcessId
28+ , createMessage
29+ )
30+import Control.Exception
31+ ( catch
32+ , throwTo
33+ , SomeException
34+ , AsyncException(ThreadKilled)
35+ )
36+import Data.List (intersperse)
37+import Data.Time.Clock (getCurrentTime)
38+import Data.Time.Format (formatTime)
39+import Debug.Trace (traceEventIO)
40+
41+import Prelude hiding (catch)
42+
43+import System.Environment (getEnv)
44+import System.IO
45+ ( Handle
46+ , IOMode(AppendMode)
47+ , BufferMode(..)
48+ , openFile
49+ , hClose
50+ , hPutStrLn
51+ , hSetBuffering
52+ )
53+import System.Locale (defaultTimeLocale)
54+
55+data TraceArg =
56+ TraceStr String
57+ | forall a. (Show a) => Trace a
58+
59+startTracing :: LocalNode -> IO LocalNode
60+startTracing node = do
61+ tracer <- defaultTracer node
62+ return node { localTracer = tracer }
63+
64+defaultTracer :: LocalNode -> IO Tracer
65+defaultTracer node = do
66+ catch (getEnv "DISTRIBUTED_PROCESS_TRACE_FILE" >>= logfileTracer)
67+ (\(_ :: IOError) -> defaultTracerAux node)
68+
69+defaultTracerAux :: LocalNode -> IO Tracer
70+defaultTracerAux node = do
71+ catch (getEnv "DISTRIBUTED_PROCESS_TRACE_CONSOLE" >> procTracer node)
72+ (\(_ :: IOError) -> return (EventLogTracer traceEventIO))
73+ where procTracer :: LocalNode -> IO Tracer
74+ procTracer n = return $ (LocalNodeTracer n)
75+
76+logfileTracer :: FilePath -> IO Tracer
77+logfileTracer p = do
78+ q <- newTQueueIO
79+ h <- openFile p AppendMode
80+ hSetBuffering h LineBuffering
81+ tid <- forkIO $ logger h q `catch` (\(_ :: SomeException) ->
82+ hClose h >> return ())
83+ return $ LogFileTracer tid q h
84+ where logger :: Handle -> TQueue String -> IO ()
85+ logger h q' = forever' $ do
86+ msg <- atomically $ readTQueue q'
87+ now <- getCurrentTime
88+ hPutStrLn h $ msg ++ (formatTime defaultTimeLocale " - %c" now)
89+
90+-- TODO: compatibility layer for GHC/base versions (e.g., where's killThread?)
91+
92+stopTracer :: Tracer -> IO () -- overzealous but harmless duplication of hClose
93+stopTracer (LogFileTracer tid _ h) = throwTo tid ThreadKilled >> hClose h
94+stopTracer _ = return ()
95+
96+trace :: Tracer -> String -> IO ()
97+trace (LogFileTracer _ q _) msg = atomically $ writeTQueue q msg
98+trace (LocalNodeTracer n) msg = sendTraceMsg n msg
99+trace (EventLogTracer t) msg = t msg
100+trace InactiveTracer _ = return ()
101+
102+traceFormat :: Tracer
103+ -> String
104+ -> [TraceArg]
105+ -> IO ()
106+traceFormat t d ls =
107+ trace t $ concat (intersperse d (map toS ls))
108+ where toS :: TraceArg -> String
109+ toS (TraceStr s) = s
110+ toS (Trace a) = show a
111+
112+sendTraceMsg :: LocalNode -> String -> IO ()
113+sendTraceMsg node string = do
114+ now <- getCurrentTime
115+ msg <- return $ (formatTime defaultTimeLocale "%c" now, string)
116+ emptyPid <- return $ (nullProcessId (localNodeId node))
117+ traceMsg <- return $ NCMsg {
118+ ctrlMsgSender = ProcessIdentifier (emptyPid)
119+ , ctrlMsgSignal = (NamedSend "logger" (createMessage msg))
120+ }
121+ writeChan (localCtrlChan node) traceMsg
122+
distributed-process/src/Control/Distributed/Process/Internal/Types.hs+52−28
@@ -14,6 +14,7 @@ module Control.Distributed.Process.Internal.Types
1414 , nullProcessId
1515 -- * Local nodes and processes
1616 , LocalNode(..)
17+ , Tracer(..)
1718 , LocalNodeState(..)
1819 , LocalProcess(..)
1920 , LocalProcessState(..)
@@ -67,6 +68,8 @@ module Control.Distributed.Process.Internal.Types
6768 , channelCounter
6869 , typedChannels
6970 , typedChannelWithId
71+ -- * Utilities
72+ , forever'
7073 ) where
7174
7275import System.Mem.Weak (Weak)
@@ -88,6 +91,7 @@ import Control.Exception (Exception)
8891import Control.Concurrent (ThreadId)
8992import Control.Concurrent.Chan (Chan)
9093import Control.Concurrent.STM (STM)
94+import qualified Control.Concurrent.STM as STM (TQueue)
9195import qualified Network.Transport as NT (EndPoint, EndPointAddress, Connection)
9296import Control.Applicative (Applicative, Alternative, (<$>), (<*>))
9397import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT)
@@ -106,6 +110,7 @@ import Control.Distributed.Process.Internal.StrictMVar (StrictMVar)
106110import Control.Distributed.Process.Internal.WeakTQueue (TQueue)
107111import Control.Distributed.Static (RemoteTable, Closure)
108112import qualified Control.Distributed.Process.Internal.StrictContainerAccessors as DAC (mapMaybe)
113+import System.IO (Handle)
109114
110115--------------------------------------------------------------------------------
111116-- Node and process identifiers --
@@ -175,19 +180,28 @@ nullProcessId nid =
175180-- Local nodes and processes --
176181--------------------------------------------------------------------------------
177182
183+-- | Required for system tracing in the node controller
184+data Tracer =
185+ LogFileTracer !ThreadId !(STM.TQueue String) !Handle
186+ | EventLogTracer !(String -> IO ())
187+ | LocalNodeTracer !LocalNode
188+ | InactiveTracer -- NB: never used, this is required to initialize LocalNode
189+
178190-- | Local nodes
179191data LocalNode = LocalNode
180192 { -- | 'NodeId' of the node
181− localNodeId :: !NodeId
193+ localNodeId :: !NodeId
182194 -- | The network endpoint associated with this node
183− , localEndPoint :: !NT.EndPoint
195+ , localEndPoint :: !NT.EndPoint
184196 -- | Local node state
185− , localState :: !(StrictMVar LocalNodeState)
197+ , localState :: !(StrictMVar LocalNodeState)
186198 -- | Channel for the node controller
187− , localCtrlChan :: !(Chan NCMsg)
199+ , localCtrlChan :: !(Chan NCMsg)
200+ -- | Current active system debug/trace log
201+ , localTracer :: !Tracer
188202 -- | Runtime lookup table for supporting closures
189203 -- TODO: this should be part of the CH state, not the local endpoint state
190− , remoteTable :: !RemoteTable
204+ , remoteTable :: !RemoteTable
191205 }
192206
193207data ImplicitReconnect = WithImplicitReconnect | NoImplicitReconnect
@@ -432,7 +446,7 @@ data WhereIsReply = WhereIsReply String (Maybe ProcessId)
432446data RegisterReply = RegisterReply String Bool
433447 deriving (Show, Typeable)
434448
435−-- | Provide information about a running process
449+-- | Provide information about a running process
436450data ProcessInfo = ProcessInfo {
437451 infoNode :: NodeId
438452 , infoRegisteredNames :: [String]
@@ -441,21 +455,9 @@ data ProcessInfo = ProcessInfo {
441455 , infoLinks :: [ProcessId]
442456 } deriving (Show, Eq, Typeable)
443457
444−instance Binary ProcessInfo where
445− get = ProcessInfo <$> get <*> get <*> get <*> get <*> get
446− put pInfo = put (infoNode pInfo)
447− >> put (infoRegisteredNames pInfo)
448− >> put (infoMessageQueueLength pInfo)
449− >> put (infoMonitors pInfo)
450− >> put (infoLinks pInfo)
451−
452458data ProcessInfoNone = ProcessInfoNone DiedReason
453459 deriving (Show, Typeable)
454460
455−instance Binary ProcessInfoNone where
456− get = ProcessInfoNone <$> get
457− put (ProcessInfoNone r) = put r
458−
459461--------------------------------------------------------------------------------
460462-- Node controller internal data types --
461463--------------------------------------------------------------------------------
@@ -531,16 +533,16 @@ instance Binary ProcessSignal where
531533 get = do
532534 header <- getWord8
533535 case header of
534− 0 -> Link <$> get
535− 1 -> Unlink <$> get
536− 2 -> Monitor <$> get
537− 3 -> Unmonitor <$> get
538− 4 -> Died <$> get <*> get
539− 5 -> Spawn <$> get <*> get
540− 6 -> WhereIs <$> get
541− 7 -> Register <$> get <*> get <*> get <*> get
542− 8 -> NamedSend <$> get <*> (payloadToMessage <$> get)
543− 9 -> Kill <$> get <*> get
536+ 0 -> Link <$> get
537+ 1 -> Unlink <$> get
538+ 2 -> Monitor <$> get
539+ 3 -> Unmonitor <$> get
540+ 4 -> Died <$> get <*> get
541+ 5 -> Spawn <$> get <*> get
542+ 6 -> WhereIs <$> get
543+ 7 -> Register <$> get <*> get <*> get <*> get
544+ 8 -> NamedSend <$> get <*> (payloadToMessage <$> get)
545+ 9 -> Kill <$> get <*> get
544546 10 -> Exit <$> get <*> (payloadToMessage <$> get)
545547 30 -> GetInfo <$> get
546548 _ -> fail "ProcessSignal.get: invalid"
@@ -589,6 +591,18 @@ instance Binary RegisterReply where
589591 put (RegisterReply label ok) = put label >> put ok
590592 get = RegisterReply <$> get <*> get
591593
594+instance Binary ProcessInfo where
595+ get = ProcessInfo <$> get <*> get <*> get <*> get <*> get
596+ put pInfo = put (infoNode pInfo)
597+ >> put (infoRegisteredNames pInfo)
598+ >> put (infoMessageQueueLength pInfo)
599+ >> put (infoMonitors pInfo)
600+ >> put (infoLinks pInfo)
601+
602+instance Binary ProcessInfoNone where
603+ get = ProcessInfoNone <$> get
604+ put (ProcessInfoNone r) = put r
605+
592606--------------------------------------------------------------------------------
593607-- Accessors --
594608--------------------------------------------------------------------------------
@@ -625,3 +639,13 @@ typedChannels = accessor _typedChannels (\cs st -> st { _typedChannels = cs })
625639
626640typedChannelWithId :: LocalSendPortId -> Accessor LocalProcessState (Maybe TypedChannel)
627641typedChannelWithId cid = typedChannels >>> DAC.mapMaybe cid
642+
643+--------------------------------------------------------------------------------
644+-- Utilities --
645+--------------------------------------------------------------------------------
646+
647+-- Like 'Control.Monad.forever' but sans space leak
648+{-# INLINE forever' #-}
649+forever' :: Monad m => m a -> m b
650+forever' a = let a' = a >> a' in a'
651+
distributed-process/src/Control/Distributed/Process/Node.hs+70−24
@@ -62,7 +62,9 @@ import Control.Distributed.Process.Internal.StrictMVar
6262 , takeMVar
6363 )
6464import Control.Concurrent.Chan (newChan, writeChan, readChan)
65−import Control.Concurrent.STM (atomically)
65+import Control.Concurrent.STM
66+ ( atomically
67+ )
6668import Control.Distributed.Process.Internal.CQueue
6769 ( CQueue
6870 , enqueue
@@ -97,6 +99,7 @@ import Control.Distributed.Process.Internal.Types
9799 , LocalProcessId(..)
98100 , ProcessId(..)
99101 , LocalNode(..)
102+ , Tracer(InactiveTracer)
100103 , LocalNodeState(..)
101104 , LocalProcess(..)
102105 , LocalProcessState(..)
@@ -108,6 +111,7 @@ import Control.Distributed.Process.Internal.Types
108111 , localPidUnique
109112 , localProcessWithId
110113 , localConnections
114+ , forever'
111115 , MonitorRef(..)
112116 , ProcessMonitorNotification(..)
113117 , NodeMonitorNotification(..)
@@ -139,6 +143,12 @@ import Control.Distributed.Process.Internal.Types
139143 , firstNonReservedProcessId
140144 , ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
141145 )
146+import Control.Distributed.Process.Internal.Trace
147+ ( TraceArg(..)
148+ , traceFormat
149+ , startTracing
150+ , stopTracer
151+ )
142152import Control.Distributed.Process.Serializable (Serializable)
143153import Control.Distributed.Process.Internal.Messaging
144154 ( sendBinary
@@ -195,16 +205,13 @@ createBareLocalNode endPoint rtable = do
195205 , localEndPoint = endPoint
196206 , localState = state
197207 , localCtrlChan = ctrlChan
208+ , localTracer = InactiveTracer
198209 , remoteTable = rtable
199210 }
200− void . forkIO $ runNodeController node
201− void . forkIO $ handleIncomingMessages node
202− return node
203−
204−-- Like 'Control.Monad.forever' but sans space leak
205−{-# INLINE forever' #-}
206−forever' :: Monad m => m a -> m b
207−forever' a = let a' = a >> a' in a'
211+ tracedNode <- startTracing node
212+ void . forkIO $ runNodeController tracedNode
213+ void . forkIO $ handleIncomingMessages tracedNode
214+ return tracedNode
208215
209216-- | Start and register the service processes on a node
210217-- (for now, this is only the logger)
@@ -218,6 +225,10 @@ startServiceProcesses node = do
218225 [ match $ \((time, pid, string) ::(String, ProcessId, String)) -> do
219226 liftIO . hPutStrLn stderr $ time ++ " " ++ show pid ++ ": " ++ string
220227 loop
228+ , match $ \((time, string) :: (String, String)) -> do
229+ -- this is a 'trace' message from the local node tracer
230+ liftIO . hPutStrLn stderr $ time ++ " [trace] " ++ string
231+ loop
221232 , match $ \(ch :: SendPort ()) -> -- a shutdown request
222233 sendChan ch ()
223234 ]
@@ -267,6 +278,7 @@ forkProcess node proc = modifyMVar (localState node) startProcess
267278 reason <- Exception.catch
268279 (runLocalProcess lproc proc >> return DiedNormal)
269280 (return . DiedException . (show :: SomeException -> String))
281+
270282 -- [Unified: Table 4, rules termination and exiting]
271283 modifyMVar_ (localState node) (cleanupProcess pid)
272284 writeChan (localCtrlChan node) NCMsg
@@ -423,22 +435,27 @@ handleIncomingMessages node = go initConnectionState
423435 $ st
424436 )
425437 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
426− fail $ "Cloud Haskell fatal error: end point failed: " ++ str
438+ fatal $ "Cloud Haskell fatal error: end point failed: " ++ str
427439 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
428− fail $ "Cloud Haskell fatal error: transport failed: " ++ str
440+ fatal $ "Cloud Haskell fatal error: transport failed: " ++ str
429441 NT.EndPointClosed ->
430− return ()
442+ stopTracer (localTracer node) >> return ()
431443 NT.ReceivedMulticast _ _ ->
432444 -- If we received a multicast message, something went horribly wrong
433445 -- and we just give up
434− fail "Cloud Haskell fatal error: received unexpected multicast"
446+ fatal "Cloud Haskell fatal error: received unexpected multicast"
447+
448+ fatal :: String -> IO ()
449+ fatal msg = stopTracer (localTracer node) >> fail msg
435450
436451 invalidRequest :: NT.ConnectionId -> ConnectionState -> IO ()
437− invalidRequest cid st =
452+ invalidRequest cid st = do
438453 -- TODO: We should treat this as a fatal error on the part of the remote
439454 -- node. That is, we should report the remote node as having died, and we
440455 -- should close incoming connections (this requires a Transport layer
441456 -- extension).
457+ traceEventFmtIO node "" [(TraceStr "[network] invalid request: "),
458+ (Trace cid)]
442459 go ( incomingAt cid ^= Nothing
443460 $ st
444461 )
@@ -490,6 +507,35 @@ instance Show ProcessKillException where
490507 show (ProcessKillException pid reason) =
491508 "killed-by=" ++ show pid ++ ",reason=" ++ reason
492509
510+--------------------------------------------------------------------------------
511+-- Tracing/Debugging --
512+--------------------------------------------------------------------------------
513+
514+-- [Issue #104]
515+
516+traceNotifyDied :: LocalNode -> Identifier -> DiedReason -> NC ()
517+traceNotifyDied node ident reason =
518+ case reason of
519+ DiedNormal -> return ()
520+ _ -> traceNcEventFmt node " "
521+ [(TraceStr "[node-controller]"),
522+ (Trace ident),
523+ (Trace reason)]
524+
525+traceNcEventFmt :: LocalNode -> String -> [TraceArg] -> NC ()
526+traceNcEventFmt node fmt args =
527+ liftIO $ traceEventFmtIO node fmt args
528+
529+traceEventFmtIO :: LocalNode
530+ -> String
531+ -> [TraceArg]
532+ -> IO ()
533+traceEventFmtIO node fmt args =
534+ withLocalTracer node $ \t -> traceFormat t fmt args
535+
536+withLocalTracer :: LocalNode -> (Tracer -> IO ()) -> IO ()
537+withLocalTracer node act = act (localTracer node)
538+
493539--------------------------------------------------------------------------------
494540-- Core functionality --
495541--------------------------------------------------------------------------------
@@ -597,6 +643,7 @@ ncEffectUnmonitor from ref = do
597643ncEffectDied :: Identifier -> DiedReason -> NC ()
598644ncEffectDied ident reason = do
599645 node <- ask
646+ traceNotifyDied node ident reason
600647 (affectedLinks, unaffectedLinks) <- gets (splitNotif ident . (^. links))
601648 (affectedMons, unaffectedMons) <- gets (splitNotif ident . (^. monitors))
602649
@@ -639,7 +686,6 @@ ncEffectDied ident reason = do
639686 , ctrlMsgSignal = Died ident reason
640687 }
641688
642−
643689-- [Unified: Table 13]
644690ncEffectSpawn :: ProcessId -> Closure (Process ()) -> SpawnRef -> NC ()
645691ncEffectSpawn pid cProc ref = do
@@ -841,18 +887,18 @@ notifyDied dest src reason mRef = do
841887
842888-- | [Unified: Table 8]
843889destNid :: ProcessSignal -> Maybe NodeId
844−destNid (Link ident) = Just $ nodeOf ident
845−destNid (Unlink ident) = Just $ nodeOf ident
846−destNid (Monitor ref) = Just $ nodeOf (monitorRefIdent ref)
847−destNid (Unmonitor ref) = Just $ nodeOf (monitorRefIdent ref)
848−destNid (Spawn _ _) = Nothing
849−destNid (Register _ _ _ _) = Nothing
850−destNid (WhereIs _) = Nothing
851−destNid (NamedSend _ _) = Nothing
890+destNid (Link ident) = Just $ nodeOf ident
891+destNid (Unlink ident) = Just $ nodeOf ident
892+destNid (Monitor ref) = Just $ nodeOf (monitorRefIdent ref)
893+destNid (Unmonitor ref) = Just $ nodeOf (monitorRefIdent ref)
894+destNid (Spawn _ _) = Nothing
895+destNid (Register _ _ _ _) = Nothing
896+destNid (WhereIs _) = Nothing
897+destNid (NamedSend _ _) = Nothing
852898-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
853899-- then when a local process dies the monitoring/linking machinery will take
854900-- care of notifying remote nodes
855−destNid (Died _ _) = Nothing
901+destNid (Died _ _) = Nothing
856902destNid (Kill pid _) = Just $ processNodeId pid
857903destNid (Exit pid _) = Just $ processNodeId pid
858904destNid (GetInfo pid) = Just $ processNodeId pid
distributed-process/tests/TestStats.hs+12−12
@@ -2,13 +2,12 @@
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33module Main where
44
5−import Prelude hiding (catch, log)
6−import Test.Framework
7− ( Test
8− , defaultMain
9− , testGroup
5+import Control.Concurrent.MVar
6+ ( MVar
7+ , newEmptyMVar
8+ , putMVar
9+ , takeMVar
1010 )
11−import Network.Transport.TCP
1211import Control.Distributed.Process
1312import Control.Distributed.Process.Node
1413 ( forkProcess
@@ -16,14 +15,15 @@ import Control.Distributed.Process.Node
1615 , initRemoteTable
1716 , closeLocalNode
1817 , LocalNode)
19−import Control.Concurrent.MVar
20− ( MVar
21− , newEmptyMVar
22− , putMVar
23− , takeMVar
24− )
2518import Data.Binary()
2619import Data.Typeable()
20+import Network.Transport.TCP
21+import Prelude hiding (catch, log)
22+import Test.Framework
23+ ( Test
24+ , defaultMain
25+ , testGroup
26+ )
2727import Test.HUnit (Assertion)
2828import Test.HUnit.Base (assertBool)
2929import Test.Framework.Providers.HUnit (testCase)