|
| 1 | +{-# LANGUAGE FlexibleInstances #-} |
| 2 | +{-# LANGUAGE LambdaCase #-} |
| 3 | +{-# LANGUAGE OverloadedStrings #-} |
| 4 | + |
| 5 | +module Simplex.Messaging.Agent.TSessionSubs |
| 6 | + ( TSessionSubs (sessionSubs), |
| 7 | + SessSubs (..), |
| 8 | + emptyIO, |
| 9 | + clear, |
| 10 | + hasActiveSub, |
| 11 | + hasPendingSub, |
| 12 | + addPendingSub, |
| 13 | + setSessionId, |
| 14 | + addActiveSub, |
| 15 | + batchAddPendingSubs, |
| 16 | + deletePendingSub, |
| 17 | + deleteSub, |
| 18 | + batchDeleteSubs, |
| 19 | + hasPendingSubs, |
| 20 | + getPendingSubs, |
| 21 | + getActiveSubs, |
| 22 | + setSubsPending, |
| 23 | + foldSessionSubs, |
| 24 | + mapSubs, |
| 25 | + ) |
| 26 | +where |
| 27 | + |
| 28 | +import Control.Concurrent.STM |
| 29 | +import Control.Monad |
| 30 | +import Data.Map.Strict (Map) |
| 31 | +import qualified Data.Map.Strict as M |
| 32 | +import Data.Maybe (isJust) |
| 33 | +import qualified Data.Set as S |
| 34 | +import Simplex.Messaging.Agent.Protocol (SMPQueue (..)) |
| 35 | +import Simplex.Messaging.Agent.Store (RcvQueueSub (..), SomeRcvQueue) |
| 36 | +import Simplex.Messaging.Client (SMPTransportSession, TransportSessionMode (..)) |
| 37 | +import Simplex.Messaging.Protocol (RecipientId) |
| 38 | +import Simplex.Messaging.TMap (TMap) |
| 39 | +import qualified Simplex.Messaging.TMap as TM |
| 40 | +import Simplex.Messaging.Transport |
| 41 | +import Simplex.Messaging.Util (($>>=)) |
| 42 | + |
| 43 | +data TSessionSubs = TSessionSubs |
| 44 | + { sessionSubs :: TMap SMPTransportSession SessSubs |
| 45 | + } |
| 46 | + |
| 47 | +data SessSubs = SessSubs |
| 48 | + { subsSessId :: TVar (Maybe SessionId), |
| 49 | + activeSubs :: TMap RecipientId RcvQueueSub, |
| 50 | + pendingSubs :: TMap RecipientId RcvQueueSub |
| 51 | + } |
| 52 | + |
| 53 | +emptyIO :: IO TSessionSubs |
| 54 | +emptyIO = TSessionSubs <$> TM.emptyIO |
| 55 | +{-# INLINE emptyIO #-} |
| 56 | + |
| 57 | +clear :: TSessionSubs -> STM () |
| 58 | +clear = TM.clear . sessionSubs |
| 59 | +{-# INLINE clear #-} |
| 60 | + |
| 61 | +lookupSubs :: SMPTransportSession -> TSessionSubs -> STM (Maybe SessSubs) |
| 62 | +lookupSubs tSess = TM.lookup tSess . sessionSubs |
| 63 | +{-# INLINE lookupSubs #-} |
| 64 | + |
| 65 | +getSessSubs :: SMPTransportSession -> TSessionSubs -> STM SessSubs |
| 66 | +getSessSubs tSess ss = lookupSubs tSess ss >>= maybe new pure |
| 67 | + where |
| 68 | + new = do |
| 69 | + s <- SessSubs <$> newTVar Nothing <*> newTVar M.empty <*> newTVar M.empty |
| 70 | + TM.insert tSess s $ sessionSubs ss |
| 71 | + pure s |
| 72 | + |
| 73 | +hasActiveSub :: RecipientId -> SMPTransportSession -> TSessionSubs -> STM Bool |
| 74 | +hasActiveSub = hasQueue_ activeSubs |
| 75 | +{-# INLINE hasActiveSub #-} |
| 76 | + |
| 77 | +hasPendingSub :: RecipientId -> SMPTransportSession -> TSessionSubs -> STM Bool |
| 78 | +hasPendingSub = hasQueue_ pendingSubs |
| 79 | +{-# INLINE hasPendingSub #-} |
| 80 | + |
| 81 | +hasQueue_ :: (SessSubs -> TMap RecipientId RcvQueueSub) -> RecipientId -> SMPTransportSession -> TSessionSubs -> STM Bool |
| 82 | +hasQueue_ subs rId tSess ss = isJust <$> (lookupSubs tSess ss $>>= TM.lookup rId . subs) |
| 83 | +{-# INLINE hasQueue_ #-} |
| 84 | + |
| 85 | +addPendingSub :: RcvQueueSub -> SMPTransportSession -> TSessionSubs -> STM () |
| 86 | +addPendingSub rq tSess ss = getSessSubs tSess ss >>= TM.insert (rcvId rq) rq . pendingSubs |
| 87 | + |
| 88 | +setSessionId :: SessionId -> SMPTransportSession -> TSessionSubs -> STM () |
| 89 | +setSessionId sessId tSess ss = do |
| 90 | + s <- getSessSubs tSess ss |
| 91 | + readTVar (subsSessId s) >>= \case |
| 92 | + Nothing -> writeTVar (subsSessId s) (Just sessId) |
| 93 | + Just sessId' -> unless (sessId == sessId') $ void $ setSubsPending_ s $ Just sessId |
| 94 | + |
| 95 | +addActiveSub :: SessionId -> RcvQueueSub -> SMPTransportSession -> TSessionSubs -> STM () |
| 96 | +addActiveSub sessId rq tSess ss = do |
| 97 | + s <- getSessSubs tSess ss |
| 98 | + sessId' <- readTVar $ subsSessId s |
| 99 | + let rId = rcvId rq |
| 100 | + if Just sessId == sessId' |
| 101 | + then do |
| 102 | + TM.insert rId rq $ activeSubs s |
| 103 | + TM.delete rId $ pendingSubs s |
| 104 | + else TM.insert rId rq $ pendingSubs s |
| 105 | + |
| 106 | +batchAddPendingSubs :: [RcvQueueSub] -> SMPTransportSession -> TSessionSubs -> STM () |
| 107 | +batchAddPendingSubs rqs tSess ss = do |
| 108 | + s <- getSessSubs tSess ss |
| 109 | + modifyTVar' (pendingSubs s) $ M.union $ M.fromList $ map (\rq -> (rcvId rq, rq)) rqs |
| 110 | + |
| 111 | +deletePendingSub :: RecipientId -> SMPTransportSession -> TSessionSubs -> STM () |
| 112 | +deletePendingSub rId tSess = lookupSubs tSess >=> mapM_ (TM.delete rId . pendingSubs) |
| 113 | + |
| 114 | +deleteSub :: RecipientId -> SMPTransportSession -> TSessionSubs -> STM () |
| 115 | +deleteSub rId tSess = lookupSubs tSess >=> mapM_ (\s -> TM.delete rId (activeSubs s) >> TM.delete rId (pendingSubs s)) |
| 116 | + |
| 117 | +batchDeleteSubs :: SomeRcvQueue q => [q] -> SMPTransportSession -> TSessionSubs -> STM () |
| 118 | +batchDeleteSubs rqs tSess = lookupSubs tSess >=> mapM_ (\s -> delete (activeSubs s) >> delete (pendingSubs s)) |
| 119 | + where |
| 120 | + rIds = S.fromList $ map queueId rqs |
| 121 | + delete = (`modifyTVar'` (`M.withoutKeys` rIds)) |
| 122 | + |
| 123 | +hasPendingSubs :: SMPTransportSession -> TSessionSubs -> STM Bool |
| 124 | +hasPendingSubs tSess = lookupSubs tSess >=> maybe (pure False) (fmap (not . null) . readTVar . pendingSubs) |
| 125 | + |
| 126 | +getPendingSubs :: SMPTransportSession -> TSessionSubs -> STM (Map RecipientId RcvQueueSub) |
| 127 | +getPendingSubs = getSubs_ pendingSubs |
| 128 | +{-# INLINE getPendingSubs #-} |
| 129 | + |
| 130 | +getActiveSubs :: SMPTransportSession -> TSessionSubs -> STM (Map RecipientId RcvQueueSub) |
| 131 | +getActiveSubs = getSubs_ activeSubs |
| 132 | +{-# INLINE getActiveSubs #-} |
| 133 | + |
| 134 | +getSubs_ :: (SessSubs -> TMap RecipientId RcvQueueSub) -> SMPTransportSession -> TSessionSubs -> STM (Map RecipientId RcvQueueSub) |
| 135 | +getSubs_ subs tSess = lookupSubs tSess >=> maybe (pure M.empty) (readTVar . subs) |
| 136 | + |
| 137 | +setSubsPending :: TransportSessionMode -> SMPTransportSession -> SessionId -> TSessionSubs -> STM (Map RecipientId RcvQueueSub) |
| 138 | +setSubsPending mode tSess@(uId, srv, connId_) sessId tss@(TSessionSubs ss) |
| 139 | + | entitySession == isJust connId_ = |
| 140 | + TM.lookup tSess ss >>= withSessSubs (`setSubsPending_` Nothing) |
| 141 | + | otherwise = |
| 142 | + TM.lookupDelete tSess ss >>= withSessSubs setPendingChangeMode |
| 143 | + where |
| 144 | + entitySession = mode == TSMEntity |
| 145 | + sessEntId = if entitySession then Just else const Nothing |
| 146 | + withSessSubs run = \case |
| 147 | + Nothing -> pure M.empty |
| 148 | + Just s -> do |
| 149 | + sessId' <- readTVar $ subsSessId s |
| 150 | + if Just sessId == sessId' then run s else pure M.empty |
| 151 | + setPendingChangeMode s = do |
| 152 | + subs <- M.union <$> readTVar (activeSubs s) <*> readTVar (pendingSubs s) |
| 153 | + unless (null subs) $ |
| 154 | + forM_ subs $ \rq -> addPendingSub rq (uId, srv, sessEntId (connId rq)) tss |
| 155 | + pure subs |
| 156 | + |
| 157 | +setSubsPending_ :: SessSubs -> Maybe SessionId -> STM (Map RecipientId RcvQueueSub) |
| 158 | +setSubsPending_ s sessId_ = do |
| 159 | + writeTVar (subsSessId s) sessId_ |
| 160 | + let as = activeSubs s |
| 161 | + subs <- readTVar as |
| 162 | + unless (null subs) $ do |
| 163 | + writeTVar as M.empty |
| 164 | + modifyTVar' (pendingSubs s) $ M.union subs |
| 165 | + pure subs |
| 166 | + |
| 167 | +foldSessionSubs :: (a -> (SMPTransportSession, SessSubs) -> IO a) -> a -> TSessionSubs -> IO a |
| 168 | +foldSessionSubs f a = foldM f a . M.assocs <=< readTVarIO . sessionSubs |
| 169 | + |
| 170 | +mapSubs :: (Map RecipientId RcvQueueSub -> a) -> SessSubs -> IO (a, a) |
| 171 | +mapSubs f s = do |
| 172 | + active <- readTVarIO $ activeSubs s |
| 173 | + pending <- readTVarIO $ pendingSubs s |
| 174 | + pure (f active, f pending) |
0 commit comments