From f6fda869ead3aae97e73d2222bfc9fdfd837491e Mon Sep 17 00:00:00 2001
From: Gabe Black <gabeblack@google.com>
Date: Thu, 30 Aug 2018 00:39:49 -0700
Subject: systemc: Keep track of more cases when we should be ready after
 resume.

If a thread self suspends, it should be marked as ready after resuming.
If a process was already ready when suspended, it should also be
remarked as ready after resuming.

Special care has to be taken in pre-initialization situations so that
processes are put on the right lists, and whether a process is tracked
is already marked as ready.

Change-Id: I15da7d747db591785358d47781297468c5f9fd09
Reviewed-on: https://gem5-review.googlesource.com/c/12445
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
---
 src/systemc/core/process.cc   | 18 +++++++++++-------
 src/systemc/core/scheduler.cc | 33 +++++++++++++++++++++++++++++++++
 src/systemc/core/scheduler.hh |  8 ++++++++
 3 files changed, 52 insertions(+), 7 deletions(-)

(limited to 'src/systemc')

diff --git a/src/systemc/core/process.cc b/src/systemc/core/process.cc
index 830b8c729..1fd16e0d9 100644
--- a/src/systemc/core/process.cc
+++ b/src/systemc/core/process.cc
@@ -160,12 +160,16 @@ Process::suspend(bool inc_kids)
 
     if (!_suspended) {
         _suspended = true;
-        _suspendedReady = false;
-    }
-
-    if (procKind() != ::sc_core::SC_METHOD_PROC_ &&
-            scheduler.current() == this) {
-        scheduler.yield();
+        _suspendedReady = scheduler.suspend(this);
+
+        if (procKind() != ::sc_core::SC_METHOD_PROC_ &&
+                scheduler.current() == this) {
+            // This isn't in the spec, but Accellera says that a thread that
+            // self suspends should be marked ready immediately when it's
+            // resumed.
+            _suspendedReady = true;
+            scheduler.yield();
+        }
     }
 }
 
@@ -178,7 +182,7 @@ Process::resume(bool inc_kids)
     if (_suspended) {
         _suspended = false;
         if (_suspendedReady)
-            ready();
+            scheduler.resume(this);
         _suspendedReady = false;
     }
 }
diff --git a/src/systemc/core/scheduler.cc b/src/systemc/core/scheduler.cc
index 46053b40f..9b431acba 100644
--- a/src/systemc/core/scheduler.cc
+++ b/src/systemc/core/scheduler.cc
@@ -200,6 +200,39 @@ Scheduler::ready(Process *p)
     scheduleReadyEvent();
 }
 
+void
+Scheduler::resume(Process *p)
+{
+    if (initDone)
+        ready(p);
+    else
+        initList.pushLast(p);
+}
+
+bool
+Scheduler::suspend(Process *p)
+{
+    if (initDone) {
+        // After initialization, the only list we can be on is the ready list.
+        bool was_ready = (p->nextListNode != nullptr);
+        p->popListNode();
+        return was_ready;
+    } else {
+        bool was_ready = false;
+        // Check the ready list to see if we find this process.
+        ListNode *n = readyList.nextListNode;
+        while (n != &readyList) {
+            if (n == p) {
+                was_ready = true;
+                break;
+            }
+        }
+        if (was_ready)
+            toFinalize.pushLast(p);
+        return was_ready;
+    }
+}
+
 void
 Scheduler::requestUpdate(Channel *c)
 {
diff --git a/src/systemc/core/scheduler.hh b/src/systemc/core/scheduler.hh
index 2da8da470..f55ff1f83 100644
--- a/src/systemc/core/scheduler.hh
+++ b/src/systemc/core/scheduler.hh
@@ -182,6 +182,14 @@ class Scheduler
     // Put a process on the ready list.
     void ready(Process *p);
 
+    // Mark a process as ready if init is finished, or put it on the list of
+    // processes to be initialized.
+    void resume(Process *p);
+
+    // Remove a process from the ready/init list if it was on one of them, and
+    // return if it was.
+    bool suspend(Process *p);
+
     // Schedule an update for a given channel.
     void requestUpdate(Channel *c);
 
-- 
cgit v1.2.3