From 4386273170e7edc1a9058fe8388cc2946025d905 Mon Sep 17 00:00:00 2001 From: Gavin Eadie Date: Wed, 22 Oct 2014 14:14:34 -0400 Subject: [PATCH] Update the two GCDAsyncUdpSocket files to latest versions (as of Oct22/14) Convert project to use Automatic Reference Counting (ARC) Tidy some file header attribution commentary --- LICENSE | 18 +- ios-ntp.xcodeproj/project.pbxproj | 18 +- lib/GCDAsyncUdpSocket.h | 383 +++++++- lib/GCDAsyncUdpSocket.m | 1532 ++++++++++++++++++----------- src/NSDate+NetworkClock.h | 11 +- src/NetAssociation.h | 3 +- src/NetAssociation.m | 18 +- src/NetworkClock.h | 7 +- src/NetworkClock.m | 38 +- test/main.m | 9 +- test/ntpAAppDelegate.h | 22 +- test/ntpAAppDelegate.m | 10 +- test/ntpAViewController.h | 12 +- test/ntpAViewController.m | 17 +- 14 files changed, 1389 insertions(+), 709 deletions(-) diff --git a/LICENSE b/LICENSE index 02c3201..5098df8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,8 +1,20 @@ The MIT License (MIT) Copyright (c) 2012, Ramsay Consulting -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/ios-ntp.xcodeproj/project.pbxproj b/ios-ntp.xcodeproj/project.pbxproj index c3c7daa..67bd9c9 100644 --- a/ios-ntp.xcodeproj/project.pbxproj +++ b/ios-ntp.xcodeproj/project.pbxproj @@ -135,9 +135,9 @@ D91E95D813D9AF8C004E4194 /* src */ = { isa = PBXGroup; children = ( + D9B2BA3213D9B6CF00C9385A /* ios-ntp.h */, D9B2BA4013D9C06100C9385A /* NSDate+NetworkClock.h */, D9B2BA4113D9C06100C9385A /* NSDate+NetworkClock.m */, - D9B2BA3213D9B6CF00C9385A /* ios-ntp.h */, D91E95D913D9AF8C004E4194 /* NetAssociation.h */, D91E95DA13D9AF8C004E4194 /* NetAssociation.m */, D91E95DB13D9AF8C004E4194 /* NetworkClock.h */, @@ -375,6 +375,7 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; @@ -382,7 +383,7 @@ GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; SDKROOT = iphoneos; }; name = Debug; @@ -391,12 +392,13 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + CLANG_ENABLE_OBJC_ARC = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_VERSION = ""; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; }; @@ -406,6 +408,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_ARC = YES; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -413,7 +416,7 @@ GCC_PREPROCESSOR_DEFINITIONS = IOS_NTP_LOGGING; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INFOPLIST_FILE = "resources/ntpA-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 1; WRAPPER_EXTENSION = app; @@ -424,12 +427,13 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_OBJC_ARC = YES; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = resources/ntpA_Prefix.pch; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INFOPLIST_FILE = "resources/ntpA-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 1; VALIDATE_PRODUCT = YES; @@ -459,7 +463,7 @@ INFOPLIST_FILE = "resources/ios-ntp-Info.plist"; INFOPLIST_OUTPUT_FORMAT = xml; INFOPLIST_PREPROCESS = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; LINK_WITH_STANDARD_LIBRARIES = NO; MACH_O_TYPE = mh_object; OTHER_LDFLAGS = ( @@ -495,7 +499,7 @@ INFOPLIST_FILE = "resources/ios-ntp-Info.plist"; INFOPLIST_OUTPUT_FORMAT = xml; INFOPLIST_PREPROCESS = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; LINK_WITH_STANDARD_LIBRARIES = NO; MACH_O_TYPE = mh_object; OTHER_LDFLAGS = ( diff --git a/lib/GCDAsyncUdpSocket.h b/lib/GCDAsyncUdpSocket.h index 0db032b..77f6367 100644 --- a/lib/GCDAsyncUdpSocket.h +++ b/lib/GCDAsyncUdpSocket.h @@ -5,16 +5,20 @@ // Originally created by Robbie Hanson of Deusty LLC. // Updated and maintained by Deusty LLC and the Apple development community. // -// http://code.google.com/p/cocoaasyncsocket/ +// https://github.com/robbiehanson/CocoaAsyncSocket // #import #import - +#import +#import extern NSString *const GCDAsyncUdpSocketException; extern NSString *const GCDAsyncUdpSocketErrorDomain; +extern NSString *const GCDAsyncUdpSocketQueueName; +extern NSString *const GCDAsyncUdpSocketThreadName; + enum GCDAsyncUdpSocketError { GCDAsyncUdpSocketNoError = 0, // Never used @@ -28,24 +32,29 @@ typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError; /** * You may optionally set a receive filter for the socket. - * This receive filter may be set to run in its own queue (independent of delegate queue). - * - * A filter can provide several useful features. + * A filter can provide several useful features: + * + * 1. Many times udp packets need to be parsed. + * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. + * The end result is a parallel socket io, datagram parsing, and packet processing. * - * 1. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. + * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. * The filter can prevent such packets from arriving at the delegate. * And because the filter can run in its own independent queue, this doesn't slow down the delegate. * - * - Since the udp protocol does not guarnatee delivery, udp packets may be lost. + * - Since the udp protocol does not guarantee delivery, udp packets may be lost. * Many protocols built atop udp thus provide various resend/re-request algorithms. * This sometimes results in duplicate packets arriving. + * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. * * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. * Such packets need to be ignored. - * - * 2. Many times udp packets need to be parsed. - * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). * * @param data - The packet that was received. * @param address - The address the data was received from. @@ -61,7 +70,7 @@ typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError; * * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { * - * MyProtocolMessage *msg = [MyProtocol parseMessage:msg]; + * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; * * *context = response; * return (response != nil); @@ -71,6 +80,34 @@ typedef enum GCDAsyncUdpSocketError GCDAsyncUdpSocketError; **/ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *address, id *context); +/** + * You may optionally set a send filter for the socket. + * A filter can provide several interesting possibilities: + * + * 1. Optional caching of resolved addresses for domain names. + * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. + * + * 2. Reusable modules of code for bandwidth monitoring. + * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). + * + * @param data - The packet that was received. + * @param address - The address the data was received from. + * See utilities section for methods to extract info from address. + * @param tag - The tag that was passed in the send method. + * + * @returns - YES if the packet should actually be sent over the socket. + * NO if the packet should be silently dropped (not sent over the socket). + * + * Regardless of the return value, the delegate will be informed that the packet was successfully sent. + * +**/ +typedef BOOL (^GCDAsyncUdpSocketSendFilterBlock)(NSData *data, NSData *address, long tag); + @interface GCDAsyncUdpSocket : NSObject @@ -84,8 +121,9 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres * * The socket queue is optional. * If you pass NULL, GCDAsyncSocket will automatically create its own socket queue. - * If you choose to provide a socket queue, the socket queue must not be a concurrent queue. - * + * If you choose to provide a socket queue, the socket queue must not be a concurrent queue, + * then please see the discussion for the method markSocketQueueTargetQueue. + * * The delegate queue and socket queue can optionally be the same. **/ - (id)init; @@ -382,6 +420,9 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres **/ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; +- (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr; +- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; + #pragma mark Broadcast /** @@ -400,32 +441,90 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres /** * Asynchronously sends the given data, with the given timeout and tag. * - * If the timeout value is negative, the receive operation will not use a timeout. - * - * - * * This method may only be used with a connected socket. * Recall that connecting is optional for a UDP socket. * For connected sockets, data can only be sent to the connected address. * For non-connected sockets, the remote destination is specified for each packet. + * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. + * + * @param data + * The data to send. + * If data is nil or zero-length, this method does nothing. + * If passing NSMutableData, please read the thread-safety notice below. + * + * @param timeout + * The timeout for the send opeartion. + * If the timeout value is negative, the send operation will not use a timeout. + * + * @param tag + * The tag is for your convenience. + * It is not sent or received over the socket in any manner what-so-ever. + * It is reported back as a parameter in the udpSocket:didSendDataWithTag: + * or udpSocket:didNotSendDataWithTag:dueToError: methods. + * You can use it as an array index, state id, type constant, etc. * - * If data is nil or zero-length this method does nothing. - * Otherwise the result is reported via the delegate methods. + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method + * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying + * that this particular send operation has completed. + * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. + * It simply retains it for performance reasons. + * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. + * Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. **/ - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; /** * Asynchronously sends the given data, with the given timeout and tag, to the given host and port. * - * If the timeout value is negative, the receive operation will not use a timeout. - * * This method cannot be used with a connected socket. * Recall that connecting is optional for a UDP socket. * For connected sockets, data can only be sent to the connected address. * For non-connected sockets, the remote destination is specified for each packet. + * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. + * + * @param data + * The data to send. + * If data is nil or zero-length, this method does nothing. + * If passing NSMutableData, please read the thread-safety notice below. + * + * @param host + * The destination to send the udp packet to. + * May be specified as a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2"). + * You may also use the convenience strings of "loopback" or "localhost". + * + * @param port + * The port of the host to send to. + * + * @param timeout + * The timeout for the send opeartion. + * If the timeout value is negative, the send operation will not use a timeout. + * + * @param tag + * The tag is for your convenience. + * It is not sent or received over the socket in any manner what-so-ever. + * It is reported back as a parameter in the udpSocket:didSendDataWithTag: + * or udpSocket:didNotSendDataWithTag:dueToError: methods. + * You can use it as an array index, state id, type constant, etc. * - * If data is nil or zero-length this method does nothing. - * Otherwise the result is reported via the delegate methods. + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method + * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying + * that this particular send operation has completed. + * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. + * It simply retains it for performance reasons. + * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. + * Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. **/ - (void)sendData:(NSData *)data toHost:(NSString *)host @@ -440,12 +539,14 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres * Recall that connecting is optional for a UDP socket. * For connected sockets, data can only be sent to the connected address. * For non-connected sockets, the remote destination is specified for each packet. + * For more information about optionally connecting udp sockets, see the documentation for the connect methods above. * * @param data * The data to send. * If data is nil or zero-length, this method does nothing. + * If passing NSMutableData, please read the thread-safety notice below. * - * @param address + * @param remoteAddr * The address to send the data to (specified as a sockaddr structure wrapped in a NSData object). * * @param timeout @@ -455,25 +556,117 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres * @param tag * The tag is for your convenience. * It is not sent or received over the socket in any manner what-so-ever. - * It is reported back as a parameter in the delegate methods. + * It is reported back as a parameter in the udpSocket:didSendDataWithTag: + * or udpSocket:didNotSendDataWithTag:dueToError: methods. * You can use it as an array index, state id, type constant, etc. * - * If data is nil or zero-length, this method does nothing. - * Otherwise the result is reported via the delegate methods. + * + * Thread-Safety Note: + * If the given data parameter is mutable (NSMutableData) then you MUST NOT alter the data while + * the socket is sending it. In other words, it's not safe to alter the data until after the delegate method + * udpSocket:didSendDataWithTag: or udpSocket:didNotSendDataWithTag:dueToError: is invoked signifying + * that this particular send operation has completed. + * This is due to the fact that GCDAsyncUdpSocket does NOT copy the data. + * It simply retains it for performance reasons. + * Often times, if NSMutableData is passed, it is because a request/response was built up in memory. + * Copying this data adds an unwanted/unneeded overhead. + * If you need to write data from an immutable buffer, and you need to alter the buffer before the socket + * completes sending the bytes (which is NOT immediately after this method returns, but rather at a later time + * when the delegate method notifies you), then you should first copy the bytes, and pass the copy to this method. **/ - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag; +/** + * You may optionally set a send filter for the socket. + * A filter can provide several interesting possibilities: + * + * 1. Optional caching of resolved addresses for domain names. + * The cache could later be consulted, resulting in fewer system calls to getaddrinfo. + * + * 2. Reusable modules of code for bandwidth monitoring. + * + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). + * + * For more information about GCDAsyncUdpSocketSendFilterBlock, see the documentation for its typedef. + * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. + * + * Note: This method invokes setSendFilter:withQueue:isAsynchronous: (documented below), + * passing YES for the isAsynchronous parameter. +**/ +- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue; + +/** + * The receive filter can be run via dispatch_async or dispatch_sync. + * Most typical situations call for asynchronous operation. + * + * However, there are a few situations in which synchronous operation is preferred. + * Such is the case when the filter is extremely minimal and fast. + * This is because dispatch_sync is faster than dispatch_async. + * + * If you choose synchronous operation, be aware of possible deadlock conditions. + * Since the socket queue is executing your block via dispatch_sync, + * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. + * For example, you can't query properties on the socket. +**/ +- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock + withQueue:(dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous; + #pragma mark Receiving /** - * Begins receiving udp packets on the socket. - * It will continue to receive packets until the socket is closed, or until pauseReceiving is called. + * There are two modes of operation for receiving packets: one-at-a-time & continuous. + * + * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. + * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, + * where your state machine may not always be ready to process incoming packets. + * + * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. + * Receiving packets continuously is better suited to real-time streaming applications. + * + * You may switch back and forth between one-at-a-time mode and continuous mode. + * If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode. + * + * When a packet is received (and not filtered by the optional receive filter), + * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. + * + * If the socket is able to begin receiving packets, this method returns YES. + * Otherwise it returns NO, and sets the errPtr with appropriate error information. + * + * An example error: + * You created a udp socket to act as a server, and immediately called receive. + * You forgot to first bind the socket to a port number, and received a error with a message like: + * "Must bind socket before you can receive data." +**/ +- (BOOL)receiveOnce:(NSError **)errPtr; + +/** + * There are two modes of operation for receiving packets: one-at-a-time & continuous. + * + * In one-at-a-time mode, you call receiveOnce everytime your delegate is ready to process an incoming udp packet. + * Receiving packets one-at-a-time may be better suited for implementing certain state machine code, + * where your state machine may not always be ready to process incoming packets. + * + * In continuous mode, the delegate is invoked immediately everytime incoming udp packets are received. + * Receiving packets continuously is better suited to real-time streaming applications. + * + * You may switch back and forth between one-at-a-time mode and continuous mode. + * If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode. * * For every received packet (not filtered by the optional receive filter), * the delegate method (udpSocket:didReceiveData:fromAddress:withFilterContext:) is invoked. * * If the socket is able to begin receiving packets, this method returns YES. * Otherwise it returns NO, and sets the errPtr with appropriate error information. + * + * An example error: + * You created a udp socket to act as a server, and immediately called receive. + * You forgot to first bind the socket to a port number, and received a error with a message like: + * "Must bind socket before you can receive data." **/ - (BOOL)beginReceiving:(NSError **)errPtr; @@ -497,44 +690,158 @@ typedef BOOL (^GCDAsyncUdpSocketReceiveFilterBlock)(NSData *data, NSData *addres * * A filter can provide several useful features. * - * 1. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. + * 1. Many times udp packets need to be parsed. + * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. + * The end result is a parallel socket io, datagram parsing, and packet processing. + * + * 2. Many times udp packets are discarded because they are duplicate/unneeded/unsolicited. * The filter can prevent such packets from arriving at the delegate. * And because the filter can run in its own independent queue, this doesn't slow down the delegate. * - * - Since the udp protocol does not guarnatee delivery, udp packets may be lost. + * - Since the udp protocol does not guarantee delivery, udp packets may be lost. * Many protocols built atop udp thus provide various resend/re-request algorithms. * This sometimes results in duplicate packets arriving. + * A filter may allow you to architect the duplicate detection code to run in parallel to normal processing. * * - Since the udp socket may be connectionless, its possible for unsolicited packets to arrive. * Such packets need to be ignored. - * - * 2. Many times udp packets need to be parsed. - * Since the filter can run in its own independent queue, you can parallelize this parsing quite easily. - * * - * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. - * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. + * 3. Sometimes traffic shapers are needed to simulate real world environments. + * A filter allows you to write custom code to simulate such environments. + * The ability to code this yourself is especially helpful when your simulated environment + * is more complicated than simple traffic shaping (e.g. simulating a cone port restricted router), + * or the system tools to handle this aren't available (e.g. on a mobile device). * * Example: * * GCDAsyncUdpSocketReceiveFilterBlock filter = ^BOOL (NSData *data, NSData *address, id *context) { * - * MyProtocolMessage *msg = [MyProtocol parseMessage:msg]; + * MyProtocolMessage *msg = [MyProtocol parseMessage:data]; * * *context = response; * return (response != nil); * }; * [udpSocket setReceiveFilter:filter withQueue:myParsingQueue]; * + * For more information about GCDAsyncUdpSocketReceiveFilterBlock, see the documentation for its typedef. + * To remove a previously set filter, invoke this method and pass a nil filterBlock and NULL filterQueue. + * + * Note: This method invokes setReceiveFilter:withQueue:isAsynchronous: (documented below), + * passing YES for the isAsynchronous parameter. **/ - (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue; +/** + * The receive filter can be run via dispatch_async or dispatch_sync. + * Most typical situations call for asynchronous operation. + * + * However, there are a few situations in which synchronous operation is preferred. + * Such is the case when the filter is extremely minimal and fast. + * This is because dispatch_sync is faster than dispatch_async. + * + * If you choose synchronous operation, be aware of possible deadlock conditions. + * Since the socket queue is executing your block via dispatch_sync, + * then you cannot perform any tasks which may invoke dispatch_sync on the socket queue. + * For example, you can't query properties on the socket. +**/ +- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock + withQueue:(dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous; + #pragma mark Closing +/** + * Immediately closes the underlying socket. + * Any pending send operations are discarded. + * + * The GCDAsyncUdpSocket instance may optionally be used again. + * (it will setup/configure/use another unnderlying BSD socket). +**/ - (void)close; + +/** + * Closes the underlying socket after all pending send operations have been sent. + * + * The GCDAsyncUdpSocket instance may optionally be used again. + * (it will setup/configure/use another unnderlying BSD socket). +**/ - (void)closeAfterSending; #pragma mark Advanced +/** + * GCDAsyncSocket maintains thread safety by using an internal serial dispatch_queue. + * In most cases, the instance creates this queue itself. + * However, to allow for maximum flexibility, the internal queue may be passed in the init method. + * This allows for some advanced options such as controlling socket priority via target queues. + * However, when one begins to use target queues like this, they open the door to some specific deadlock issues. + * + * For example, imagine there are 2 queues: + * dispatch_queue_t socketQueue; + * dispatch_queue_t socketTargetQueue; + * + * If you do this (pseudo-code): + * socketQueue.targetQueue = socketTargetQueue; + * + * Then all socketQueue operations will actually get run on the given socketTargetQueue. + * This is fine and works great in most situations. + * But if you run code directly from within the socketTargetQueue that accesses the socket, + * you could potentially get deadlock. Imagine the following code: + * + * - (BOOL)socketHasSomething + * { + * __block BOOL result = NO; + * dispatch_block_t block = ^{ + * result = [self someInternalMethodToBeRunOnlyOnSocketQueue]; + * } + * if (is_executing_on_queue(socketQueue)) + * block(); + * else + * dispatch_sync(socketQueue, block); + * + * return result; + * } + * + * What happens if you call this method from the socketTargetQueue? The result is deadlock. + * This is because the GCD API offers no mechanism to discover a queue's targetQueue. + * Thus we have no idea if our socketQueue is configured with a targetQueue. + * If we had this information, we could easily avoid deadlock. + * But, since these API's are missing or unfeasible, you'll have to explicitly set it. + * + * IF you pass a socketQueue via the init method, + * AND you've configured the passed socketQueue with a targetQueue, + * THEN you should pass the end queue in the target hierarchy. + * + * For example, consider the following queue hierarchy: + * socketQueue -> ipQueue -> moduleQueue + * + * This example demonstrates priority shaping within some server. + * All incoming client connections from the same IP address are executed on the same target queue. + * And all connections for a particular module are executed on the same target queue. + * Thus, the priority of all networking for the entire module can be changed on the fly. + * Additionally, networking traffic from a single IP cannot monopolize the module. + * + * Here's how you would accomplish something like that: + * - (dispatch_queue_t)newSocketQueueForConnectionFromAddress:(NSData *)address onSocket:(GCDAsyncSocket *)sock + * { + * dispatch_queue_t socketQueue = dispatch_queue_create("", NULL); + * dispatch_queue_t ipQueue = [self ipQueueForAddress:address]; + * + * dispatch_set_target_queue(socketQueue, ipQueue); + * dispatch_set_target_queue(iqQueue, moduleQueue); + * + * return socketQueue; + * } + * - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket + * { + * [clientConnections addObject:newSocket]; + * [newSocket markSocketQueueTargetQueue:moduleQueue]; + * } + * + * Note: This workaround is ONLY needed if you intend to execute code directly on the ipQueue or moduleQueue. + * This is often NOT the case, as such queues are used solely for execution shaping. + **/ +- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreConfiguredTargetQueue; +- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketQueuesPreviouslyConfiguredTargetQueue; /** * It's not thread-safe to access certain variables from outside the socket's internal queue. diff --git a/lib/GCDAsyncUdpSocket.m b/lib/GCDAsyncUdpSocket.m index dbf85cc..d37e1af 100644 --- a/lib/GCDAsyncUdpSocket.m +++ b/lib/GCDAsyncUdpSocket.m @@ -3,13 +3,18 @@ // // This class is in the public domain. // Originally created by Robbie Hanson of Deusty LLC. -// Updated and maintained by Deusty LLC and the Mac development community. +// Updated and maintained by Deusty LLC and the Apple development community. // -// http://code.google.com/p/cocoaasyncsocket/ +// https://github.com/robbiehanson/CocoaAsyncSocket // #import "GCDAsyncUdpSocket.h" +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +// For more information see: https://github.com/robbiehanson/CocoaAsyncSocket/wiki/ARC +#endif + #if TARGET_OS_IPHONE #import #import @@ -89,31 +94,41 @@ **/ #define SOCKET_NULL -1 +/** + * Just to type less code. +**/ +#define AutoreleasedBlock(block) ^{ @autoreleasepool { block(); }} + + @class GCDAsyncUdpSendPacket; NSString *const GCDAsyncUdpSocketException = @"GCDAsyncUdpSocketException"; NSString *const GCDAsyncUdpSocketErrorDomain = @"GCDAsyncUdpSocketErrorDomain"; +NSString *const GCDAsyncUdpSocketQueueName = @"GCDAsyncUdpSocket"; +NSString *const GCDAsyncUdpSocketThreadName = @"GCDAsyncUdpSocket-CFStream"; + enum GCDAsyncUdpSocketFlags { kDidCreateSockets = 1 << 0, // If set, the sockets have been created. kDidBind = 1 << 1, // If set, bind has been called. kConnecting = 1 << 2, // If set, a connection attempt is in progress. kDidConnect = 1 << 3, // If set, socket is connected. - kReceive = 1 << 4, // If set, receive is enabled - kIPv4Deactivated = 1 << 5, // If set, socket4 was closed due to bind or connect on IPv6. - kIPv6Deactivated = 1 << 6, // If set, socket6 was closed due to bind or connect on IPv4. - kSend4SourceSuspended = 1 << 7, // If set, send4Source is suspended. - kSend6SourceSuspended = 1 << 8, // If set, send6Source is suspended. - kReceive4SourceSuspended = 1 << 9, // If set, receive4Source is suspended. - kReceive6SourceSuspended = 1 << 10, // If set, receive6Source is suspended. - kSock4CanAcceptBytes = 1 << 11, // If set, we know socket4 can accept bytes. If unset, it's unknown. - kSock6CanAcceptBytes = 1 << 12, // If set, we know socket6 can accept bytes. If unset, it's unknown. - kForbidSendReceive = 1 << 13, // If set, no new send or receive operations are allowed to be queued. - kCloseAfterSends = 1 << 14, // If set, close as soon as no more sends are queued. - kFlipFlop = 1 << 15, // Used to alternate between IPv4 and IPv6 sockets. + kReceiveOnce = 1 << 4, // If set, one-at-a-time receive is enabled + kReceiveContinuous = 1 << 5, // If set, continuous receive is enabled + kIPv4Deactivated = 1 << 6, // If set, socket4 was closed due to bind or connect on IPv6. + kIPv6Deactivated = 1 << 7, // If set, socket6 was closed due to bind or connect on IPv4. + kSend4SourceSuspended = 1 << 8, // If set, send4Source is suspended. + kSend6SourceSuspended = 1 << 9, // If set, send6Source is suspended. + kReceive4SourceSuspended = 1 << 10, // If set, receive4Source is suspended. + kReceive6SourceSuspended = 1 << 11, // If set, receive6Source is suspended. + kSock4CanAcceptBytes = 1 << 12, // If set, we know socket4 can accept bytes. If unset, it's unknown. + kSock6CanAcceptBytes = 1 << 13, // If set, we know socket6 can accept bytes. If unset, it's unknown. + kForbidSendReceive = 1 << 14, // If set, no new send or receive operations are allowed to be queued. + kCloseAfterSends = 1 << 15, // If set, close as soon as no more sends are queued. + kFlipFlop = 1 << 16, // Used to alternate between IPv4 and IPv6 sockets. #if TARGET_OS_IPHONE - kAddedStreamListener = 1 << 16, // If set, CFStreams have been added to listener thread + kAddedStreamListener = 1 << 17, // If set, CFStreams have been added to listener thread #endif }; @@ -131,11 +146,20 @@ @interface GCDAsyncUdpSocket () { - id delegate; +#if __has_feature(objc_arc_weak) + __weak id delegate; +#else + __unsafe_unretained id delegate; +#endif dispatch_queue_t delegateQueue; - GCDAsyncUdpSocketReceiveFilterBlock filterBlock; - dispatch_queue_t filterQueue; + GCDAsyncUdpSocketReceiveFilterBlock receiveFilterBlock; + dispatch_queue_t receiveFilterQueue; + BOOL receiveFilterAsync; + + GCDAsyncUdpSocketSendFilterBlock sendFilterBlock; + dispatch_queue_t sendFilterQueue; + BOOL sendFilterAsync; uint32_t flags; uint16_t config; @@ -160,6 +184,8 @@ @interface GCDAsyncUdpSocket () unsigned long socket4FDBytesAvailable; unsigned long socket6FDBytesAvailable; + uint32_t pendingFilterOperations; + NSData *cachedLocalAddress4; NSString *cachedLocalHost4; uint16_t cachedLocalPort4; @@ -172,6 +198,8 @@ @interface GCDAsyncUdpSocket () NSString *cachedConnectedHost; uint16_t cachedConnectedPort; int cachedConnectedFamily; + + void *IsOnSocketQueueOrTargetQueueKey; #if TARGET_OS_IPHONE CFStreamClientContext streamContext; @@ -195,6 +223,7 @@ - (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr; - (BOOL)connectWithAddress6:(NSData *)address6 error:(NSError **)errPtr; - (void)maybeDequeueSend; +- (void)doPreSend; - (void)doSend; - (void)endCurrentSend; - (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout; @@ -204,6 +233,8 @@ - (void)doReceiveEOF; - (void)closeWithError:(NSError *)error; +- (BOOL)performMulticastRequest:(int)requestType forGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr; + #if TARGET_OS_IPHONE - (BOOL)createReadAndWriteStreams:(NSError **)errPtr; - (BOOL)registerForStreamCallbacks:(NSError **)errPtr; @@ -218,6 +249,11 @@ + (NSString *)hostFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6; + (uint16_t)portFromSockaddr4:(const struct sockaddr_in *)pSockaddr4; + (uint16_t)portFromSockaddr6:(const struct sockaddr_in6 *)pSockaddr6; +#if TARGET_OS_IPHONE +// Forward declaration ++ (void)listenerThread; +#endif + @end //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -234,9 +270,13 @@ @interface GCDAsyncUdpSendPacket : NSObject { long tag; BOOL resolveInProgress; + BOOL filterInProgress; - NSArray *addresses; - NSError *error; + NSArray *resolvedAddresses; + NSError *resolveError; + + NSData *address; + int addressFamily; } - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i; @@ -249,7 +289,7 @@ - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i { if ((self = [super init])) { - buffer = [d retain]; + buffer = d; timeout = t; tag = i; @@ -258,13 +298,6 @@ - (id)initWithData:(NSData *)d timeout:(NSTimeInterval)t tag:(long)i return self; } -- (void)dealloc -{ - [buffer release]; - [addresses release]; - [error release]; - [super dealloc]; -} @end @@ -294,12 +327,6 @@ - (id)init return self; } -- (void)dealloc -{ - [addresses release]; - [error release]; - [super dealloc]; -} @end @@ -340,8 +367,10 @@ - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQu if (dq) { - dispatch_retain(dq); delegateQueue = dq; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(delegateQueue); + #endif } max4ReceiveSize = 9216; @@ -359,16 +388,40 @@ - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQu NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), @"The given socketQueue parameter must not be a concurrent queue."); - dispatch_retain(sq); socketQueue = sq; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(socketQueue); + #endif } else { - socketQueue = dispatch_queue_create("GCDAsyncUdpSocket", NULL); + socketQueue = dispatch_queue_create([GCDAsyncUdpSocketQueueName UTF8String], NULL); } + + // The dispatch_queue_set_specific() and dispatch_get_specific() functions take a "void *key" parameter. + // From the documentation: + // + // > Keys are only compared as pointers and are never dereferenced. + // > Thus, you can use a pointer to a static variable for a specific subsystem or + // > any other value that allows you to identify the value uniquely. + // + // We're just going to use the memory address of an ivar. + // Specifically an ivar that is explicitly named for our purpose to make the code more readable. + // + // However, it feels tedious (and less readable) to include the "&" all the time: + // dispatch_get_specific(&IsOnSocketQueueOrTargetQueueKey) + // + // So we're going to make it so it doesn't matter if we use the '&' or not, + // by assigning the value of the ivar to the address of the ivar. + // Thus: IsOnSocketQueueOrTargetQueueKey == &IsOnSocketQueueOrTargetQueueKey; + + IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey; + + void *nonNullUnusedPointer = (__bridge void *)self; + dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); - sendQueue = [[NSMutableArray alloc] initWithCapacity:5]; currentSend = nil; + sendQueue = [[NSMutableArray alloc] initWithCapacity:5]; #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] addObserver:self @@ -388,7 +441,7 @@ - (void)dealloc [[NSNotificationCenter defaultCenter] removeObserver:self]; #endif - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { [self closeWithError:nil]; } @@ -400,20 +453,17 @@ - (void)dealloc } delegate = nil; - if (delegateQueue) - dispatch_release(delegateQueue); + #if !OS_OBJECT_USE_OBJC + if (delegateQueue) dispatch_release(delegateQueue); + #endif delegateQueue = NULL; - if (socketQueue) - dispatch_release(socketQueue); + #if !OS_OBJECT_USE_OBJC + if (socketQueue) dispatch_release(socketQueue); + #endif socketQueue = NULL; - [sendQueue release]; - [userData release]; - LogInfo(@"%@ - %@ (finish)", THIS_METHOD, self); - - [super dealloc]; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -422,13 +472,13 @@ - (void)dealloc - (id)delegate { - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { return delegate; } else { - __block id result; + __block id result = nil; dispatch_sync(socketQueue, ^{ result = delegate; @@ -444,7 +494,7 @@ - (void)setDelegate:(id)newDelegate synchronously:(BOOL)synchronously delegate = newDelegate; }; - if (dispatch_get_current_queue() == socketQueue) { + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { block(); } else { @@ -467,13 +517,13 @@ - (void)synchronouslySetDelegate:(id)newDelegate - (dispatch_queue_t)delegateQueue { - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { return delegateQueue; } else { - __block dispatch_queue_t result; + __block dispatch_queue_t result = NULL; dispatch_sync(socketQueue, ^{ result = delegateQueue; @@ -487,16 +537,15 @@ - (void)setDelegateQueue:(dispatch_queue_t)newDelegateQueue synchronously:(BOOL) { dispatch_block_t block = ^{ - if (delegateQueue) - dispatch_release(delegateQueue); - - if (newDelegateQueue) - dispatch_retain(newDelegateQueue); + #if !OS_OBJECT_USE_OBJC + if (delegateQueue) dispatch_release(delegateQueue); + if (newDelegateQueue) dispatch_retain(newDelegateQueue); + #endif delegateQueue = newDelegateQueue; }; - if (dispatch_get_current_queue() == socketQueue) { + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { block(); } else { @@ -519,7 +568,7 @@ - (void)synchronouslySetDelegateQueue:(dispatch_queue_t)newDelegateQueue - (void)getDelegate:(id *)delegatePtr delegateQueue:(dispatch_queue_t *)delegateQueuePtr { - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { if (delegatePtr) *delegatePtr = delegate; if (delegateQueuePtr) *delegateQueuePtr = delegateQueue; @@ -545,16 +594,15 @@ - (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQ delegate = newDelegate; - if (delegateQueue) - dispatch_release(delegateQueue); - - if (newDelegateQueue) - dispatch_retain(newDelegateQueue); + #if !OS_OBJECT_USE_OBJC + if (delegateQueue) dispatch_release(delegateQueue); + if (newDelegateQueue) dispatch_retain(newDelegateQueue); + #endif delegateQueue = newDelegateQueue; }; - if (dispatch_get_current_queue() == socketQueue) { + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { block(); } else { @@ -586,7 +634,7 @@ - (BOOL)isIPv4Enabled result = ((config & kIPv4Disabled) == 0); }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -608,7 +656,7 @@ - (void)setIPv4Enabled:(BOOL)flag config |= kIPv4Disabled; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -625,7 +673,7 @@ - (BOOL)isIPv6Enabled result = ((config & kIPv6Disabled) == 0); }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -647,7 +695,7 @@ - (void)setIPv6Enabled:(BOOL)flag config |= kIPv6Disabled; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -655,13 +703,13 @@ - (void)setIPv6Enabled:(BOOL)flag - (BOOL)isIPv4Preferred { - __block BOOL result; + __block BOOL result = NO; dispatch_block_t block = ^{ result = (config & kPreferIPv4) ? YES : NO; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -671,13 +719,13 @@ - (BOOL)isIPv4Preferred - (BOOL)isIPv6Preferred { - __block BOOL result; + __block BOOL result = NO; dispatch_block_t block = ^{ result = (config & kPreferIPv6) ? YES : NO; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -687,13 +735,13 @@ - (BOOL)isIPv6Preferred - (BOOL)isIPVersionNeutral { - __block BOOL result; + __block BOOL result = NO; dispatch_block_t block = ^{ result = (config & (kPreferIPv4 | kPreferIPv6)) == 0; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -712,7 +760,7 @@ - (void)setPreferIPv4 }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -729,7 +777,7 @@ - (void)setPreferIPv6 }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -746,7 +794,7 @@ - (void)setIPVersionNeutral }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -761,7 +809,7 @@ - (uint16_t)maxReceiveIPv4BufferSize result = max4ReceiveSize; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -778,7 +826,7 @@ - (void)setMaxReceiveIPv4BufferSize:(uint16_t)max max4ReceiveSize = max; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -793,7 +841,7 @@ - (uint32_t)maxReceiveIPv6BufferSize result = max6ReceiveSize; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -810,7 +858,7 @@ - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max max6ReceiveSize = max; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -819,19 +867,19 @@ - (void)setMaxReceiveIPv6BufferSize:(uint32_t)max - (id)userData { - __block id result; + __block id result = nil; dispatch_block_t block = ^{ - result = [userData retain]; + result = userData; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); - return [result autorelease]; + return result; } - (void)setUserData:(id)arbitraryUserData @@ -840,12 +888,11 @@ - (void)setUserData:(id)arbitraryUserData if (userData != arbitraryUserData) { - [userData release]; - userData = [arbitraryUserData retain]; + userData = arbitraryUserData; } }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -862,7 +909,7 @@ - (void)notifyDidConnectToAddress:(NSData *)anAddress if (delegateQueue && [delegate respondsToSelector:@selector(udpSocket:didConnectToAddress:)]) { id theDelegate = delegate; - NSData *address = [[anAddress copy] autorelease]; // In case param is NSMutableData + NSData *address = [anAddress copy]; // In case param is NSMutableData dispatch_async(delegateQueue, ^{ @autoreleasepool { @@ -1039,7 +1086,7 @@ - (NSError *)otherError:(NSString *)errMsg - (BOOL)preOp:(NSError **)errPtr { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); if (delegate == nil) // Must have delegate set { @@ -1095,7 +1142,7 @@ - (void)asyncResolveHost:(NSString *)aHost // So we want to copy it now, within this block that will be executed synchronously. // This way the asynchronous lookup block below doesn't have to worry about it changing. - NSString *host = [[aHost copy] autorelease]; + NSString *host = [aHost copy]; dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); @@ -1189,7 +1236,7 @@ - (void)asyncResolveHost:(NSString *)aHost **/ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses:(NSArray *)addresses { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert([addresses count] > 0, @"Expected at least one address"); int resultAF = AF_UNSPEC; @@ -1220,7 +1267,10 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address(es)."; resultError = [self otherError:msg]; - goto SetParamPtrsAndReturn; + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; } if (isIPv6Disabled && !resolvedIPv4Address) @@ -1228,7 +1278,10 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address(es)."; resultError = [self otherError:msg]; - goto SetParamPtrsAndReturn; + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; } BOOL isIPv4Deactivated = (flags & kIPv4Deactivated) ? YES : NO; @@ -1239,7 +1292,10 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses NSString *msg = @"IPv4 has been deactivated due to bind/connect, and DNS lookup found no IPv6 address(es)."; resultError = [self otherError:msg]; - goto SetParamPtrsAndReturn; + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; } if (isIPv6Deactivated && !resolvedIPv4Address) @@ -1247,7 +1303,10 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses NSString *msg = @"IPv6 has been deactivated due to bind/connect, and DNS lookup found no IPv4 address(es)."; resultError = [self otherError:msg]; - goto SetParamPtrsAndReturn; + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; + + return resultAF; } // Extract first IPv4 and IPv6 address in list @@ -1308,16 +1367,8 @@ - (int)getAddress:(NSData **)addressPtr error:(NSError **)errorPtr fromAddresses resultAddress = address6; } - goto SetParamPtrsAndReturn; - - -SetParamPtrsAndReturn: - - if (addressPtr) - *addressPtr = [[resultAddress retain] autorelease]; - - if (errorPtr) - *errorPtr = resultError; + if (addressPtr) *addressPtr = resultAddress; + if (errorPtr) *errorPtr = resultError; return resultAF; } @@ -1522,7 +1573,7 @@ - (void)convertNumericHost:(NSString *)numericHost - (BOOL)isConnectedToAddress4:(NSData *)someAddr4 { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(flags & kDidConnect, @"Not connected"); NSAssert(cachedConnectedAddress, @"Expected cached connected address"); @@ -1548,7 +1599,7 @@ - (BOOL)isConnectedToAddress4:(NSData *)someAddr4 - (BOOL)isConnectedToAddress6:(NSData *)someAddr6 { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(flags & kDidConnect, @"Not connected"); NSAssert(cachedConnectedAddress, @"Expected cached connected address"); @@ -1655,7 +1706,7 @@ - (unsigned int)indexOfInterfaceAddr6:(NSData *)interfaceAddr6 - (void)setupSendAndReceiveSourcesForSocket4 { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); send4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket4FD, 0, socketQueue); receive4Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket4FD, 0, socketQueue); @@ -1665,11 +1716,32 @@ - (void)setupSendAndReceiveSourcesForSocket4 dispatch_source_set_event_handler(send4Source, ^{ @autoreleasepool { LogVerbose(@"send4EventBlock"); - LogVerbose(@"dispatch_source_get_data(send4Source) = %lu", dispatch_source_get_data(send4Source)); flags |= kSock4CanAcceptBytes; - [self doSend]; + + // If we're ready to send data, do so immediately. + // Otherwise pause the send source or it will continue to fire over and over again. + + if (currentSend == nil) + { + LogVerbose(@"Nothing to send"); + [self suspendSend4Source]; + } + else if (currentSend->resolveInProgress) + { + LogVerbose(@"currentSend - waiting for address resolve"); + [self suspendSend4Source]; + } + else if (currentSend->filterInProgress) + { + LogVerbose(@"currentSend - waiting on sendFilter"); + [self suspendSend4Source]; + } + else + { + [self doSend]; + } }}); @@ -1693,15 +1765,19 @@ - (void)setupSendAndReceiveSourcesForSocket4 int theSocketFD = socket4FD; + #if !OS_OBJECT_USE_OBJC dispatch_source_t theSendSource = send4Source; dispatch_source_t theReceiveSource = receive4Source; + #endif dispatch_source_set_cancel_handler(send4Source, ^{ LogVerbose(@"send4CancelBlock"); + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(send4Source)"); dispatch_release(theSendSource); + #endif if (--socketFDRefCount == 0) { @@ -1714,8 +1790,10 @@ - (void)setupSendAndReceiveSourcesForSocket4 LogVerbose(@"receive4CancelBlock"); + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(receive4Source)"); dispatch_release(theReceiveSource); + #endif if (--socketFDRefCount == 0) { @@ -1739,7 +1817,7 @@ - (void)setupSendAndReceiveSourcesForSocket4 - (void)setupSendAndReceiveSourcesForSocket6 { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); send6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, socket6FD, 0, socketQueue); receive6Source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, socket6FD, 0, socketQueue); @@ -1749,11 +1827,32 @@ - (void)setupSendAndReceiveSourcesForSocket6 dispatch_source_set_event_handler(send6Source, ^{ @autoreleasepool { LogVerbose(@"send6EventBlock"); - LogVerbose(@"dispatch_source_get_data(send6Source) = %lu", dispatch_source_get_data(send6Source)); flags |= kSock6CanAcceptBytes; - [self doSend]; + + // If we're ready to send data, do so immediately. + // Otherwise pause the send source or it will continue to fire over and over again. + + if (currentSend == nil) + { + LogVerbose(@"Nothing to send"); + [self suspendSend6Source]; + } + else if (currentSend->resolveInProgress) + { + LogVerbose(@"currentSend - waiting for address resolve"); + [self suspendSend6Source]; + } + else if (currentSend->filterInProgress) + { + LogVerbose(@"currentSend - waiting on sendFilter"); + [self suspendSend6Source]; + } + else + { + [self doSend]; + } }}); @@ -1777,15 +1876,19 @@ - (void)setupSendAndReceiveSourcesForSocket6 int theSocketFD = socket6FD; + #if !OS_OBJECT_USE_OBJC dispatch_source_t theSendSource = send6Source; dispatch_source_t theReceiveSource = receive6Source; + #endif dispatch_source_set_cancel_handler(send6Source, ^{ LogVerbose(@"send6CancelBlock"); + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(send6Source)"); dispatch_release(theSendSource); + #endif if (--socketFDRefCount == 0) { @@ -1798,8 +1901,10 @@ - (void)setupSendAndReceiveSourcesForSocket6 LogVerbose(@"receive6CancelBlock"); + #if !OS_OBJECT_USE_OBJC LogVerbose(@"dispatch_release(receive6Source)"); dispatch_release(theReceiveSource); + #endif if (--socketFDRefCount == 0) { @@ -1824,7 +1929,7 @@ - (BOOL)createSocket4:(BOOL)useIPv4 socket6:(BOOL)useIPv6 error:(NSError **)errP { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(((flags & kDidCreateSockets) == 0), @"Sockets have already been created"); // CreateSocket Block @@ -2055,9 +2160,6 @@ - (void)closeSocket4 // Clear cached info - [cachedLocalAddress4 release]; - [cachedLocalHost4 release]; - cachedLocalAddress4 = nil; cachedLocalHost4 = nil; cachedLocalPort4 = 0; @@ -2096,9 +2198,6 @@ - (void)closeSocket6 // Clear cached info - [cachedLocalAddress6 release]; - [cachedLocalHost6 release]; - cachedLocalAddress6 = nil; cachedLocalHost6 = nil; cachedLocalPort6 = 0; @@ -2144,7 +2243,7 @@ - (BOOL)getLocalAddress:(NSData **)dataPtr LogWarn(@"Error in getsockname: %@", [self errnoError]); } } - else if (socketFamily == AF_INET) + else if (socketFamily == AF_INET6) { struct sockaddr_in6 sockaddr6; socklen_t sockaddr6len = sizeof(sockaddr6); @@ -2170,55 +2269,45 @@ - (BOOL)getLocalAddress:(NSData **)dataPtr - (void)maybeUpdateCachedLocalAddress4Info { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); if ( cachedLocalAddress4 || ((flags & kDidBind) == 0) || (socket4FD == SOCKET_NULL) ) { return; } - @autoreleasepool { + NSData *address = nil; + NSString *host = nil; + uint16_t port = 0; - NSData *address = nil; - NSString *host = nil; - uint16_t port = 0; + if ([self getLocalAddress:&address host:&host port:&port forSocket:socket4FD withFamily:AF_INET]) + { - if ([self getLocalAddress:&address host:&host port:&port forSocket:socket4FD withFamily:AF_INET]) - { - [cachedLocalAddress4 release]; - [cachedLocalHost4 release]; - - cachedLocalAddress4 = [address retain]; - cachedLocalHost4 = [host retain]; - cachedLocalPort4 = port; - } + cachedLocalAddress4 = address; + cachedLocalHost4 = host; + cachedLocalPort4 = port; } } - (void)maybeUpdateCachedLocalAddress6Info { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); if ( cachedLocalAddress6 || ((flags & kDidBind) == 0) || (socket6FD == SOCKET_NULL) ) { return; } - @autoreleasepool { + NSData *address = nil; + NSString *host = nil; + uint16_t port = 0; - NSData *address = nil; - NSString *host = nil; - uint16_t port = 0; + if ([self getLocalAddress:&address host:&host port:&port forSocket:socket6FD withFamily:AF_INET6]) + { - if ([self getLocalAddress:&address host:&host port:&port forSocket:socket6FD withFamily:AF_INET6]) - { - [cachedLocalAddress6 release]; - [cachedLocalHost6 release]; - - cachedLocalAddress6 = [address retain]; - cachedLocalHost6 = [host retain]; - cachedLocalPort6 = port; - } + cachedLocalAddress6 = address; + cachedLocalHost6 = host; + cachedLocalPort6 = port; } } @@ -2226,42 +2315,53 @@ - (NSData *)localAddress { __block NSData *result = nil; - dispatch_block_t block = ^{ @autoreleasepool { + dispatch_block_t block = ^{ if (socket4FD != SOCKET_NULL) - result = [[self localAddress_IPv4] retain]; + { + [self maybeUpdateCachedLocalAddress4Info]; + result = cachedLocalAddress4; + } else - result = [[self localAddress_IPv6] retain]; + { + [self maybeUpdateCachedLocalAddress6Info]; + result = cachedLocalAddress6; + } - }}; + }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (NSString *)localHost { __block NSString *result = nil; - dispatch_block_t block = ^{ @autoreleasepool { + dispatch_block_t block = ^{ if (socket4FD != SOCKET_NULL) - result = [[self localHost_IPv4] retain]; + { + [self maybeUpdateCachedLocalAddress4Info]; + result = cachedLocalHost4; + } else - result = [[self localHost_IPv6] retain]; - - }}; + { + [self maybeUpdateCachedLocalAddress6Info]; + result = cachedLocalHost6; + } + }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (uint16_t)localPort @@ -2271,58 +2371,64 @@ - (uint16_t)localPort dispatch_block_t block = ^{ if (socket4FD != SOCKET_NULL) - result = [self localPort_IPv4]; + { + [self maybeUpdateCachedLocalAddress4Info]; + result = cachedLocalPort4; + } else - result = [self localPort_IPv6]; + { + [self maybeUpdateCachedLocalAddress6Info]; + result = cachedLocalPort6; + } }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); return result; } - (NSData *)localAddress_IPv4 { - __block NSData *result; + __block NSData *result = nil; dispatch_block_t block = ^{ [self maybeUpdateCachedLocalAddress4Info]; - result = [cachedLocalAddress4 retain]; + result = cachedLocalAddress4; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (NSString *)localHost_IPv4 { - __block NSString *result; + __block NSString *result = nil; dispatch_block_t block = ^{ [self maybeUpdateCachedLocalAddress4Info]; - result = [cachedLocalHost4 retain]; + result = cachedLocalHost4; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (uint16_t)localPort_IPv4 { - __block uint16_t result; + __block uint16_t result = 0; dispatch_block_t block = ^{ @@ -2330,53 +2436,53 @@ - (uint16_t)localPort_IPv4 result = cachedLocalPort4; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); return result; } - (NSData *)localAddress_IPv6 { - __block NSData *result; + __block NSData *result = nil; dispatch_block_t block = ^{ [self maybeUpdateCachedLocalAddress6Info]; - result = [cachedLocalAddress6 retain]; + result = cachedLocalAddress6; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (NSString *)localHost_IPv6 { - __block NSString *result; + __block NSString *result = nil; dispatch_block_t block = ^{ [self maybeUpdateCachedLocalAddress6Info]; - result = [cachedLocalHost6 retain]; + result = cachedLocalHost6; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (uint16_t)localPort_IPv6 { - __block uint16_t result; + __block uint16_t result = 0; dispatch_block_t block = ^{ @@ -2384,114 +2490,109 @@ - (uint16_t)localPort_IPv6 result = cachedLocalPort6; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); return result; } - (void)maybeUpdateCachedConnectedAddressInfo { - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); if (cachedConnectedAddress || (flags & kDidConnect) == 0) { return; } - @autoreleasepool { + NSData *data = nil; + NSString *host = nil; + uint16_t port = 0; + int family = AF_UNSPEC; - NSData *data = nil; - NSString *host = nil; - uint16_t port = 0; - int family = AF_UNSPEC; + if (socket4FD != SOCKET_NULL) + { + struct sockaddr_in sockaddr4; + socklen_t sockaddr4len = sizeof(sockaddr4); - if (socket4FD != SOCKET_NULL) + if (getpeername(socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) { - struct sockaddr_in sockaddr4; - socklen_t sockaddr4len = sizeof(sockaddr4); - - if (getpeername(socket4FD, (struct sockaddr *)&sockaddr4, &sockaddr4len) == 0) - { - data = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; - host = [[self class] hostFromSockaddr4:&sockaddr4]; - port = [[self class] portFromSockaddr4:&sockaddr4]; - family = AF_INET; - } - else - { - LogWarn(@"Error in getpeername: %@", [self errnoError]); - } + data = [NSData dataWithBytes:&sockaddr4 length:sockaddr4len]; + host = [[self class] hostFromSockaddr4:&sockaddr4]; + port = [[self class] portFromSockaddr4:&sockaddr4]; + family = AF_INET; } - else if (socket6FD != SOCKET_NULL) + else { - struct sockaddr_in6 sockaddr6; - socklen_t sockaddr6len = sizeof(sockaddr6); - - if (getpeername(socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) - { - data = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; - host = [[self class] hostFromSockaddr6:&sockaddr6]; - port = [[self class] portFromSockaddr6:&sockaddr6]; - family = AF_INET6; - } - else - { - LogWarn(@"Error in getpeername: %@", [self errnoError]); - } + LogWarn(@"Error in getpeername: %@", [self errnoError]); } + } + else if (socket6FD != SOCKET_NULL) + { + struct sockaddr_in6 sockaddr6; + socklen_t sockaddr6len = sizeof(sockaddr6); - [cachedConnectedAddress release]; - [cachedConnectedHost release]; - - cachedConnectedAddress = [data retain]; - cachedConnectedHost = [host retain]; - cachedConnectedPort = port; - cachedConnectedFamily = family; + if (getpeername(socket6FD, (struct sockaddr *)&sockaddr6, &sockaddr6len) == 0) + { + data = [NSData dataWithBytes:&sockaddr6 length:sockaddr6len]; + host = [[self class] hostFromSockaddr6:&sockaddr6]; + port = [[self class] portFromSockaddr6:&sockaddr6]; + family = AF_INET6; + } + else + { + LogWarn(@"Error in getpeername: %@", [self errnoError]); + } } + + + cachedConnectedAddress = data; + cachedConnectedHost = host; + cachedConnectedPort = port; + cachedConnectedFamily = family; } - (NSData *)connectedAddress { - __block NSData *result; + __block NSData *result = nil; dispatch_block_t block = ^{ [self maybeUpdateCachedConnectedAddressInfo]; - result = [cachedConnectedAddress retain]; + result = cachedConnectedAddress; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (NSString *)connectedHost { - __block NSString *result; + __block NSString *result = nil; dispatch_block_t block = ^{ [self maybeUpdateCachedConnectedAddressInfo]; - result = [cachedConnectedHost retain]; + result = cachedConnectedHost; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); - return [result autorelease]; + return result; } - (uint16_t)connectedPort { - __block uint16_t result; + __block uint16_t result = 0; dispatch_block_t block = ^{ @@ -2499,23 +2600,23 @@ - (uint16_t)connectedPort result = cachedConnectedPort; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else - dispatch_sync(socketQueue, block); + dispatch_sync(socketQueue, AutoreleasedBlock(block)); return result; } - (BOOL)isConnected { - __block BOOL result; + __block BOOL result = NO; dispatch_block_t block = ^{ result = (flags & kDidConnect) ? YES : NO; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -2532,7 +2633,7 @@ - (BOOL)isClosed result = (flags & kDidCreateSockets) ? NO : YES; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -2542,7 +2643,7 @@ - (BOOL)isClosed - (BOOL)isIPv4 { - __block BOOL result; + __block BOOL result = NO; dispatch_block_t block = ^{ @@ -2556,7 +2657,7 @@ - (BOOL)isIPv4 } }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -2566,7 +2667,7 @@ - (BOOL)isIPv4 - (BOOL)isIPv6 { - __block BOOL result; + __block BOOL result = NO; dispatch_block_t block = ^{ @@ -2580,7 +2681,7 @@ - (BOOL)isIPv6 } }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -2655,7 +2756,6 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError if (![self preBind:&err]) { - [err retain]; return_from_block; } @@ -2669,7 +2769,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError if ((interface4 == nil) && (interface6 == nil)) { NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2680,7 +2780,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError if (isIPv4Disabled && (interface6 == nil)) { NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2688,7 +2788,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError if (isIPv6Disabled && (interface4 == nil)) { NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2704,7 +2804,6 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError { if (![self createSocket4:useIPv4 socket6:useIPv6 error:&err]) { - [err retain]; return_from_block; } } @@ -2721,7 +2820,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError [self closeSockets]; NSString *reason = @"Error in bind() function"; - err = [[self errnoErrorWithReason:reason] retain]; + err = [self errnoErrorWithReason:reason]; return_from_block; } @@ -2735,7 +2834,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError [self closeSockets]; NSString *reason = @"Error in bind() function"; - err = [[self errnoErrorWithReason:reason] retain]; + err = [self errnoErrorWithReason:reason]; return_from_block; } @@ -2752,7 +2851,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -2761,9 +2860,7 @@ - (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError LogError(@"Error binding to port/interface: %@", err); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -2779,7 +2876,6 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr if (![self preBind:&err]) { - [err retain]; return_from_block; } @@ -2790,7 +2886,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr if (addressFamily == AF_UNSPEC) { NSString *msg = @"A valid IPv4 or IPv6 address was not given"; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2804,7 +2900,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr if (isIPv4Disabled && localAddr4) { NSString *msg = @"IPv4 has been disabled and an IPv4 address was passed."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2812,7 +2908,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr if (isIPv6Disabled && localAddr6) { NSString *msg = @"IPv6 has been disabled and an IPv6 address was passed."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2828,7 +2924,6 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr { if (![self createSocket4:useIPv4 socket6:useIPv6 error:&err]) { - [err retain]; return_from_block; } } @@ -2847,7 +2942,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr [self closeSockets]; NSString *reason = @"Error in bind() function"; - err = [[self errnoErrorWithReason:reason] retain]; + err = [self errnoErrorWithReason:reason]; return_from_block; } @@ -2864,7 +2959,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr [self closeSockets]; NSString *reason = @"Error in bind() function"; - err = [[self errnoErrorWithReason:reason] retain]; + err = [self errnoErrorWithReason:reason]; return_from_block; } @@ -2881,7 +2976,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -2890,9 +2985,7 @@ - (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr LogError(@"Error binding to address: %@", err); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -2949,7 +3042,6 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e if (![self preConnect:&err]) { - [err retain]; return_from_block; } @@ -2958,7 +3050,7 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e if (host == nil) { NSString *msg = @"The host param is nil. Should be domain name or IP address string."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -2969,14 +3061,13 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e { if (![self createSockets:&err]) { - [err retain]; return_from_block; } } // Create special connect packet - GCDAsyncUdpSpecialPacket *packet = [[[GCDAsyncUdpSpecialPacket alloc] init] autorelease]; + GCDAsyncUdpSpecialPacket *packet = [[GCDAsyncUdpSpecialPacket alloc] init]; packet->resolveInProgress = YES; // Start asynchronous DNS resolve for host:port on background queue @@ -2991,8 +3082,8 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e packet->resolveInProgress = NO; - packet->addresses = [addresses retain]; - packet->error = [error retain]; + packet->addresses = addresses; + packet->error = error; [self maybeConnect]; }]; @@ -3007,7 +3098,7 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e result = YES; }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -3016,9 +3107,7 @@ - (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)e LogError(@"Error connecting to host/port: %@", err); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -3034,7 +3123,6 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr if (![self preConnect:&err]) { - [err retain]; return_from_block; } @@ -3043,7 +3131,7 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr if (remoteAddr == nil) { NSString *msg = @"The address param is nil. Should be a valid address."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -3054,7 +3142,6 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr { if (![self createSockets:&err]) { - [err retain]; return_from_block; } } @@ -3062,11 +3149,11 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr // The remoteAddr parameter could be of type NSMutableData. // So we copy it to be safe. - NSData *address = [[remoteAddr copy] autorelease]; + NSData *address = [remoteAddr copy]; NSArray *addresses = [NSArray arrayWithObject:address]; - GCDAsyncUdpSpecialPacket *packet = [[[GCDAsyncUdpSpecialPacket alloc] init] autorelease]; - packet->addresses = [addresses retain]; + GCDAsyncUdpSpecialPacket *packet = [[GCDAsyncUdpSpecialPacket alloc] init]; + packet->addresses = addresses; // Updates flags, add connect packet to send queue, and pump send queue @@ -3078,7 +3165,7 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr result = YES; }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -3087,9 +3174,7 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr LogError(@"Error connecting to address: %@", err); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -3097,7 +3182,7 @@ - (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr - (void)maybeConnect { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); BOOL sendQueueReady = [currentSend isKindOfClass:[GCDAsyncUdpSpecialPacket class]]; @@ -3108,7 +3193,7 @@ - (void)maybeConnect if (connectPacket->resolveInProgress) { - LogVerbose(@"%@: Waiting for DNS resolve..."); + LogVerbose(@"Waiting for DNS resolve..."); } else { @@ -3138,11 +3223,8 @@ - (void)maybeConnect flags |= kDidBind; flags |= kDidConnect; - [cachedConnectedAddress release]; - [cachedConnectedHost release]; - - cachedConnectedAddress = [address retain]; - cachedConnectedHost = [[[self class] hostFromAddress:address] retain]; + cachedConnectedAddress = address; + cachedConnectedHost = [[self class] hostFromAddress:address]; cachedConnectedPort = [[self class] portFromAddress:address]; cachedConnectedFamily = addressFamily; @@ -3165,7 +3247,7 @@ - (void)maybeConnect - (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); int status = connect(socket4FD, (struct sockaddr *)[address4 bytes], (socklen_t)[address4 length]); if (status != 0) @@ -3185,7 +3267,7 @@ - (BOOL)connectWithAddress4:(NSData *)address4 error:(NSError **)errPtr - (BOOL)connectWithAddress6:(NSData *)address6 error:(NSError **)errPtr { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); int status = connect(socket6FD, (struct sockaddr *)[address6 bytes], (socklen_t)[address6 length]); if (status != 0) @@ -3242,6 +3324,26 @@ - (BOOL)joinMulticastGroup:(NSString *)group error:(NSError **)errPtr } - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr +{ + // IP_ADD_MEMBERSHIP == IPV6_JOIN_GROUP + return [self performMulticastRequest:IP_ADD_MEMBERSHIP forGroup:group onInterface:interface error:errPtr]; +} + +- (BOOL)leaveMulticastGroup:(NSString *)group error:(NSError **)errPtr +{ + return [self leaveMulticastGroup:group onInterface:nil error:errPtr]; +} + +- (BOOL)leaveMulticastGroup:(NSString *)group onInterface:(NSString *)interface error:(NSError **)errPtr +{ + // IP_DROP_MEMBERSHIP == IPV6_LEAVE_GROUP + return [self performMulticastRequest:IP_DROP_MEMBERSHIP forGroup:group onInterface:interface error:errPtr]; +} + +- (BOOL)performMulticastRequest:(int)requestType + forGroup:(NSString *)group + onInterface:(NSString *)interface + error:(NSError **)errPtr { __block BOOL result = NO; __block NSError *err = nil; @@ -3252,7 +3354,6 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e if (![self preJoin:&err]) { - [err retain]; return_from_block; } @@ -3266,7 +3367,7 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e if ((groupAddr4 == nil) && (groupAddr6 == nil)) { NSString *msg = @"Unknown group. Specify valid group IP address."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -3281,7 +3382,7 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e if ((interfaceAddr4 == nil) && (interfaceAddr6 == nil)) { NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address."; - err = [[self badParamError:msg] retain]; + err = [self badParamError:msg]; return_from_block; } @@ -3297,10 +3398,10 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e imreq.imr_multiaddr = nativeGroup->sin_addr; imreq.imr_interface = nativeIface->sin_addr; - int status = setsockopt(socket4FD, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&imreq, sizeof(imreq)); + int status = setsockopt(socket4FD, IPPROTO_IP, requestType, (const void *)&imreq, sizeof(imreq)); if (status != 0) { - err = [[self errnoErrorWithReason:@"Error in setsockopt() function"] retain]; + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; return_from_block; } @@ -3310,8 +3411,7 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e result = YES; } - - if ((socket6FD != SOCKET_NULL) && groupAddr6 && interfaceAddr6) + else if ((socket6FD != SOCKET_NULL) && groupAddr6 && interfaceAddr6) { const struct sockaddr_in6 *nativeGroup = (struct sockaddr_in6 *)[groupAddr6 bytes]; @@ -3319,10 +3419,10 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e imreq.ipv6mr_multiaddr = nativeGroup->sin6_addr; imreq.ipv6mr_interface = [self indexOfInterfaceAddr6:interfaceAddr6]; - int status = setsockopt(socket6FD, IPPROTO_IP, IPV6_JOIN_GROUP, (const void *)&imreq, sizeof(imreq)); + int status = setsockopt(socket6FD, IPPROTO_IPV6, requestType, (const void *)&imreq, sizeof(imreq)); if (status != 0) { - err = [[self errnoErrorWithReason:@"Error in setsockopt() function"] retain]; + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; return_from_block; } @@ -3332,18 +3432,23 @@ - (BOOL)joinMulticastGroup:(NSString *)group onInterface:(NSString *)interface e result = YES; } + else + { + NSString *msg = @"Socket, group, and interface do not have matching IP versions"; + err = [self badParamError:msg]; + + return_from_block; + } }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -3361,7 +3466,6 @@ - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr if (![self preOp:&err]) { - [err retain]; return_from_block; } @@ -3369,7 +3473,6 @@ - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr { if (![self createSockets:&err]) { - [err retain]; return_from_block; } } @@ -3381,7 +3484,7 @@ - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr if (error) { - err = [[self errnoErrorWithReason:@"Error in setsockopt() function"] retain]; + err = [self errnoErrorWithReason:@"Error in setsockopt() function"]; return_from_block; } @@ -3393,15 +3496,13 @@ - (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -3431,10 +3532,8 @@ - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)ta [sendQueue addObject:packet]; [self maybeDequeueSend]; - }}); - [packet release]; } - (void)sendData:(NSData *)data @@ -3462,13 +3561,13 @@ - (void)sendData:(NSData *)data packet->resolveInProgress = NO; - packet->addresses = [addresses retain]; - packet->error = [error retain]; + packet->resolvedAddresses = addresses; + packet->resolveError = error; if (packet == currentSend) { LogVerbose(@"currentSend - address resolved"); - [self doSend]; + [self doPreSend]; } }]; @@ -3479,7 +3578,6 @@ - (void)sendData:(NSData *)data }}); - [packet release]; } - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag @@ -3493,22 +3591,60 @@ - (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTi } GCDAsyncUdpSendPacket *packet = [[GCDAsyncUdpSendPacket alloc] initWithData:data timeout:timeout tag:tag]; - packet->addresses = [[NSArray alloc] initWithObjects:remoteAddr, nil]; + packet->addressFamily = [GCDAsyncUdpSocket familyFromAddress:remoteAddr]; + packet->address = remoteAddr; dispatch_async(socketQueue, ^{ @autoreleasepool { [sendQueue addObject:packet]; [self maybeDequeueSend]; - }}); +} + +- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue +{ + [self setSendFilter:filterBlock withQueue:filterQueue isAsynchronous:YES]; +} + +- (void)setSendFilter:(GCDAsyncUdpSocketSendFilterBlock)filterBlock + withQueue:(dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous +{ + GCDAsyncUdpSocketSendFilterBlock newFilterBlock = NULL; + dispatch_queue_t newFilterQueue = NULL; + + if (filterBlock) + { + NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block."); + + newFilterBlock = [filterBlock copy]; + newFilterQueue = filterQueue; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(newFilterQueue); + #endif + } + + dispatch_block_t block = ^{ + + #if !OS_OBJECT_USE_OBJC + if (sendFilterQueue) dispatch_release(sendFilterQueue); + #endif + + sendFilterBlock = newFilterBlock; + sendFilterQueue = newFilterQueue; + sendFilterAsync = isAsynchronous; + }; - [packet release]; + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_async(socketQueue, block); } - (void)maybeDequeueSend { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); // If we don't have a send operation already in progress if (currentSend == nil) @@ -3527,7 +3663,7 @@ - (void)maybeDequeueSend while ([sendQueue count] > 0) { // Dequeue the next object in the queue - currentSend = [[sendQueue objectAtIndex:0] retain]; + currentSend = [sendQueue objectAtIndex:0]; [sendQueue removeObjectAtIndex:0]; if ([currentSend isKindOfClass:[GCDAsyncUdpSpecialPacket class]]) @@ -3536,24 +3672,20 @@ - (void)maybeDequeueSend return; // The maybeConnect method, if it connects, will invoke this method again } - else if (currentSend->error) + else if (currentSend->resolveError) { // Notify delegate - [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:currentSend->error]; + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:currentSend->resolveError]; // Clear currentSend - [currentSend release]; currentSend = nil; continue; } else { - // Setup send timer (if needed) - [self setupSendTimerWithTimeout:currentSend->timeout]; - - // Immediately send (if possible) - [self doSend]; + // Start preprocessing checks on the send packet + [self doPreSend]; break; } @@ -3566,175 +3698,262 @@ - (void)maybeDequeueSend } } -- (void)doSend +/** + * This method is called after a sendPacket has been dequeued. + * It performs various preprocessing checks on the packet, + * and queries the sendFilter (if set) to determine if the packet can be sent. + * + * If the packet passes all checks, it will be passed on to the doSend method. +**/ +- (void)doPreSend { LogTrace(); - if (currentSend == nil) - { - // If the sendXSource is firing, we need to pause it - // or else it will continue to fire over and over again. - // - // If the sendXSource is not firing, - // we want it to continue monitoring the socket. - - LogVerbose(@"Nothing to send"); - - if (flags & kSock4CanAcceptBytes) { - [self suspendSend4Source]; - } - if (flags & kSock6CanAcceptBytes) { - [self suspendSend6Source]; - } - - return; - } - - ssize_t result = 0; + // + // 1. Check for problems with send packet + // BOOL waitingForResolve = NO; - BOOL waitingForSocket = NO; - BOOL checkResult = NO; - BOOL done = NO; + NSError *error = nil; if (flags & kDidConnect) { - // // Connected socket - // - if (currentSend->resolveInProgress || currentSend->addresses || currentSend->error) + if (currentSend->resolveInProgress || currentSend->resolvedAddresses || currentSend->resolveError) { NSString *msg = @"Cannot specify destination of packet for connected socket"; - NSError *error = [self badConfigError:msg]; - - [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:error]; - done = YES; + error = [self badConfigError:msg]; } else { - const void *buffer = [currentSend->buffer bytes]; - size_t length = (size_t)[currentSend->buffer length]; - - if (socket4FD != SOCKET_NULL) - result = send(socket4FD, buffer, length, 0); - else - result = send(socket6FD, buffer, length, 0); - - LogVerbose(@"send(socket%@FD) = %i (connected)", (socket4FD != SOCKET_NULL ? @"4" : @"6"), (int)result); - - checkResult = YES; + currentSend->address = cachedConnectedAddress; + currentSend->addressFamily = cachedConnectedFamily; } } else { - // // Non-Connected socket - // if (currentSend->resolveInProgress) { // We're waiting for the packet's destination to be resolved. waitingForResolve = YES; } - else if (currentSend->error) - { - [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:currentSend->error]; - done = YES; - } - else if (currentSend->addresses == nil) + else if (currentSend->resolveError) { - NSString *msg = @"You must specify destination of packet for a non-connected socket"; - NSError *error = [self badConfigError:msg]; - - [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:error]; - done = YES; + error = currentSend->resolveError; } - else + else if (currentSend->address == nil) { - NSData *addr = nil; - NSError *err = nil; - - int af = [self getAddress:&addr error:&err fromAddresses:currentSend->addresses]; - - if (err) + if (currentSend->resolvedAddresses == nil) { - [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:err]; - done = YES; + NSString *msg = @"You must specify destination of packet for a non-connected socket"; + error = [self badConfigError:msg]; } else { - const void *buffer = [currentSend->buffer bytes]; - size_t length = (size_t)[currentSend->buffer length]; + // Pick the proper address to use (out of possibly several resolved addresses) - const void *dst = [addr bytes]; - socklen_t dstSize = (socklen_t)[addr length]; - - if (af == AF_INET) - result = sendto(socket4FD, buffer, length, 0, dst, dstSize); - else - result = sendto(socket6FD, buffer, length, 0, dst, dstSize); + NSData *address = nil; + int addressFamily = AF_UNSPEC; - LogVerbose(@"send(socket%@FD) = %i (non-connected)", (af == AF_INET ? @"4" : @"6"), (int)result); + addressFamily = [self getAddress:&address error:&error fromAddresses:currentSend->resolvedAddresses]; - checkResult = YES; + currentSend->address = address; + currentSend->addressFamily = addressFamily; } } } - NSError *error = nil; - - if (checkResult) + if (waitingForResolve) { - // If the socket wasn't bound before, it is now + // We're waiting for the packet's destination to be resolved. - if ((flags & kDidBind) == 0) - { - flags |= kDidBind; + LogVerbose(@"currentSend - waiting for address resolve"); + + if (flags & kSock4CanAcceptBytes) { + [self suspendSend4Source]; + } + if (flags & kSock6CanAcceptBytes) { + [self suspendSend6Source]; } - // From the send() & sendto() manpage: - // - // Upon successful completion, the number of bytes which were sent is returned. - // Otherwise, -1 is returned and the global variable errno is set to indicate the error. + return; + } + + if (error) + { + // Unable to send packet due to some error. + // Notify delegate and move on. - if (result == 0) + [self notifyDidNotSendDataWithTag:currentSend->tag dueToError:error]; + [self endCurrentSend]; + [self maybeDequeueSend]; + + return; + } + + // + // 2. Query sendFilter (if applicable) + // + + if (sendFilterBlock && sendFilterQueue) + { + // Query sendFilter + + if (sendFilterAsync) { - waitingForSocket = YES; + // Scenario 1 of 3 - Need to asynchronously query sendFilter + + currentSend->filterInProgress = YES; + GCDAsyncUdpSendPacket *sendPacket = currentSend; + + dispatch_async(sendFilterQueue, ^{ @autoreleasepool { + + BOOL allowed = sendFilterBlock(sendPacket->buffer, sendPacket->address, sendPacket->tag); + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + sendPacket->filterInProgress = NO; + if (sendPacket == currentSend) + { + if (allowed) + { + [self doSend]; + } + else + { + LogVerbose(@"currentSend - silently dropped by sendFilter"); + + [self notifyDidSendDataWithTag:currentSend->tag]; + [self endCurrentSend]; + [self maybeDequeueSend]; + } + } + }}); + }}); } - else if (result < 0) + else { - if (errno == EAGAIN) { - waitingForSocket = YES; + // Scenario 2 of 3 - Need to synchronously query sendFilter + + __block BOOL allowed = YES; + + dispatch_sync(sendFilterQueue, ^{ @autoreleasepool { + + allowed = sendFilterBlock(currentSend->buffer, currentSend->address, currentSend->tag); + }}); + + if (allowed) + { + [self doSend]; } - else { - error = [self errnoErrorWithReason:@"Error in send() function."]; + else + { + LogVerbose(@"currentSend - silently dropped by sendFilter"); + + [self notifyDidSendDataWithTag:currentSend->tag]; + [self endCurrentSend]; + [self maybeDequeueSend]; } } + } + else // if (!sendFilterBlock || !sendFilterQueue) + { + // Scenario 3 of 3 - No sendFilter. Just go straight into sending. + + [self doSend]; + } +} + +/** + * This method performs the actual sending of data in the currentSend packet. + * It should only be called if the +**/ +- (void)doSend +{ + LogTrace(); + + NSAssert(currentSend != nil, @"Invalid logic"); + + // Perform the actual send + + ssize_t result = 0; + + if (flags & kDidConnect) + { + // Connected socket + + const void *buffer = [currentSend->buffer bytes]; + size_t length = (size_t)[currentSend->buffer length]; + + if (currentSend->addressFamily == AF_INET) + { + result = send(socket4FD, buffer, length, 0); + LogVerbose(@"send(socket4FD) = %d", result); + } else { - [self notifyDidSendDataWithTag:currentSend->tag]; - done = YES; + result = send(socket6FD, buffer, length, 0); + LogVerbose(@"send(socket6FD) = %d", result); } } - - if (waitingForResolve) + else { - // We're waiting for the packet's destination to be resolved. + // Non-Connected socket - LogVerbose(@"currentSend - waiting for address resolve"); + const void *buffer = [currentSend->buffer bytes]; + size_t length = (size_t)[currentSend->buffer length]; - if (flags & kSock4CanAcceptBytes) { - [self suspendSend4Source]; + const void *dst = [currentSend->address bytes]; + socklen_t dstSize = (socklen_t)[currentSend->address length]; + + if (currentSend->addressFamily == AF_INET) + { + result = sendto(socket4FD, buffer, length, 0, dst, dstSize); + LogVerbose(@"sendto(socket4FD) = %d", result); } - if (flags & kSock6CanAcceptBytes) { - [self suspendSend6Source]; + else + { + result = sendto(socket6FD, buffer, length, 0, dst, dstSize); + LogVerbose(@"sendto(socket6FD) = %d", result); } } - else if (waitingForSocket) + + // If the socket wasn't bound before, it is now + + if ((flags & kDidBind) == 0) + { + flags |= kDidBind; + } + + // Check the results. + // + // From the send() & sendto() manpage: + // + // Upon successful completion, the number of bytes which were sent is returned. + // Otherwise, -1 is returned and the global variable errno is set to indicate the error. + + BOOL waitingForSocket = NO; + NSError *socketError = nil; + + if (result == 0) + { + waitingForSocket = YES; + } + else if (result < 0) + { + if (errno == EAGAIN) + waitingForSocket = YES; + else + socketError = [self errnoErrorWithReason:@"Error in send() function."]; + } + + if (waitingForSocket) { // Not enough room in the underlying OS socket send buffer. - // Waiting for a notification. + // Wait for a notification of available space. LogVerbose(@"currentSend - waiting for socket"); @@ -3744,30 +3963,47 @@ - (void)doSend if (!(flags & kSock6CanAcceptBytes)) { [self resumeSend6Source]; } + + if ((sendTimer == NULL) && (currentSend->timeout >= 0.0)) + { + // Unable to send packet right away. + // Start timer to timeout the send operation. + + [self setupSendTimerWithTimeout:currentSend->timeout]; + } } - else if (error) + else if (socketError) { - [self closeWithError:error]; + [self closeWithError:socketError]; } - else if (done) + else // done { + [self notifyDidSendDataWithTag:currentSend->tag]; [self endCurrentSend]; [self maybeDequeueSend]; } } +/** + * Releases all resources associated with the currentSend. +**/ - (void)endCurrentSend { if (sendTimer) { dispatch_source_cancel(sendTimer); + #if !OS_OBJECT_USE_OBJC + dispatch_release(sendTimer); + #endif sendTimer = NULL; } - [currentSend release]; currentSend = nil; } +/** + * Performs the operations to timeout the current send operation, and move on. +**/ - (void)doSendTimeout { LogTrace(); @@ -3777,38 +4013,80 @@ - (void)doSendTimeout [self maybeDequeueSend]; } +/** + * Sets up a timer that fires to timeout the current send operation. + * This method should only be called once per send packet. +**/ - (void)setupSendTimerWithTimeout:(NSTimeInterval)timeout { NSAssert(sendTimer == NULL, @"Invalid logic"); + NSAssert(timeout >= 0.0, @"Invalid logic"); - if (timeout >= 0.0) - { - LogTrace(); - - sendTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); - - dispatch_source_set_event_handler(sendTimer, ^{ @autoreleasepool { - - [self doSendTimeout]; - }}); - - dispatch_source_t theSendTimer = sendTimer; - dispatch_source_set_cancel_handler(sendTimer, ^{ - LogVerbose(@"dispatch_release(sendTimer)"); - dispatch_release(theSendTimer); - }); - - dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (timeout * NSEC_PER_SEC)); + LogTrace(); + + sendTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, socketQueue); + + dispatch_source_set_event_handler(sendTimer, ^{ @autoreleasepool { - dispatch_source_set_timer(sendTimer, tt, DISPATCH_TIME_FOREVER, 0); - dispatch_resume(sendTimer); - } + [self doSendTimeout]; + }}); + + dispatch_time_t tt = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); + + dispatch_source_set_timer(sendTimer, tt, DISPATCH_TIME_FOREVER, 0); + dispatch_resume(sendTimer); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma mark Receiving //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +- (BOOL)receiveOnce:(NSError **)errPtr +{ + LogTrace(); + + __block BOOL result = NO; + __block NSError *err = nil; + + dispatch_block_t block = ^{ + + if ((flags & kReceiveOnce) == 0) + { + if ((flags & kDidCreateSockets) == 0) + { + NSString *msg = @"Must bind socket before you can receive data. " + @"You can do this explicitly via bind, or implicitly via connect or by sending data."; + + err = [self badConfigError:msg]; + return_from_block; + } + + flags |= kReceiveOnce; // Enable + flags &= ~kReceiveContinuous; // Disable + + dispatch_async(socketQueue, ^{ @autoreleasepool { + + [self doReceive]; + }}); + } + + result = YES; + }; + + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); + + if (err) + LogError(@"Error in beginReceiving: %@", err); + + if (errPtr) + *errPtr = err; + + return result; +} + - (BOOL)beginReceiving:(NSError **)errPtr { LogTrace(); @@ -3818,18 +4096,19 @@ - (BOOL)beginReceiving:(NSError **)errPtr dispatch_block_t block = ^{ - if ((flags & kReceive) == 0) + if ((flags & kReceiveContinuous) == 0) { if ((flags & kDidCreateSockets) == 0) { NSString *msg = @"Must bind socket before you can receive data. " @"You can do this explicitly via bind, or implicitly via connect or by sending data."; - err = [[self badConfigError:msg] retain]; + err = [self badConfigError:msg]; return_from_block; } - flags |= kReceive; + flags |= kReceiveContinuous; // Enable + flags &= ~kReceiveOnce; // Disable dispatch_async(socketQueue, ^{ @autoreleasepool { @@ -3840,7 +4119,7 @@ - (BOOL)beginReceiving:(NSError **)errPtr result = YES; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -3849,9 +4128,7 @@ - (BOOL)beginReceiving:(NSError **)errPtr LogError(@"Error in beginReceiving: %@", err); if (errPtr) - *errPtr = [err autorelease]; - else - [err release]; + *errPtr = err; return result; } @@ -3862,7 +4139,8 @@ - (void)pauseReceiving dispatch_block_t block = ^{ - flags &= ~kReceive; + flags &= ~kReceiveOnce; // Disable + flags &= ~kReceiveContinuous; // Disable if (socket4FDBytesAvailable > 0) { [self suspendReceive4Source]; @@ -3872,44 +4150,47 @@ - (void)pauseReceiving } }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); } -- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)inFilterBlock withQueue:(dispatch_queue_t)inFilterQueue +- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock withQueue:(dispatch_queue_t)filterQueue +{ + [self setReceiveFilter:filterBlock withQueue:filterQueue isAsynchronous:YES]; +} + +- (void)setReceiveFilter:(GCDAsyncUdpSocketReceiveFilterBlock)filterBlock + withQueue:(dispatch_queue_t)filterQueue + isAsynchronous:(BOOL)isAsynchronous { - GCDAsyncUdpSocketReceiveFilterBlock newFilterBlock; - dispatch_queue_t newFilterQueue; + GCDAsyncUdpSocketReceiveFilterBlock newFilterBlock = NULL; + dispatch_queue_t newFilterQueue = NULL; - if (inFilterBlock) + if (filterBlock) { - NSAssert(inFilterQueue, @"Must provide a dispatch_queue in which to run the filter block."); + NSAssert(filterQueue, @"Must provide a dispatch_queue in which to run the filter block."); - newFilterBlock = [inFilterBlock copy]; - newFilterQueue = inFilterQueue; + newFilterBlock = [filterBlock copy]; + newFilterQueue = filterQueue; + #if !OS_OBJECT_USE_OBJC dispatch_retain(newFilterQueue); - } - else - { - newFilterBlock = NULL; - newFilterQueue = NULL; + #endif } dispatch_block_t block = ^{ - if (filterBlock) - [filterBlock release]; - - if (filterQueue) - dispatch_release(filterQueue); + #if !OS_OBJECT_USE_OBJC + if (receiveFilterQueue) dispatch_release(receiveFilterQueue); + #endif - filterBlock = newFilterBlock; - filterQueue = newFilterQueue; + receiveFilterBlock = newFilterBlock; + receiveFilterQueue = newFilterQueue; + receiveFilterAsync = isAsynchronous; }; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -3919,7 +4200,7 @@ - (void)doReceive { LogTrace(); - if ((flags & kReceive) == 0) + if ((flags & (kReceiveOnce | kReceiveContinuous)) == 0) { LogVerbose(@"Receiving is paused..."); @@ -3933,6 +4214,20 @@ - (void)doReceive return; } + if ((flags & kReceiveOnce) && (pendingFilterOperations > 0)) + { + LogVerbose(@"Receiving is temporarily paused (pending filter operations)..."); + + if (socket4FDBytesAvailable > 0) { + [self suspendReceive4Source]; + } + if (socket6FDBytesAvailable > 0) { + [self suspendReceive6Source]; + } + + return; + } + if ((socket4FDBytesAvailable == 0) && (socket6FDBytesAvailable == 0)) { LogVerbose(@"No data available to receive..."); @@ -4006,12 +4301,12 @@ - (void)doReceive if (result > 0) { - if (result >= socket4FDBytesAvailable) + if ((size_t)result >= socket4FDBytesAvailable) socket4FDBytesAvailable = 0; else socket4FDBytesAvailable -= result; - if (result != bufSize) { + if ((size_t)result != bufSize) { buf = realloc(buf, result); } @@ -4021,6 +4316,7 @@ - (void)doReceive else { LogVerbose(@"recvfrom(socket4FD) = %@", [self errnoError]); + socket4FDBytesAvailable = 0; free(buf); } } @@ -4040,12 +4336,12 @@ - (void)doReceive if (result > 0) { - if (result >= socket6FDBytesAvailable) + if ((size_t)result >= socket6FDBytesAvailable) socket6FDBytesAvailable = 0; else socket6FDBytesAvailable -= result; - if (result != bufSize) { + if ((size_t)result != bufSize) { buf = realloc(buf, result); } @@ -4055,13 +4351,17 @@ - (void)doReceive else { LogVerbose(@"recvfrom(socket6FD) = %@", [self errnoError]); + socket6FDBytesAvailable = 0; free(buf); } } BOOL waitingForSocket = NO; - NSError *error = nil; + BOOL notifiedDelegate = NO; + BOOL ignored = NO; + + NSError *socketError = nil; if (result == 0) { @@ -4072,12 +4372,10 @@ - (void)doReceive if (errno == EAGAIN) waitingForSocket = YES; else - error = [self errnoErrorWithReason:@"Error in recvfrom() function"]; + socketError = [self errnoErrorWithReason:@"Error in recvfrom() function"]; } else { - BOOL ignored = NO; - if (flags & kDidConnect) { if (addr4 && ![self isConnectedToAddress4:addr4]) @@ -4090,50 +4388,120 @@ - (void)doReceive if (!ignored) { - if (filterBlock && filterQueue) + if (receiveFilterBlock && receiveFilterQueue) { // Run data through filter, and if approved, notify delegate - dispatch_async(filterQueue, ^{ @autoreleasepool { - - id filterContext = nil; + + __block id filterContext = nil; + __block BOOL allowed = NO; + + if (receiveFilterAsync) + { + pendingFilterOperations++; + dispatch_async(receiveFilterQueue, ^{ @autoreleasepool { + + allowed = receiveFilterBlock(data, addr, &filterContext); + + // Transition back to socketQueue to get the current delegate / delegateQueue + dispatch_async(socketQueue, ^{ @autoreleasepool { + + pendingFilterOperations--; + + if (allowed) + { + [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; + } + else + { + LogVerbose(@"received packet silently dropped by receiveFilter"); + } + + if (flags & kReceiveOnce) + { + if (allowed) + { + // The delegate has been notified, + // so our receive once operation has completed. + flags &= ~kReceiveOnce; + } + else if (pendingFilterOperations == 0) + { + // All pending filter operations have completed, + // and none were allowed through. + // Our receive once operation hasn't completed yet. + [self doReceive]; + } + } + }}); + }}); + } + else // if (!receiveFilterAsync) + { + dispatch_sync(receiveFilterQueue, ^{ @autoreleasepool { + + allowed = receiveFilterBlock(data, addr, &filterContext); + }}); - if (filterBlock(data, addr, &filterContext)) + if (allowed) { - // Transition back to socketQueue to get the current delegate / delegateQueue - dispatch_async(socketQueue, ^{ - [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; - }); + [self notifyDidReceiveData:data fromAddress:addr withFilterContext:filterContext]; + notifiedDelegate = YES; } - - }}); + else + { + LogVerbose(@"received packet silently dropped by receiveFilter"); + ignored = YES; + } + } } - else + else // if (!receiveFilterBlock || !receiveFilterQueue) { - // Notify delegate [self notifyDidReceiveData:data fromAddress:addr withFilterContext:nil]; + notifiedDelegate = YES; } } } if (waitingForSocket) { - // Not enough room in the underlying OS socket send buffer. - // Wait for a notification. + // Wait for a notification of available data. - if (!(flags & kSock4CanAcceptBytes)) { - [self resumeSend4Source]; + if (socket4FDBytesAvailable == 0) { + [self resumeReceive4Source]; } - if (!(flags & kSock6CanAcceptBytes)) { - [self resumeSend6Source]; + if (socket6FDBytesAvailable == 0) { + [self resumeReceive6Source]; } } - else if (error) + else if (socketError) { - [self closeWithError:error]; + [self closeWithError:socketError]; } else { - [self doReceive]; + if (flags & kReceiveContinuous) + { + // Continuous receive mode + [self doReceive]; + } + else + { + // One-at-a-time receive mode + if (notifiedDelegate) + { + // The delegate has been notified (no set filter). + // So our receive once operation has completed. + flags &= ~kReceiveOnce; + } + else if (ignored) + { + [self doReceive]; + } + else + { + // Waiting on asynchronous receive filter... + } + } } } @@ -4152,7 +4520,7 @@ - (void)closeWithError:(NSError *)error { LogVerbose(@"closeWithError: %@", error); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); if (currentSend) [self endCurrentSend]; @@ -4186,7 +4554,7 @@ - (void)close [self closeWithError:nil]; }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_sync(socketQueue, block); @@ -4206,7 +4574,7 @@ - (void)closeAfterSending } }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -4220,6 +4588,9 @@ - (void)closeAfterSending static NSThread *listenerThread; ++ (void)ignore:(id)_ +{} + + (void)startListenerThreadIfNeeded { static dispatch_once_t predicate; @@ -4236,7 +4607,7 @@ + (void)listenerThread { @autoreleasepool { - [[NSThread currentThread] setName:@"GCDAsyncUdpSocket-CFStream"]; + [[NSThread currentThread] setName:GCDAsyncUdpSocketThreadName]; LogInfo(@"ListenerThread: Started"); @@ -4297,7 +4668,7 @@ + (void)removeStreamListener:(GCDAsyncUdpSocket *)asyncUdpSocket static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void *pInfo) { @autoreleasepool { - GCDAsyncUdpSocket *asyncUdpSocket = [(GCDAsyncUdpSocket *)pInfo retain]; + GCDAsyncUdpSocket *asyncUdpSocket = (__bridge GCDAsyncUdpSocket *)pInfo; switch(type) { @@ -4314,10 +4685,10 @@ static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, case kCFStreamEventErrorOccurred: case kCFStreamEventEndEncountered: { - NSError *error = NSMakeCollectable(CFReadStreamCopyError(stream)); + NSError *error = (__bridge_transfer NSError *)CFReadStreamCopyError(stream); if (error == nil && type == kCFStreamEventEndEncountered) { - error = [[asyncUdpSocket socketClosedError] retain]; + error = [asyncUdpSocket socketClosedError]; } dispatch_async(asyncUdpSocket->socketQueue, ^{ @autoreleasepool { @@ -4336,7 +4707,6 @@ static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, }}); - [error release]; break; } default: @@ -4344,15 +4714,13 @@ static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType type, LogCError(@"CFReadStreamCallback - UnknownType: %i", (int)type); } } - - [asyncUdpSocket release]; } } static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void *pInfo) { @autoreleasepool { - GCDAsyncUdpSocket *asyncUdpSocket = [(GCDAsyncUdpSocket *)pInfo retain]; + GCDAsyncUdpSocket *asyncUdpSocket = (__bridge GCDAsyncUdpSocket *)pInfo; switch(type) { @@ -4369,10 +4737,10 @@ static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType typ case kCFStreamEventErrorOccurred: case kCFStreamEventEndEncountered: { - NSError *error = NSMakeCollectable(CFWriteStreamCopyError(stream)); + NSError *error = (__bridge_transfer NSError *)CFWriteStreamCopyError(stream); if (error == nil && type == kCFStreamEventEndEncountered) { - error = [[asyncUdpSocket socketClosedError] retain]; + error = [asyncUdpSocket socketClosedError]; } dispatch_async(asyncUdpSocket->socketQueue, ^{ @autoreleasepool { @@ -4391,7 +4759,6 @@ static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType typ }}); - [error release]; break; } default: @@ -4399,15 +4766,13 @@ static void CFWriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType typ LogCError(@"CFWriteStreamCallback - UnknownType: %i", (int)type); } } - - [asyncUdpSocket release]; } } - (BOOL)createReadAndWriteStreams:(NSError **)errPtr { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSError *err = nil; @@ -4493,13 +4858,13 @@ - (BOOL)registerForStreamCallbacks:(NSError **)errPtr { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); NSError *err = nil; streamContext.version = 0; - streamContext.info = self; + streamContext.info = (__bridge void *)self; streamContext.retain = nil; streamContext.release = nil; streamContext.copyDescription = nil; @@ -4570,7 +4935,7 @@ - (BOOL)addStreamsToRunLoop:(NSError **)errPtr { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); if (!(flags & kAddedStreamListener)) @@ -4591,7 +4956,7 @@ - (BOOL)openStreams:(NSError **)errPtr { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); NSAssert(readStream4 || writeStream4 || readStream6 || writeStream6, @"Read/Write streams are null"); NSError *err = nil; @@ -4630,7 +4995,7 @@ - (BOOL)openStreams:(NSError **)errPtr - (void)removeStreamsFromRunLoop { LogTrace(); - NSAssert(dispatch_get_current_queue() == socketQueue, @"Must be dispatched on socketQueue"); + NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue"); if (flags & kAddedStreamListener) { @@ -4692,7 +5057,7 @@ - (void)applicationWillEnterForeground:(NSNotification *)notification [self resumeReceive6Source]; }}; - if (dispatch_get_current_queue() == socketQueue) + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) block(); else dispatch_async(socketQueue, block); @@ -4702,14 +5067,34 @@ - (void)applicationWillEnterForeground:(NSNotification *)notification #pragma mark Advanced //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * See header file for big discussion of this method. + **/ +- (void)markSocketQueueTargetQueue:(dispatch_queue_t)socketNewTargetQueue +{ + void *nonNullUnusedPointer = (__bridge void *)self; + dispatch_queue_set_specific(socketNewTargetQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL); +} + +/** + * See header file for big discussion of this method. + **/ +- (void)unmarkSocketQueueTargetQueue:(dispatch_queue_t)socketOldTargetQueue +{ + dispatch_queue_set_specific(socketOldTargetQueue, IsOnSocketQueueOrTargetQueueKey, NULL, NULL); +} + - (void)performBlock:(dispatch_block_t)block { - dispatch_sync(socketQueue, block); + if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) + block(); + else + dispatch_sync(socketQueue, block); } - (int)socketFD { - if (dispatch_get_current_queue() != socketQueue) + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", THIS_FILE, THIS_METHOD); @@ -4724,7 +5109,7 @@ - (int)socketFD - (int)socket4FD { - if (dispatch_get_current_queue() != socketQueue) + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", THIS_FILE, THIS_METHOD); @@ -4736,7 +5121,7 @@ - (int)socket4FD - (int)socket6FD { - if (dispatch_get_current_queue() != socketQueue) + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", THIS_FILE, THIS_METHOD); @@ -4750,7 +5135,7 @@ - (int)socket6FD - (CFReadStreamRef)readStream { - if (dispatch_get_current_queue() != socketQueue) + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", THIS_FILE, THIS_METHOD); @@ -4774,7 +5159,7 @@ - (CFReadStreamRef)readStream - (CFWriteStreamRef)writeStream { - if (dispatch_get_current_queue() != socketQueue) + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", THIS_FILE, THIS_METHOD); @@ -4796,13 +5181,16 @@ - (CFWriteStreamRef)writeStream - (BOOL)enableBackgroundingOnSockets { - if (dispatch_get_current_queue() != socketQueue) + if (! dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey)) { LogWarn(@"%@: %@ - Method only available from within the context of a performBlock: invocation", THIS_FILE, THIS_METHOD); return NO; } + // Why is this commented out? + // See comments below. + // NSError *err = nil; // if (![self createReadAndWriteStreams:&err]) // { @@ -4845,7 +5233,7 @@ - (BOOL)enableBackgroundingOnSockets // // One tiny problem: the sockets will still get closed when the app gets backgrounded. // - // Edit: Actually I forgot to set the voip flag in the Info.plist, so I need to test this again... + // Apple does not officially support backgrounding UDP sockets. return NO; } diff --git a/src/NSDate+NetworkClock.h b/src/NSDate+NetworkClock.h index 83006d8..6c25623 100644 --- a/src/NSDate+NetworkClock.h +++ b/src/NSDate+NetworkClock.h @@ -14,12 +14,11 @@ + (NSTimeInterval) timeIntervalSinceNetworkDate; + (NSDate *) networkDate; -+ (NSDate *) threadsafeNetworkDate; - // the threadsafe version guards against reading a double that could be - // potentially being updated at the same time. Since doubles are 8 words, - // and arm is 32bit, this is not atomic and could provide bad values. - -@end + // the threadsafe version guards against reading a double that could be + // potentially being updated at the same time. Since doubles are 8 words, + // and arm is 32bit, this is not atomic and could provide bad values. ++ (NSDate *) threadsafeNetworkDate; +@end \ No newline at end of file diff --git a/src/NetAssociation.h b/src/NetAssociation.h index e09817e..a1e0a1b 100644 --- a/src/NetAssociation.h +++ b/src/NetAssociation.h @@ -1,8 +1,7 @@ /*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ NetAssociation.h ║ ║ ║ - ║ Created by Gavin Eadie on Nov03/10 ║ - ║ Copyright 2010 Ramsay Consulting. All rights reserved. ║ + ║ Created by Gavin Eadie on Nov03/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import diff --git a/src/NetAssociation.m b/src/NetAssociation.m index b188fee..6abe98f 100644 --- a/src/NetAssociation.m +++ b/src/NetAssociation.m @@ -1,8 +1,7 @@ /*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ NetAssociation.m ║ ║ ║ - ║ Created by Gavin Eadie on Nov03/10 ║ - ║ Copyright 2010 Ramsay Consulting. All rights reserved. ║ + ║ Created by Gavin Eadie on Nov03/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import "NetAssociation.h" @@ -67,7 +66,7 @@ - (id) initWithServerName:(NSString *)serverName queue:(dispatch_queue_t)queue { /*┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ Create a UDP socket that will communicate with the time server and set its delegate ... │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘*/ - server = [serverName retain]; + server = serverName; socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:queue ? queue : dispatch_get_main_queue()]; /*┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -81,20 +80,16 @@ - (id) initWithServerName:(NSString *)serverName queue:(dispatch_queue_t)queue { │ Finally, initialize the repeating timer that queries the server, set it's trigger time to the │ │ infinite future, and put it on the run loop .. nothing will happen (yet) │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘*/ - repeatingTimer = [[NSTimer timerWithTimeInterval:pollIntervals[pollingIntervalIndex] + repeatingTimer = [NSTimer timerWithTimeInterval:pollIntervals[pollingIntervalIndex] target:self selector:@selector(queryTimeServer:) - userInfo:nil repeats:YES] retain]; + userInfo:nil repeats:YES]; [repeatingTimer setFireDate:[NSDate distantFuture]]; [[NSRunLoop mainRunLoop] addTimer:repeatingTimer forMode:NSDefaultRunLoopMode]; return self; } - (void)dealloc { - [server release]; [repeatingTimer invalidate]; - [repeatingTimer release]; - [socket release]; - [super dealloc]; } /*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ @@ -274,10 +269,9 @@ - (void) evaluatePacket { NTP_Logging(@"[%@] poll interval adusted: %3.1f >> %3.1f", server, [repeatingTimer timeInterval], pollIntervals[pollingIntervalIndex]); [repeatingTimer invalidate]; - [repeatingTimer release]; - repeatingTimer = [[NSTimer scheduledTimerWithTimeInterval:pollIntervals[pollingIntervalIndex] + repeatingTimer = [NSTimer scheduledTimerWithTimeInterval:pollIntervals[pollingIntervalIndex] target:self selector:@selector(queryTimeServer:) - userInfo:nil repeats:YES] retain]; + userInfo:nil repeats:YES]; } }); } diff --git a/src/NetworkClock.h b/src/NetworkClock.h index 23034e5..9f4db5c 100644 --- a/src/NetworkClock.h +++ b/src/NetworkClock.h @@ -1,8 +1,7 @@ /*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ NetworkClock.h ║ ║ ║ - ║ Created by Gavin Eadie on Oct17/10 ║ - ║ Copyright 2010 Ramsay Consulting. All rights reserved. ║ + ║ Created by Gavin Eadie on Oct17/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import @@ -22,10 +21,10 @@ NSMutableArray * timeAssociations; @private - + NSSortDescriptor * dispersionSortDescriptor; NSArray * sortDescriptors; - + dispatch_queue_t associationDelegateQueue; } diff --git a/src/NetworkClock.m b/src/NetworkClock.m index 2fc71dc..52231bb 100644 --- a/src/NetworkClock.m +++ b/src/NetworkClock.m @@ -1,8 +1,7 @@ /*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ NetworkClock.m ║ ║ ║ - ║ Created by Gavin Eadie on Oct17/10 ║ - ║ Copyright 2010 Ramsay Consulting. All rights reserved. ║ + ║ Created by Gavin Eadie on Oct17/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import @@ -43,6 +42,17 @@ + (id)sharedNetworkClock { return sharedNetworkClockInstance; } +/*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + ┃ Return the device clock time adjusted for the offset to network-derived UTC. Since this could ┃ + ┃ be called very frequently, we recompute the average offset every 30 seconds. ┃ + ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛*/ +- (NSDate *) networkTime { + return [[NSDate date] dateByAddingTimeInterval:-timeIntervalSinceDeviceTime]; + + //[[NSNotificationCenter defaultCenter] postNotificationName:@"net-time" object:self]; + +} + - (id) init { if (!(self = [super init])) return nil; /*┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -50,15 +60,15 @@ - (id) init { │ array of empty associations to use ... │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘*/ dispersionSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"dispersion" ascending:YES]; - sortDescriptors = [[NSArray arrayWithObject:dispersionSortDescriptor] retain]; - timeAssociations = [[NSMutableArray arrayWithCapacity:48] retain]; + sortDescriptors = [NSArray arrayWithObject:dispersionSortDescriptor]; + timeAssociations = [NSMutableArray arrayWithCapacity:48]; associationDelegateQueue = dispatch_queue_create("org.ios-ntp.delegates", 0); - + /*┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ .. and fill that array with the time hosts obtained from "ntp.hosts" .. │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘*/ - [self createAssociations]; + [self createAssociations]; /*┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ prepare to catch our application entering and leaving the background .. │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘*/ @@ -74,9 +84,8 @@ - (id) init { - (void)dealloc { [self finishAssociations]; - dispatch_release(associationDelegateQueue); +// dispatch_release(associationDelegateQueue); [[NSNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; } /*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ @@ -112,17 +121,6 @@ - (void) offsetAverage { //### } -/*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - ┃ Return the device clock time adjusted for the offset to network-derived UTC. Since this could ┃ - ┃ be called very frequently, we recompute the average offset every 30 seconds. ┃ - ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛*/ -- (NSDate *) networkTime { - return [[NSDate date] dateByAddingTimeInterval:-timeIntervalSinceDeviceTime]; - - //[[NSNotificationCenter defaultCenter] postNotificationName:@"net-time" object:self]; - -} - #pragma mark I n t e r n a l • M e t h o d s /*┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ @@ -138,7 +136,6 @@ - (void) createAssociations { NSArray * ntpDomains = [fileData componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]]; - [fileData release]; /*┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ for each NTP service domain name in the 'ntp.hosts' file : "0.pool.ntp.org" etc ... │ @@ -153,7 +150,6 @@ - (void) createAssociations { └──────────────────────────────────────────────────────────────────────────────────────────────────┘*/ NetAssociation* timeAssociation = [[NetAssociation alloc] initWithServerName:ntpDomainName queue:associationDelegateQueue]; [timeAssociations addObject:timeAssociation]; - [timeAssociation release]; } // Enable associations. diff --git a/test/main.m b/test/main.m index f2f0ba5..87c37ed 100644 --- a/test/main.m +++ b/test/main.m @@ -9,8 +9,9 @@ #import int main(int argc, char *argv[]) { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - int retVal = UIApplicationMain(argc, argv, nil, nil); - [pool release]; - return retVal; + + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, nil); + } + } diff --git a/test/ntpAAppDelegate.h b/test/ntpAAppDelegate.h index 10b3019..a400c26 100644 --- a/test/ntpAAppDelegate.h +++ b/test/ntpAAppDelegate.h @@ -1,28 +1,26 @@ -// -// ntpAAppDelegate.h -// ntpA -// -// Created by Gavin Eadie on 10/16/10. -// Copyright (c) 2010 Ramsay Consulting. All rights reserved. -// +/*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ntpAAppDelegate.h ║ + ║ ║ + ║ Created by Gavin Eadie on Nov16/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import @class ntpAViewController; @interface ntpAAppDelegate : NSObject { - + NSDate * systemTime; NSDate * networkTime; - + IBOutlet UILabel * sysClockLabel; IBOutlet UILabel * netClockLabel; IBOutlet UILabel * differenceLabel; - + } -@property (nonatomic, retain) IBOutlet UIWindow * window; -@property (nonatomic, retain) IBOutlet ntpAViewController * viewController; +@property (nonatomic, strong) IBOutlet UIWindow * window; +@property (nonatomic, strong) IBOutlet ntpAViewController * viewController; - (void) repeatingMethod:(NSTimer*)theTimer; diff --git a/test/ntpAAppDelegate.m b/test/ntpAAppDelegate.m index cb82b81..d0c0c57 100644 --- a/test/ntpAAppDelegate.m +++ b/test/ntpAAppDelegate.m @@ -1,8 +1,7 @@ /*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ ║ ntpAAppDelegate.m ║ ║ ║ - ║ Created by Gavin Eadie on Nov16/10 ║ - ║ Copyright © 2010 Ramsay Consulting. All rights reserved. ║ + ║ Created by Gavin Eadie on Nov16/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import "ntpAAppDelegate.h" @@ -30,7 +29,6 @@ - (BOOL) application:(UIApplication *) app didFinishLaunchingWithOptions:(NSDict userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:repeatingTimer forMode:NSDefaultRunLoopMode]; - [repeatingTimer release]; return YES; } @@ -50,11 +48,5 @@ - (void)applicationWillTerminate:(UIApplication *)application { [[NetworkClock sharedNetworkClock] finishAssociations]; // be nice and let all the servers go ... } -- (void)dealloc { - - [window release]; - [viewController release]; - [super dealloc]; -} @end \ No newline at end of file diff --git a/test/ntpAViewController.h b/test/ntpAViewController.h index f7e4412..2486358 100644 --- a/test/ntpAViewController.h +++ b/test/ntpAViewController.h @@ -1,10 +1,8 @@ -// -// ntpAViewController.h -// ntpA -// -// Created by Gavin Eadie on 10/16/10. -// Copyright (c) 2010 Ramsay Consulting. All rights reserved. -// +/*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ntpAViewController.h ║ + ║ ║ + ║ Created by Gavin Eadie on Nov16/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import diff --git a/test/ntpAViewController.m b/test/ntpAViewController.m index 1008afa..f5bcebf 100644 --- a/test/ntpAViewController.m +++ b/test/ntpAViewController.m @@ -1,10 +1,8 @@ -// -// ntpAViewController.m -// ntpA -// -// Created by Gavin Eadie on 10/16/10. -// Copyright (c) 2010 Ramsay Consulting. All rights reserved. -// +/*╔══════════════════════════════════════════════════════════════════════════════════════════════════╗ + ║ ntpAViewController.m ║ + ║ ║ + ║ Created by Gavin Eadie on Nov16/10 ... Copyright 2010-14 Ramsay Consulting. All rights reserved. ║ + ╚══════════════════════════════════════════════════════════════════════════════════════════════════╝*/ #import "ntpAViewController.h" @@ -17,9 +15,4 @@ - (void)didReceiveMemoryWarning { - (void)viewDidUnload { } - -- (void)dealloc { - [super dealloc]; -} - @end \ No newline at end of file