each command takes lock if it needs it (#273)

This commit is contained in:
Evgeny Poberezkin 2022-02-06 08:21:40 +00:00 committed by GitHub
parent 5aabf87898
commit 9b67aa537a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -112,9 +112,9 @@ execChatCommand :: (MonadUnliftIO m, MonadReader ChatController m) => ByteString
execChatCommand s = case parseAll chatCommandP $ B.dropWhileEnd isSpace s of
Left e -> pure . CRChatError . ChatError $ CECommandError e
Right cmd -> do
ChatController {chatLock = l, smpAgent = a, currentUser} <- ask
ChatController {currentUser} <- ask
user <- readTVarIO currentUser
withAgentLock a . withLock l $ either CRChatCmdError id <$> runExceptT (processChatCommand user cmd)
either CRChatCmdError id <$> runExceptT (processChatCommand user cmd)
toView :: ChatMonad m => ChatResponse -> m ()
toView event = do
@ -129,7 +129,7 @@ processChatCommand user@User {userId, profile} = \case
CTGroup -> CRApiChat . AChat SCTGroup <$> withStore (\st -> getGroupChat st user cId pagination)
CTContactRequest -> pure $ CRChatError ChatErrorNotImplemented
APIGetChatItems _count -> pure $ CRChatError ChatErrorNotImplemented
APISendMessage cType chatId mc -> case cType of
APISendMessage cType chatId mc -> withChatLock $ case cType of
CTDirect -> do
ct@Contact {localDisplayName = c} <- withStore $ \st -> getContact st userId chatId
ci <- sendDirectChatItem userId ct (XMsgNew mc) (CISndMsgContent mc)
@ -148,7 +148,7 @@ processChatCommand user@User {userId, profile} = \case
withStore (\st -> getContactGroupNames st userId ct) >>= \case
[] -> do
conns <- withStore $ \st -> getContactConnections st userId ct
procCmd $ do
withChatLock . procCmd $ do
withAgent $ \a -> forM_ conns $ \conn ->
deleteConnection a (aConnId conn) `catchError` \(_ :: AgentErrorType) -> pure ()
withStore $ \st -> deleteContact st userId ct
@ -160,11 +160,11 @@ processChatCommand user@User {userId, profile} = \case
APIAcceptContact connReqId -> do
UserContactRequest {agentInvitationId = AgentInvId invId, localDisplayName = cName, profileId, profile = p} <- withStore $ \st ->
getContactRequest st userId connReqId
procCmd $ do
withChatLock . procCmd $ do
connId <- withAgent $ \a -> acceptContact a invId . directMessage $ XInfo profile
acceptedContact <- withStore $ \st -> createAcceptedContact st userId connId cName profileId p
pure $ CRAcceptingContactRequest acceptedContact
APIRejectContact connReqId -> do
APIRejectContact connReqId -> withChatLock $ do
cReq@UserContactRequest {agentContactConnId = AgentConnId connId, agentInvitationId = AgentInvId invId} <-
withStore $ \st ->
getContactRequest st userId connReqId
@ -173,29 +173,29 @@ processChatCommand user@User {userId, profile} = \case
pure $ CRContactRequestRejected cReq
ChatHelp section -> pure $ CRChatHelp section
Welcome -> pure $ CRWelcome user
AddContact -> procCmd $ do
AddContact -> withChatLock . procCmd $ do
(connId, cReq) <- withAgent (`createConnection` SCMInvitation)
withStore $ \st -> createDirectConnection st userId connId
pure $ CRInvitation cReq
Connect (Just (ACR SCMInvitation cReq)) -> procCmd $ do
Connect (Just (ACR SCMInvitation cReq)) -> withChatLock . procCmd $ do
connect cReq $ XInfo profile
pure CRSentConfirmation
Connect (Just (ACR SCMContact cReq)) -> procCmd $ do
Connect (Just (ACR SCMContact cReq)) -> withChatLock . procCmd $ do
connect cReq $ XContact profile Nothing
pure CRSentInvitation
Connect Nothing -> throwChatError CEInvalidConnReq
ConnectAdmin -> procCmd $ do
ConnectAdmin -> withChatLock . procCmd $ do
connect adminContactReq $ XContact profile Nothing
pure CRSentInvitation
DeleteContact cName -> do
contactId <- withStore $ \st -> getContactIdByName st userId cName
processChatCommand user $ APIDeleteChat CTDirect contactId
ListContacts -> CRContactsList <$> withStore (`getUserContacts` user)
CreateMyAddress -> procCmd $ do
CreateMyAddress -> withChatLock . procCmd $ do
(connId, cReq) <- withAgent (`createConnection` SCMContact)
withStore $ \st -> createUserContactLink st userId connId cReq
pure $ CRUserContactLinkCreated cReq
DeleteMyAddress -> do
DeleteMyAddress -> withChatLock $ do
conns <- withStore $ \st -> getUserContactLinkConnections st userId
procCmd $ do
withAgent $ \a -> forM_ conns $ \conn ->
@ -216,7 +216,7 @@ processChatCommand user@User {userId, profile} = \case
NewGroup gProfile -> do
gVar <- asks idsDrg
CRGroupCreated <$> withStore (\st -> createNewGroup st gVar user gProfile)
AddMember gName cName memRole -> do
AddMember gName cName memRole -> withChatLock $ do
-- TODO for large groups: no need to load all members to determine if contact is a member
(group, contact) <- withStore $ \st -> (,) <$> getGroupByName st user gName <*> getContactByName st userId cName
let Group gInfo@GroupInfo {groupId, groupProfile, membership} members = group
@ -243,7 +243,7 @@ processChatCommand user@User {userId, profile} = \case
| otherwise -> throwChatError $ CEGroupDuplicateMember cName
JoinGroup gName -> do
ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g} <- withStore $ \st -> getGroupInvitation st user gName
procCmd $ do
withChatLock . procCmd $ do
agentConnId <- withAgent $ \a -> joinConnection a connRequest . directMessage . XGrpAcpt $ memberId (membership g :: GroupMember)
withStore $ \st -> do
createMemberConnection st userId fromMember agentConnId
@ -258,14 +258,14 @@ processChatCommand user@User {userId, profile} = \case
Just m@GroupMember {memberId = mId, memberRole = mRole, memberStatus = mStatus} -> do
let userRole = memberRole (membership :: GroupMember)
when (userRole < GRAdmin || userRole < mRole) $ throwChatError CEGroupUserRole
procCmd $ do
withChatLock . procCmd $ do
when (mStatus /= GSMemInvited) . void . sendGroupMessage members $ XGrpMemDel mId
deleteMemberConnection m
withStore $ \st -> updateGroupMemberStatus st userId m GSMemRemoved
pure $ CRUserDeletedMember gInfo m
LeaveGroup gName -> do
Group gInfo@GroupInfo {membership} members <- withStore $ \st -> getGroupByName st user gName
procCmd $ do
withChatLock . procCmd $ do
void $ sendGroupMessage members XGrpLeave
mapM_ deleteMemberConnection members
withStore $ \st -> updateGroupMemberStatus st userId membership GSMemLeft
@ -277,7 +277,7 @@ processChatCommand user@User {userId, profile} = \case
memberRole (membership :: GroupMember) == GROwner
|| (s == GSMemRemoved || s == GSMemLeft || s == GSMemGroupDeleted || s == GSMemInvited)
unless canDelete $ throwChatError CEGroupUserRole
procCmd $ do
withChatLock . procCmd $ do
when (memberActive membership) . void $ sendGroupMessage members XGrpDel
mapM_ deleteMemberConnection members
withStore $ \st -> deleteGroup st user g
@ -288,7 +288,7 @@ processChatCommand user@User {userId, profile} = \case
groupId <- withStore $ \st -> getGroupIdByName st user gName
let mc = MCText $ safeDecodeUtf8 msg
processChatCommand user $ APISendMessage CTGroup groupId mc
SendFile cName f -> do
SendFile cName f -> withChatLock $ do
(fileSize, chSize) <- checkSndFile f
contact <- withStore $ \st -> getContactByName st userId cName
(agentConnId, fileConnReq) <- withAgent (`createConnection` SCMInvitation)
@ -299,7 +299,7 @@ processChatCommand user@User {userId, profile} = \case
withStore $ \st -> updateFileTransferChatItemId st fileId $ chatItemId ci
setActive $ ActiveC cName
pure . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat contact) ci
SendGroupFile gName f -> do
SendGroupFile gName f -> withChatLock $ do
(fileSize, chSize) <- checkSndFile f
Group gInfo@GroupInfo {membership} members <- withStore $ \st -> getGroupByName st user gName
unless (memberActive membership) $ throwChatError CEGroupMemberUserRemoved
@ -322,7 +322,7 @@ processChatCommand user@User {userId, profile} = \case
ReceiveFile fileId filePath_ -> do
ft@RcvFileTransfer {fileInvitation = FileInvitation {fileName, fileConnReq}, fileStatus} <- withStore $ \st -> getRcvFileTransfer st userId fileId
unless (fileStatus == RFSNew) . throwChatError $ CEFileAlreadyReceiving fileName
procCmd $ do
withChatLock . procCmd $ do
tryError (withAgent $ \a -> joinConnection a fileConnReq . directMessage $ XFileAcpt fileName) >>= \case
Right agentConnId -> do
filePath <- getRcvFilePath fileId filePath_ fileName
@ -333,7 +333,7 @@ processChatCommand user@User {userId, profile} = \case
Left e -> throwError e
CancelFile fileId -> do
ft' <- withStore (\st -> getFileTransfer st userId fileId)
procCmd $ case ft' of
withChatLock . procCmd $ case ft' of
FTSnd fts -> do
forM_ fts $ \ft -> cancelSndFileTransfer ft
pure $ CRSndGroupFileCancelled fts
@ -350,12 +350,15 @@ processChatCommand user@User {userId, profile} = \case
let user' = (user :: User) {localDisplayName = displayName, profile = p}
asks currentUser >>= atomically . (`writeTVar` user')
contacts <- withStore (`getUserContacts` user)
procCmd $ do
withChatLock . procCmd $ do
forM_ contacts $ \ct -> sendDirectMessage (contactConn ct) $ XInfo p
pure $ CRUserProfileUpdated profile p
QuitChat -> liftIO exitSuccess
ShowVersion -> pure CRVersionInfo
where
withChatLock action = do
ChatController {chatLock = l, smpAgent = a} <- ask
withAgentLock a . withLock l $ action
-- below code would make command responses asynchronous where they can be slow
-- in View.hs `r'` should be defined as `id` in this case
-- procCmd :: m ChatResponse -> m ChatResponse