@@ -163,6 +163,7 @@ module Control.Distributed.Process
163import Control.Monad.IO.Class (liftIO)
163import Control.Monad.IO.Class (liftIO)
164import Control.Applicative
164import Control.Applicative
165import Control.Monad.Reader (ask)
165import Control.Monad.Reader (ask)
166+import Control.Concurrent (killThread)
166import Control.Concurrent.MVar
167import Control.Concurrent.MVar
168 , newEmptyMVar
169 , newEmptyMVar
@@ -197,6 +198,7 @@ import Control.Distributed.Process.Internal.Types
197 , RegisterReply(..)
198 , RegisterReply(..)
198 , LocalProcess(processNode)
199 , LocalProcess(processNode)
199 , Message
200 , Message
201+ , localProcessWithId
201import Control.Distributed.Process.Serializable (Serializable)
203import Control.Distributed.Process.Serializable (Serializable)
202import Control.Distributed.Process.Internal.Primitives
204import Control.Distributed.Process.Internal.Primitives
@@ -305,6 +307,10 @@ import Control.Distributed.Process.Internal.Primitives
305 , reconnectPort
307 , reconnectPort
307import Control.Distributed.Process.Node (forkProcess)
309import Control.Distributed.Process.Node (forkProcess)
310+import Control.Distributed.Process.Internal.Types
312+ , withValidLocalState
308import Control.Distributed.Process.Internal.Spawn
314import Control.Distributed.Process.Internal.Spawn
309 ( -- Spawning Processes/Channels
315 ( -- Spawning Processes/Channels
@@ -321,6 +327,11 @@ import Prelude
322import Prelude hiding (catch)
328import Prelude hiding (catch)
330+import Control.Distributed.Process.Internal.StrictMVar (readMVar)
331+import qualified Control.Exception as Exception (onException)
332+import Data.Accessor ((^.))
333+import Data.Foldable (forM_)
325-- INTERNAL NOTES
336-- INTERNAL NOTES
@@ -418,8 +429,20 @@ spawnChannelLocal proc = do
418-- Silently dropping messages may not always be the best approach.
429-- Silently dropping messages may not always be the best approach.
419callLocal :: Process a -> Process a
430callLocal :: Process a -> Process a
420callLocal proc = Catch.mask $ \release -> do
431callLocal proc = Catch.mask $ \release -> do
421− mv <- liftIO newEmptyMVar :: Process (MVar (Either Catch.SomeException a))
432+ mv <- liftIO newEmptyMVar :: Process (MVar (Either Catch.SomeException a))
422 child <- spawnLocal $ Catch.try (release proc) >>= liftIO . putMVar mv
433 child <- spawnLocal $ Catch.try (release proc) >>= liftIO . putMVar mv
423− rs <- liftIO (takeMVar mv) `Catch.onException`
434+ lproc <- ask
424− (kill child "exception in parent process" >> liftIO (takeMVar mv))
435+ liftIO $ do
436+ rs <- Exception.onException (takeMVar mv) $ Catch.uninterruptibleMask_ $
437+ -- Exceptions need to be prevented from interrupting the clean up or
438+ -- the original exception which caused entering the handler could be
439+ -- forgotten. For instance, this could have a problematic effect
440+ -- when the original exception was meant to kill the thread and the
441+ -- second exception doesn't (like the exception thrown by
442+ -- 'System.Timeout.timeout').
443+ do mchildThreadId <- withValidLocalState (processNode lproc) $
444+ \vst -> return $ fmap processThread $
445+ vst ^. localProcessWithId (processLocalId child)
446+ forM_ mchildThreadId killThread
425− either Catch.throwM return rs
448+ either Catch.throwM return rs