Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(273)

Unified Diff: ios/net/crn_http_protocol_handler_proxy_with_client_thread.mm

Issue 1142383006: CrNet: add pauseable NSURLProtocol and switch to using it (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixes Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/net/crn_http_protocol_handler_proxy_with_client_thread.mm
diff --git a/ios/net/crn_http_protocol_handler_proxy_with_client_thread.mm b/ios/net/crn_http_protocol_handler_proxy_with_client_thread.mm
index 0289e81d630f848a358f9dac2a4aae8fb0025f61..60c51323670eb494514216679ad09706a829cac4 100644
--- a/ios/net/crn_http_protocol_handler_proxy_with_client_thread.mm
+++ b/ios/net/crn_http_protocol_handler_proxy_with_client_thread.mm
@@ -36,16 +36,23 @@
// |requestComplete_| is used in debug to check that the client is not called
// after completion.
BOOL _requestComplete;
+ BOOL _paused;
+
+ base::scoped_nsobject<NSMutableArray> _queuedInvocations;
}
// Performs the selector on |clientThread_| using |runLoopModes_|.
-- (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg;
-// These functions are just wrappers around the corresponding
+- (void)runInvocationQueueOnClientThread;
+- (void)postToClientThread:(SEL)aSelector, ...
+ NS_REQUIRES_NIL_TERMINATION;
+- (void)invokeOnClientThread:(NSInvocation*)invocation;
+// hese functions are just wrappers around the corresponding
// NSURLProtocolClient methods, used for task posting.
- (void)didFailWithErrorOnClientThread:(NSError*)error;
- (void)didLoadDataOnClientThread:(NSData*)data;
- (void)didReceiveResponseOnClientThread:(NSURLResponse*)response;
-- (void)wasRedirectedToRequestOnClientThread:(NSArray*)params;
+- (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request
+ redirectResponse:(NSURLResponse*)response;
- (void)didFinishLoadingOnClientThread;
@end
@@ -68,6 +75,7 @@
_runLoopModes.reset([@[ NSRunLoopCommonModes ] retain]);
else
_runLoopModes.reset([@[ mode, NSRunLoopCommonModes ] retain]);
+ _queuedInvocations.reset([[NSMutableArray alloc] init]);
}
return self;
}
@@ -76,16 +84,70 @@
DCHECK([NSThread currentThread] == _clientThread);
_protocol = nil;
_requestComplete = YES;
+ // TODO(ellyjones): This DCHECK fails sometimes. What this implies is that a
+ // callback is coming from the chrome network stack, being enqueued, and then
+ // this proxy is being invalidated before that callback has ever been
+ // delivered. Disturbingly, in practice these seem to be didLoadData and
+ // similar...
+ DCHECK(_queuedInvocations.get().count == 0);
droger 2015/06/11 08:56:19 I think it's expected that the queue may not be em
Elly Fong-Jones 2015/06/11 13:49:33 Done.
+}
+
+- (void)runInvocationQueueOnClientThread {
+ DCHECK([NSThread currentThread] == _clientThread);
+ DCHECK(!_requestComplete || !_protocol);
+ while (!_paused && _queuedInvocations.get().count > 0) {
droger 2015/06/11 08:56:20 I don't understand this. Do you expect _paused or
Elly Fong-Jones 2015/06/11 13:49:33 Yes, that is the crux of it. A callback can lead t
+ NSInvocation* inv = [_queuedInvocations objectAtIndex:0];
+ // Since |_queuedInvocations| owns the only reference to each queued
+ // invocation, this function has to retain another reference before removing
+ // the queued invocation from the array.
+ [inv retain];
+ [_queuedInvocations removeObjectAtIndex:0];
+ [inv invoke];
+ [inv release];
+ }
}
-- (void)performSelectorOnClientThread:(SEL)aSelector withObject:(id)arg {
- [self performSelector:aSelector
+- (void)postToClientThread:(SEL)aSelector, ... {
+ // Build an NSInvocation representing an invocation of |aSelector| on |self|
+ // with the supplied varargs passed as arguments to the invocation.
+ NSMethodSignature* sig = [self methodSignatureForSelector:aSelector];
+ DCHECK(sig != nil);
+ NSInvocation* inv = [NSInvocation invocationWithMethodSignature:sig];
+ [inv setTarget:self];
+ [inv setSelector:aSelector];
+ [inv retainArguments];
+
+ size_t arg_index = 2;
+ va_list args;
+ va_start(args, aSelector);
+ NSObject* arg = va_arg(args, NSObject*);
+ while (arg != nil) {
+ [inv setArgument:&arg atIndex:arg_index];
+ arg = va_arg(args, NSObject*);
+ arg_index++;
+ }
+ va_end(args);
+
+ DCHECK(arg_index == sig.numberOfArguments);
+ [inv retain];
droger 2015/06/11 08:56:20 Can we remove this retain and the corresponding re
Elly Fong-Jones 2015/06/11 13:49:33 Done.
+ [self performSelector:@selector(invokeOnClientThread:)
onThread:_clientThread
- withObject:arg
+ withObject:inv
waitUntilDone:NO
modes:_runLoopModes];
}
+- (void)invokeOnClientThread:(NSInvocation*)invocation {
+ DCHECK([NSThread currentThread] == _clientThread);
+ DCHECK(!_requestComplete || !_protocol);
+ if (!_paused) {
+ [invocation invoke];
+ } else {
+ [_queuedInvocations addObject:invocation];
+ }
+ [invocation release];
+}
+
#pragma mark Proxy methods called from any thread.
- (void)didFailWithNSErrorCode:(NSInteger)nsErrorCode
@@ -95,25 +157,24 @@
return;
NSError* error =
net::GetIOSError(nsErrorCode, netErrorCode, _url, _creationTime);
- [self performSelectorOnClientThread:@selector(didFailWithErrorOnClientThread:)
- withObject:error];
+ [self postToClientThread:@selector(didFailWithErrorOnClientThread:),
+ error, nil];
}
- (void)didLoadData:(NSData*)data {
DCHECK(_clientThread);
if (!_protocol)
return;
- [self performSelectorOnClientThread:@selector(didLoadDataOnClientThread:)
- withObject:data];
+ [self postToClientThread:@selector(didLoadDataOnClientThread:),
+ data, nil];
}
- (void)didReceiveResponse:(NSURLResponse*)response {
DCHECK(_clientThread);
if (!_protocol)
return;
- [self
- performSelectorOnClientThread:@selector(didReceiveResponseOnClientThread:)
- withObject:response];
+ [self postToClientThread:@selector(didReceiveResponseOnClientThread:),
+ response, nil];
}
- (void)wasRedirectedToRequest:(NSURLRequest*)request
@@ -122,17 +183,16 @@
DCHECK(_clientThread);
if (!_protocol)
return;
- [self performSelectorOnClientThread:@selector(
- wasRedirectedToRequestOnClientThread:)
- withObject:@[ request, redirectResponse ]];
+ [self postToClientThread:@selector(wasRedirectedToRequestOnClientThread:),
+ request, redirectResponse, nil];
}
- (void)didFinishLoading {
DCHECK(_clientThread);
if (!_protocol)
return;
- [self performSelectorOnClientThread:@selector(didFinishLoadingOnClientThread)
- withObject:nil];
+ [self postToClientThread:@selector(didFinishLoadingOnClientThread),
+ nil];
}
// Feature support methods that don't forward to the NSURLProtocolClient.
@@ -160,42 +220,44 @@
#pragma mark Proxy methods called from the client thread.
- (void)didFailWithErrorOnClientThread:(NSError*)error {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
_requestComplete = YES;
[[_protocol client] URLProtocol:_protocol didFailWithError:error];
}
- (void)didLoadDataOnClientThread:(NSData*)data {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
[[_protocol client] URLProtocol:_protocol didLoadData:data];
}
- (void)didReceiveResponseOnClientThread:(NSURLResponse*)response {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
[[_protocol client] URLProtocol:_protocol
didReceiveResponse:response
cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
-- (void)wasRedirectedToRequestOnClientThread:(NSArray*)params {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK_EQ(2u, [params count]);
- DCHECK([params[0] isKindOfClass:[NSURLRequest class]]);
- DCHECK([params[1] isKindOfClass:[NSURLResponse class]]);
- DCHECK(!_requestComplete || !_protocol);
+- (void)wasRedirectedToRequestOnClientThread:(NSURLRequest*)request
+ redirectResponse:(NSURLResponse*)redirectResponse {
[[_protocol client] URLProtocol:_protocol
- wasRedirectedToRequest:params[0]
- redirectResponse:params[1]];
+ wasRedirectedToRequest:request
+ redirectResponse:redirectResponse];
}
- (void)didFinishLoadingOnClientThread {
- DCHECK([NSThread currentThread] == _clientThread);
- DCHECK(!_requestComplete || !_protocol);
_requestComplete = YES;
[[_protocol client] URLProtocolDidFinishLoading:_protocol];
}
+- (void)pause {
+ DCHECK([NSThread currentThread] == _clientThread);
+ // It's legal (in fact, required) for |pause| to be called after the request
+ // has already finished, so the usual invalidation DCHECK is missing here.
+ _paused = YES;
+}
+
+- (void)resume {
+ DCHECK([NSThread currentThread] == _clientThread);
+ DCHECK(!_requestComplete || !_protocol);
+ _paused = NO;
+ [self runInvocationQueueOnClientThread];
+}
+
@end
« ios/net/crn_http_protocol_handler_proxy.h ('K') | « ios/net/crn_http_protocol_handler_proxy.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698