Permalink
Browse files

Progress updates should now only happen in the main thread, fixing ip…

…hone issues
  • Loading branch information...
pokeb committed Nov 9, 2008
1 parent c3974a4 commit 53e0cedf107c9c3dcfdb1649a489d1f3779fa153
Showing with 76 additions and 23 deletions.
  1. +0 −3 ASIHTTPRequest.h
  2. +64 −18 ASIHTTPRequest.m
  3. +10 −2 ASINetworkQueue.m
  4. +2 −0 README
View
@@ -112,9 +112,6 @@
//This lock will block the request until the delegate supplies authentication info
NSConditionLock *authenticationLock;
- //This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
- NSLock *progressLock;
-
//Called on the delegate when the request completes successfully
SEL didFinishSelector;
View
@@ -31,13 +31,21 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
[((ASIHTTPRequest*)clientCallBackInfo) handleNetworkEvent: type];
}
+//This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
+
+static NSLock *progressLock;
@implementation ASIHTTPRequest
#pragma mark init / dealloc
+- (void)initialize
+{
+ progressLock = [[NSLock alloc] init];
+}
+
- (id)initWithURL:(NSURL *)newURL
{
self = [super init];
@@ -85,7 +93,6 @@ - (void)dealloc
[domain release];
[authenticationRealm release];
[url release];
- [progressLock release];
[authenticationLock release];
[lastActivityTime release];
[responseCookies release];
@@ -214,9 +221,6 @@ - (void)main
- (void)loadRequest
{
CFRunLoopAddCommonMode(CFRunLoopGetCurrent(),ASIHTTPRequestRunMode);
-
- [progressLock release];
- progressLock = [[NSLock alloc] init];
[authenticationLock release];
authenticationLock = [[NSConditionLock alloc] initWithCondition:1];
@@ -270,14 +274,18 @@ - (void)loadRequest
}
//Record when the request started, so we can timeout if nothing happens
- [self setLastActivityTime:[[NSDate new] autorelease]];
+ [self setLastActivityTime:[NSDate date]];
// Wait for the request to finish
while (!complete) {
+ //This may take a while, so we'll release the pool each cycle to stop a giant backlog building up
+ [pool release];
+ pool = [[NSAutoreleasePool alloc] init];
+
//See if we need to timeout
if (lastActivityTime && timeOutSeconds > 0) {
- if ([[[NSDate new] autorelease] timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
+ if ([[NSDate date] timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
[self failWithProblem:@"Request timed out"];
[self cancelLoad];
complete = YES;
@@ -292,7 +300,7 @@ - (void)loadRequest
complete = YES;
break;
}
- [self updateProgressIndicators];
+ [self performSelectorOnMainThread:@selector(updateProgressIndicators) withObject:nil waitUntilDone:YES];
//This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does.
CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES);
@@ -332,20 +340,16 @@ - (void)cancelLoad
- (void)updateProgressIndicators
{
- [progressLock lock];
- if ([self isCancelled]) {
- [progressLock unlock];
- return;
- }
+
[self updateUploadProgress];
[self updateDownloadProgress];
- [progressLock unlock];
}
+ (void)setProgress:(double)progress forProgressIndicator:(id)indicator
{
+
SEL selector;
//Cocoa Touch: UIProgressView
@@ -393,6 +397,12 @@ - (void)setUploadProgressDelegate:(id)newDelegate
-(void)removeUploadProgressSoFar
{
+ [progressLock lock];
+ if ([self isCancelled]) {
+ [progressLock unlock];
+ return;
+ }
+
//We're using a progress queue or compatible controller to handle progress
if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) {
int value = 0-lastBytesSent;
@@ -407,12 +417,19 @@ -(void)removeUploadProgressSoFar
//We aren't using a queue, we should just set progress of the indicator to 0
} else {
[ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate];
- }
+ }
+ [progressLock unlock];
}
- (void)resetUploadProgress:(NSNumber *)max
{
+ [progressLock lock];
+ if ([self isCancelled]) {
+ [progressLock unlock];
+ return;
+ }
+
//We're using a progress queue or compatible controller to handle progress
if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadSizeBy:)]) {
int value = [max intValue];
@@ -426,11 +443,18 @@ - (void)resetUploadProgress:(NSNumber *)max
} else {
[ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate];
}
+ [progressLock unlock];
}
- (void)updateUploadProgress
{
- [self setLastActivityTime:[[NSDate new] autorelease]];
+ [progressLock lock];
+ if ([self isCancelled]) {
+ [progressLock unlock];
+ return;
+ }
+
+ [self setLastActivityTime:[NSDate date]];
unsigned int byteCount = [[(NSNumber *)CFReadStreamCopyProperty (readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount) autorelease] unsignedIntValue];
if (uploadProgressDelegate) {
@@ -439,7 +463,8 @@ - (void)updateUploadProgress
if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) {
int value = byteCount-lastBytesSent;
SEL selector = @selector(incrementUploadProgressBy:);
- NSMethodSignature *signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector];
+ NSMethodSignature *signature = nil;
+ signature = [[uploadProgressDelegate class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:uploadProgressDelegate];
[invocation setSelector:selector];
@@ -453,11 +478,19 @@ - (void)updateUploadProgress
}
lastBytesSent = byteCount;
+ [progressLock unlock];
}
- (void)resetDownloadProgress:(NSNumber *)max
{
+ [progressLock lock];
+ if ([self isCancelled]) {
+ [progressLock unlock];
+ return;
+ }
+
+
//We're using a progress queue or compatible controller to handle progress
if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) {
int value = [max intValue];
@@ -471,17 +504,27 @@ - (void)resetDownloadProgress:(NSNumber *)max
} else {
[ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate];
}
+ [progressLock unlock];
}
- (void)updateDownloadProgress
{
- [self setLastActivityTime:[[NSDate new] autorelease]];
+ [progressLock lock];
+ if ([self isCancelled]) {
+ [progressLock unlock];
+ return;
+ }
+
+ [self setLastActivityTime:[NSDate date]];
//We won't update downlaod progress until we've examined the headers, since we might need to authenticate
if (downloadProgressDelegate && responseHeaders) {
//We're using a progress queue or compatible controller to handle progress
if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) {
+
+ NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
+
int value = totalBytesRead-lastBytesRead;
SEL selector = @selector(incrementDownloadProgressBy:);
NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
@@ -491,13 +534,16 @@ - (void)updateDownloadProgress
[invocation setArgument:&value atIndex:2];
[invocation invoke];
+ [thePool release];
+
//We aren't using a queue, we should just set progress of the indicator to 0
} else if (contentLength > 0) {
[ASIHTTPRequest setProgress:(double)(totalBytesRead/contentLength) forProgressIndicator:downloadProgressDelegate];
}
lastBytesRead = totalBytesRead;
- }
+ }
+ [progressLock unlock];
}
#pragma mark handling request complete / failure
View
@@ -80,8 +80,16 @@ - (void)addOperation:(NSOperation *)operation
{
if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
requestsCount++;
- [(ASIHTTPRequest *)operation setUploadProgressDelegate:self];
- [(ASIHTTPRequest *)operation setDownloadProgressDelegate:self];
+ if (uploadProgressDelegate) {
+ [(ASIHTTPRequest *)operation setUploadProgressDelegate:self];
+ } else {
+ [(ASIHTTPRequest *)operation setUploadProgressDelegate:NULL];
+ }
+ if (downloadProgressDelegate) {
+ [(ASIHTTPRequest *)operation setDownloadProgressDelegate:self];
+ } else {
+ [(ASIHTTPRequest *)operation setDownloadProgressDelegate:NULL];
+ }
[(ASIHTTPRequest *)operation setDelegate:self];
[(ASIHTTPRequest *)operation setDidFailSelector:@selector(requestDidFail:)];
[(ASIHTTPRequest *)operation setDidFinishSelector:@selector(requestDidFinish:)];
View
2 README
@@ -1,5 +1,7 @@
To do:
+Add note about not locking main thread when using progress
Replace this with a link to the docs?
+Add to release notes - lots of leaks fixed, and it it releases temporary stuff during a request rather than waiting until the end
[NEW!] Documentation is available at: http://allseeing-i.com/asi-http-request

0 comments on commit 53e0ced

Please sign in to comment.