Skip to content

Commit

Permalink
Adding support to cancel a headless task (#20416)
Browse files Browse the repository at this point in the history
Summary:
On UWP, when the app is executing a Background task, the OS can request it to cancel the task (for various reasons). If the app does not cancel its background task quickly, the OS might terminate the app.
This change adds support to cancel the headless task which can be used by native code to forward the cancellation request from the OS to the JS code.
Pull Request resolved: #20416

Differential Revision: D10052080

Pulled By: cpojer

fbshipit-source-id: 2c0322ebb45f7835739f68bdf82a7100d968c516
  • Loading branch information
psivaram authored and facebook-github-bot committed Feb 15, 2019
1 parent d4adf50 commit c9ee902
Showing 1 changed file with 39 additions and 6 deletions.
45 changes: 39 additions & 6 deletions Libraries/ReactNative/AppRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const renderApplication = require('renderApplication');

type Task = (taskData: any) => Promise<void>;
type TaskProvider = () => Task;
type TaskCanceller = () => void;
type TaskCancelProvider = () => TaskCanceller;

/* $FlowFixMe(>=0.90.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.90 was deployed. To see the error, delete this
* comment and run Flow. */
Expand Down Expand Up @@ -50,7 +53,8 @@ export type WrapperComponentProvider = any => React$ComponentType<*>;
const runnables: Runnables = {};
let runCount = 1;
const sections: Runnables = {};
const tasks: Map<string, TaskProvider> = new Map();
const taskProviders: Map<string, TaskProvider> = new Map();
const taskCancelProviders: Map<string, TaskCancelProvider> = new Map();
let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook = (
component: ComponentProvider,
) => component();
Expand Down Expand Up @@ -212,13 +216,29 @@ const AppRegistry = {
*
* See http://facebook.github.io/react-native/docs/appregistry.html#registerheadlesstask
*/
registerHeadlessTask(taskKey: string, task: TaskProvider): void {
if (tasks.has(taskKey)) {
registerHeadlessTask(taskKey: string, taskProvider: TaskProvider): void {
this.registerCancellableHeadlessTask(taskKey, taskProvider, () => () => {
/* Cancel is no-op */
});
},

/**
* Register a cancellable headless task. A headless task is a bit of code that runs without a UI.
*
* See http://facebook.github.io/react-native/docs/appregistry.html#registercancellableheadlesstask
*/
registerCancellableHeadlessTask(
taskKey: string,
taskProvider: TaskProvider,
taskCancelProvider: TaskCancelProvider,
): void {
if (taskProviders.has(taskKey)) {
console.warn(
`registerHeadlessTask called multiple times for same key '${taskKey}'`,
`registerHeadlessTask or registerCancellableHeadlessTask called multiple times for same key '${taskKey}'`,
);
}
tasks.set(taskKey, task);
taskProviders.set(taskKey, taskProvider);
taskCancelProviders.set(taskKey, taskCancelProvider);
},

/**
Expand All @@ -227,7 +247,7 @@ const AppRegistry = {
* See http://facebook.github.io/react-native/docs/appregistry.html#startheadlesstask
*/
startHeadlessTask(taskId: number, taskKey: string, data: any): void {
const taskProvider = tasks.get(taskKey);
const taskProvider = taskProviders.get(taskKey);
if (!taskProvider) {
throw new Error(`No task registered for key ${taskKey}`);
}
Expand All @@ -240,6 +260,19 @@ const AppRegistry = {
NativeModules.HeadlessJsTaskSupport.notifyTaskFinished(taskId);
});
},

/**
* Only called from native code. Cancels a headless task.
*
* See http://facebook.github.io/react-native/docs/appregistry.html#cancelheadlesstask
*/
cancelHeadlessTask(taskId: number, taskKey: string): void {
const taskCancelProvider = taskCancelProviders.get(taskKey);
if (!taskCancelProvider) {
throw new Error(`No task canceller registered for key '${taskKey}'`);
}
taskCancelProvider()();
},
};

BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);
Expand Down

0 comments on commit c9ee902

Please sign in to comment.