summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2020-01-13 06:58:48 -0500
committerJoe Crayne <joe@jerkface.net>2020-01-14 03:37:41 -0500
commitcb28281a2acabf87e91582ce5ace562544ae2730 (patch)
tree4c038981711cf2d1625c4c5766b7f8eab21de0b9
parentb56ac5aa4a4d9c026f6aadad58daeee7729e9f4c (diff)
Fixed race condition in thread instrumentation.
-rw-r--r--lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs75
1 files changed, 37 insertions, 38 deletions
diff --git a/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs b/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs
index a0bb7dc5..bd6ee4b8 100644
--- a/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs
+++ b/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs
@@ -1,4 +1,5 @@
1{-# LANGUAGE FlexibleContexts #-} 1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE RankNTypes #-}
2module Control.Concurrent.Lifted.Instrument 3module Control.Concurrent.Lifted.Instrument
3 ( module Control.Concurrent.Lifted 4 ( module Control.Concurrent.Lifted
4 , forkLabeled 5 , forkLabeled
@@ -11,6 +12,7 @@ module Control.Concurrent.Lifted.Instrument
11 , PerThread(..) 12 , PerThread(..)
12 ) where 13 ) where
13 14
15import Control.Concurrent as Raw (forkOSWithUnmask)
14import qualified Control.Concurrent.Lifted as Raw 16import qualified Control.Concurrent.Lifted as Raw
15import Control.Concurrent.Lifted hiding (fork,forkOS) 17import Control.Concurrent.Lifted hiding (fork,forkOS)
16import Control.Exception (fromException) 18import Control.Exception (fromException)
@@ -47,28 +49,29 @@ globals = unsafePerformIO $ newMVar $ GlobalState
47 49
48forkLabeled :: HasCallStack => String -> IO () -> IO ThreadId 50forkLabeled :: HasCallStack => String -> IO () -> IO ThreadId
49forkLabeled lbl action = do 51forkLabeled lbl action = do
50 t <- instrumented GHC.forkIO action 52 t <- instrumented GHC.forkIOWithUnmask action
51 labelThread t lbl 53 labelThread t lbl
52 return t 54 return t
53{-# INLINE forkLabeled #-} 55{-# INLINE forkLabeled #-}
54 56
55forkOSLabeled :: HasCallStack => String -> IO () -> IO ThreadId 57forkOSLabeled :: HasCallStack => String -> IO () -> IO ThreadId
56forkOSLabeled lbl action = do 58forkOSLabeled lbl action = do
57 t <- instrumented Raw.forkOS action 59 t <- instrumented Raw.forkOSWithUnmask action
58 labelThread t lbl 60 labelThread t lbl
59 return t 61 return t
60{-# INLINE forkOSLabeled #-} 62{-# INLINE forkOSLabeled #-}
61 63
62forkIO :: HasCallStack => IO () -> IO ThreadId 64forkIO :: HasCallStack => IO () -> IO ThreadId
63forkIO = instrumented GHC.forkIO 65forkIO = instrumented GHC.forkIOWithUnmask
64{-# INLINE forkIO #-} 66{-# INLINE forkIO #-}
65 67
66forkOS :: ( HasCallStack, MonadBaseControl IO m ) => m () -> m ThreadId 68forkOS :: -- ( HasCallStack, MonadBaseControl IO m ) => m () -> m ThreadId
67forkOS = instrumented Raw.forkOS 69 HasCallStack => IO () -> IO ThreadId
70forkOS = instrumented Raw.forkOSWithUnmask
68{-# INLINE forkOS #-} 71{-# INLINE forkOS #-}
69 72
70fork :: ( HasCallStack, MonadBaseControl IO m ) => m () -> m ThreadId 73fork :: ( HasCallStack, MonadBaseControl IO m ) => m () -> m ThreadId
71fork = instrumented Raw.fork 74fork = instrumented Raw.forkWithUnmask
72{-# INLINE fork #-} 75{-# INLINE fork #-}
73 76
74shortCallStack :: [([Char], SrcLoc)] -> String 77shortCallStack :: [([Char], SrcLoc)] -> String
@@ -80,47 +83,43 @@ defaultLabel stack = case getCallStack stack of
80 _ : sites -> shortCallStack sites 83 _ : sites -> shortCallStack sites
81 sites -> shortCallStack sites 84 sites -> shortCallStack sites
82 85
86
83instrumented :: ( HasCallStack, MonadBaseControl IO m ) => 87instrumented :: ( HasCallStack, MonadBaseControl IO m ) =>
84 (m () -> m ThreadId) -> m () -> m ThreadId 88 (((forall a. m a -> m a) -> m ()) -> m ThreadId) -> m () -> m ThreadId
85instrumented rawFork action = do 89instrumented rawFork action = do
86 mvar <- newEmptyMVar 90 mvar <- newEmptyMVar
87 t <- rawFork $ do 91 tm <- liftBase getCurrentTime
88 tid <- myThreadId 92 t <- mask_ $ rawFork $ \unmask -> do
89 tm <- liftBase getCurrentTime 93 tid <- myThreadId
90 bracket_ (modifyThreads $! \ts -> Map.union ts (Map.singleton tid (PerThread (defaultLabel callStack) tm))) 94 let scrapIt = do takeMVar mvar
91 (return ()) 95 modifyThreads $! Map.delete tid
92 $ do catch action $ \e -> case fromException e of 96 io <- catch (unmask action >> return scrapIt) $ \e -> case fromException e of
93 Just ThreadKilled -> return () 97 Just ThreadKilled -> return scrapIt
94 Nothing -> liftBase $ do 98 Nothing -> liftBase $ do
95 bracket (takeMVar globals) 99 g <- takeMVar globals
96 (\g -> do 100 let l = concat [ show e
97 let l = concat [ show e 101 , " ("
98 , " (" 102 , maybe "" lbl $ Map.lookup tid (threads g)
99 , maybe "" lbl $ Map.lookup tid (threads g) 103 , ")"
100 , ")" 104 ]
101 ] 105 reportException g l
102 foldr seq (return ()) l 106 let l = concat [ show e
103 putMVar globals $! g { threads = Map.insert tid (PerThread l tm) $ threads g } 107 , " ("
104 throwIO e) 108 , maybe "" lbl $ Map.lookup tid (threads g)
105 (\g -> do 109 , ")"
106 let l = concat [ show e 110 ]
107 , " (" 111 foldr seq (return ()) l
108 , maybe "" lbl $ Map.lookup tid (threads g) 112 putMVar globals $! g { threads = Map.insert tid (PerThread l tm) $ threads g }
109 , ")" 113 return $ return () -- Remove the thread only if it terminated normally or was killed.
110 ] 114 io -- scrap record on normal termination
111 reportException g l) 115 liftBase $ labelThread_ t (defaultLabel callStack) tm
112 -- Remove the thread only if it terminated normally or was killed.
113 takeMVar mvar
114 modifyThreads $! Map.delete tid
115 liftBase $ labelThread_ t (defaultLabel callStack)
116 putMVar mvar () 116 putMVar mvar ()
117 return t 117 return t
118 118
119labelThread_ :: ThreadId -> String -> IO () 119labelThread_ :: ThreadId -> String -> UTCTime -> IO ()
120labelThread_ tid s = do 120labelThread_ tid s tm = do
121 foldr seq (return ()) s 121 foldr seq (return ()) s
122 GHC.labelThread tid s 122 GHC.labelThread tid s
123 tm <- liftBase getCurrentTime
124 let updateIt (Just pt) = Just $ pt { lbl = s } 123 let updateIt (Just pt) = Just $ pt { lbl = s }
125 updateIt Nothing = Just $ PerThread s tm 124 updateIt Nothing = Just $ PerThread s tm
126 modifyThreads $! Map.alter updateIt tid 125 modifyThreads $! Map.alter updateIt tid