react-native: Xcode 14.0 beta 4 reports "Hang Risk" in RCTWebSocket

Description

Error reported: Thread running at QOS_CLASS_USER_INTERACTIVE waiting on a lower QoS thread running at QOS_CLASS_DEFAULT. Investigate ways to avoid priority inversions. Using Xcode Version 14.0 beta 4 (14A5284g).

Screenshot 2022-08-12 at 17 44 11

Seemingly originates from RCTSRWebSocket.m

Version

0.66.3

Output of npx react-native info

info Fetching system and libraries information… System: OS: macOS 12.5 CPU: (16) x64 Intel® Core™ i9-9880H CPU @ 2.30GHz Memory: 186.13 MB / 16.00 GB Shell: 5.8 - /usr/local/bin/zsh Binaries: Node: 14.17.1 - ~/.nvm/versions/node/v14.17.1/bin/node Yarn: 1.22.10 - ~/src/github.com/umbermoney/daisy/node_modules/.bin/yarn npm: 6.14.13 - ~/.nvm/versions/node/v14.17.1/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.10.1 - /Users/lucas/.rbenv/shims/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.4, macOS 12.3, tvOS 15.4, watchOS 8.5 Android SDK: API Levels: 21, 28, 29, 30 Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.2 System Images: android-29 | Google APIs Intel x86 Atom, android-31 | Google APIs Intel x86 Atom_64 Android NDK: Not Found IDEs: Android Studio: 2021.2 AI-212.5712.43.2112.8609683 Xcode: 13.3/13E113 - /usr/bin/xcodebuild Languages: Java: 1.8.0_251 - /Users/lucas/.jenv/shims/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.66.3 => 0.66.3 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

  1. Create react-native project on Xcode Version 14.0 beta 4 (14A5284g)
  2. Run application

Snack, code example, screenshot, or link to a repository

https://github.com/facebook/react-native/blob/6fcfe2e1b35e9bdf319bcdc1647c8c0d997b58c7/Libraries/WebSocket/RCTSRWebSocket.m#L1636

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 78
  • Comments: 45 (4 by maintainers)

Most upvoted comments

Screenshot 2022-11-09 at 15 34 21 this worked for me

described here https://developer.apple.com/documentation/xcode/diagnosing-performance-issues-early

You just turned off the checker, that doesn’t mean the underlying issue is solved.

I had the same problem ( and I finally fixed it without really knowing why…, probably an indexing issue from XCODE 14).

If may be it can help you, these are the steps I follow : 1) I cleaned the build folder from XCODE (Product > Clean Build Folder) 2) I removed the DerivedData folder in ~/Library/Developer/Xcode/DerivedData 3) I restarted my computer

After that, I opened my app workspace from XCODE, a file indexing process started and took a very long time (something like 15 or 20 minutes).

Once indexing process finished, I tried to build again and VOILA, this time it worked !

I really hope it will help you.

Regards,

Jean-Baptiste

My system: OS: macOS 12.6 Monterey CPU: 1.8 GHz Intel Core i5 Memory: 8 GB 1600 Mhz DDR3 Node : v16.17.1 Xcode: 14.0.1 React: 18.1.0 React-native: 0.70.6

I don’t work on React but as I mentioned in my comment above, this issue is pointing to a priority inversion. Priority inversions won’t crash your program but can cause performance issues especially when system is under heavy resource contention. This inversion is coming from the React framework itself. Folks who maintain the React framework should audit their usage of dispatch_group_wait – it’s a primitive that doesn’t provide priority inversion avoidance.The link (https://developer.apple.com/documentation/xcode/diagnosing-performance-issues-early) explains how these inversions should be addressed.

As for app developers who are seeing this (because they are using React), they should keep an eye on whether this inversion is causing hangs in their app for their app users. If it is, you probably want to deep dive more in the architecture of your app to avoid this inversion.

FWIW, elevating the thread priority suppresses the priority inversion warning:

diff --git a/Libraries/WebSocket/RCTSRWebSocket.m b/Libraries/WebSocket/RCTSRWebSocket.m
index 925596f8677..a6244fb677e 100644
--- a/Libraries/WebSocket/RCTSRWebSocket.m
+++ b/Libraries/WebSocket/RCTSRWebSocket.m
@@ -1688,6 +1688,7 @@ static NSRunLoop *networkRunLoop = nil;
   dispatch_once(&onceToken, ^{
     networkThread = [_RCTSRRunLoopThread new];
     networkThread.name = @"com.squareup.SocketRocket.NetworkThread";
+    networkThread.qualityOfService = NSQualityOfServiceUserInteractive;
     [networkThread start];
     networkRunLoop = networkThread.runLoop;
   });

I have no idea whether this doesn’t negatively affects anything else as I have no clue what it is used for.

That said, it looks like this module has recently been replaced: https://github.com/facebook/react-native/pull/36471

Any updates on this issue, I can reproduce this bug on Fresh react native 0.70.2.

The commit to move SocketRocket to 0.7.0 (which resolves the issue) is already on main: https://github.com/facebook/react-native/commit/bab9c839eaad1ffc358fa5c87440a6de7cbe981b but it doesn’t look like it made it into 0.73.0 or 0.73.1 from what I can see in the release tags.

You could request it to be picked to 0.73.2 here: https://github.com/reactwg/react-native-releases/discussions/95

yes, we are considering it. We couldn’t do it before due to transitive dependencies that would have make other libraries incompatible.

// // Copyright 2012 Square Inc. // Portions Copyright © 2016-present, Facebook, Inc. // // All rights reserved. // // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. An additional grant // of patent rights can be found in the PATENTS file in the same directory. //

#import “SRRunLoopThread.h” #import <pthread.h> @interface SRRunLoopThread () { dispatch_group_t _waitGroup; }

@property (nonatomic, strong, readwrite) NSRunLoop *runLoop; @property (strong, nonatomic) NSLock *lock1; @property (assign, nonatomic) pthread_mutex_t lock; @property (assign, nonatomic) pthread_cond_t cond; @end

@implementation SRRunLoopThread

  • (void)dealloc { // 不用需要销毁 pthread_mutex_destroy(&_lock); pthread_cond_destroy(&_cond); }

  • (void)__initMutex:(pthread_mutex_t *)mutex { // #define PTHREAD_MUTEX_INITIALIZER {_PTHREAD_MUTEX_SIG_init, {0}} // self.mutex = PTHREAD_MUTEX_INITIALIZER;

    // 静态初始化方法,所以这里不能用这种方式初始化,用下面👇🏻方法初始化 // 1.初始化属性 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); /*

    • Mutex type attributes */ // #define PTHREAD_MUTEX_NORMAL 0 // #define PTHREAD_MUTEX_ERRORCHECK 1 // #define PTHREAD_MUTEX_RECURSIVE 2 // #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL

    // pthread_mutexattr_settype(&attr, NULL); 传空,默认 PTHREAD_MUTEX_DEFAULT pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    // 2.初始化锁 pthread_mutex_init(mutex, &attr);

    // 3.销毁属性 pthread_mutexattr_destroy(&attr);

    // 初始化条件 pthread_cond_init(&_cond, NULL);

    pthread_mutex_lock(mutex); }

  • (instancetype)sharedThread { static SRRunLoopThread *thread; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ thread = [[SRRunLoopThread alloc] init]; thread.name = @“com.facebook.SocketRocket.NetworkThread”; [thread start]; }); return thread; }
  • (instancetype)init { self = [super init]; if (self) { // _waitGroup = dispatch_group_create(); // dispatch_group_enter(_waitGroup); [self __initMutex:&_lock]; } return self; }

  • (void)main {

    @autoreleasepool { _runLoop = [NSRunLoop currentRunLoop]; // dispatch_group_leave(_waitGroup); // 激活一个等待该条件的线程 pthread_cond_signal(&_cond); NSLog(@“2222”); // Add an empty run loop source to prevent runloop from spinning. CFRunLoopSourceContext sourceCtx = { .version = 0, .info = NULL, .retain = NULL, .release = NULL, .copyDescription = NULL, .equal = NULL, .hash = NULL, .schedule = NULL, .cancel = NULL, .perform = NULL }; CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source);

      while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
          
      }
      assert(NO);
    

    } }

  • (NSRunLoop *)runLoop; { // 等待条件(进入休眠,放开mutex锁;被唤醒后,会再次对mutex加锁) pthread_cond_wait(&_cond, &_lock); NSLog(@“1111”); return _runLoop; }

@end

这个是我修改的关于SRRunLoopThread.m 的修复。 通过测试socket 可以正常使用。

+1 This seems present on a Fresh RN upgrade

I’ve seen this alternative PR to fix the issue, https://github.com/facebookincubator/SocketRocket/pull/659, but not sure which is the best solution. I guess that whichever it is, we’d only be able to get it in a future version of react-native? Just for interest’s sake, if this projected updated it’s dependency from 0.61 (current) to 0.70 or 0.71, would this still be in a react-native patch release, or would we have to wait for 0.73?