common taskscheduler: fix a potential crash issue. 53/292353/1
authorHermet Park <hermetpark@gmail.com>
Tue, 25 Apr 2023 07:27:41 +0000 (16:27 +0900)
committerPatryk Kaczmarek <patryk.k@partner.samsung.com>
Thu, 4 May 2023 09:25:30 +0000 (11:25 +0200)
Guarantee the tasks are not deleted until the taskscheduler finished them.

@Issue: https://github.com/thorvg/thorvg/issues/1370

Change-Id: I9304539f5960a05405ac3993f39894d46a4ff57b

src/lib/tvgTaskScheduler.cpp
src/lib/tvgTaskScheduler.h

index 4aef6b3..0194680 100644 (file)
@@ -100,13 +100,12 @@ struct TaskQueue {
 };
 
 
-class TaskSchedulerImpl
+struct TaskSchedulerImpl
 {
-public:
-    unsigned                       threadCnt;
+    uint32_t                       threadCnt;
     vector<thread>                 threads;
     vector<TaskQueue>              taskQueues;
-    atomic<unsigned>               idx{0};
+    atomic<uint32_t>               idx{0};
 
     TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt)
     {
@@ -190,4 +189,4 @@ unsigned TaskScheduler::threads()
 {
     if (inst) return inst->threadCnt;
     return 0;
-}
\ No newline at end of file
+}
index 7dd1dce..72c17fb 100644 (file)
@@ -43,47 +43,83 @@ struct TaskScheduler
 struct Task
 {
 private:
-    mutex                   mtx;
+    mutex                   finishedMtx;
+    mutex                   preparedMtx;
     condition_variable      cv;
-    bool                    ready{true};
-    bool                    pending{false};
+    bool                    finished = true;        //if run() finished
+    bool                    prepared = false;       //the task is requested
 
 public:
-    virtual ~Task() = default;
+    virtual ~Task()
+    {
+        if (!prepared) return;
+
+        //Guarantee the task is finished by TaskScheduler.
+        unique_lock<mutex> lock(preparedMtx);
 
-    void done()
+        while (prepared) {
+            cv.wait(lock);
+        }
+    }
+
+    void done(unsigned tid = 0)
     {
-        if (!pending) return;
+        if (finished) return;
+
+        lock_guard<mutex> lock(finishedMtx);
 
-        unique_lock<mutex> lock(mtx);
-        while (!ready) cv.wait(lock);
-        pending = false;
+        if (finished) return;
+
+        //the job hasn't been launched yet.
+
+        //set finished so that operator() quickly returns.
+        finished = true;
+
+        run(tid);
     }
 
 protected:
     virtual void run(unsigned tid) = 0;
 
 private:
+    void finish()
+    {
+        lock_guard<mutex> lock(preparedMtx);
+        prepared = false;
+        cv.notify_one();
+    }
+
     void operator()(unsigned tid)
     {
+        if (finished) {
+            finish();
+            return;
+        }
+
+        lock_guard<mutex> lock(finishedMtx);
+
+        if (finished) {
+            finish();
+            return;
+        }
+
         run(tid);
 
-        lock_guard<mutex> lock(mtx);
-        ready = true;
-        cv.notify_one();
+        finished = true;
+
+        finish();
     }
 
     void prepare()
     {
-        ready = false;
-        pending = true;
+        finished = false;
+        prepared = true;
     }
 
-    friend class TaskSchedulerImpl;
+    friend struct TaskSchedulerImpl;
 };
 
-
-
 }
 
 #endif //_TVG_TASK_SCHEDULER_H_