Exclusive StartService when 2 apps are racing 39/303439/1
authorDongHyun Song <dh81.song@samsung.com>
Mon, 11 Sep 2023 14:19:55 +0000 (23:19 +0900)
committerSangYong Park <sy302.park@samsung.com>
Thu, 28 Dec 2023 02:09:58 +0000 (11:09 +0900)
2 or more service apps are simultaneously running, there have been
some race condition issues.
 - crash, smack error

So, it needs to run service apps exclusively on the start()
function basis.

If previous app's start() is not done yet, then new request app
will be defered until done.

Change-Id: I6be232cb872e7edb074ffd8c7f1ac5f243333c11
Signed-off-by: DongHyun Song <dh81.song@samsung.com>
wrt_app/service/service_manager.ts
wrt_app/service/service_runner.ts

index 4209af397e46fd45230785b8202e8d91297bff7b..37fea323b83a30bc0f2cd03f5c267eb13bc77463 100644 (file)
@@ -11,7 +11,8 @@ interface MessageObserver {
 }
 
 let workers: WorkerMap = {};
-let dyingWorkerQueue: WorkerMap = {};
+let workerStatus: WorkerMap = {};
+let readyWorkers: WorkerMap = {};
 let messageObservers: MessageObserver = {};
 
 Object.defineProperty(global, 'serviceType', {
@@ -20,25 +21,35 @@ Object.defineProperty(global, 'serviceType', {
 });
 
 function checkDyingWorker() {
-  let dyingWorkers = Object.keys(dyingWorkerQueue);
-  if (dyingWorkers.length) {
-    let workerId = dyingWorkers[0];
-    if (dyingWorkerQueue[workerId] === 'will-terminate') {
-      dyingWorkerQueue[workerId] = 'terminated';
+  for (let workerId in workerStatus) {
+    if (workerStatus[workerId] === 'will-terminate') {
+      workerStatus[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'))
+function isDyingWorker(id: string) {
+  return workerStatus[id] && ((workerStatus[id] === 'will-terminate') || (workerStatus[id] === 'terminated'))
+}
+
+function hasPendingService() {
+  for (let workerId in workerStatus) {
+    if (workerStatus[workerId] === 'starting')
+      return true;
   }
   return false;
 }
 
+function flushFirstReadyWorker() {
+  let readyWorkerKeys = Object.keys(readyWorkers);
+  if (!readyWorkerKeys.length) return;
+  let workerId = readyWorkerKeys[0];
+  console.log(`flushFirstReadyWorker : ${workerId}`);
+  createWorker(readyWorkers[workerId].id, readyWorkers[workerId].startService, readyWorkers[workerId].filename);
+  delete readyWorkers[workerId];
+}
+
 function createWorker(id: string, startService: string, filename: string) {
   if (workers[id]) {
     workers[id].postMessage({ type: 'wake' });
@@ -46,6 +57,14 @@ function createWorker(id: string, startService: string, filename: string) {
   }
 
   wrt.tv?.serviceMount(id);
+  workerStatus[id] = 'starting';
+  setTimeout(() => {
+    // if worker status doesn't become 'started' within 5s, forcely set it 'started'
+    if (workerStatus[id] === 'starting') {
+      workerStatus[id] = 'started';
+      flushFirstReadyWorker();
+    }
+  }, 5000);
   workers[id] = new Worker(startService, {
     workerData: {
       id,
@@ -54,9 +73,13 @@ function createWorker(id: string, startService: string, filename: string) {
   });
   workers[id].on('message', (message: any) => {
     switch (message.type) {
+      case 'started':
+        workerStatus[id] = 'started';
+        flushFirstReadyWorker();
+        break;
       case 'will-terminate':
         delete messageObservers[id];
-        dyingWorkerQueue[id] = 'will-terminate';
+        workerStatus[id] = 'will-terminate';
         checkDyingWorker();
         break;
       case 'register-message':
@@ -71,7 +94,7 @@ function createWorker(id: string, startService: string, filename: string) {
   });
   workers[id].on('exit', (code: number) => {
     wrt.tv?.serviceUmount(id);
-    delete dyingWorkerQueue[id];
+    delete workerStatus[id];
     delete workers[id];
     let runningServices = Object.keys(workers);
     console.debug(`${id} terminated, remain services(${runningServices})`);
@@ -95,7 +118,7 @@ let initializeExtensionOnMain = (id: string) => {
   // 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", () => { }, () => { });
+  global.tizen.systeminfo.getPropertyValue('CPU', () => { }, () => { });
   if (global['serviceType'] !== 'STANDALONE') {
     global.tizen.alarm.getAll();
   } else {
@@ -107,14 +130,18 @@ let initializeExtensionOnMain = (id: string) => {
 }
 
 export function startService(id: string, filename: string) {
-  if(IsDyingWorker(id)) {
+  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);
+  if (!hasPendingService()) {
+    createWorker(id, startService, filename)
+  } else {
+    readyWorkers[id] = { id, startService, filename };
+  }
 }
 
 export function stopService(id: string) {
index ef210c22350a4da4813ac4fab2715cca0d6dd545..35db1a60652a906b151f0afb0621d4e1146d9681 100644 (file)
@@ -157,7 +157,7 @@ function run() {
 
   let filename = workerData.filename;
   start(id, filename);
-
+  parentPort?.postMessage({ type : 'started' });
   parentPort?.on('message', (message) => {
     console.debug(`Received message type : ${message.type}`);
     switch (message.type) {