From 31e408b3916173bf3b43ec4284506e8cc3de5be4 Mon Sep 17 00:00:00 2001 From: SHANKS Date: Wed, 23 Jan 2019 02:39:51 -0800 Subject: [PATCH] Fix RCTImageLoader multi thread crash (#22746) Summary: Fix crash similar to #22410 react-native: 0.51.0 react: 16.0.0 Changelog: ---------- [iOS] [Changed] - Use onw serial queue to execute invalidate and send request action in RCTHTTPRequestHandler.mm. Message: -------- ``` - (void)invalidate { [_session invalidateAndCancel]; _session = nil; } - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate { // Lazy setup if (!_session && [self isValid]) { NSOperationQueue *callbackQueue = [NSOperationQueue new]; callbackQueue.maxConcurrentOperationCount = 1; callbackQueue.underlyingQueue = [[_bridge networking] methodQueue]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; [configuration setHTTPShouldSetCookies:YES]; [configuration setHTTPCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; [configuration setHTTPCookieStorage:[NSHTTPCookieStorage sharedHTTPCookieStorage]]; _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:callbackQueue]; std::lock_guard lock(_mutex); _delegates = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:0]; } NSURLSessionDataTask *task = [_session dataTaskWithRequest:request]; { std::lock_guard lock(_mutex); [_delegates setObject:delegate forKey:task]; } [task resume]; return task; } ``` now the invalidate function is called by the RCTBridge.invalidate->RCTCxxBridge.invalidate->[moduleData.instance invalidate] , this is on the "com.facebook.react.HTTPRequestHandlerQueue". the sendRequest:withDelegate function is called by RCTImageLoader and is on the "com.facebook.react.imageLoaderURLRequestQueue". when one thread step in invalidate and execute [_session invalidateAndCancel] and the another thread step in sendRequest:withDelegate and execute [_session dataTaskWithRequest:request], the _session is invalidate, so there will be a crash "Task created in a session that has been invalidated" Pull Request resolved: https://github.com/facebook/react-native/pull/22746 Differential Revision: D13781512 Pulled By: cpojer fbshipit-source-id: bd5fd1edf593e2bcdcc18596a29e906882bac8a4 --- Libraries/Network/RCTHTTPRequestHandler.mm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Libraries/Network/RCTHTTPRequestHandler.mm b/Libraries/Network/RCTHTTPRequestHandler.mm index 863fea857ddfd7..2ddbb8a52b7e1d 100644 --- a/Libraries/Network/RCTHTTPRequestHandler.mm +++ b/Libraries/Network/RCTHTTPRequestHandler.mm @@ -23,13 +23,16 @@ @implementation RCTHTTPRequestHandler } @synthesize bridge = _bridge; +@synthesize methodQueue = _methodQueue; RCT_EXPORT_MODULE() - (void)invalidate { - [_session invalidateAndCancel]; - _session = nil; + dispatch_async(self->_methodQueue, ^{ + [self->_session invalidateAndCancel]; + self->_session = nil; + }); } - (BOOL)isValid @@ -73,8 +76,10 @@ - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request valueOptions:NSPointerFunctionsStrongMemory capacity:0]; } - - NSURLSessionDataTask *task = [_session dataTaskWithRequest:request]; + __block NSURLSessionDataTask *task = nil; + dispatch_sync(self->_methodQueue, ^{ + task = [self->_session dataTaskWithRequest:request]; + }); { std::lock_guard lock(_mutex); [_delegates setObject:delegate forKey:task];