on master help sign in
Add GC based cleanup of SVars
commit05ca066661fe775d9c7b041dc8b284fb5dd4a922 authorHarendra Kumar <harendra.kumar@gmail.com> dateWed 15 Aug 2018, 05:56 (8 years ago) parent89eec18
4 files changed+54−22
src/Streamly/SVar.hs+16−4 src/Streamly/Streams/Async.hs+2−2 src/Streamly/Streams/SVar.hs+10−6 test/Main.hs+26−10
src/Streamly/SVar.hs+16−4
@@ -45,6 +45,8 @@ module Streamly.SVar
4545 , getYieldLimit
4646 , setYieldLimit
4747
48+ , cleanupSVar
49+
4850 -- SVar related
4951 , newAheadVar
5052 , newParallelVar
@@ -95,7 +97,7 @@ module Streamly.SVar
9597where
9698
9799import Control.Concurrent
98− (ThreadId, myThreadId, threadDelay, getNumCapabilities)
100+ (ThreadId, myThreadId, threadDelay, getNumCapabilities, throwTo)
99101import Control.Concurrent.MVar
100102 (MVar, newEmptyMVar, tryPutMVar, takeMVar, newMVar)
101103import Control.Exception (SomeException(..), catch, mask, assert, Exception)
@@ -318,9 +320,9 @@ data SVar t m a = SVar
318320 , workerStopMVar :: MVar ()
319321
320322 , svarStats :: SVarStats
321−#ifdef DIAGNOSTICS
322323 -- to track garbage collection of SVar
323324 , svarRef :: Maybe (IORef ())
325+#ifdef DIAGNOSTICS
324326 , outputHeap :: IORef (Heap (Entry Int (AheadHeapEntry t m a)) , Int)
325327 -- Shared work queue (stream, seqNo)
326328 , aheadWorkQueue :: IORef ([t m a], Int)
@@ -460,6 +462,16 @@ setStreamLatency n st =
460462getStreamLatency :: State t m a -> Maybe NanoSecs
461463getStreamLatency = _streamLatency
462464
465+-------------------------------------------------------------------------------
466+-- Cleanup
467+-------------------------------------------------------------------------------
468+
469+cleanupSVar :: SVar t m a -> IO ()
470+cleanupSVar sv = do
471+ workers <- readIORef (workerThreads sv)
472+ Prelude.mapM_ (\tid -> throwTo tid ThreadAbort)
473+ (S.toList workers)
474+
463475-------------------------------------------------------------------------------
464476-- Dumping the SVar for debug/diag
465477-------------------------------------------------------------------------------
@@ -1648,8 +1660,8 @@ getAheadSVar st f = do
16481660 , workerCount = active
16491661 , accountThread = delThread sv
16501662 , workerStopMVar = stopMVar
1651−#ifdef DIAGNOSTICS
16521663 , svarRef = Nothing
1664+#ifdef DIAGNOSTICS
16531665 , aheadWorkQueue = q
16541666 , outputHeap = outH
16551667#endif
@@ -1733,8 +1745,8 @@ getParallelSVar st = do
17331745 , workerCount = active
17341746 , accountThread = modifyThread sv
17351747 , workerStopMVar = undefined
1736−#ifdef DIAGNOSTICS
17371748 , svarRef = Nothing
1749+#ifdef DIAGNOSTICS
17381750 , aheadWorkQueue = undefined
17391751 , outputHeap = undefined
17401752#endif
src/Streamly/Streams/Async.hs+2−2
@@ -303,8 +303,8 @@ getLifoSVar st = do
303303 , workerCount = active
304304 , accountThread = delThread sv
305305 , workerStopMVar = undefined
306−#ifdef DIAGNOSTICS
307306 , svarRef = Nothing
307+#ifdef DIAGNOSTICS
308308 , aheadWorkQueue = undefined
309309 , outputHeap = undefined
310310#endif
@@ -397,8 +397,8 @@ getFifoSVar st = do
397397 , workerCount = active
398398 , accountThread = delThread sv
399399 , workerStopMVar = undefined
400−#ifdef DIAGNOSTICS
401400 , svarRef = Nothing
401+#ifdef DIAGNOSTICS
402402 , aheadWorkQueue = undefined
403403 , outputHeap = undefined
404404#endif
src/Streamly/Streams/SVar.hs+10−6
@@ -39,9 +39,10 @@ where
3939import Control.Exception (fromException)
4040import Control.Monad.Catch (throwM)
4141import Data.Int (Int64)
42−#ifdef DIAGNOSTICS
4342import Control.Monad.IO.Class (liftIO)
44−import Data.IORef (newIORef, writeIORef, mkWeakIORef)
43+import Data.IORef (newIORef, mkWeakIORef)
44+#ifdef DIAGNOSTICS
45+import Data.IORef (writeIORef)
4546import System.IO (hPutStrLn, stderr)
4647import System.Clock (Clock(Monotonic), getTime)
4748#endif
@@ -111,13 +112,16 @@ fromStreamVar sv = Stream $ \st stp sng yld -> do
111112fromSVar :: (MonadAsync m, IsStream t) => SVar Stream m a -> t m a
112113fromSVar sv = do
113114 fromStream $ Stream $ \st stp sng yld -> do
114−#ifdef DIAGNOSTICS_VERBOSE
115115 ref <- liftIO $ newIORef ()
116− _ <- liftIO $ mkWeakIORef ref (printSVar sv "SVar Garbage Collected")
116+ _ <- liftIO $ mkWeakIORef ref hook
117117 unStream (fromStreamVar sv{svarRef = Just ref}) st stp sng yld
118−#else
119− unStream (fromStreamVar sv) st stp sng yld
118+ where
119+
120+ hook = do
121+#ifdef DIAGNOSTICS_VERBOSE
122+ printSVar sv "SVar Garbage Collected"
120123#endif
124+ cleanupSVar sv
121125
122126-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
123127-- be read back from the SVar using 'fromSVar'.
test/Main.hs+26−10
@@ -12,6 +12,7 @@ import Control.Monad.Error.Class (throwError, MonadError)
1212import Control.Monad.Trans.Except (runExceptT, ExceptT)
1313import Data.Foldable (forM_, fold)
1414import Data.List (sort)
15+import System.Mem (performMajorGC)
1516
1617import Data.IORef
1718import Test.Hspec as H
@@ -88,17 +89,24 @@ main = hspec $ do
8889 it "take 1 aheadly" $ checkCleanup aheadly (S.take 1)
8990 it "take 1 parallely" $ checkCleanup parallely (S.take 1)
9091
91−{-
9292 it "takeWhile (< 0) asyncly" $ checkCleanup asyncly (S.takeWhile (< 0))
9393 it "takeWhile (< 0) wAsyncly" $ checkCleanup wAsyncly (S.takeWhile (< 0))
9494 it "takeWhile (< 0) aheadly" $ checkCleanup aheadly (S.takeWhile (< 0))
9595 it "takeWhile (< 0) parallely" $ checkCleanup parallely (S.takeWhile (< 0))
9696
97− it "head asyncly" $ checkCleanupFold asyncly (S.head)
98− it "head wAsyncly" $ checkCleanupFold wAsyncly (S.head)
99− it "head aheadly" $ checkCleanupFold aheadly (S.head)
100− it "head parallely" $ checkCleanupFold parallely (S.head)
101−-}
97+ testFoldOpsCleanup "head" S.head
98+ testFoldOpsCleanup "null" S.null
99+ testFoldOpsCleanup "elem" (S.elem 0)
100+ testFoldOpsCleanup "notElem" (S.notElem 0)
101+ testFoldOpsCleanup "elemIndex" (S.elemIndex 0)
102+ -- S.lookup
103+ testFoldOpsCleanup "notElem" (S.notElem 0)
104+ testFoldOpsCleanup "find" (S.find (==0))
105+ testFoldOpsCleanup "findIndex" (S.findIndex (==0))
106+ testFoldOpsCleanup "all" (S.all (==1))
107+ testFoldOpsCleanup "any" (S.any (==0))
108+ testFoldOpsCleanup "and" (S.and . S.map (==1))
109+ testFoldOpsCleanup "or" (S.or . S.map (==0))
102110
103111 ---------------------------------------------------------------------------
104112 -- Semigroup/Monoidal Composition strict ordering checks
@@ -133,13 +141,13 @@ checkCleanup t op = do
133141 runStream . serially $ do
134142 _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
135143 return ()
144+ performMajorGC
136145 threadDelay 500000
137146 res <- readIORef r
138147 res `shouldBe` 0
139148 where
140− delay ref i = threadDelay (i*100000) >> writeIORef ref i >> return i
149+ delay ref i = threadDelay (i*200000) >> writeIORef ref i >> return i
141150
142−{-
143151checkCleanupFold :: IsStream t
144152 => (t IO Int -> SerialT IO Int)
145153 -> (SerialT IO Int -> IO (Maybe Int))
@@ -147,12 +155,20 @@ checkCleanupFold :: IsStream t
147155checkCleanupFold t op = do
148156 r <- newIORef (-1 :: Int)
149157 _ <- op $ t $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
158+ performMajorGC
150159 threadDelay 500000
151160 res <- readIORef r
152161 res `shouldBe` 0
153162 where
154− delay ref i = threadDelay (i*100000) >> writeIORef ref i >> return i
155−-}
163+ delay ref i = threadDelay (i*200000) >> writeIORef ref i >> return i
164+
165+testFoldOpsCleanup :: String -> (SerialT IO Int -> IO a) -> Spec
166+testFoldOpsCleanup name f = do
167+ let testOp op x = op x >> return Nothing
168+ it (name ++ " asyncly") $ checkCleanupFold asyncly (testOp f)
169+ it (name ++ " wAsyncly") $ checkCleanupFold wAsyncly (testOp f)
170+ it (name ++ " aheadly") $ checkCleanupFold aheadly (testOp f)
171+ it (name ++ " parallely") $ checkCleanupFold parallely (testOp f)
156172
157173parallelTests :: SpecWith ()
158174parallelTests = H.parallel $ do