Skip to content

Commit 66b59a1

Browse files
committed
Adding ability to run receive filter synchronously.
1 parent fb41b56 commit 66b59a1

File tree

3 files changed

+105
-59
lines changed

3 files changed

+105
-59
lines changed

GCD/GCDAsyncUdpSocket.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,9 +652,29 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres
652652
*
653653
* For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef.
654654
* To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue.
655+
*
656+
* Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below),
657+
* passing YES for the isAsynchronous parameter.
655658
**/
656659
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue;
657660

661+
/**
662+
* The receive filter can be run via dispatch_async or dispatch_sync.
663+
* Most typical situations call for asynchronous operation.
664+
*
665+
* However, there are a few situations in which synchronous operation is preferred.
666+
* Such is the case when the filter is extremely minimal and fast.
667+
* This is because dispatch_sync is faster than dispatch_async.
668+
*
669+
* If you choose synchronous operation, be aware of possible deadlock conditions.
670+
* Since the socket queue is executing your block via dispatch_sync,
671+
* then you cannot perform any tasks which may invoke dispatch_sync on the socket queue.
672+
* For example, you can't query properties on the socket.
673+
**/
674+
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock
675+
withQueue:(dispatch_queue_t)filterQueue
676+
isAsynchronous:(BOOL)isAsynchronous;
677+
658678
#pragma mark Closing
659679

660680
/**

GCD/GCDAsyncUdpSocket.m

Lines changed: 84 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ @interface GCDAsyncUdpSocket ()
146146
id delegate;
147147
dispatch_queue_t delegateQueue;
148148

149-
GCDAsyncUdpSocketReceiveFilterBlock filterBlock;
150-
dispatch_queue_t filterQueue;
149+
GCDAsyncUdpSocketReceiveFilterBlock receiveFilterBlock;
150+
dispatch_queue_t receiveFilterQueue;
151+
BOOL receiveFilterAsync;
151152

152153
uint32_t flags;
153154
uint16_t config;
@@ -3921,33 +3922,35 @@ - (void)pauseReceiving
39213922
dispatch_async(socketQueue, block);
39223923
}
39233924

3924-
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)inFilterBlock withQueue:(dispatch_queue_t)inFilterQueue
3925+
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue
39253926
{
3926-
GCDAsyncUdpSocketReceiveFilterBlock newFilterBlock;
3927-
dispatch_queue_t newFilterQueue;
3927+
[self setReceiveFilter:filterBlock withQueue:filterQueue isAsynchronous:YES];
3928+
}
3929+
3930+
- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock
3931+
withQueue:(dispatch_queue_t)filterQueue
3932+
isAsynchronous:(BOOL)isAsynchronous
3933+
{
3934+
GCDAsyncUdpSocketReceiveFilterBlock newFilterBlock = NULL;
3935+
dispatch_queue_t newFilterQueue = NULL;
39283936

3929-
if (inFilterBlock)
3937+
if (filterBlock)
39303938
{
3931-
NSAssert(inFilterQueue, @"Must provide a dispatch_queue in which to run the filter block.");
3939+
NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block.");
39323940

3933-
newFilterBlock = [inFilterBlock copy];
3934-
newFilterQueue = inFilterQueue;
3941+
newFilterBlock = [filterBlock copy];
3942+
newFilterQueue = filterQueue;
39353943
dispatch_retain(newFilterQueue);
39363944
}
3937-
else
3938-
{
3939-
newFilterBlock = NULL;
3940-
newFilterQueue = NULL;
3941-
}
39423945

39433946
dispatch_block_t block = ^{
39443947

3948+
if (receiveFilterQueue)
3949+
dispatch_release(receiveFilterQueue);
39453950

3946-
if (filterQueue)
3947-
dispatch_release(filterQueue);
3948-
3949-
filterBlock = newFilterBlock;
3950-
filterQueue = newFilterQueue;
3951+
receiveFilterBlock = newFilterBlock;
3952+
receiveFilterQueue = newFilterQueue;
3953+
receiveFilterAsync = isAsynchronous;
39513954
};
39523955

39533956
if (dispatch_get_current_queue() == socketQueue)
@@ -4116,7 +4119,8 @@ - (void)doReceive
41164119

41174120

41184121
BOOL waitingForSocket = NO;
4119-
BOOL ignoredDueToAddress = NO;
4122+
BOOL notifiedDelegate = NO;
4123+
BOOL ignored = NO;
41204124

41214125
NSError *error = nil;
41224126

@@ -4136,60 +4140,78 @@ - (void)doReceive
41364140
if (flags & kDidConnect)
41374141
{
41384142
if (addr4 && ![self isConnectedToAddress4:addr4])
4139-
ignoredDueToAddress = YES;
4143+
ignored = YES;
41404144
if (addr6 && ![self isConnectedToAddress6:addr6])
4141-
ignoredDueToAddress = YES;
4145+
ignored = YES;
41424146
}
41434147

41444148
NSData *addr = (addr4 != nil) ? addr4 : addr6;
41454149

4146-
if (!ignoredDueToAddress)
4150+
if (!ignored)
41474151
{
4148-
if (filterBlock && filterQueue)
4152+
if (receiveFilterBlock && receiveFilterQueue)
41494153
{
41504154
// Run data through filter, and if approved, notify delegate
4151-
pendingFilterOperations++;
41524155

4153-
dispatch_async(filterQueue, ^{ @autoreleasepool {
4154-
4155-
id filterContext = nil;
4156-
BOOL allowed = filterBlock(data, addr, &filterContext);
4157-
4158-
// Transition back to socketQueue to get the current delegate / delegateQueue
4159-
4160-
dispatch_async(socketQueue, ^{ @autoreleasepool {
4161-
4162-
pendingFilterOperations--;
4156+
__block id filterContext = nil;
4157+
__block BOOL allowed = NO;
4158+
4159+
if (receiveFilterAsync)
4160+
{
4161+
pendingFilterOperations++;
4162+
dispatch_async(receiveFilterQueue, ^{ @autoreleasepool {
41634163

4164-
if (allowed)
4165-
{
4166-
[self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext];
4167-
}
4164+
allowed = receiveFilterBlock(data, addr, &filterContext);
41684165

4169-
if (flags & kReceiveOnce)
4170-
{
4166+
// Transition back to socketQueue to get the current delegate / delegateQueue
4167+
dispatch_async(socketQueue, ^{ @autoreleasepool {
4168+
4169+
pendingFilterOperations--;
4170+
41714171
if (allowed)
41724172
{
4173-
// The delegate has been notified,
4174-
// so our receive once operation has completed.
4175-
flags &= ~kReceiveOnce;
4173+
[self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext];
41764174
}
4177-
else if (pendingFilterOperations == 0)
4175+
4176+
if (flags & kReceiveOnce)
41784177
{
4179-
// All pending filter operations have completed,
4180-
// and none were allowed through.
4181-
// Our receive once operation hasn't completed yet.
4182-
[self doReceive];
4178+
if (allowed)
4179+
{
4180+
// The delegate has been notified,
4181+
// so our receive once operation has completed.
4182+
flags &= ~kReceiveOnce;
4183+
}
4184+
else if (pendingFilterOperations == 0)
4185+
{
4186+
// All pending filter operations have completed,
4187+
// and none were allowed through.
4188+
// Our receive once operation hasn't completed yet.
4189+
[self doReceive];
4190+
}
41834191
}
4184-
}
4192+
}});
4193+
}});
4194+
}
4195+
else // if (!receiveFilterAsync)
4196+
{
4197+
dispatch_sync(receiveFilterQueue, ^{ @autoreleasepool {
4198+
4199+
allowed = receiveFilterBlock(data, addr, &filterContext);
41854200
}});
41864201

4187-
}});
4202+
if (allowed) {
4203+
[self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext];
4204+
notifiedDelegate = YES;
4205+
}
4206+
else {
4207+
ignored = YES;
4208+
}
4209+
}
41884210
}
4189-
else
4211+
else // if (!receiveFilterBlock || !receiveFilterQueue)
41904212
{
4191-
// Notify delegate
41924213
[self notifyDidReceiveData:data fromAddress:addr withFilterContext:nil];
4214+
notifiedDelegate = YES;
41934215
}
41944216
}
41954217
}
@@ -4219,16 +4241,20 @@ - (void)doReceive
42194241
else
42204242
{
42214243
// One-at-a-time receive mode
4222-
if (ignoredDueToAddress)
4223-
{
4224-
[self doReceive];
4225-
}
4226-
else if (pendingFilterOperations == 0)
4244+
if (notifiedDelegate)
42274245
{
42284246
// The delegate has been notified (no set filter).
42294247
// So our receive once operation has completed.
42304248
flags &= ~kReceiveOnce;
42314249
}
4250+
else if (ignored)
4251+
{
4252+
[self doReceive];
4253+
}
4254+
else
4255+
{
4256+
// Waiting on asynchronous receive filter...
4257+
}
42324258
}
42334259
}
42344260
}

GCD/Xcode/EchoServer/EchoServer.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
29B97313FDCFA39411CA2CEA /* Project object */ = {
208208
isa = PBXProject;
209209
attributes = {
210-
LastUpgradeCheck = 0420;
210+
LastUpgradeCheck = 0430;
211211
};
212212
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "EchoServer" */;
213213
compatibilityVersion = "Xcode 3.2";

0 commit comments

Comments
 (0)