Protect _handlers in RCTNetworking with mutex even if RCTNetworking has been deallocated

Summary:
Changelog:
[iOS][Fixed][Internal] - Protect handlers in RCTNetworking with mutex even if RCTNetworking has been deallocated

# The Logview
We get this error in LogView: `Unhandled JS Exception: Error: Exception in HostFunction: mutex lock failed: Invalid argument`, which is an C++ std::error. "This typically happens when .lock() is called on a mutex that is not yet constructed, or has already been destructed."

# Hypothesis of issue
The LogView says the line that throws this softerror is [RCTNetworking.ios.js](8bd3edec88/Libraries/Network/RCTNetworking.ios.js (L87)).

Inside RCTNetworking, there's only [one mutex and only one line where is is being used](8bd3edec88/Libraries/Network/RCTNetworking.mm (L207-L215)
), to protect the _handlers array.

My guess is that RCTNetworking was deallocated, which made `_handlersLock` nil, so it resulted in this error when we tried to lock it.

# This diff

* Add `std::lock_guard<std::mutex> lock(_handlersLock);` to `invalidate()`
* Move `_handlersLock` logic to its own method instead of using a lambda. If `self` is being deallocated, I would guess that this method (`[self prioritizedHandlers]`) would return nil.

Referencing this for correct ways to use lock_guard with mutex: https://devblogs.microsoft.com/oldnewthing/20210709-00/?p=105425

Reviewed By: fkgozali

Differential Revision: D36886233

fbshipit-source-id: 60246f4d9bbc1d834497e4fb8a61d9c0e9623510
This commit is contained in:
Paige Sun 2022-06-05 11:02:11 -07:00 коммит произвёл Facebook GitHub Bot
Родитель 0035cc9292
Коммит 5ed6ac1f25
1 изменённых файлов: 34 добавлений и 28 удалений

Просмотреть файл

@ -179,6 +179,8 @@ RCT_EXPORT_MODULE()
{
[super invalidate];
std::lock_guard<std::mutex> lock(_handlersLock);
for (NSNumber *requestID in _tasksByRequestID) {
[_tasksByRequestID[requestID] cancel];
}
@ -203,37 +205,13 @@ RCT_EXPORT_MODULE()
if (!request.URL) {
return nil;
}
{
std::lock_guard<std::mutex> lock(_handlersLock);
if (!_handlers) {
if (_handlersProvider) {
_handlers = _handlersProvider(self.moduleRegistry);
} else {
_handlers = [self.bridge modulesConformingToProtocol:@protocol(RCTURLRequestHandler)];
}
// Get handlers, sorted in reverse priority order (highest priority first)
_handlers = [_handlers sortedArrayUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
}
}
NSArray<id<RCTURLRequestHandler>> *handlers = [self prioritizedHandlers];
if (RCT_DEBUG) {
// Check for handler conflicts
float previousPriority = 0;
id<RCTURLRequestHandler> previousHandler = nil;
for (id<RCTURLRequestHandler> handler in _handlers) {
for (id<RCTURLRequestHandler> handler in handlers) {
float priority = [handler respondsToSelector:@selector(handlerPriority)] ? [handler handlerPriority] : 0;
if (previousHandler && priority < previousPriority) {
return previousHandler;
@ -256,7 +234,7 @@ RCT_EXPORT_MODULE()
}
// Normal code path
for (id<RCTURLRequestHandler> handler in _handlers) {
for (id<RCTURLRequestHandler> handler in handlers) {
if ([handler canHandleRequest:request]) {
return handler;
}
@ -264,6 +242,34 @@ RCT_EXPORT_MODULE()
return nil;
}
- (NSArray<id<RCTURLRequestHandler>> *)prioritizedHandlers
{
std::lock_guard<std::mutex> lock(_handlersLock);
if (_handlers) {
return _handlers;
}
NSArray<id<RCTURLRequestHandler>> *newHandlers = _handlersProvider
? _handlersProvider(self.moduleRegistry)
: [self.bridge modulesConformingToProtocol:@protocol(RCTURLRequestHandler)];
// Get handlers, sorted in reverse priority order (highest priority first)
newHandlers = [newHandlers sortedArrayUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
_handlers = newHandlers;
return newHandlers;
}
- (NSDictionary<NSString *, id> *)stripNullsInRequestHeaders:(NSDictionary<NSString *, id> *)headers
{
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:headers.count];