Ben Copsey

Fix potential crasher that could have been triggered when requests are cancelled

... ... @@ -668,6 +668,9 @@ static NSLock *sessionCookiesLock = nil;
NSDate *now = [NSDate date];
// We won't let the request cancel until we're done with this cycle of the loop
[[self cancelledLock] lock];
// See if we need to timeout
if (lastActivityTime && timeOutSeconds > 0 && [now timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
... ... @@ -676,6 +679,7 @@ static NSLock *sessionCookiesLock = nil;
// This workaround prevents erroneous timeouts in low bandwidth situations (eg iPhone)
if (totalBytesSent || postLength <= uploadBufferSize || (uploadBufferSize > 0 && totalBytesSent > uploadBufferSize)) {
[self failWithError:ASIRequestTimedOutError];
[[self cancelledLock] unlock];
[self cancelLoad];
[self setComplete:YES];
break;
... ... @@ -684,6 +688,7 @@ static NSLock *sessionCookiesLock = nil;
// Do we need to redirect?
if ([self needsRedirect]) {
[[self cancelledLock] unlock];
[self cancelLoad];
[self setNeedsRedirect:NO];
[self setRedirectCount:[self redirectCount]+1];
... ... @@ -699,7 +704,8 @@ static NSLock *sessionCookiesLock = nil;
}
// See if our NSOperationQueue told us to cancel
if ([self isCancelled]) {
if ([self isCancelled] || [self complete]) {
[[self cancelledLock] unlock];
break;
}
... ... @@ -708,10 +714,11 @@ static NSLock *sessionCookiesLock = nil;
[self setLastActivityTime:[NSDate date]];
[self setLastBytesSent:totalBytesSent];
}
// Find out how much data we've uploaded so far
[[self cancelledLock] lock];
[self setTotalBytesSent:[[(NSNumber *)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedLongLongValue]];
// Updating the progress indicators will attempt to aquire the lock again when needed
[[self cancelledLock] unlock];
[self updateProgressIndicators];
... ...
... ... @@ -31,6 +31,7 @@ IMPORTANT
NSMutableArray *finishedRequests;
ASINetworkQueue *releaseTestQueue;
ASINetworkQueue *cancelQueue;
}
- (void)testFailure;
... ... @@ -50,8 +51,13 @@ IMPORTANT
- (void)testMultipleDownloadsThrottlingBandwidth;
- (void)testMultipleUploadsThrottlingBandwidth;
/*
- (void)testCancelStressTest;
*/
@property (retain) NSOperationQueue *immediateCancelQueue;
@property (retain) NSMutableArray *failedRequests;
@property (retain) NSMutableArray *finishedRequests;
@property (retain) ASINetworkQueue *releaseTestQueue;
@property (retain) ASINetworkQueue *cancelQueue;
@end
... ...
... ... @@ -522,6 +522,28 @@ IMPORTANT
complete = YES;
}
// A test for a potential crasher that used to exist when requests were cancelled
// We aren't testing a specific condition here, but rather attempting to trigger a crash
// This test is commented out because it may generate enough load to kill a low-memory server
// PLEASE DO NOT RUN THIS TEST ON A NON-LOCAL SERVER
/*
- (void)testCancelStressTest
{
[self setCancelQueue:[ASINetworkQueue queue]];
// Increase the risk of this crash
[[self cancelQueue] setMaxConcurrentOperationCount:25];
int i;
for (i=0; i<100; i++) {
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://127.0.0.1"]];
[[self cancelQueue] addOperation:request];
}
[[self cancelQueue] go];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[[self cancelQueue] cancelAllOperations];
[self setCancelQueue:nil];
}
*/
// Not strictly an ASINetworkQueue test, but queue related
// As soon as one request finishes or fails, we'll cancel the others and ensure that no requests are both finished and failed
... ... @@ -768,4 +790,5 @@ IMPORTANT
@synthesize failedRequests;
@synthesize finishedRequests;
@synthesize releaseTestQueue;
@synthesize cancelQueue;
@end
... ...