Skip to content

Commit 8407f28

Browse files
committed
Merge pull request robbiehanson#129 from mgratzer/master
Fixes the iOS6 dispatch_get_current_queue() warnings in GCDAsyncUdpSocket.m
2 parents e011eb3 + 346a185 commit 8407f28

File tree

2 files changed

+198
-82
lines changed

2 files changed

+198
-82
lines changed

GCD/GCDAsyncUdpSocket.h

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
120120
*
121121
* The socket queue is optional.
122122
* If you pass NULL, GCDAsyncSocket will automatically create its own socket queue.
123-
* If you choose to provide a socket queue, the socket queue must not be a concurrent queue.
124-
*
123+
* If you choose to provide a socket queue, the socket queue must not be a concurrent queue,
124+
* then please see the discussion for the method markSocketQueueTargetQueue.
125+
*
125126
* The delegate queue and socket queue can optionally be the same.
126127
**/
127128
- (id)init;
@@ -766,6 +767,80 @@ typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address,
766767
- (void)closeAfterSending;
767768

768769
#pragma mark Advanced
770+
/**
771+
* GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue.
772+
* In most cases, the instance creates this queue itself.
773+
* However, to allow for maximum flexibility, the internal queue may be passed in the init method.
774+
* This allows for some advanced options such as controlling socket priority via target queues.
775+
* However, when one begins to use target queues like this, they open the door to some specific deadlock issues.
776+
*
777+
* For example, imagine there are 2 queues:
778+
* dispatch_queue_t socketQueue;
779+
* dispatch_queue_t socketTargetQueue;
780+
*
781+
* If you do this (pseudo-code):
782+
* socketQueue.targetQueue = socketTargetQueue;
783+
*
784+
* Then all socketQueue operations will actually get run on the given socketTargetQueue.
785+
* This is fine and works great in most situations.
786+
* But if you run code directly from within the socketTargetQueue that accesses the socket,
787+
* you could potentially get deadlock. Imagine the following code:
788+
*
789+
* - (BOOL)socketHasSomething
790+
* {
791+
* __block BOOL result = NO;
792+
* dispatch_block_t block = ^{
793+
* result = [self someInternalMethodToBeRunOnlyOnSocketQueue];
794+
* }
795+
* if (is_executing_on_queue(socketQueue))
796+
* block();
797+
* else
798+
* dispatch_sync(socketQueue, block);
799+
*
800+
* return result;
801+
* }
802+
*
803+
* What happens if you call this method from the socketTargetQueue? The result is deadlock.
804+
* This is because the GCD API offers no mechanism to discover a queue's targetQueue.
805+
* Thus we have no idea if our socketQueue is configured with a targetQueue.
806+
* If we had this information, we could easily avoid deadlock.
807+
* But, since these API's are missing or unfeasible, you'll have to explicitly set it.
808+
*
809+
* IF you pass a socketQueue via the init method,
810+
* AND you've configured the passed socketQueue with a targetQueue,
811+
* THEN you should pass the end queue in the target hierarchy.
812+
*
813+
* For example, consider the following queue hierarchy:
814+
* socketQueue -> ipQueue -> moduleQueue
815+
*
816+
* This example demonstrates priority shaping within some server.
817+
* All incoming client connections from the same IP address are executed on the same target queue.
818+
* And all connections for a particular module are executed on the same target queue.
819+
* Thus, the priority of all networking for the entire module can be changed on the fly.
820+
* Additionally, networking traffic from a single IP cannot monopolize the module.
821+
*
822+
* Here's how you would accomplish something like that:
823+
* - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock
824+
* {
825+
* dispatch_queue_t socketQueue = dispatch_queue_create("", NULL);
826+
* dispatch_queue_t ipQueue = [self ipQueueForAddress:address];
827+
*
828+
* dispatch_set_target_queue(socketQueue, ipQueue);
829+
* dispatch_set_target_queue(iqQueue, moduleQueue);
830+
*
831+
* return socketQueue;
832+
* }
833+
* - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
834+
* {
835+
* [clientConnections addObject:newSocket];
836+
* [newSocket markSocketQueueTargetQueue:moduleQueue];
837+
* }
838+
*
839+
* Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue.
840+
* This is often NOT the case, as such queues are used solely for execution shaping.
841+
**/
842+
- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue;
843+
- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue;
769844

770845
/**
771846
* It's not thread-safe to access certain variables from outside the socket's internal queue.

0 commit comments

Comments
 (0)