162
162
163
163
#if TARGET_OS_IPHONE
164
164
static NSThread *cfstreamThread; // Used for CFStreams
165
+
166
+ static uint64_t cfstreamThreadRetainCount; // setup & teardown
167
+ static dispatch_queue_t cfstreamThreadSetupQueue; // setup & teardown
165
168
#endif
166
169
167
170
// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -6612,14 +6615,64 @@ + (void)ignore:(id)_
6612
6615
6613
6616
+ (void )startCFStreamThreadIfNeeded
6614
6617
{
6618
+ LogTrace ();
6619
+
6615
6620
static dispatch_once_t predicate;
6616
6621
dispatch_once (&predicate, ^{
6617
6622
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);
6622
6625
});
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
+ }});
6623
6676
}
6624
6677
6625
6678
+ (void )cfstreamThread { @autoreleasepool
@@ -6636,7 +6689,15 @@ + (void)cfstreamThread { @autoreleasepool
6636
6689
userInfo: nil
6637
6690
repeats: YES ];
6638
6691
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
+ }
6640
6701
6641
6702
LogInfo (@" CFStreamThread: Stopped" );
6642
6703
}}
@@ -6937,6 +6998,7 @@ - (void)removeStreamsFromRunLoop
6937
6998
onThread: cfstreamThread
6938
6999
withObject: self
6939
7000
waitUntilDone: YES ];
7001
+ [[self class ] stopCFStreamThreadIfNeeded ];
6940
7002
6941
7003
flags &= ~kAddedStreamsToRunLoop ;
6942
7004
}
0 commit comments