Files
react-native/React/CoreModules/RCTDeviceInfo.mm
T
Ramanpreet Nara ddc4225dd4 Move NativeModule initialization logic outside of setModuleRegistry
Summary:
## Context
A while ago, we introduced a new initialization API in NativeModules via RCTInitializing.h (diff: D28435078 (https://github.com/facebook/react-native/commit/9b45df1fced066f40034b0a58be6f4caafd5f785)).

## Problem
A number of our NativeModules still use setModuleRegistry to perform initialization.

## Changes
This diff migrates those NativeModules to instead use the initialize API.

## Motivation
In bridgeless mode each NativeModule object is [created and decorated by the RCTInstance](https://www.internalfb.com/code/fbsource/[89f6c0df78e453a20555975e06bc46b4e0d2bbe9]/fbobjc/Apps/Internal/Venice/Core/RCTInstance.mm?lines=180-189), while [holding the TurboModuleManagerDelegate mutex](https://www.internalfb.com/code/fbsource/[c50ce2bb3fb078d28e1f9afdef5e8793f1413472]/xplat/js/react-native-github/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm?lines=429%2C431). After D30753286, setModuleRegistry will be called in RCTInstance getModuleInstanceForClass, which means that we'll start calling setModuleRegistry while holding the TurboModuleManagerDelegate lock. This leads to a deadlock, because calling setModuleRegistry on RCTDeviceInfo [creates RCTAccessibilityManager](https://www.internalfb.com/code/fbsource/[89f6c0df78e453a20555975e06bc46b4e0d2bbe9]/xplat/js/react-native-github/React/CoreModules/RCTDeviceInfo.mm?lines=50), which tries to acquire the TurboModuleManagerDelegate lock again. The NativeModule initialize method isn't called while holding the TurboModuleManagerDelegate lock. That's why moving all initialization logic to the initialize method mitigates this deadlock hazard.

In general, we shouldn't do any sort of initialization inside setters for these bridge/bridgeless APIs. No other NativeModules do initialization outside of initialize.

Changelog: [Internal]

Reviewed By: sammy-SC

Differential Revision: D30754870

fbshipit-source-id: 7c2d50f995cba6f58ee2dfebfabd36f640579bcb
2021-09-08 12:46:35 -07:00

222 lines
7.4 KiB
Plaintext

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDeviceInfo.h"
#import <FBReactNativeSpec/FBReactNativeSpec.h>
#import <React/RCTAccessibilityManager.h>
#import <React/RCTAssert.h>
#import <React/RCTConstants.h>
#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTInitializing.h>
#import <React/RCTUIUtils.h>
#import <React/RCTUtils.h>
#import "CoreModulesPlugins.h"
using namespace facebook::react;
@interface RCTDeviceInfo () <NativeDeviceInfoSpec, RCTInitializing>
@end
@implementation RCTDeviceInfo {
UIInterfaceOrientation _currentInterfaceOrientation;
NSDictionary *_currentInterfaceDimensions;
}
@synthesize moduleRegistry = _moduleRegistry;
RCT_EXPORT_MODULE()
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (void)initialize
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveNewContentSizeMultiplier)
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
object:[_moduleRegistry moduleForName:"AccessibilityManager"]];
_currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceOrientationDidChange)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
_currentInterfaceDimensions = RCTExportedDimensions(_moduleRegistry);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceFrameDidChange)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceFrameDidChange)
name:RCTUserInterfaceStyleDidChangeNotification
object:nil];
}
static BOOL RCTIsIPhoneX()
{
static BOOL isIPhoneX = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
RCTAssertMainQueue();
CGSize screenSize = [UIScreen mainScreen].nativeBounds.size;
CGSize iPhoneXScreenSize = CGSizeMake(1125, 2436);
CGSize iPhoneXMaxScreenSize = CGSizeMake(1242, 2688);
CGSize iPhoneXRScreenSize = CGSizeMake(828, 1792);
CGSize iPhone12ScreenSize = CGSizeMake(1170, 2532);
CGSize iPhone12MiniScreenSize = CGSizeMake(1080, 2340);
CGSize iPhone12ProMaxScreenSize = CGSizeMake(1284, 2778);
isIPhoneX = CGSizeEqualToSize(screenSize, iPhoneXScreenSize) ||
CGSizeEqualToSize(screenSize, iPhoneXMaxScreenSize) || CGSizeEqualToSize(screenSize, iPhoneXRScreenSize) ||
CGSizeEqualToSize(screenSize, iPhone12ScreenSize) || CGSizeEqualToSize(screenSize, iPhone12MiniScreenSize) ||
CGSizeEqualToSize(screenSize, iPhone12ProMaxScreenSize);
;
});
return isIPhoneX;
}
static NSDictionary *RCTExportedDimensions(RCTModuleRegistry *moduleRegistry)
{
RCTAssertMainQueue();
RCTDimensions dimensions;
if (moduleRegistry) {
dimensions = RCTGetDimensions(
((RCTAccessibilityManager *)[moduleRegistry moduleForName:"AccessibilityManager"]).multiplier ?: 1.0);
} else {
RCTAssert(false, @"ModuleRegistry must be set to properly init dimensions.");
}
__typeof(dimensions.window) window = dimensions.window;
NSDictionary<NSString *, NSNumber *> *dimsWindow = @{
@"width" : @(window.width),
@"height" : @(window.height),
@"scale" : @(window.scale),
@"fontScale" : @(window.fontScale)
};
__typeof(dimensions.screen) screen = dimensions.screen;
NSDictionary<NSString *, NSNumber *> *dimsScreen = @{
@"width" : @(screen.width),
@"height" : @(screen.height),
@"scale" : @(screen.scale),
@"fontScale" : @(screen.fontScale)
};
return @{@"window" : dimsWindow, @"screen" : dimsScreen};
}
- (NSDictionary<NSString *, id> *)constantsToExport
{
return [self getConstants];
}
- (NSDictionary<NSString *, id> *)getConstants
{
__block NSDictionary<NSString *, id> *constants;
RCTModuleRegistry *moduleRegistry = _moduleRegistry;
RCTUnsafeExecuteOnMainQueueSync(^{
constants = @{
@"Dimensions" : RCTExportedDimensions(moduleRegistry),
// Note:
// This prop is deprecated and will be removed in a future release.
// Please use this only for a quick and temporary solution.
// Use <SafeAreaView> instead.
@"isIPhoneX_deprecated" : @(RCTIsIPhoneX()),
};
});
return constants;
}
- (void)didReceiveNewContentSizeMultiplier
{
RCTModuleRegistry *moduleRegistry = _moduleRegistry;
RCTExecuteOnMainQueue(^{
// Report the event across the bridge.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"didUpdateDimensions"
body:RCTExportedDimensions(moduleRegistry)];
#pragma clang diagnostic pop
});
}
- (void)interfaceOrientationDidChange
{
__weak __typeof(self) weakSelf = self;
RCTExecuteOnMainQueue(^{
[weakSelf _interfaceOrientationDidChange];
});
}
- (void)_interfaceOrientationDidChange
{
UIInterfaceOrientation nextOrientation = [RCTSharedApplication() statusBarOrientation];
// Update when we go from portrait to landscape, or landscape to portrait
if ((UIInterfaceOrientationIsPortrait(_currentInterfaceOrientation) &&
!UIInterfaceOrientationIsPortrait(nextOrientation)) ||
(UIInterfaceOrientationIsLandscape(_currentInterfaceOrientation) &&
!UIInterfaceOrientationIsLandscape(nextOrientation))) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"didUpdateDimensions"
body:RCTExportedDimensions(_moduleRegistry)];
#pragma clang diagnostic pop
}
_currentInterfaceOrientation = nextOrientation;
}
- (void)interfaceFrameDidChange
{
__weak __typeof(self) weakSelf = self;
RCTExecuteOnMainQueue(^{
[weakSelf _interfaceFrameDidChange];
});
}
- (void)_interfaceFrameDidChange
{
NSDictionary *nextInterfaceDimensions = RCTExportedDimensions(_moduleRegistry);
if (!([nextInterfaceDimensions isEqual:_currentInterfaceDimensions])) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"didUpdateDimensions"
body:nextInterfaceDimensions];
#pragma clang diagnostic pop
}
_currentInterfaceDimensions = nextInterfaceDimensions;
}
- (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
{
return std::make_shared<NativeDeviceInfoSpecJSI>(params);
}
@end
Class RCTDeviceInfoCls(void)
{
return RCTDeviceInfo.class;
}