Ben Copsey

Progress updates should now only happen in the main thread, fixing iphone issues

@@ -112,9 +112,6 @@ @@ -112,9 +112,6 @@
112 //This lock will block the request until the delegate supplies authentication info 112 //This lock will block the request until the delegate supplies authentication info
113 NSConditionLock *authenticationLock; 113 NSConditionLock *authenticationLock;
114 114
115 - //This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa  
116 - NSLock *progressLock;  
117 -  
118 //Called on the delegate when the request completes successfully 115 //Called on the delegate when the request completes successfully
119 SEL didFinishSelector; 116 SEL didFinishSelector;
120 117
@@ -31,6 +31,9 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -31,6 +31,9 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
31 [((ASIHTTPRequest*)clientCallBackInfo) handleNetworkEvent: type]; 31 [((ASIHTTPRequest*)clientCallBackInfo) handleNetworkEvent: type];
32 } 32 }
33 33
  34 +//This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
  35 +
  36 +static NSLock *progressLock;
34 37
35 @implementation ASIHTTPRequest 38 @implementation ASIHTTPRequest
36 39
@@ -38,6 +41,11 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -38,6 +41,11 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
38 41
39 #pragma mark init / dealloc 42 #pragma mark init / dealloc
40 43
  44 +- (void)initialize
  45 +{
  46 + progressLock = [[NSLock alloc] init];
  47 +}
  48 +
41 - (id)initWithURL:(NSURL *)newURL 49 - (id)initWithURL:(NSURL *)newURL
42 { 50 {
43 self = [super init]; 51 self = [super init];
@@ -85,7 +93,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -85,7 +93,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
85 [domain release]; 93 [domain release];
86 [authenticationRealm release]; 94 [authenticationRealm release];
87 [url release]; 95 [url release];
88 - [progressLock release];  
89 [authenticationLock release]; 96 [authenticationLock release];
90 [lastActivityTime release]; 97 [lastActivityTime release];
91 [responseCookies release]; 98 [responseCookies release];
@@ -214,9 +221,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -214,9 +221,6 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
214 - (void)loadRequest 221 - (void)loadRequest
215 { 222 {
216 CFRunLoopAddCommonMode(CFRunLoopGetCurrent(),ASIHTTPRequestRunMode); 223 CFRunLoopAddCommonMode(CFRunLoopGetCurrent(),ASIHTTPRequestRunMode);
217 -  
218 - [progressLock release];  
219 - progressLock = [[NSLock alloc] init];  
220 224
221 [authenticationLock release]; 225 [authenticationLock release];
222 authenticationLock = [[NSConditionLock alloc] initWithCondition:1]; 226 authenticationLock = [[NSConditionLock alloc] initWithCondition:1];
@@ -270,14 +274,18 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -270,14 +274,18 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
270 } 274 }
271 275
272 //Record when the request started, so we can timeout if nothing happens 276 //Record when the request started, so we can timeout if nothing happens
273 - [self setLastActivityTime:[[NSDate new] autorelease]]; 277 + [self setLastActivityTime:[NSDate date]];
274 278
275 // Wait for the request to finish 279 // Wait for the request to finish
276 while (!complete) { 280 while (!complete) {
277 281
  282 + //This may take a while, so we'll release the pool each cycle to stop a giant backlog building up
  283 + [pool release];
  284 + pool = [[NSAutoreleasePool alloc] init];
  285 +
278 //See if we need to timeout 286 //See if we need to timeout
279 if (lastActivityTime && timeOutSeconds > 0) { 287 if (lastActivityTime && timeOutSeconds > 0) {
280 - if ([[[NSDate new] autorelease] timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) { 288 + if ([[NSDate date] timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
281 [self failWithProblem:@"Request timed out"]; 289 [self failWithProblem:@"Request timed out"];
282 [self cancelLoad]; 290 [self cancelLoad];
283 complete = YES; 291 complete = YES;
@@ -292,7 +300,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -292,7 +300,7 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
292 complete = YES; 300 complete = YES;
293 break; 301 break;
294 } 302 }
295 - [self updateProgressIndicators]; 303 + [self performSelectorOnMainThread:@selector(updateProgressIndicators) withObject:nil waitUntilDone:YES];
296 304
297 //This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does. 305 //This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does.
298 CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES); 306 CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES);
@@ -332,20 +340,16 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -332,20 +340,16 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
332 340
333 - (void)updateProgressIndicators 341 - (void)updateProgressIndicators
334 { 342 {
335 - [progressLock lock]; 343 +
336 - if ([self isCancelled]) {  
337 - [progressLock unlock];  
338 - return;  
339 - }  
340 [self updateUploadProgress]; 344 [self updateUploadProgress];
341 [self updateDownloadProgress]; 345 [self updateDownloadProgress];
342 - [progressLock unlock];  
343 346
344 } 347 }
345 348
346 349
347 + (void)setProgress:(double)progress forProgressIndicator:(id)indicator 350 + (void)setProgress:(double)progress forProgressIndicator:(id)indicator
348 { 351 {
  352 +
349 SEL selector; 353 SEL selector;
350 354
351 //Cocoa Touch: UIProgressView 355 //Cocoa Touch: UIProgressView
@@ -393,6 +397,12 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -393,6 +397,12 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
393 397
394 -(void)removeUploadProgressSoFar 398 -(void)removeUploadProgressSoFar
395 { 399 {
  400 + [progressLock lock];
  401 + if ([self isCancelled]) {
  402 + [progressLock unlock];
  403 + return;
  404 + }
  405 +
396 //We're using a progress queue or compatible controller to handle progress 406 //We're using a progress queue or compatible controller to handle progress
397 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) { 407 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) {
398 int value = 0-lastBytesSent; 408 int value = 0-lastBytesSent;
@@ -407,12 +417,19 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -407,12 +417,19 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
407 //We aren't using a queue, we should just set progress of the indicator to 0 417 //We aren't using a queue, we should just set progress of the indicator to 0
408 } else { 418 } else {
409 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate]; 419 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate];
410 - } 420 + }
  421 + [progressLock unlock];
411 } 422 }
412 423
413 424
414 - (void)resetUploadProgress:(NSNumber *)max 425 - (void)resetUploadProgress:(NSNumber *)max
415 { 426 {
  427 + [progressLock lock];
  428 + if ([self isCancelled]) {
  429 + [progressLock unlock];
  430 + return;
  431 + }
  432 +
416 //We're using a progress queue or compatible controller to handle progress 433 //We're using a progress queue or compatible controller to handle progress
417 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadSizeBy:)]) { 434 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadSizeBy:)]) {
418 int value = [max intValue]; 435 int value = [max intValue];
@@ -426,11 +443,18 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -426,11 +443,18 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
426 } else { 443 } else {
427 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate]; 444 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate];
428 } 445 }
  446 + [progressLock unlock];
429 } 447 }
430 448
431 - (void)updateUploadProgress 449 - (void)updateUploadProgress
432 { 450 {
433 - [self setLastActivityTime:[[NSDate new] autorelease]]; 451 + [progressLock lock];
  452 + if ([self isCancelled]) {
  453 + [progressLock unlock];
  454 + return;
  455 + }
  456 +
  457 + [self setLastActivityTime:[NSDate date]];
434 458
435 unsigned int byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedIntValue]; 459 unsigned int byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedIntValue];
436 if (uploadProgressDelegate) { 460 if (uploadProgressDelegate) {
@@ -439,7 +463,8 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -439,7 +463,8 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
439 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) { 463 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) {
440 int value = byteCount-lastBytesSent; 464 int value = byteCount-lastBytesSent;
441 SEL selector = @selector(incrementUploadProgressBy:); 465 SEL selector = @selector(incrementUploadProgressBy:);
442 - NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 466 + NSMethodSignature *signature = nil;
  467 + signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector];
443 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 468 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
444 [invocation setTarget:uploadProgressDelegate]; 469 [invocation setTarget:uploadProgressDelegate];
445 [invocation setSelector:selector]; 470 [invocation setSelector:selector];
@@ -453,11 +478,19 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -453,11 +478,19 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
453 478
454 } 479 }
455 lastBytesSent = byteCount; 480 lastBytesSent = byteCount;
  481 + [progressLock unlock];
456 } 482 }
457 483
458 484
459 - (void)resetDownloadProgress:(NSNumber *)max 485 - (void)resetDownloadProgress:(NSNumber *)max
460 { 486 {
  487 + [progressLock lock];
  488 + if ([self isCancelled]) {
  489 + [progressLock unlock];
  490 + return;
  491 + }
  492 +
  493 +
461 //We're using a progress queue or compatible controller to handle progress 494 //We're using a progress queue or compatible controller to handle progress
462 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) { 495 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) {
463 int value = [max intValue]; 496 int value = [max intValue];
@@ -471,17 +504,27 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -471,17 +504,27 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
471 } else { 504 } else {
472 [ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate]; 505 [ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate];
473 } 506 }
  507 + [progressLock unlock];
474 } 508 }
475 509
476 - (void)updateDownloadProgress 510 - (void)updateDownloadProgress
477 { 511 {
478 - [self setLastActivityTime:[[NSDate new] autorelease]]; 512 + [progressLock lock];
  513 + if ([self isCancelled]) {
  514 + [progressLock unlock];
  515 + return;
  516 + }
  517 +
  518 + [self setLastActivityTime:[NSDate date]];
479 519
480 //We won't update downlaod progress until we've examined the headers, since we might need to authenticate 520 //We won't update downlaod progress until we've examined the headers, since we might need to authenticate
481 if (downloadProgressDelegate && responseHeaders) { 521 if (downloadProgressDelegate && responseHeaders) {
482 522
483 //We're using a progress queue or compatible controller to handle progress 523 //We're using a progress queue or compatible controller to handle progress
484 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) { 524 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) {
  525 +
  526 + NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
  527 +
485 int value = totalBytesRead-lastBytesRead; 528 int value = totalBytesRead-lastBytesRead;
486 SEL selector = @selector(incrementDownloadProgressBy:); 529 SEL selector = @selector(incrementDownloadProgressBy:);
487 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 530 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
@@ -491,13 +534,16 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -491,13 +534,16 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
491 [invocation setArgument:&value atIndex:2]; 534 [invocation setArgument:&value atIndex:2];
492 [invocation invoke]; 535 [invocation invoke];
493 536
  537 + [thePool release];
  538 +
494 //We aren't using a queue, we should just set progress of the indicator to 0 539 //We aren't using a queue, we should just set progress of the indicator to 0
495 } else if (contentLength > 0) { 540 } else if (contentLength > 0) {
496 [ASIHTTPRequest setProgress:(double)(totalBytesRead/contentLength) forProgressIndicator:downloadProgressDelegate]; 541 [ASIHTTPRequest setProgress:(double)(totalBytesRead/contentLength) forProgressIndicator:downloadProgressDelegate];
497 } 542 }
498 543
499 lastBytesRead = totalBytesRead; 544 lastBytesRead = totalBytesRead;
500 - } 545 + }
  546 + [progressLock unlock];
501 } 547 }
502 548
503 #pragma mark handling request complete / failure 549 #pragma mark handling request complete / failure
@@ -80,8 +80,16 @@ @@ -80,8 +80,16 @@
80 { 80 {
81 if ([operation isKindOfClass:[ASIHTTPRequest class]]) { 81 if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
82 requestsCount++; 82 requestsCount++;
83 - [(ASIHTTPRequest *)operation setUploadProgressDelegate:self]; 83 + if (uploadProgressDelegate) {
84 - [(ASIHTTPRequest *)operation setDownloadProgressDelegate:self]; 84 + [(ASIHTTPRequest *)operation setUploadProgressDelegate:self];
  85 + } else {
  86 + [(ASIHTTPRequest *)operation setUploadProgressDelegate:NULL];
  87 + }
  88 + if (downloadProgressDelegate) {
  89 + [(ASIHTTPRequest *)operation setDownloadProgressDelegate:self];
  90 + } else {
  91 + [(ASIHTTPRequest *)operation setDownloadProgressDelegate:NULL];
  92 + }
85 [(ASIHTTPRequest *)operation setDelegate:self]; 93 [(ASIHTTPRequest *)operation setDelegate:self];
86 [(ASIHTTPRequest *)operation setDidFailSelector:@selector(requestDidFail:)]; 94 [(ASIHTTPRequest *)operation setDidFailSelector:@selector(requestDidFail:)];
87 [(ASIHTTPRequest *)operation setDidFinishSelector:@selector(requestDidFinish:)]; 95 [(ASIHTTPRequest *)operation setDidFinishSelector:@selector(requestDidFinish:)];
1 To do: 1 To do:
  2 +Add note about not locking main thread when using progress
2 Replace this with a link to the docs? 3 Replace this with a link to the docs?
  4 +Add to release notes - lots of leaks fixed, and it it releases temporary stuff during a request rather than waiting until the end
3 5
4 6
5 [NEW!] Documentation is available at: http://allseeing-i.com/asi-http-request 7 [NEW!] Documentation is available at: http://allseeing-i.com/asi-http-request