Skip to content

Commit fcead14

Browse files
Saadnajmifacebook-github-bot
authored andcommitted
Fix Deadlock in RCTi18nUtil (iOS) (#31032)
Summary: Note: PR to react-native-macos here microsoft#733 Internally in Microsoft code, we ran into a deadlock where the main queue and the UIManager queue were both trying to access `[RCTI18nUtil sharedInstance]`, and were blocked on each other. This is similar to an earlier issue with RCTScreenScale decsribed [here](#18096). To summarize: 1- RCTShadowView (on the UIManager queue) and RCTView (on the main queue) both try to access `[RCTI18nUtil sharedInstance]` 2- The UIManager thread gets there first, and lazily initializes the sharedInstance. Meanwhile, the main thread is waiting on a lock possessed by the UIManager thread 3- As part of the initialization, we set an NSUserDefault, which seems to require the (blocked) main thread. 4- Deadlock. For whatever reason, this only happens on debug. I did not figure out why, but I do know based on [this comment](#18096 (comment)), that the UIManagerQueue should never block the main queue. The fix is to not use NSUserDefaults, and simpy use atomic properties instead. We get the thread safety for free, and it also simplifies the code somewhat without changing the public API. The downside is values aren't persisted anymore, but I do not think that was necessary / intended. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [iOS] [Fixed] - Fix deadlock on RCTi18nUtil Pull Request resolved: #31032 Test Plan: Ran the RTLExample in RNTester, and ensured switching to RTL still worked, and that setting forceRTL would still work after reloading the bundle. https://user-images.githubusercontent.com/6722175/108775429-aefdae80-7526-11eb-9a89-3114f7ddc2af.mov Reviewed By: javache Differential Revision: D29522152 Pulled By: RSNara fbshipit-source-id: 160840f63a7b1d6721b0fd8294fb11990a4509fa
1 parent 91437d6 commit fcead14

File tree

2 files changed

+15
-53
lines changed

2 files changed

+15
-53
lines changed

React/Modules/RCTI18nUtil.h

+14-6
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@
1818
+ (instancetype)sharedInstance;
1919

2020
- (BOOL)isRTL;
21-
- (BOOL)isRTLAllowed;
22-
- (void)allowRTL:(BOOL)value;
23-
- (BOOL)isRTLForced;
24-
- (void)forceRTL:(BOOL)value;
25-
- (BOOL)doLeftAndRightSwapInRTL;
26-
- (void)swapLeftAndRightInRTL:(BOOL)value;
21+
22+
/**
23+
* Should be used very early during app start up
24+
* Before the bridge is initialized
25+
*/
26+
@property (atomic, setter=allowRTL:) BOOL isRTLAllowed;
27+
28+
/**
29+
* Could be used to test RTL layout with English
30+
* Used for development and testing purpose
31+
*/
32+
@property (atomic, setter=forceRTL:) BOOL isRTLForced;
33+
34+
@property (atomic, setter=swapLeftAndRightInRTL:) BOOL doLeftAndRightSwapInRTL;
2735

2836
@end

React/Modules/RCTI18nUtil.m

+1-47
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ + (instancetype)sharedInstance
1818
dispatch_once(&onceToken, ^{
1919
sharedInstance = [self new];
2020
[sharedInstance swapLeftAndRightInRTL:true];
21+
[sharedInstance allowRTL:true];
2122
});
2223

2324
return sharedInstance;
@@ -40,53 +41,6 @@ - (BOOL)isRTL
4041
return NO;
4142
}
4243

43-
/**
44-
* Should be used very early during app start up
45-
* Before the bridge is initialized
46-
* @return whether the app allows RTL layout, default is true
47-
*/
48-
- (BOOL)isRTLAllowed
49-
{
50-
NSNumber *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"RCTI18nUtil_allowRTL"];
51-
if (value == nil) {
52-
return YES;
53-
}
54-
return [value boolValue];
55-
}
56-
57-
- (void)allowRTL:(BOOL)rtlStatus
58-
{
59-
[[NSUserDefaults standardUserDefaults] setBool:rtlStatus forKey:@"RCTI18nUtil_allowRTL"];
60-
[[NSUserDefaults standardUserDefaults] synchronize];
61-
}
62-
63-
/**
64-
* Could be used to test RTL layout with English
65-
* Used for development and testing purpose
66-
*/
67-
- (BOOL)isRTLForced
68-
{
69-
BOOL rtlStatus = [[NSUserDefaults standardUserDefaults] boolForKey:@"RCTI18nUtil_forceRTL"];
70-
return rtlStatus;
71-
}
72-
73-
- (void)forceRTL:(BOOL)rtlStatus
74-
{
75-
[[NSUserDefaults standardUserDefaults] setBool:rtlStatus forKey:@"RCTI18nUtil_forceRTL"];
76-
[[NSUserDefaults standardUserDefaults] synchronize];
77-
}
78-
79-
- (BOOL)doLeftAndRightSwapInRTL
80-
{
81-
return [[NSUserDefaults standardUserDefaults] boolForKey:@"RCTI18nUtil_makeRTLFlipLeftAndRightStyles"];
82-
}
83-
84-
- (void)swapLeftAndRightInRTL:(BOOL)value
85-
{
86-
[[NSUserDefaults standardUserDefaults] setBool:value forKey:@"RCTI18nUtil_makeRTLFlipLeftAndRightStyles"];
87-
[[NSUserDefaults standardUserDefaults] synchronize];
88-
}
89-
9044
// Check if the current device language is RTL
9145
- (BOOL)isDevicePreferredLanguageRTL
9246
{

0 commit comments

Comments
 (0)