sim,cpu: make exit_group halt all threads in a group
authorTuan Ta <qtt2@cornell.edu>
Mon, 2 Apr 2018 20:21:37 +0000 (16:21 -0400)
committerTuan Ta <qtt2@cornell.edu>
Fri, 8 Feb 2019 15:25:30 +0000 (15:25 +0000)
When a thread calls exit_group, in addition to halting the thread
itself, it needs to halt all other threads in its group (i.e., threads
sharing the same thread group ID). This patch enables threads to do
that.

Change-Id: Ib2e158fb27cf98843f177a64a2d643b1bbc94d03
Reviewed-on: https://gem5-review.googlesource.com/c/9623
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>

src/cpu/o3/cpu.cc
src/sim/syscall_emul.cc
src/sim/system.cc

index e50741ec01378a373fc815bf280b6908f73b1bc8..965ab04e0fdf06b322a123acdfc5b0a7c20aea7a 100644 (file)
@@ -1861,9 +1861,8 @@ FullO3CPU<Impl>::addThreadToExitingList(ThreadID tid)
 {
     DPRINTF(O3CPU, "Thread %d is inserted to exitingThreads list\n", tid);
 
-    // make sure the thread is Active
-    assert(std::find(activeThreads.begin(), activeThreads.end(), tid)
-                                              != activeThreads.end());
+    // the thread trying to exit can't be already halted
+    assert(tcBase(tid)->status() != ThreadContext::Halted);
 
     // make sure the thread has not been added to the list yet
     assert(exitingThreads.count(tid) == 0);
index 25e0b6856dbaebe2a14e96f685fda6e746fb2a89..fbfe21a935274409fe3732777d311d4cd381d1b3 100644 (file)
@@ -102,28 +102,6 @@ exitImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc,
 
     System *sys = tc->getSystemPtr();
 
-    int activeContexts = 0;
-    for (auto &system: sys->systemList)
-        activeContexts += system->numRunningContexts();
-    if (activeContexts == 1) {
-        /**
-         * Even though we are terminating the final thread context, dist-gem5
-         * requires the simulation to remain active and provide
-         * synchronization messages to the switch process. So we just halt
-         * the last thread context and return. The simulation will be
-         * terminated by dist-gem5 in a coordinated manner once all nodes
-         * have signaled their readiness to exit. For non dist-gem5
-         * simulations, readyToExit() always returns true.
-         */
-        if (!DistIface::readyToExit(0)) {
-            tc->halt();
-            return status;
-        }
-
-        exitSimLoop("exiting with last active thread context", status & 0xff);
-        return status;
-    }
-
     if (group)
         *p->exitGroup = true;
 
@@ -146,16 +124,34 @@ exitImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc,
         if (walk->pid() == p->tgid())
             tg_lead = walk;
 
-        if ((sys->threadContexts[i]->status() != ThreadContext::Halted)
-            && (walk != p)) {
+        if ((sys->threadContexts[i]->status() != ThreadContext::Halted) &&
+            (sys->threadContexts[i]->status() != ThreadContext::Halting) &&
+            (walk != p)) {
             /**
              * Check if we share thread group with the pointer; this denotes
              * that we are not the last thread active in the thread group.
              * Note that setting this to false also prevents further
              * iterations of the loop.
              */
-            if (walk->tgid() == p->tgid())
-                last_thread = false;
+            if (walk->tgid() == p->tgid()) {
+                /**
+                 * If p is trying to exit_group and both walk and p are in
+                 * the same thread group (i.e., sharing the same tgid),
+                 * we need to halt walk's thread context. After all threads
+                 * except p are halted, p becomes the last thread in the
+                 * group.
+                 *
+                 * If p is not doing exit_group and there exists another
+                 * active thread context in the group, last_thread is
+                 * set to false to prevent the parent thread from killing
+                 * all threads in the group.
+                 */
+                if (*(p->exitGroup)) {
+                    sys->threadContexts[i]->halt();
+                } else {
+                    last_thread = false;
+                }
+            }
 
             /**
              * A corner case exists which involves execve(). After execve(),
@@ -189,6 +185,33 @@ exitImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc,
     }
 
     tc->halt();
+
+    /**
+     * check to see if there is no more active thread in the system. If so,
+     * exit the simulation loop
+     */
+    int activeContexts = 0;
+    for (auto &system: sys->systemList)
+        activeContexts += system->numRunningContexts();
+
+    if (activeContexts == 0) {
+        /**
+         * Even though we are terminating the final thread context, dist-gem5
+         * requires the simulation to remain active and provide
+         * synchronization messages to the switch process. So we just halt
+         * the last thread context and return. The simulation will be
+         * terminated by dist-gem5 in a coordinated manner once all nodes
+         * have signaled their readiness to exit. For non dist-gem5
+         * simulations, readyToExit() always returns true.
+         */
+        if (!DistIface::readyToExit(0)) {
+            return status;
+        }
+
+        exitSimLoop("exiting with last active thread context", status & 0xff);
+        return status;
+    }
+
     return status;
 }
 
index fc2578f861f2fc366531b6b950f7a64d15f9adfa..ffa8edaa639a9dab4c05d516114728ce975ba22c 100644 (file)
@@ -291,7 +291,8 @@ System::numRunningContexts()
         threadContexts.cbegin(),
         threadContexts.cend(),
         [] (ThreadContext* tc) {
-            return tc->status() != ThreadContext::Halted;
+            return ((tc->status() != ThreadContext::Halted) &&
+                    (tc->status() != ThreadContext::Halting));
         }
     );
 }