Почему я не могу отменить разрешение информации CFHost?
Насколько мне известно CFHost
предлагает единственный общедоступный API в OS X (и iOS) для разрешения DNS, который является асинхронным и / или может быть отменен (так что можно реализовать пользовательское время ожидания). Все остальные API-интерфейсы являются синхронными и не могут быть отменены, поэтому для выполнения асинхронной или остановимой операции придется тратить один поток на поиск DNS (даже Grand Central Dispatch будет тратить один поток на поиск, вам просто не нужно создавать потоки сам). Наличие одного блокирующего потока на вызов разрешения DNS (и такой вызов может блокировать довольно долго, в моей системе тайм-аут составляет 30 секунд до истечения времени ожидания вызова), это действительно не тот путь, если вам нужно разрешить огромное количество DNS имена хостов.
CFHost
Казалось, хороший обходной для меня. Его можно использовать синхронно, в этом случае в документации говорится, что запрос на блокировку может быть отменен из другого потока, и его можно использовать асинхронно, в этом случае запрос выполняется в фоновом режиме, а также может быть отменен, если это необходимо, но все же он не будет блокировать поток, пока он не завершится успешно или не истечет время ожидания. внутренне CFHost
использования getaddrinfo_async_*
функции, но это не публичный API, насколько мне известно, эти функции являются частными, и их не следует использовать напрямую.
Итак, вот простой кусок кода, который я написал для тестирования CFHost
поиск с отменой, но он не работает, как ожидалось, и я понятия не имею, почему.
#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;
}
Если DNS-серверы настроены правильно, этот код разрешит имя довольно быстро:
0: Starting name resolution.
0: Name resolution terminated.
Это ожидается. Однако, если я "неверно сконфигурирую" DNS в моей системе, поэтому все запросы DNS прекратят работу, вот что я получу:
0: Starting name resolution.
5: Timeout limit reached, canceling.
30: Name resolution terminated.
Таймер отмены срабатывает через 5 секунд, и я отменяю запрос, но запрос не остановится, он заблокируется еще на 25 секунд. На самом деле, если я не отменю запрос, он также будет блокироваться на 30 секунд, поскольку, как я уже говорил выше, это естественный тайм-аут DNS моей системы. Так что призыв к CFHostCancelInfoResolution
ничего не делает.
Цитировать из Apple CFHost
документация:
CFHostStartInfoResolution
[...]
В синхронном режиме эта функция блокируется до тех пор, пока разрешение не будет завершено, и в этом случае эта функция возвращает TRUE, пока разрешение не будет остановлено путем вызова CFHostCancelInfoResolution из другого потока, в этом случае эта функция возвращает FALSE, или пока не произойдет ошибка.
Хорошо, я вызываю CFHostCancelInfoResolution из другого потока, но функция продолжает блокироваться. Либо это ошибка в API, ошибка в документации, либо я слишком глуп, чтобы правильно использовать этот API, и здесь я упускаю из виду кое-что очень фундаментальное.
Обновить
Это действительно может быть ошибкой. Я только что протестировал приведенный выше код на 10.6, и он работает точно так, как ожидалось, поиск отменяется через 5 секунд. На 10.7 и 10.8 отмена вызова ничего не делает, код блокируется до тех пор, пока не будет достигнут обычный таймаут DNS.