From 76687ae940d5553a2b0d93a0249e1f32c5d5bed1 Mon Sep 17 00:00:00 2001 From: Sivaram Prasad Pulla <37389881+psivaram@users.noreply.github.com> Date: Thu, 26 Jul 2018 17:11:33 -0700 Subject: [PATCH] Adding support to cancel a headless task 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 ths JS code. --- Libraries/ReactNative/AppRegistry.js | 39 +++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/Libraries/ReactNative/AppRegistry.js b/Libraries/ReactNative/AppRegistry.js index a2fdf91e632d45..19ea57fa9cd0ab 100644 --- a/Libraries/ReactNative/AppRegistry.js +++ b/Libraries/ReactNative/AppRegistry.js @@ -21,6 +21,9 @@ const renderApplication = require('renderApplication'); type Task = (taskData: any) => Promise; type TaskProvider = () => Task; +type TaskCanceller = () => void; +type TaskCancelProvider = () => TaskCanceller; + export type ComponentProvider = () => React$ComponentType; export type ComponentProviderInstrumentationHook = ( component: ComponentProvider, @@ -47,7 +50,8 @@ export type WrapperComponentProvider = any => React$ComponentType<*>; const runnables: Runnables = {}; let runCount = 1; const sections: Runnables = {}; -const tasks: Map = new Map(); +const taskProviders: Map = new Map(); +const taskCancelProviders: Map = new Map(); let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook = ( component: ComponentProvider, ) => component(); @@ -209,13 +213,23 @@ 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); }, /** @@ -224,7 +238,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}`); } @@ -237,6 +251,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);