@@ -829,19 +829,21 @@ -(void) unfreeze
829
829
// this operation has highest priority to make sure it will be executed first once unfrozen
830
830
NSBlockOperation * unfreezeOperation = [NSBlockOperation blockOperationWithBlock: ^{
831
831
// this has to be the very first thing even before unfreezing the parse or send queues
832
- if (self.accountState < kStateReconnecting )
833
- {
834
- DDLogInfo (@" Reloading UNfrozen account %@ " , self.accountNo );
835
- // (re)read persisted state (could be changed by appex)
836
- [self readState ];
832
+ @synchronized (self->_stateLockObject ) {
833
+ if (self.accountState < kStateReconnecting )
834
+ {
835
+ DDLogInfo (@" Reloading UNfrozen account %@ " , self.accountNo );
836
+ // (re)read persisted state (could be changed by appex)
837
+ [self readState ];
838
+ }
839
+ else
840
+ DDLogInfo (@" Not reloading UNfrozen account %@ , already connected" , self.accountNo );
841
+
842
+ // this must be inside the dispatch async, because it will dispatch *SYNC* to the receive queue and potentially block or even deadlock the system
843
+ [self unfreezeParseQueue ];
844
+
845
+ [self unfreezeSendQueue ];
837
846
}
838
- else
839
- DDLogInfo (@" Not reloading UNfrozen account %@ , already connected" , self.accountNo );
840
-
841
- // this must be inside the dispatch async, because it will dispatch *SYNC* to the receive queue and potentially block or even deadlock the system
842
- [self unfreezeParseQueue ];
843
-
844
- [self unfreezeSendQueue ];
845
847
}];
846
848
unfreezeOperation.queuePriority = NSOperationQueuePriorityVeryHigh; // make sure this will become the first operation executed once unfrozen
847
849
[self ->_receiveQueue addOperations: @[unfreezeOperation] waitUntilFinished: NO ];
@@ -962,11 +964,67 @@ -(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError
962
964
-(void ) disconnectWithStreamError : (MLXMLNode* _Nullable) streamError andExplicitLogout : (BOOL ) explicitLogout
963
965
{
964
966
DDLogInfo (@" disconnect called..." );
967
+ // commonly used by shortcut outside of receive queue and called from inside the receive queue, too
968
+ monal_void_block_t doExplicitLogout = ^{
969
+ @synchronized (self->_stateLockObject ) {
970
+ DDLogVerbose (@" explicitLogout == YES --> clearing state" );
971
+
972
+ // preserve unAckedStanzas even on explicitLogout and resend them on next connect
973
+ // if we don't do this, messages could get lost when logging out directly after sending them
974
+ // and: sending messages twice is less intrusive than silently loosing them
975
+ NSMutableArray * stanzas = self.unAckedStanzas ;
976
+
977
+ // reset smacks state to sane values (this can be done even if smacks is not supported)
978
+ [self initSM3 ];
979
+ self.unAckedStanzas = stanzas;
980
+
981
+ // inform all old iq handlers of invalidation and clear _iqHandlers dictionary afterwards
982
+ @synchronized (self->_iqHandlers ) {
983
+ for (NSString * iqid in [self ->_iqHandlers allKeys ])
984
+ {
985
+ DDLogWarn (@" Invalidating iq handler for iq id '%@ '" , iqid);
986
+ if (self->_iqHandlers [iqid][@" handler" ] != nil )
987
+ $invalidate (self->_iqHandlers [iqid][@" handler" ], $ID (account, self), $ID (reason, @" disconnect" ));
988
+ else if (self->_iqHandlers [iqid][@" errorHandler" ])
989
+ ((monal_iq_handler_t )self->_iqHandlers [iqid][@" errorHandler" ])(nil );
990
+ }
991
+ self->_iqHandlers = [NSMutableDictionary new ];
992
+ }
993
+
994
+ // invalidate pubsub queue (*after* iq handlers that also might invalidate a result handler of the queued operation)
995
+ [self .pubsub invalidateQueue ];
996
+
997
+ // clear pipeline cache
998
+ self->_pipeliningState = kPipelinedNothing ;
999
+ self->_cachedStreamFeaturesBeforeAuth = nil ;
1000
+ self->_cachedStreamFeaturesAfterAuth = nil ;
1001
+
1002
+ // clear all reconnection handlers
1003
+ @synchronized (self->_reconnectionHandlers ) {
1004
+ [self ->_reconnectionHandlers removeAllObjects ];
1005
+ }
1006
+
1007
+ // persist these changes
1008
+ [self persistState ];
1009
+ }
1010
+
1011
+ [[DataLayer sharedInstance ] resetContactsForAccount: self .accountNo];
1012
+
1013
+ // trigger view updates to make sure enabled/disabled account state propagates to all ui elements
1014
+ [[MLNotificationQueue currentQueue ] postNotificationName: kMonalRefresh object: nil userInfo: nil ];
1015
+ };
965
1016
966
1017
// short-circuit common case without dispatching to receive queue
967
1018
// this allows calling a noop disconnect while the receive queue is frozen
968
- if (self->_accountState <kStateReconnecting && !explicitLogout)
1019
+ // every change to our state is locked by our _stateLockObject and the receive queue unfreeze uses this lock, too
1020
+ // --> an unfreeze can not happen half way through this explicit logout and therefore can't corrupt any state
1021
+ // --> an unfreeze is needed to dispatch to the receive queue which is used by our connect method
1022
+ if (self->_accountState <kStateReconnecting )
1023
+ {
1024
+ if (explicitLogout)
1025
+ doExplicitLogout ();
969
1026
return ;
1027
+ }
970
1028
971
1029
MLAssert (!_receiveQueue.suspended , @" receive queue suspended while trying to disconnect!" );
972
1030
@@ -996,54 +1054,7 @@ -(void) disconnectWithStreamError:(MLXMLNode* _Nullable) streamError andExplicit
996
1054
{
997
1055
DDLogVerbose (@" not doing logout because already logged out, but clearing state if explicitLogout was yes" );
998
1056
if (explicitLogout)
999
- {
1000
- @synchronized (self->_stateLockObject ) {
1001
- DDLogVerbose (@" explicitLogout == YES --> clearing state" );
1002
-
1003
- // preserve unAckedStanzas even on explicitLogout and resend them on next connect
1004
- // if we don't do this, messages could get lost when logging out directly after sending them
1005
- // and: sending messages twice is less intrusive than silently loosing them
1006
- NSMutableArray * stanzas = self.unAckedStanzas ;
1007
-
1008
- // reset smacks state to sane values (this can be done even if smacks is not supported)
1009
- [self initSM3 ];
1010
- self.unAckedStanzas = stanzas;
1011
-
1012
- // inform all old iq handlers of invalidation and clear _iqHandlers dictionary afterwards
1013
- @synchronized (self->_iqHandlers ) {
1014
- for (NSString * iqid in [self ->_iqHandlers allKeys ])
1015
- {
1016
- DDLogWarn (@" Invalidating iq handler for iq id '%@ '" , iqid);
1017
- if (self->_iqHandlers [iqid][@" handler" ] != nil )
1018
- $invalidate (self->_iqHandlers [iqid][@" handler" ], $ID (account, self), $ID (reason, @" disconnect" ));
1019
- else if (self->_iqHandlers [iqid][@" errorHandler" ])
1020
- ((monal_iq_handler_t )self->_iqHandlers [iqid][@" errorHandler" ])(nil );
1021
- }
1022
- self->_iqHandlers = [NSMutableDictionary new ];
1023
- }
1024
-
1025
- // invalidate pubsub queue (*after* iq handlers that also might invalidate a result handler of the queued operation)
1026
- [self .pubsub invalidateQueue ];
1027
-
1028
- // clear pipeline cache
1029
- self->_pipeliningState = kPipelinedNothing ;
1030
- self->_cachedStreamFeaturesBeforeAuth = nil ;
1031
- self->_cachedStreamFeaturesAfterAuth = nil ;
1032
-
1033
- // clear all reconnection handlers
1034
- @synchronized (self->_reconnectionHandlers ) {
1035
- [self ->_reconnectionHandlers removeAllObjects ];
1036
- }
1037
-
1038
- // persist these changes
1039
- [self persistState ];
1040
- }
1041
-
1042
- [[DataLayer sharedInstance ] resetContactsForAccount: self .accountNo];
1043
-
1044
- // trigger view updates to make sure enabled/disabled account state propagates to all ui elements
1045
- [[MLNotificationQueue currentQueue ] postNotificationName: kMonalRefresh object: nil userInfo: nil ];
1046
- }
1057
+ doExplicitLogout ();
1047
1058
return ;
1048
1059
}
1049
1060
DDLogInfo (@" disconnecting" );
0 commit comments