From 68461492bc8b885d96c22ae0e4847dd32add6f51 Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Mon, 28 Aug 2017 22:37:51 -0700 Subject: [PATCH] iOS: Warn about slow main thread React methods Summary: When code blocks the UI thread for too long, it's a bad sign because this can prevent the app from remaining responsive. This change helps detect such responsiveness issues by warning when a React method executes on the UI thread longer than some threshold. **Test Plan** Changed AppState's getCurrentAppState method to sleep for 500 ms and verified that a warning was emitted: ``` 2017-08-17 19:45:29.479 [warn][tid:main][RCTModuleMethod.mm:527] mainThreadWatchdog: invocation of [RCTAppState getCurrentAppState:error:] took 501ms ``` Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15542 Differential Revision: D5724764 Pulled By: shergin fbshipit-source-id: f1dc4bf17d3657c397720a47fabc7f32bf81b7ac --- React/Base/RCTModuleMethod.mm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/React/Base/RCTModuleMethod.mm b/React/Base/RCTModuleMethod.mm index 1309db17b..755b8d40e 100644 --- a/React/Base/RCTModuleMethod.mm +++ b/React/Base/RCTModuleMethod.mm @@ -22,6 +22,10 @@ #import "RCTProfile.h" #import "RCTUtils.h" +#if !defined(RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD) && defined(RCT_DEBUG) +#define RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD 0.020 // seconds +#endif + typedef BOOL (^RCTArgumentBlock)(RCTBridge *, NSUInteger, id); @implementation RCTMethodArgument @@ -516,7 +520,22 @@ RCT_EXTERN_C_END } // Invoke method +#ifdef RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD + if (RCTIsMainQueue()) { + CFTimeInterval start = CACurrentMediaTime(); + [_invocation invokeWithTarget:module]; + CFTimeInterval duration = CACurrentMediaTime() - start; + if (duration > RCT_MAIN_THREAD_WATCH_DOG_THRESHOLD) { + RCTLogWarn(@"mainThreadWatchdog: invocation of %@ blocked the main thread for %dms. Consider spending less time on the main thread to keep the app's UI responsive.", + [self methodName], + (int)(duration * 1000)); + } + } else { + [_invocation invokeWithTarget:module]; + } +#else [_invocation invokeWithTarget:module]; +#endif index = 2; [_retainedObjects removeAllObjects];