[Service] Skip start service when app terminate & Check wrt-service launcher to 4s
[platform/framework/web/wrtjs.git] / wrt_app / service / service_manager.ts
1 import { Worker, isMainThread } from 'worker_threads';
2 import { wrt } from '../browser/wrt';
3 import * as XWalkExtension from '../common/wrt_xwalk_extension';
4
5 interface WorkerMap {
6   [id: string]: any;
7 }
8 let workers: WorkerMap = {};
9 let dyingWorkerQueue: WorkerMap = {};
10
11 Object.defineProperty(global, 'serviceType', {
12   value: wrt.getServiceModel(),
13   writable: false
14 });
15
16 function checkDyingWorker() {
17   let dyingWorkers = Object.keys(dyingWorkerQueue);
18   if (dyingWorkers.length) {
19     let workerId = dyingWorkers[0];
20     if (dyingWorkerQueue[workerId] === 'will-terminate') {
21       dyingWorkerQueue[workerId] = 'terminated';
22       workers[workerId].terminate();
23     }
24   }
25 }
26
27 function IsDyingWorker(id: string) {
28   let dyingWorkers = Object.keys(dyingWorkerQueue);
29   if (dyingWorkers.length && dyingWorkerQueue[id]) {
30     return ((dyingWorkerQueue[id] === 'will-terminate') ||
31             (dyingWorkerQueue[id] === 'terminated'))
32   }
33   return false;
34 }
35
36 function createWorker(id: string, startService: string, filename: string) {
37   if (workers[id]) {
38     workers[id].postMessage({ type: 'wake' });
39     return;
40   }
41
42   wrt.tv?.serviceMount(id);
43   workers[id] = new Worker(startService, {
44     workerData: {
45       id,
46       filename
47     }
48   });
49   workers[id].on('message', (message: string) => {
50     if (message === 'will-terminate') {
51       dyingWorkerQueue[id] = message;
52       checkDyingWorker();
53     }
54   });
55   workers[id].on('exit', (code: number) => {
56     wrt.tv?.serviceUmount(id);
57     delete dyingWorkerQueue[id];
58     delete workers[id];
59     let runningServices = Object.keys(workers);
60     console.debug(`${id} terminated, remain services(${runningServices})`);
61     checkDyingWorker();
62   });
63 }
64
65 function terminateWorker(id: string, delay: number) {
66   if (!workers[id]) {
67     console.debug(`This worker is already terminated. ${id}`);
68     return;
69   }
70   console.debug(`${id} will shutdown after ${delay}ms`);
71   workers[id].postMessage({ type: 'stop', delay });
72 }
73
74 let initializeExtensionOnMain = () => {
75   initializeExtensionOnMain = () => {};
76   XWalkExtension.initialize();
77   // This is workaround solution to make webapis's singleton worker, which has
78   // same smack label with pid's.
79   // It must be handled ahead of dropThreadPrivilege()
80   // Otherwise, smack violation might hanppen from 'libdbuspolicy'.
81   global.tizen.systeminfo.getPropertyValue("CPU", () => { }, () => { });
82 }
83
84 export function startService(id: string, filename: string) {
85   if(IsDyingWorker(id)) {
86     console.debug(`startService - ${id} is in stop status, skip start`);
87     return;
88   }
89   console.debug(`startService - ${id}`);
90   initializeExtensionOnMain();
91   if (global['serviceType'] === 'STANDALONE') {
92     let ids = id.split(':');
93     let serviceId = ids[0];
94     let packageId = serviceId.split('.')[0];
95     wrt.security?.dropThreadPrivilege(packageId, serviceId);
96   }
97   let startService = `${__dirname}/service_runner.js`;
98   createWorker(id, startService, filename);
99 }
100
101 export function stopService(id: string) {
102   console.debug(`stopService - ${id}`);
103   terminateWorker(id, 500);
104 }
105
106 export function handleBuiltinService(serviceId: string, serviceName: string) {
107   if (!serviceName) {
108     return;
109   }
110   let need_stop = (serviceName.substr(0, 5) === 'stop_');
111   if (need_stop) {
112     console.debug(`${serviceName} will be terminated.`);
113     workers[serviceId].terminate();
114   } else {
115     console.debug(`Builtin service is ${serviceName}`);
116     let startService = `${__dirname}/../service/builtins/${serviceName}.js`;
117     createWorker(serviceId, startService, '');
118   }
119 }