From 809f1e97dea85cd259207ed729dcde6ed65a9348 Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Fri, 18 Aug 2017 13:08:56 -0700 Subject: [PATCH] Avoid race condition for AppState.currentState Summary: When the AppState module is initialized, it subscribes to the `appStateDidChange` event and sends an async native method call to the AppState native module. There is a small race condition window where the native module can read the current app state as `uninitialized` before calling the JavaScript callback, and then be interrupted by the underlying mechanism to trigger the `appStateDidChange` event. If the `appStateDidChange` event is processed before the JavaScript callback, the resulting value of `AppState.currentState` will be invalid. Fixes Microsoft/react-native-windows#1300 (Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!) I simulated (over-exaggerated) the race condition by injecting "thread sleep" calls in the native method call and the native mechanism for updating the app state. I then ran the AppStateExample in the RNTester and found that the current app state was set to `uninitialized`, as opposed to the expected value of `active`. Once I made this JavaScript change, the over-exaggerated race condition no longer resulted in an invalid app state. Closes https://github.com/facebook/react-native/pull/15499 Differential Revision: D5660620 Pulled By: hramos fbshipit-source-id: 47c0dca75d37f677191c48f2148a72edd9cdd0e2 --- Libraries/AppState/AppState.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Libraries/AppState/AppState.js b/Libraries/AppState/AppState.js index 365b6a58b..61cfd5d0c 100644 --- a/Libraries/AppState/AppState.js +++ b/Libraries/AppState/AppState.js @@ -102,6 +102,8 @@ class AppState extends NativeEventEmitter { // the Android implementation. this.currentState = RCTAppState.initialAppState || 'active'; + let eventUpdated = false; + // TODO: this is a terrible solution - in order to ensure `currentState` prop // is up to date, we have to register an observer that updates it whenever // the state changes, even if nobody cares. We should just deprecate the @@ -109,6 +111,7 @@ class AppState extends NativeEventEmitter { this.addListener( 'appStateDidChange', (appStateData) => { + eventUpdated = true; this.currentState = appStateData.app_state; } ); @@ -118,7 +121,9 @@ class AppState extends NativeEventEmitter { // and expose `getCurrentAppState` method directly. RCTAppState.getCurrentAppState( (appStateData) => { - this.currentState = appStateData.app_state; + if (!eventUpdated) { + this.currentState = appStateData.app_state; + } }, logError );