core: wait for delivery to avoid race between connection deletion and sending service messages about entity deletion (#3849)

This commit is contained in:
spaced4ndy
2024-03-01 11:26:54 +04:00
committed by GitHub
parent c27973d202
commit 05383477d9
3 changed files with 31 additions and 18 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: 050a921fbbdf21690cab7765bf6237fdc5a419cb
tag: 294d7ec8dde9898b66188a346f6d9d17119763da
source-repository-package
type: git
+1 -1
View File
@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."050a921fbbdf21690cab7765bf6237fdc5a419cb" = "0bc8x3pv3l6wjcfx06yhyydf2amaw5jjax2wcbgbxzrhqz10xf1v";
"https://github.com/simplex-chat/simplexmq.git"."294d7ec8dde9898b66188a346f6d9d17119763da" = "06a4rzzc6ky11h6mw7ja5wb7ykq4dgvwa47wlns9wmpvbfqpmxrh";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
+29 -16
View File
@@ -941,10 +941,10 @@ processChatCommand' vr = \case
withChatLock "deleteChat direct" . procCmd $ do
cancelFilesInProgress user filesInfo
deleteFilesLocally filesInfo
when (contactReady ct && contactActive ct && notify) $
void (sendDirectContactMessage ct XDirectDel) `catchChatError` const (pure ())
let doSendDel = contactReady ct && contactActive ct && notify
when doSendDel $ void (sendDirectContactMessage ct XDirectDel) `catchChatError` const (pure ())
contactConnIds <- map aConnId <$> withStore' (\db -> getContactConnections db userId ct)
deleteAgentConnectionsAsync user contactConnIds
deleteAgentConnectionsAsync' user contactConnIds doSendDel
-- functions below are called in separate transactions to prevent crashes on android
-- (possibly, race condition on integrity check?)
withStore' $ \db -> deleteContactConnectionsAndFiles db userId ct
@@ -965,9 +965,10 @@ processChatCommand' vr = \case
withChatLock "deleteChat group" . procCmd $ do
cancelFilesInProgress user filesInfo
deleteFilesLocally filesInfo
when (memberActive membership && isOwner) . void $ sendGroupMessage' user gInfo members XGrpDel
let doSendDel = memberActive membership && isOwner
when doSendDel . void $ sendGroupMessage' user gInfo members XGrpDel
deleteGroupLinkIfExists user gInfo
deleteMembersConnections user members
deleteMembersConnections' user members doSendDel
updateCIGroupInvitationStatus user gInfo CIGISRejected `catchChatError` \_ -> pure ()
-- functions below are called in separate transactions to prevent crashes on android
-- (possibly, race condition on integrity check?)
@@ -1696,7 +1697,7 @@ processChatCommand' vr = \case
(msg, _) <- sendGroupMessage user gInfo members $ XGrpMemDel mId
ci <- saveSndChatItem user (CDGroupSnd gInfo) msg (CISndGroupEvent $ SGEMemberDeleted memberId (fromLocalProfile memberProfile))
toView $ CRNewChatItem user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci)
deleteMemberConnection user m
deleteMemberConnection' user m True
-- undeleted "member connected" chat item will prevent deletion of member record
deleteOrUpdateMemberRecord user m
pure $ CRUserDeletedMember user gInfo m {memberStatus = GSMemRemoved}
@@ -1711,7 +1712,7 @@ processChatCommand' vr = \case
-- TODO delete direct connections that were unused
deleteGroupLinkIfExists user gInfo
-- member records are not deleted to keep history
deleteMembersConnections user members
deleteMembersConnections' user members True
withStore' $ \db -> updateGroupMemberStatus db userId membership GSMemLeft
pure $ CRLeftMemberUser user gInfo {membership = membership {memberStatus = GSMemLeft}}
APIListMembers groupId -> withUser $ \user ->
@@ -5865,17 +5866,23 @@ closeFileHandle fileId files = do
liftIO $ mapM_ hClose h_ `catchAll_` pure ()
deleteMembersConnections :: ChatMonad m => User -> [GroupMember] -> m ()
deleteMembersConnections user members = do
deleteMembersConnections user members = deleteMembersConnections' user members False
deleteMembersConnections' :: ChatMonad m => User -> [GroupMember] -> Bool -> m ()
deleteMembersConnections' user members waitDelivery = do
let memberConns =
filter (\Connection {connStatus} -> connStatus /= ConnDeleted) $
mapMaybe (\GroupMember {activeConn} -> activeConn) members
deleteAgentConnectionsAsync user $ map aConnId memberConns
deleteAgentConnectionsAsync' user (map aConnId memberConns) waitDelivery
void . withStoreBatch' $ \db -> map (\conn -> updateConnectionStatus db conn ConnDeleted) memberConns
deleteMemberConnection :: ChatMonad m => User -> GroupMember -> m ()
deleteMemberConnection user GroupMember {activeConn} = do
deleteMemberConnection user mem = deleteMemberConnection' user mem False
deleteMemberConnection' :: ChatMonad m => User -> GroupMember -> Bool -> m ()
deleteMemberConnection' user GroupMember {activeConn} waitDelivery = do
forM_ activeConn $ \conn -> do
deleteAgentConnectionAsync user $ aConnId conn
deleteAgentConnectionAsync' user (aConnId conn) waitDelivery
withStore' $ \db -> updateConnectionStatus db conn ConnDeleted
deleteOrUpdateMemberRecord :: ChatMonad m => User -> GroupMember -> m ()
@@ -6248,13 +6255,19 @@ agentAcceptContactAsync user enableNtfs invId msg subMode = do
pure (cmdId, connId)
deleteAgentConnectionAsync :: ChatMonad m => User -> ConnId -> m ()
deleteAgentConnectionAsync user acId =
withAgent (`deleteConnectionAsync` acId) `catchChatError` (toView . CRChatError (Just user))
deleteAgentConnectionAsync user acId = deleteAgentConnectionAsync' user acId False
deleteAgentConnectionAsync' :: ChatMonad m => User -> ConnId -> Bool -> m ()
deleteAgentConnectionAsync' user acId waitDelivery = do
withAgent (\a -> deleteConnectionAsync a waitDelivery acId) `catchChatError` (toView . CRChatError (Just user))
deleteAgentConnectionsAsync :: ChatMonad m => User -> [ConnId] -> m ()
deleteAgentConnectionsAsync _ [] = pure ()
deleteAgentConnectionsAsync user acIds =
withAgent (`deleteConnectionsAsync` acIds) `catchChatError` (toView . CRChatError (Just user))
deleteAgentConnectionsAsync user acIds = deleteAgentConnectionsAsync' user acIds False
deleteAgentConnectionsAsync' :: ChatMonad m => User -> [ConnId] -> Bool -> m ()
deleteAgentConnectionsAsync' _ [] _ = pure ()
deleteAgentConnectionsAsync' user acIds waitDelivery = do
withAgent (\a -> deleteConnectionsAsync a waitDelivery acIds) `catchChatError` (toView . CRChatError (Just user))
agentXFTPDeleteRcvFile :: ChatMonad m => RcvFileId -> FileTransferId -> m ()
agentXFTPDeleteRcvFile aFileId fileId = do