Skip to content

Commit

Permalink
fix: Android headless JS timeout (#33044)
Browse files Browse the repository at this point in the history
Summary:
Fixes #33043 and thereby invertase/react-native-firebase#3955.

The issue arised because when there currently is no available React context, `HeadlessJsTaskService` will create a new one in background and start the task using `onReactContextInitialized` of `ReactInstanceManager.addReactInstanceEventListener`.
https://github.com/facebook/react-native/blob/7ef14af81f3a1532ca1a703da666ea2e5a70a265/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.java#L94-L113
The `TimingModule` however is initialized asynchronously, meaning the headless JS is started before its initialization. That's an issue because the `TimingModule` is only run when there is JS code executing (meaning if the application is running or there is a headless task running) - this is checked by registering a `HeadlessJsTaskEventListener` on the `HeadlessJsTaskContext` in `TimingModule.initialize()`.
https://github.com/facebook/react-native/blob/7ef14af81f3a1532ca1a703da666ea2e5a70a265/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.java#L69-L75
However this event listener is never invoked because the task was started before `TimingModule.initialize()` is called -> `TimingModule.onHeadlessJsTaskStart(...)` is not called and the timer never resumes.

In order to fix this we can just invoke `HeadlessJsTaskEventListener.onHeadlessJsTaskStart(...)` for all currently running tasks when a new listener is added to `HeadlessJsTaskContext`. This call then needs to be `synchronized` as otherwise there's a race condition with `HeadlessJsTaskContext.finishTask(...)` where `onHeadlessJsTaskFinish(...)` could be called before `onHeadlessJsTaskStart(...)`. See the diff for the exact changes.

## 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
-->

[Android] [Fix] - Fixed `TimingModule` related functions for headless JS tasks, eg. `setTimeout`

Pull Request resolved: #33044

Test Plan: I did a local build with the changes and tested the provided example code from #33043 there.

Reviewed By: sshic

Differential Revision: D34006573

Pulled By: dmitryrykun

fbshipit-source-id: d6a821bbd6476ba278c1d8895edb4a0ba16d889e
  • Loading branch information
marcesengel authored and facebook-github-bot committed Apr 11, 2022
1 parent 65abc36 commit dac56ce
Showing 1 changed file with 8 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,15 @@ private HeadlessJsTaskContext(ReactContext reactContext) {
mReactContext = new WeakReference<ReactContext>(reactContext);
}

/** Register a task lifecycle event listener. */
public void addTaskEventListener(HeadlessJsTaskEventListener listener) {
/**
* Register a task lifecycle event listener. Synchronized in order to prevent race conditions with
* finishTask, as the listener will be invoked for already running tasks.
*/
public synchronized void addTaskEventListener(HeadlessJsTaskEventListener listener) {
mHeadlessJsTaskEventListeners.add(listener);
for (Integer activeTaskId : mActiveTasks) {
listener.onHeadlessJsTaskStart(activeTaskId);
}
}

/** Unregister a task lifecycle event listener. */
Expand Down

0 comments on commit dac56ce

Please sign in to comment.