forked from gnachman/iTerm2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AsyncHostLookupController.m
103 lines (92 loc) · 3 KB
/
AsyncHostLookupController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//
// AsyncHostLookupController.m
// iTerm
//
// Created by George Nachman on 12/14/13.
//
//
#import "AsyncHostLookupController.h"
#import "DebugLogging.h"
#include <netdb.h>
@implementation AsyncHostLookupController {
// Created at initialization and used to perform blocking gethostbyname calls.
dispatch_queue_t _queue;
// Set of hostnames waiting to be looked up. If a hostname is removed from
// this set then it won't be looked up when its turn comes around.
NSMutableSet *_pending;
// Maps hostname -> @YES or @NO, indicating if it resolved.
NSMutableDictionary *_cache;
}
+ (instancetype)sharedInstance {
static AsyncHostLookupController *instance;
static dispatch_once_t once;
dispatch_once(&once, ^{
instance = [[self alloc] init];
});
return instance;
}
- (id)init {
self = [super init];
if (self) {
_queue = dispatch_queue_create("AsyncHostLookupControllerQueue", NULL);
_pending = [[NSMutableSet alloc] init];
_cache = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
// The logic for cleaning up the dispatch queue isn't written, so just make sure the singleton
// never gets dealloced.
assert(false);
[super dealloc];
}
- (void)getAddressForHost:(NSString *)hostname
completion:(void (^)(BOOL, NSString *))completion {
NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
@synchronized(self) {
if ([_pending containsObject:hostname]) {
DLog(@"Already pending %@", hostname);
return;
}
NSNumber *okNumber = _cache[hostname];
if (okNumber) {
completion([okNumber boolValue], hostname);
return;
}
[_pending addObject:hostname];
}
dispatch_async(_queue, ^() {
@synchronized(self) {
if (![_pending containsObject:hostname]) {
DLog(@"Abort nslookup for %@", hostname);
return;
}
}
struct hostent *hbuf;
// On Mac OS this is thread-safe (it uses TLS for the hostent), and gethostbyname_r is not
// defined.
hbuf = gethostbyname([hostname UTF8String]);
BOOL ok = (hbuf != NULL);
@synchronized(self) {
_cache[hostname] = @(ok);
}
dispatch_async(dispatch_get_main_queue(), ^() {
@synchronized(self) {
if (![_pending containsObject:hostname]) {
DLog(@"Finished nslookup but don't call block for %@", hostname);
return;
}
[_pending removeObject:hostname];
}
DLog(@"Host %@: %@", hostname, ok ? @"Ok" : @"Unknown");
completion(ok, hostname);
});
});
DLog(@"Blocked main thread for %f sec", [NSDate timeIntervalSinceReferenceDate] - start);
}
- (void)cancelRequestForHostname:(NSString *)hostname {
@synchronized(self) {
[_pending removeObject:hostname];
}
}
@end