import { Worker, isMainThread } from 'worker_threads';
import { wrt } from '../browser/wrt';
+import * as XWalkExtension from '../common/wrt_xwalk_extension';
interface WorkerMap {
[id: string]: any;
}
+
+interface MessageObserver {
+ [id: string]: Set<string>;
+}
+
let workers: WorkerMap = {};
-let runner: any;
+let dyingWorkerQueue: WorkerMap = {};
+let messageObservers: MessageObserver = {};
Object.defineProperty(global, 'serviceType', {
value: wrt.getServiceModel(),
writable: false
});
+function checkDyingWorker() {
+ let dyingWorkers = Object.keys(dyingWorkerQueue);
+ if (dyingWorkers.length) {
+ let workerId = dyingWorkers[0];
+ if (dyingWorkerQueue[workerId] === 'will-terminate') {
+ dyingWorkerQueue[workerId] = 'terminated';
+ workers[workerId].terminate();
+ }
+ }
+}
+
+function IsDyingWorker(id: string) {
+ let dyingWorkers = Object.keys(dyingWorkerQueue);
+ if (dyingWorkers.length && dyingWorkerQueue[id]) {
+ return ((dyingWorkerQueue[id] === 'will-terminate') ||
+ (dyingWorkerQueue[id] === 'terminated'))
+ }
+ return false;
+}
+
function createWorker(id: string, startService: string, filename: string) {
if (workers[id]) {
workers[id].postMessage({ type: 'wake' });
return;
}
+ wrt.tv?.serviceMount(id);
workers[id] = new Worker(startService, {
workerData: {
id,
filename
}
});
- workers[id].on('message', (message: string) => {
- if (message === 'will-terminate') {
- workers[id].terminate();
+ workers[id].on('message', (message: any) => {
+ switch (message.type) {
+ case 'will-terminate':
+ delete messageObservers[id];
+ dyingWorkerQueue[id] = 'will-terminate';
+ checkDyingWorker();
+ break;
+ case 'register-message':
+ if (!messageObservers[id]) {
+ messageObservers[id] = new Set();
+ }
+ messageObservers[id].add(message.listener);
+ break;
+ default:
+ break;
}
});
workers[id].on('exit', (code: number) => {
+ wrt.tv?.serviceUmount(id);
+ delete dyingWorkerQueue[id];
delete workers[id];
- let runningServices = Object.keys(workers).length;
- console.log(`exit code(${code}), remain services(${runningServices})`);
+ let runningServices = Object.keys(workers);
+ console.debug(`${id} terminated, remain services(${runningServices})`);
+ checkDyingWorker();
});
}
function terminateWorker(id: string, delay: number) {
if (!workers[id]) {
- console.log(`This worker is already terminated. ${id}`);
+ console.debug(`This worker is already terminated. ${id}`);
return;
}
- console.log(`${id} will shutdown after ${delay}ms`);
+ console.debug(`${id} will shutdown after ${delay}ms`);
workers[id].postMessage({ type: 'stop', delay });
}
-export function startService(id: string, filename: string) {
- console.log(`startService - ${id}`);
- if (global['serviceType'] === 'STANDALONE') {
+let initializeExtensionOnMain = (id: string) => {
+ initializeExtensionOnMain = (id: string) => {};
+ XWalkExtension.initialize();
+ // This is workaround solution to make webapis's singleton worker, which has
+ // same smack label with pid's.
+ // It must be handled ahead of dropThreadPrivilege()
+ // Otherwise, smack violation might hanppen from 'libdbuspolicy'.
+ global.tizen.systeminfo.getPropertyValue("CPU", () => { }, () => { });
+ if (global['serviceType'] !== 'STANDALONE') {
+ global.tizen.alarm.getAll();
+ } else {
let ids = id.split(':');
let serviceId = ids[0];
let packageId = serviceId.split('.')[0];
wrt.security?.dropThreadPrivilege(packageId, serviceId);
}
+}
+
+export function startService(id: string, filename: string) {
+ if(IsDyingWorker(id)) {
+ console.debug(`startService - ${id} is in stop status, skip start`);
+ return;
+ }
+ console.debug(`startService - ${id}`);
+ initializeExtensionOnMain(id);
let startService = `${__dirname}/service_runner.js`;
createWorker(id, startService, filename);
}
export function stopService(id: string) {
- console.log(`stopService - ${id}`);
+ console.debug(`stopService - ${id}`);
terminateWorker(id, 500);
}
}
let need_stop = (serviceName.substr(0, 5) === 'stop_');
if (need_stop) {
- console.log(`${serviceName} will be terminated.`);
+ console.debug(`${serviceName} will be terminated.`);
workers[serviceId].terminate();
} else {
- console.log(`Builtin service is ${serviceName}`);
+ console.debug(`Builtin service is ${serviceName}`);
let startService = `${__dirname}/../service/builtins/${serviceName}.js`;
createWorker(serviceId, startService, '');
}
+}
+
+export function notifyMessage(listener: string, data: string) {
+ for (const id in messageObservers) {
+ if (messageObservers[id].has(listener)) {
+ console.debug(`notify message - ${listener}`);
+ if (workers[id]) {
+ const notification = { listener, data };
+ workers[id].postMessage({ type: 'notify-message', notification });
+ }
+ }
+ }
}
\ No newline at end of file