Skip to content
This repository
Browse code

Fixing potential deadlock situation involving modules and autoDelegates.

  • Loading branch information...
commit 35ab9a847af5ae35f04384808994a2eb326bfd4e 1 parent 86f7a8f
Robbie Hanson authored

Showing 3 changed files with 32 additions and 19 deletions. Show diff stats Hide diff stats

  1. +13 1 Core/XMPPInternal.h
  2. +13 17 Core/XMPPModule.m
  3. +6 1 Core/XMPPStream.m
14 Core/XMPPInternal.h
@@ -2,7 +2,8 @@
2 2 // This file is for XMPPStream and various internal components.
3 3 //
4 4
5   -#import "XMPPSASLAuthentication.h"
  5 +#import "XMPPStream.h"
  6 +#import "XMPPModule.h"
6 7
7 8 // Define the various states we'll use to track our progress
8 9 enum XMPPStreamState
@@ -60,3 +61,14 @@ extern NSString *const XMPPStreamDidChangeMyJIDNotification;
60 61 - (void)injectElement:(NSXMLElement *)element;
61 62
62 63 @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
30 Core/XMPPModule.m
@@ -178,36 +178,32 @@ - (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
178 178 dispatch_async(moduleQueue, block);
179 179 }
180 180
181   -- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
  181 +- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue synchronously:(BOOL)synchronously
182 182 {
183   - // Synchronous operation
184   - //
185   - // Delegate removal MUST always be synchronous.
186   -
187 183 dispatch_block_t block = ^{
188 184 [multicastDelegate removeDelegate:delegate delegateQueue:delegateQueue];
189 185 };
190 186
191 187 if (dispatch_get_current_queue() == moduleQueue)
192 188 block();
193   - else
  189 + else if (synchronously)
194 190 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];
195 200 }
196 201
197 202 - (void)removeDelegate:(id)delegate
198 203 {
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)
206 205
207   - if (dispatch_get_current_queue() == moduleQueue)
208   - block();
209   - else
210   - dispatch_sync(moduleQueue, block);
  206 + [self removeDelegate:delegate delegateQueue:NULL synchronously:YES];
211 207 }
212 208
213 209 - (NSString *)moduleName
7 Core/XMPPStream.m
@@ -3923,7 +3923,12 @@ - (void)unregisterModule:(XMPPModule *)module
3923 3923
3924 3924 while ([autoDelegatesEnumerator getNextDelegate:&delegate delegateQueue:&delegateQueue])
3925 3925 {
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];
3927 3932 }
3928 3933
3929 3934 // Unregister modules

0 comments on commit 35ab9a8

Please sign in to comment.
Something went wrong with that request. Please try again.