Skip to content

Commit 35ab9a8

Browse files
committed
Fixing potential deadlock situation involving modules and autoDelegates.
1 parent 86f7a8f commit 35ab9a8

File tree

3 files changed

+32
-19
lines changed

3 files changed

+32
-19
lines changed

Core/XMPPInternal.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// This file is for XMPPStream and various internal components.
33
//
44

5-
#import "XMPPSASLAuthentication.h"
5+
#import "XMPPStream.h"
6+
#import "XMPPModule.h"
67

78
// Define the various states we'll use to track our progress
89
enum XMPPStreamState
@@ -60,3 +61,14 @@ extern NSString *const XMPPStreamDidChangeMyJIDNotification;
6061
- (void)injectElement:(NSXMLElement *)element;
6162

6263
@end
64+
65+
@interface XMPPModule (/* Internal */)
66+
67+
/**
68+
* Used internally by methods like XMPPStream's unregisterModule:.
69+
* Normally removing a delegate is a synchronous operation, but due to multiple dispatch_sync operations,
70+
* it must occasionally be done asynchronously to avoid deadlock.
71+
**/
72+
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue synchronously:(BOOL)synchronously;
73+
74+
@end

Core/XMPPModule.m

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -178,36 +178,32 @@ - (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
178178
dispatch_async(moduleQueue, block);
179179
}
180180

181-
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
181+
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue synchronously:(BOOL)synchronously
182182
{
183-
// Synchronous operation
184-
//
185-
// Delegate removal MUST always be synchronous.
186-
187183
dispatch_block_t block = ^{
188184
[multicastDelegate removeDelegate:delegate delegateQueue:delegateQueue];
189185
};
190186

191187
if (dispatch_get_current_queue() == moduleQueue)
192188
block();
193-
else
189+
else if (synchronously)
194190
dispatch_sync(moduleQueue, block);
191+
else
192+
dispatch_async(moduleQueue, block);
193+
194+
}
195+
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
196+
{
197+
// Synchronous operation (common-case default)
198+
199+
[self removeDelegate:delegate delegateQueue:delegateQueue synchronously:YES];
195200
}
196201

197202
- (void)removeDelegate:(id)delegate
198203
{
199-
// Synchronous operation
200-
//
201-
// Delegate remove MUST always be synchronous.
202-
203-
dispatch_block_t block = ^{
204-
[multicastDelegate removeDelegate:delegate];
205-
};
204+
// Synchronous operation (common-case default)
206205

207-
if (dispatch_get_current_queue() == moduleQueue)
208-
block();
209-
else
210-
dispatch_sync(moduleQueue, block);
206+
[self removeDelegate:delegate delegateQueue:NULL synchronously:YES];
211207
}
212208

213209
- (NSString *)moduleName

Core/XMPPStream.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3923,7 +3923,12 @@ - (void)unregisterModule:(XMPPModule *)module
39233923

39243924
while ([autoDelegatesEnumerator getNextDelegate:&delegate delegateQueue:&delegateQueue])
39253925
{
3926-
[module removeDelegate:delegate delegateQueue:delegateQueue];
3926+
// The module itself has dispatch_sync'd in order to invoke its deactivate method,
3927+
// which has in turn invoked this method. If we call back into the module,
3928+
// and have it dispatch_sync again, we're going to get a deadlock.
3929+
// So we must remove the delegate(s) asynchronously.
3930+
3931+
[module removeDelegate:delegate delegateQueue:delegateQueue synchronously:NO];
39273932
}
39283933

39293934
// Unregister modules

0 commit comments

Comments
 (0)