AFAIK CFHost offers the only public API for OS X (and iOS) to resolve DNS, which is asynchronous and / or can be canceled (so that you can implement a custom timeout). All other APIs are synchronous and cannot be canceled, so you would have to spend one thread on the DNS lookup to make the operation asynchronous or stopped (even Grand Central Dispatch would spend one thread on the lookup, you just don't need to create the threads yourself) . Having one blocking thread per DNS resolution call (and such a call can be blocked for quite a while, in my system the timeout is 30 seconds before the call will ultimately be a timeout) is actually not what you need if you you need to solve a huge number of DNS host names.
CFHost seemed like a good job to me. It can be used synchronously, and in this case, the documentation says that the lock request can be canceled from another thread, and it can be used asynchronously, in which case the request is executed in the background and can also be canceled if necessary, but this is not will block the thread until it succeeds or passes naturally. Internally, CFHost uses the getaddrinfo_async_* functions, but this is not a public API, as far as I know, these functions are private and should not be used directly.
So, here is a simple piece of code that I wrote to check the CFHost search with cancellation, but it does not work as expected, and I have no idea why.
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <dispatch/dispatch.h> #include <CoreServices/CoreServices.h> int main (int argc, char ** argv ) { CFHostRef host; dispatch_time_t timeout; CFAbsoluteTime startTime; dispatch_queue_t timeoutQueue; startTime = CFAbsoluteTimeGetCurrent(); host = CFHostCreateWithName(kCFAllocatorDefault, CFSTR("www.apple.com")); assert(host); timeout = dispatch_time(DISPATCH_TIME_NOW, 5 * 1000 * 1000 * 1000ull); timeoutQueue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ); assert(timeoutQueue); dispatch_after(timeout, timeoutQueue, ^{ printf("%u: Timeout limit reached, canceling.\n", (unsigned)(CFAbsoluteTimeGetCurrent() - startTime) ); CFHostCancelInfoResolution(host, kCFHostAddresses); } ); printf("%u: Starting name resolution.\n", (unsigned)(CFAbsoluteTimeGetCurrent() - startTime) ); CFHostStartInfoResolution(host, kCFHostAddresses, NULL); printf("%u: Name resolution terminated.\n", (unsigned)(CFAbsoluteTimeGetCurrent() - startTime) ); return 0; }
If the DNS servers are configured correctly, this code will quickly resolve the name:
0: Starting name resolution. 0: Name resolution terminated.
Expected. However, if I "configure" DNS incorrectly on my system, so all DNS queries will timeout, this is what I get:
0: Starting name resolution. 5: Timeout limit reached, canceling. 30: Name resolution terminated.
Cancel the timer hits after 5 seconds, and I cancel the request, but the request does not stop, it will be blocked for another 25 seconds. In fact, if I do not cancel the request, it will also be blocked for 30 seconds, since, as I said above, this is the natural DNS timeout of my system. Thus, a call to CFHostCancelInfoResolution is executed for EXACTLY ANYTHING .
To quote from Apple CFHost documentation:
CFHostStartInfoResolution
[...]
In synchronous mode, this function is blocked until the resolution is completed, in which case this function returns TRUE, while the resolution is stopped by calling CFHostCancelInfoResolution from another thread, in which case this function returns FALSE, or until an error occurs.
Ok, I am calling CFHostCancelInfoResolution from another thread, but the function continues to block. Either this is an error in the API, or an error in the documentation, or I'm too stupid to use this API correctly, and there is something very fundamental that I forget about here.
Update
This may indeed be a mistake. I just checked the code above at 10.6 and it works exactly as expected, the search is canceled after 5 seconds. On 10.7 and 10.8, the cancellation call does nothing, the code is blocked until the usual DNS timeout is reached.