Skip to content
This repository was archived by the owner on Dec 29, 2023. It is now read-only.

Commit b43a5b3

Browse files
authored
Merge pull request cocos#33 from dumganhar/fix/SRWebSocket-memory-leak
Memory leak fix and cancel connection timeout timer in scheduleCleanup
2 parents 87575ef + 4eb591a commit b43a5b3

File tree

5 files changed

+47
-22
lines changed

5 files changed

+47
-22
lines changed

sources/SocketRocket/Internal/Delegate/SRDelegateController.h

-4
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,7 @@ typedef void(^SRDelegateBlock)(id<SRWebSocketDelegate> _Nullable delegate, SRDel
3333
@property (nonatomic, weak) id<SRWebSocketDelegate> delegate;
3434
@property (atomic, readonly) SRDelegateAvailableMethods availableDelegateMethods;
3535

36-
#if OS_OBJECT_USE_OBJC
3736
@property (nullable, nonatomic, strong) dispatch_queue_t dispatchQueue;
38-
#else
39-
@property (nullable, nonatomic, assign) dispatch_queue_t dispatchQueue;
40-
#endif
4137
@property (nullable, nonatomic, strong) NSOperationQueue *operationQueue;
4238

4339
///--------------------------------------

sources/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.h

+2
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@
2525
unmaskBytes:(BOOL)unmaskBytes;
2626
- (void)returnConsumer:(SRIOConsumer *)consumer;
2727

28+
- (void)clear;
29+
2830
@end

sources/SocketRocket/Internal/IOConsumer/SRIOConsumerPool.m

+6
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,10 @@ - (void)returnConsumer:(SRIOConsumer *)consumer;
6161
}
6262
}
6363

64+
-(void)clear
65+
{
66+
_poolSize = 0;
67+
_bufferedConsumers = nil;
68+
}
69+
6470
@end

sources/SocketRocket/Internal/Proxy/SRProxyConnect.m

+4-4
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,11 @@ - (void)_fetchPAC:(NSURL *)PACurl withProxySettings:(NSDictionary *)proxySetting
234234
[self _openConnection];
235235
return;
236236
}
237-
__weak __typeof__(self) wself = self;
237+
__weak __typeof(self) wself = self;
238238
NSURLRequest *request = [NSURLRequest requestWithURL:PACurl];
239239
NSURLSession *session = [NSURLSession sharedSession];
240240
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
241-
__strong __typeof__(wself) sself = wself;
241+
__strong __typeof(wself) sself = wself;
242242
if (!error) {
243243
NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
244244
[sself _runPACScript:script withProxySettings:proxySettings];
@@ -454,9 +454,9 @@ - (void)_writeData:(NSData *)data
454454
{
455455
const uint8_t * bytes = data.bytes;
456456
__block NSInteger timeout = (NSInteger)(SRProxyConnectWriteTimeout * 1000000); // wait timeout before giving up
457-
__weak __typeof__(self) wself = self;
457+
__weak __typeof(self) wself = self;
458458
dispatch_async(_writeQueue, ^{
459-
__strong __typeof__(wself) sself = self;
459+
__strong __typeof(wself) sself = self;
460460
if (!sself) {
461461
return;
462462
}

sources/SocketRocket/SRWebSocket.m

+35-14
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,14 @@ + (BOOL)automaticallyNotifiesObserversOfReadyState {
295295
return NO;
296296
}
297297

298+
-(void)_onTimeout
299+
{
300+
if (self.readyState == SR_CONNECTING) {
301+
NSError *error = SRErrorWithDomainCodeDescription(NSURLErrorDomain, NSURLErrorTimedOut, @"Timed out connecting to server.");
302+
[self _failWithError:error];
303+
}
304+
}
305+
298306
///--------------------------------------
299307
#pragma mark - Open / Close
300308
///--------------------------------------
@@ -307,18 +315,12 @@ - (void)open
307315
_selfRetain = self;
308316

309317
if (_urlRequest.timeoutInterval > 0) {
310-
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_urlRequest.timeoutInterval * NSEC_PER_SEC));
311-
dispatch_after(popTime, dispatch_get_main_queue(), ^{
312-
if (self.readyState == SR_CONNECTING) {
313-
NSError *error = SRErrorWithDomainCodeDescription(NSURLErrorDomain, NSURLErrorTimedOut, @"Timed out connecting to server.");
314-
[self _failWithError:error];
315-
}
316-
});
318+
[self performSelector:@selector(_onTimeout) withObject:nil afterDelay:_urlRequest.timeoutInterval];
317319
}
318320

319321
_proxyConnect = [[SRProxyConnect alloc] initWithURL:_url];
320322

321-
__weak __typeof__(self) wself = self;
323+
__weak __typeof(self) wself = self;
322324
[_proxyConnect openNetworkStreamWithCompletion:^(NSError *error, NSInputStream *readStream, NSOutputStream *writeStream) {
323325
[wself _connectionDoneWithError:error readStream:readStream writeStream:writeStream];
324326
}];
@@ -419,14 +421,23 @@ - (void)_readHTTPHeader;
419421
_receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
420422
}
421423

424+
// Uses weak self object in the block, otherwise Consumers will retain SRWebSocket instance,
425+
// and SRWebSocket instance also hold consumers, cycle reference will occur.
426+
__weak __typeof(self) wself = self;
427+
422428
[self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *socket, NSData *data) {
423-
CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
424429

425-
if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
426-
SRDebugLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
427-
[self _HTTPHeadersDidFinish];
430+
__strong __typeof(wself) sself = wself;
431+
if (sself == nil)
432+
return;
433+
434+
CFHTTPMessageAppendBytes(sself.receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
435+
436+
if (CFHTTPMessageIsHeaderComplete(sself.receivedHTTPHeaders)) {
437+
SRDebugLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(sself.receivedHTTPHeaders)));
438+
[sself _HTTPHeadersDidFinish];
428439
} else {
429-
[self _readHTTPHeader];
440+
[sself _readHTTPHeader];
430441
}
431442
}];
432443
}
@@ -1127,6 +1138,16 @@ - (void)_scheduleCleanup
11271138

11281139
_cleanupScheduled = YES;
11291140

1141+
// _consumers retain SRWebSocket instance by block copy, if there are consumers here, clear them.
1142+
[_consumers removeAllObjects];
1143+
[_consumerPool clear];
1144+
1145+
// Cancel the timer which retains SRWebSocket instance.
1146+
// If we don't cancel the timer, the 'dealloc' method will be invoked only after the time (default: 60s) have come, which may cause memory increase.
1147+
dispatch_async(dispatch_get_main_queue(), ^(){
1148+
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_onTimeout) object:nil];
1149+
});
1150+
11301151
// Cleanup NSStream delegate's in the same RunLoop used by the streams themselves:
11311152
// This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc
11321153
NSTimer *timer = [NSTimer timerWithTimeInterval:(0.0f) target:self selector:@selector(_cleanupSelfReference:) userInfo:nil repeats:NO];
@@ -1392,7 +1413,7 @@ - (void)_sendFrameWithOpcode:(SROpCode)opCode data:(NSData *)data
13921413

13931414
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
13941415
{
1395-
__weak __typeof__(self) wself = self;
1416+
__weak __typeof(self) wself = self;
13961417

13971418
if (_requestRequiresSSL && !_streamSecurityValidated &&
13981419
(eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) {

0 commit comments

Comments
 (0)