Exclusive StartService when 2 apps are racing 16/298616/5 submit/tizen/20230922.160024
authorDongHyun Song <dh81.song@samsung.com>
Mon, 11 Sep 2023 14:19:55 +0000 (23:19 +0900)
committerDongHyun Song <dh81.song@samsung.com>
Wed, 20 Sep 2023 09:15:04 +0000 (18:15 +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 4209af3..37fea32 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 ef210c2..35db1a6 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) {