@@ -1155,12 +1155,150 @@ data SessionState t m k a b = SessionState
1156data SessionEntry a b = LiveSession !a !b | ZombieSession
1156data SessionEntry a b = LiveSession !a !b | ZombieSession
1158+-- delete from map and output the fold accumulator
1159+ejectEntry :: (Monad m, IsStream t, IsMap f) =>
1167+ -> m (heap, f entry, t m (Key f, b), Int)
1168+ejectEntry extract hp mp out cnt acc key = do
1169+ sess <- extract acc
1170+ let out1 = (key, sess) `cons` out
1171+ let mp1 = IsMap.mapDelete key mp
1172+ return (hp, mp1, out1, cnt - 1)
1174+{-# NOINLINE flush #-}
1175+flush :: (Monad m, Ord k, IsStream t) =>
1177+ -> SessionState t m k (SessionEntry a s) b
1178+ -> m (SessionState t m k (SessionEntry a s) b)
1179+flush extract session@SessionState{..} = do
1180+ (hp', mp', out, count) <-
1183+ , sessionKeyValueMap
1188+ { sessionCount = count
1189+ , sessionTimerHeap = hp'
1190+ , sessionKeyValueMap = mp'
1191+ , sessionOutputStream = out
1196+ ejectAll (hp, mp, out, !cnt) = do
1197+ let hres = H.uncons hp
1199+ Just (Entry _ key, hp1) -> do
1200+ r <- case IsMap.mapLookup key mp of
1201+ Nothing -> return (hp1, mp, out, cnt)
1202+ Just ZombieSession ->
1203+ return (hp1, IsMap.mapDelete key mp, out, cnt)
1204+ Just (LiveSession _ acc) ->
1205+ ejectEntry extract hp1 mp out cnt acc key
1208+ assert (Map.null mp) (return ())
1209+ return (hp, mp, out, cnt)
1211+{-# NOINLINE ejectOne #-}
1212+ejectOne :: (IsMap f, IsStream t, Ord k, Monad m) =>
1215+ -> ( H.Heap (Entry k (Key f))
1216+ , f (SessionEntry k acc)
1220+ -> m ( H.Heap (Entry k (Key f))
1221+ , f (SessionEntry k acc)
1222+ , t m (Key f, b), Int
1224+ejectOne reset extract = go
1228+ go (hp, mp, out, !cnt) = do
1229+ let hres = H.uncons hp
1231+ Just (Entry expiry key, hp1) ->
1232+ case IsMap.mapLookup key mp of
1233+ Nothing -> go (hp1, mp, out, cnt)
1234+ Just ZombieSession ->
1235+ go (hp1, IsMap.mapDelete key mp, out, cnt)
1236+ Just (LiveSession expiry1 acc) -> do
1237+ if not reset || expiry1 <= expiry
1238+ then ejectEntry extract hp1 mp out cnt acc key
1240+ -- reset the session timeout and continue
1241+ let hp2 = H.insert (Entry expiry1 key) hp1
1242+ in go (hp2, mp, out, cnt)
1244+ assert (IsMap.mapNull mp) (return ())
1245+ return (hp, mp, out, cnt)
1247+{-# NOINLINE ejectExpired #-}
1248+ejectExpired :: (Ord k, IsStream t, Monad m) =>
1252+ -> SessionState t m k (SessionEntry AbsTime acc) b
1254+ -> m (SessionState t m k (SessionEntry AbsTime acc) b)
1255+ejectExpired reset ejectPred extract session@SessionState{..} curTime = do
1256+ (hp', mp', out, count) <-
1258+ sessionTimerHeap sessionKeyValueMap IsStream.nil sessionCount
1260+ { sessionCurTime = curTime
1261+ , sessionCount = count
1262+ , sessionTimerHeap = hp'
1263+ , sessionKeyValueMap = mp'
1264+ , sessionOutputStream = out
1269+ ejectLoop hp mp out !cnt = do
1270+ let hres = H.uncons hp
1272+ Just (Entry expiry key, hp1) -> do
1274+ if curTime >= expiry
1275+ then return (True, False)
1281+ case IsMap.mapLookup key mp of
1282+ Nothing -> ejectLoop hp1 mp out cnt
1283+ Just ZombieSession ->
1284+ ejectLoop hp1 (IsMap.mapDelete key mp) out cnt
1285+ Just (LiveSession expiry1 acc) -> do
1286+ if expiry1 <= curTime || not reset || force
1288+ (hp2,mp1,out1,cnt1) <-
1289+ ejectEntry extract hp1 mp out cnt acc key
1290+ ejectLoop hp2 mp1 out1 cnt1
1292+ -- reset the session timeout and continue
1293+ let hp2 = H.insert (Entry expiry1 key) hp1
1294+ in ejectLoop hp2 mp out cnt
1295+ else return (hp, mp, out, cnt)
1297+ assert (IsMap.mapNull mp) (return ())
1298+ return (hp, mp, out, cnt)
1158-- XXX Use mutable IORef in accumulator
1300-- XXX Use mutable IORef in accumulator
1160−-- XXX this fuses with INLINE but it may be too much work for the compiler.
1161−-- Maybe we can NOINLINE the heap processing stuff and then INLINE it to reduce
1163−{-# INLINABLE classifySessionsByGeneric #-}
1301+{-# INLINE classifySessionsByGeneric #-}
1164classifySessionsByGeneric
1302classifySessionsByGeneric
1165 :: (IsStream t, MonadAsync m, Ord (Key f))
1303 :: (IsStream t, MonadAsync m, Ord (Key f))
1166 => Proxy (f s)
1304 => Proxy (f s)
@@ -1174,7 +1312,7 @@ classifySessionsByGeneric
1174classifySessionsByGeneric _ tick reset ejectPred tmout
1312classifySessionsByGeneric _ tick reset ejectPred tmout
1175 (Fold step initial extract) input =
1313 (Fold step initial extract) input =
1176 Expand.unfoldMany (Unfold.lmap sessionOutputStream Unfold.fromStream)
1314 Expand.unfoldMany (Unfold.lmap sessionOutputStream Unfold.fromStream)
1177− $ scanlMAfter' sstep (return szero) flush
1315+ $ scanlMAfter' sstep (return szero) (flush extract)
1178 $ interjectSuffix tick (return Nothing)
1316 $ interjectSuffix tick (return Nothing)
1179 $ map Just input
1317 $ map Just input
@@ -1279,7 +1417,7 @@ classifySessionsByGeneric _ tick reset ejectPred tmout
1279 eject <- ejectPred sessionCount
1417 eject <- ejectPred sessionCount
1280 (hp, mp, out, cnt) <-
1418 (hp, mp, out, cnt) <-
1281 if eject
1419 if eject
1282− then ejectOne vars
1420+ then ejectOne reset extract vars
1283 else return vars
1421 else return vars
1285 -- Insert the new session in heap
1423 -- Insert the new session in heap
@@ -1315,108 +1453,7 @@ classifySessionsByGeneric _ tick reset ejectPred tmout
1315 -- Got a timer tick event
1453 -- Got a timer tick event
1316 sstep sessionState@SessionState{..} Nothing =
1454 sstep sessionState@SessionState{..} Nothing =
1317 let curTime = addToAbsTime sessionCurTime tickMs
1455 let curTime = addToAbsTime sessionCurTime tickMs
1318− in ejectExpired sessionState curTime
1456+ in ejectExpired reset ejectPred extract sessionState curTime
1320− flush session@SessionState{..} = do
1321− (hp', mp', out, count) <-
1324− , sessionKeyValueMap
1329− { sessionCount = count
1330− , sessionTimerHeap = hp'
1331− , sessionKeyValueMap = mp'
1332− , sessionOutputStream = out
1335− -- delete from map and output the fold accumulator
1336− ejectEntry hp mp out cnt acc key = do
1337− sess <- extract acc
1338− let out1 = (key, sess) `cons` out
1339− let mp1 = IsMap.mapDelete key mp
1340− return (hp, mp1, out1, cnt - 1)
1342− ejectAll (hp, mp, out, !cnt) = do
1343− let hres = H.uncons hp
1345− Just (Entry _ key, hp1) -> do
1346− r <- case IsMap.mapLookup key mp of
1347− Nothing -> return (hp1, mp, out, cnt)
1348− Just ZombieSession ->
1349− return (hp1, IsMap.mapDelete key mp, out, cnt)
1350− Just (LiveSession _ acc) ->
1351− ejectEntry hp1 mp out cnt acc key
1354− assert (Map.null mp) (return ())
1355− return (hp, mp, out, cnt)
1357− ejectOne (hp, mp, out, !cnt) = do
1358− let hres = H.uncons hp
1360− Just (Entry expiry key, hp1) ->
1361− case IsMap.mapLookup key mp of
1362− Nothing -> ejectOne (hp1, mp, out, cnt)
1363− Just ZombieSession ->
1364− ejectOne (hp1, IsMap.mapDelete key mp, out, cnt)
1365− Just (LiveSession expiry1 acc) -> do
1366− if not reset || expiry1 <= expiry
1367− then ejectEntry hp1 mp out cnt acc key
1369− -- reset the session timeout and continue
1370− let hp2 = H.insert (Entry expiry1 key) hp1
1371− in ejectOne (hp2, mp, out, cnt)
1373− assert (IsMap.mapNull mp) (return ())
1374− return (hp, mp, out, cnt)
1376− ejectExpired session@SessionState{..} curTime = do
1377− (hp', mp', out, count) <-
1379− sessionTimerHeap sessionKeyValueMap IsStream.nil sessionCount
1381− { sessionCurTime = curTime
1382− , sessionCount = count
1383− , sessionTimerHeap = hp'
1384− , sessionKeyValueMap = mp'
1385− , sessionOutputStream = out
1390− ejectLoop hp mp out !cnt = do
1391− let hres = H.uncons hp
1393− Just (Entry expiry key, hp1) -> do
1395− if curTime >= expiry
1396− then return (True, False)
1402− case IsMap.mapLookup key mp of
1403− Nothing -> ejectLoop hp1 mp out cnt
1404− Just ZombieSession ->
1405− ejectLoop hp1 (IsMap.mapDelete key mp) out cnt
1406− Just (LiveSession expiry1 acc) -> do
1407− if expiry1 <= curTime || not reset || force
1409− (hp2,mp1,out1,cnt1) <-
1410− ejectEntry hp1 mp out cnt acc key
1411− ejectLoop hp2 mp1 out1 cnt1
1413− -- reset the session timeout and continue
1414− let hp2 = H.insert (Entry expiry1 key) hp1
1415− in ejectLoop hp2 mp out cnt
1416− else return (hp, mp, out, cnt)
1418− assert (IsMap.mapNull mp) (return ())
1419− return (hp, mp, out, cnt)
1421-- | @classifySessionsBy tick keepalive predicate timeout fold stream@
1458-- | @classifySessionsBy tick keepalive predicate timeout fold stream@
1422-- classifies an input event @stream@ consisting of @(timestamp, (key,
1459-- classifies an input event @stream@ consisting of @(timestamp, (key,