Skip to content

Commit c82ff4d

Browse files
committed
GCDAsyncSocket now tears down its CFStreamThread after 30 seconds of non-use. This helps reduce thread count & resource usage.
1 parent 121a295 commit c82ff4d

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

GCD/GCDAsyncSocket.m

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@
162162

163163
#if TARGET_OS_IPHONE
164164
static NSThread *cfstreamThread; // Used for CFStreams
165+
166+
static uint64_t cfstreamThreadRetainCount; // setup & teardown
167+
static dispatch_queue_t cfstreamThreadSetupQueue; // setup & teardown
165168
#endif
166169

167170
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -6612,14 +6615,64 @@ + (void)ignore:(id)_
66126615

66136616
+ (void)startCFStreamThreadIfNeeded
66146617
{
6618+
LogTrace();
6619+
66156620
static dispatch_once_t predicate;
66166621
dispatch_once(&predicate, ^{
66176622

6618-
cfstreamThread = [[NSThread alloc] initWithTarget:self
6619-
selector:@selector(cfstreamThread)
6620-
object:nil];
6621-
[cfstreamThread start];
6623+
cfstreamThreadRetainCount = 0;
6624+
cfstreamThreadSetupQueue = dispatch_queue_create("GCDAsyncSocket-CFStreamThreadSetup", DISPATCH_QUEUE_SERIAL);
66226625
});
6626+
6627+
dispatch_sync(cfstreamThreadSetupQueue, ^{ @autoreleasepool {
6628+
6629+
if (++cfstreamThreadRetainCount == 1)
6630+
{
6631+
cfstreamThread = [[NSThread alloc] initWithTarget:self
6632+
selector:@selector(cfstreamThread)
6633+
object:nil];
6634+
[cfstreamThread start];
6635+
}
6636+
}});
6637+
}
6638+
6639+
+ (void)stopCFStreamThreadIfNeeded
6640+
{
6641+
LogTrace();
6642+
6643+
// The creation of the cfstreamThread is relatively expensive.
6644+
// So we'd like to keep it available for recycling.
6645+
// However, there's a tradeoff here, because it shouldn't remain alive forever.
6646+
// So what we're going to do is use a little delay before taking it down.
6647+
// This way it can be reused properly in situations where multiple sockets are continually in flux.
6648+
6649+
int delayInSeconds = 30;
6650+
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
6651+
dispatch_after(when, cfstreamThreadSetupQueue, ^{ @autoreleasepool {
6652+
#pragma clang diagnostic push
6653+
#pragma clang diagnostic warning "-Wimplicit-retain-self"
6654+
6655+
if (cfstreamThreadRetainCount == 0)
6656+
{
6657+
LogWarn(@"Logic error concerning cfstreamThread start / stop");
6658+
return_from_block;
6659+
}
6660+
6661+
if (--cfstreamThreadRetainCount == 0)
6662+
{
6663+
[cfstreamThread cancel]; // set isCancelled flag
6664+
6665+
// wake up the thread
6666+
[GCDAsyncSocket performSelector:@selector(ignore:)
6667+
onThread:cfstreamThread
6668+
withObject:[NSNull null]
6669+
waitUntilDone:NO];
6670+
6671+
cfstreamThread = nil;
6672+
}
6673+
6674+
#pragma clang diagnostic pop
6675+
}});
66236676
}
66246677

66256678
+ (void)cfstreamThread { @autoreleasepool
@@ -6636,7 +6689,15 @@ + (void)cfstreamThread { @autoreleasepool
66366689
userInfo:nil
66376690
repeats:YES];
66386691

6639-
[[NSRunLoop currentRunLoop] run];
6692+
NSThread *currentThread = [NSThread currentThread];
6693+
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
6694+
6695+
BOOL isCancelled = [currentThread isCancelled];
6696+
6697+
while (!isCancelled && [currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
6698+
{
6699+
isCancelled = [currentThread isCancelled];
6700+
}
66406701

66416702
LogInfo(@"CFStreamThread: Stopped");
66426703
}}
@@ -6937,6 +6998,7 @@ - (void)removeStreamsFromRunLoop
69376998
onThread:cfstreamThread
69386999
withObject:self
69397000
waitUntilDone:YES];
7001+
[[self class] stopCFStreamThreadIfNeeded];
69407002

69417003
flags &= ~kAddedStreamsToRunLoop;
69427004
}

GCD/Xcode/SimpleHTTPClient/Desktop/SimpleHTTPClient/SimpleHTTPClientAppDelegate.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
126126

127127
#if MANUALLY_EVALUATE_TRUST
128128
{
129+
// Use socket:shouldTrustPeer: delegate method for manual trust evaluation
130+
129131
NSDictionary *options = @{
130132
GCDAsyncSocketManuallyEvaluateTrust : @(YES)
131133
};
@@ -135,6 +137,8 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
135137
}
136138
#else
137139
{
140+
// Use default trust evaluation, and provide basic security parameters
141+
138142
NSDictionary *options = @{
139143
(NSString *)kCFStreamSSLPeerName : CERT_HOST
140144
};

GCD/Xcode/SimpleHTTPClient/Mobile/SimpleHTTPClient/SimpleHTTPClientAppDelegate.m

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define CERT_HOST @"www.apple.com"
1313

1414
#define USE_SECURE_CONNECTION 1
15+
#define USE_CFSTREAM_FOR_TLS 0 // Use old-school CFStream style technique
1516
#define MANUALLY_EVALUATE_TRUST 0
1617

1718
#define READ_HEADER_LINE_BY_LINE 0
@@ -126,8 +127,22 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
126127
//
127128
// The options passed to the startTLS method are fully documented in the GCDAsyncSocket header file.
128129

129-
#if MANUALLY_EVALUATE_TRUST
130+
#if USE_CFSTREAM_FOR_TLS
130131
{
132+
// Use old-school CFStream style technique
133+
134+
NSDictionary *options = @{
135+
GCDAsyncSocketUseCFStreamForTLS : @(YES),
136+
(NSString *)kCFStreamSSLPeerName : CERT_HOST
137+
};
138+
139+
DDLogVerbose(@"Requesting StartTLS with options:\n%@", options);
140+
[asyncSocket startTLS:options];
141+
}
142+
#elif MANUALLY_EVALUATE_TRUST
143+
{
144+
// Use socket:shouldTrustPeer: delegate method for manual trust evaluation
145+
131146
NSDictionary *options = @{
132147
GCDAsyncSocketManuallyEvaluateTrust : @(YES)
133148
};
@@ -137,6 +152,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
137152
}
138153
#else
139154
{
155+
// Use default trust evaluation, and provide basic security parameters
156+
140157
NSDictionary *options = @{
141158
(NSString *)kCFStreamSSLPeerName : CERT_HOST
142159
};

0 commit comments

Comments
 (0)