From c857021bca7f49c3f478c8d3d5f824298dc74f32 Mon Sep 17 00:00:00 2001
From: Terraneo Federico <fede.tft@hotmail.it>
Date: Thu, 3 May 2012 13:31:08 +0200
Subject: [PATCH] Finished implementing waitpid, needs testing

---
 main.cpp                  |   8 +-
 miosix/kernel/process.cpp | 161 +++++++++++++++++++++++++++-----------
 miosix/kernel/process.h   |  10 ++-
 3 files changed, 127 insertions(+), 52 deletions(-)

diff --git a/main.cpp b/main.cpp
index b2cc9a27..75f70ce4 100644
--- a/main.cpp
+++ b/main.cpp
@@ -25,12 +25,14 @@ int main()
     Thread::create(ledThread,STACK_MIN);
     
     ElfProgram prog(reinterpret_cast<const unsigned int*>(main_elf),main_elf_len);
-    for(;;)
+    for(int i=0;;i++)
     {
         getchar();
-        Process::create(prog);
+        pid_t child=Process::create(prog);
         int ec;
-        pid_t pid=Process::wait(&ec);
+        pid_t pid;
+        if(i%2==0) pid=Process::wait(&ec);
+        else pid=Process::waitpid(child,&ec,0);
         iprintf("Process %d terminated\n",pid);
         if(WIFEXITED(ec))
         {
diff --git a/miosix/kernel/process.cpp b/miosix/kernel/process.cpp
index 240b1a21..9ac56c20 100644
--- a/miosix/kernel/process.cpp
+++ b/miosix/kernel/process.cpp
@@ -79,8 +79,13 @@ pid_t Process::create(const ElfProgram& program)
         Lock<Mutex> l(procMutex);
         proc->pid=getNewPid();
         if(Thread::getCurrentThread()->proc!=0)
+        {
             proc->ppid=Thread::getCurrentThread()->proc->pid;
-        else proc->ppid=0;
+            Thread::getCurrentThread()->proc->childs.push_back(proc.get());
+        } else {
+            proc->ppid=0;
+            kernelChilds.push_back(proc.get());
+        }
         processes[proc->pid]=proc.get();
     }
     Thread *thr=Thread::createUserspace(Process::start,0,Thread::DEFAULT,
@@ -89,6 +94,10 @@ pid_t Process::create(const ElfProgram& program)
     {
         Lock<Mutex> l(procMutex);
         processes.erase(proc->pid);
+        if(Thread::getCurrentThread()->proc!=0)
+        {
+            Thread::getCurrentThread()->proc->childs.remove(proc.get());
+        } else kernelChilds.remove(proc.get());
         throw runtime_error("Thread creation failed");
     }
     //Cannot throw bad_alloc due to the reserve in Process's constructor.
@@ -113,60 +122,102 @@ pid_t Process::getppid(pid_t proc)
 pid_t Process::waitpid(pid_t pid, int* exit, int options)
 {
     Lock<Mutex> l(procMutex);
-    pid_t result=-1;
-    if(pid<=0)
+    Process *self=Thread::getCurrentThread()->proc;
+    if(self==0)
     {
-        //Wait for a generic child process
-        Process *self=Thread::getCurrentThread()->proc;
-        pid_t ppid=self==0 ? 0 : self->pid;
-        multimap<pid_t,Process *>::iterator it=zombies.find(ppid);
-        if((options & WNOHANG) && it==zombies.end()) return 0;
-        while(it==zombies.end())
+        //The wait is performed by the kernel
+        if(pid<=0)
         {
-            genericWaiting.wait(l);
-            it=zombies.find(ppid);
+            //Wait for a generic child process
+            if(kernelZombies.empty() && (options & WNOHANG)) return 0;
+            while(kernelZombies.empty())
+            {
+                if(kernelChilds.empty()) return -1;
+                genericWaiting.wait(l);
+            }
+            Process *joined=kernelZombies.front();
+            kernelZombies.pop_front();
+            processes.erase(joined->pid);
+            if(joined->waitCount!=0) errorHandler(UNEXPECTED);
+            if(exit!=0) *exit=joined->exitCode;
+            pid_t result=joined->pid;
+            delete joined;
+            return result;
+        } else {
+            //Wait on a specific child process
+            map<pid_t,Process *>::iterator it=processes.find(pid);
+            if(it==processes.end() || it->second->ppid!=0) return -1;
+            Process *joined=it->second;
+            if(joined->zombie==false)
+            {
+                //Process hasn't terminated yet
+                if(options & WNOHANG) return 0;
+                joined->waitCount++;
+                joined->waiting.wait(l);
+                joined->waitCount--;
+                if(joined->waitCount<0 || joined->zombie==false)
+                    errorHandler(UNEXPECTED);
+            }
+            //If multiple threads call waitpid on the same child, the last
+            //gets the return value, the other -1
+            pid_t result=-1;
+            if(joined->waitCount==0)
+            {
+                if(exit!=0) *exit=joined->exitCode;
+                result=joined->pid;
+                kernelZombies.remove(joined);
+                processes.erase(joined->pid);
+                delete joined;
+            }
+            return result;
         }
-        Process *joined=it->second;
-        if(joined->waitCount!=0) errorHandler(UNEXPECTED);
-        result=joined->pid;
-        if(exit!=0) *exit=joined->exitCode;
-        zombies.erase(it);
-        processes.erase(result);
-        delete joined;
     } else {
-        //Wait on a specific process
-        map<pid_t,Process *>::iterator it=processes.find(pid);
-        if(it!=processes.end())
+        //The wait is performed by a process
+        if(pid<=0)
         {
+            //Wait for a generic child process
+            if(self->zombies.empty() && (options & WNOHANG)) return 0;
+            while(self->zombies.empty())
+            {
+                if(self->childs.empty()) return -1;
+                genericWaiting.wait(l);
+            }
+            Process *joined=self->zombies.front();
+            self->zombies.pop_front();
+            processes.erase(joined->pid);
+            if(joined->waitCount!=0) errorHandler(UNEXPECTED);
+            if(exit!=0) *exit=joined->exitCode;
+            pid_t result=joined->pid;
+            delete joined;
+            return result;
+        } else {
+            //Wait on a specific child process
+            map<pid_t,Process *>::iterator it=processes.find(pid);
+            if(it==processes.end() || it->second->ppid!=self->pid
+                    || pid==self->pid) return -1;
             Process *joined=it->second;
-            if(joined->exitCode==-1)
+            if(joined->zombie==false)
             {
                 //Process hasn't terminated yet
                 if(options & WNOHANG) return 0;
                 joined->waitCount++;
                 joined->waiting.wait(l);
                 joined->waitCount--;
-                if(joined->waitCount<0 || joined->exitCode==-1)
+                if(joined->waitCount<0 || joined->zombie==false)
                     errorHandler(UNEXPECTED);
             }
-            result=joined->pid;
-            if(exit!=0) *exit=joined->exitCode;
+            pid_t result=-1;
             if(joined->waitCount==0)
             {
-                typedef typename multimap<pid_t,Process *>::iterator iterator;
-                pair<iterator,iterator> p=zombies.equal_range(joined->ppid);
-                for(iterator it=p.first;it!=p.second;++it)
-                {
-                    if(it->second!=joined) continue;
-                    zombies.erase(it);
-                    break;
-                }
-                processes.erase(result);
+                result=joined->pid;
+                if(exit!=0) *exit=joined->exitCode;
+                self->zombies.remove(joined);
+                processes.erase(joined->pid);
                 delete joined;
             }
+            return result;
         }
     }
-    return result;
 }
 
 Process::~Process()
@@ -177,7 +228,7 @@ Process::~Process()
 }
 
 Process::Process(const ElfProgram& program) : program(program), waitCount(0),
-        exitCode(-1)
+        zombie(false)
 {
     //This is required so that bad_alloc can never be thrown when the first
     //thread of the process will be stored in this vector
@@ -222,14 +273,13 @@ void *Process::start(void *argv)
     #endif //__CODE_IN_XRAM
     Thread::setupUserspaceContext(entry,proc->image.getProcessBasePointer(),
         proc->image.getProcessImageSize());
-    int returnValue=0;
     bool running=true;
     do {
         miosix_private::SyscallParameters sp=Thread::switchToUserspace();
         if(proc->fault.faultHappened())
         {
             running=false;
-            returnValue=SIGSEGV; //Segfault
+            proc->exitCode=SIGSEGV; //Segfault
             #ifdef WITH_ERRLOG
             iprintf("Process %d terminated due to a fault\n"
                     "* Code base address was 0x%x\n"
@@ -248,7 +298,7 @@ void *Process::start(void *argv)
             {
                 case 2:
                     running=false;
-                    returnValue=(sp.getFirstParameter() & 0xff)<<8;
+                    proc->exitCode=(sp.getFirstParameter() & 0xff)<<8;
                     break;
                 case 3:
                     //FIXME: check that the pointer belongs to the process
@@ -267,7 +317,7 @@ void *Process::start(void *argv)
                     break;
                 default:
                     running=false;
-                    returnValue=SIGSYS; //Bad syscall
+                    proc->exitCode=SIGSYS; //Bad syscall
                     #ifdef WITH_ERRLOG
                     iprintf("Unexpected syscall number %d\n",sp.getSyscallId());
                     #endif //WITH_ERRLOG
@@ -278,11 +328,29 @@ void *Process::start(void *argv)
     } while(running);
     {
         Lock<Mutex> l(procMutex);
-        proc->exitCode=returnValue;
-        if(proc->waitCount>0) proc->waiting.broadcast();
-        else {
-            zombies.insert(make_pair(proc->ppid,proc));
-            genericWaiting.broadcast();
+        proc->zombie=true;
+        list<Process*>::iterator it;
+        for(it=proc->childs.begin();it!=proc->childs.end();++it) (*it)->ppid=0;
+        for(it=proc->zombies.begin();it!=proc->zombies.end();++it) (*it)->ppid=0;
+        kernelChilds.splice(kernelChilds.begin(),proc->childs);
+        kernelZombies.splice(kernelZombies.begin(),proc->zombies);
+        if(proc->ppid!=0)
+        {
+            map<pid_t,Process *>::iterator it=processes.find(proc->ppid);
+            if(it==processes.end()) errorHandler(UNEXPECTED);
+            it->second->childs.remove(proc);
+            if(proc->waitCount>0) proc->waiting.broadcast();
+            else {
+                it->second->zombies.push_back(proc);
+                genericWaiting.broadcast();
+            }
+        } else {
+            kernelChilds.remove(proc);
+            if(proc->waitCount>0) proc->waiting.broadcast();
+            else {
+                kernelZombies.push_back(proc);
+                genericWaiting.broadcast();
+            }
         }
     }
     return 0;
@@ -301,7 +369,8 @@ pid_t Process::getNewPid()
 }
 
 map<pid_t,Process*> Process::processes;
-multimap<pid_t,Process *> Process::zombies;
+std::list<Process *> Process::kernelChilds;
+std::list<Process *> Process::kernelZombies;
 pid_t Process::pidCounter=1;
 Mutex Process::procMutex;
 ConditionVariable Process::genericWaiting;
diff --git a/miosix/kernel/process.h b/miosix/kernel/process.h
index 98fa0919..dc54e1af 100644
--- a/miosix/kernel/process.h
+++ b/miosix/kernel/process.h
@@ -30,6 +30,7 @@
 
 #include <vector>
 #include <map>
+#include <list>
 #include <sys/types.h>
 #include "kernel.h"
 #include "sync.h"
@@ -122,6 +123,8 @@ private:
     miosix_private::MPUConfiguration mpu; ///<Memory protection data
     
     std::vector<Thread *> threads; ///<Threads that belong to the process
+    std::list<Process *> childs;   ///<Living child processes are stored here
+    std::list<Process *> zombies;  ///<Dead child processes are stored here
     pid_t pid;  ///<The pid of this process
     pid_t ppid; ///<The parent pid of this process
     ///Contains the count of active wait calls which specifically requested
@@ -130,12 +133,13 @@ private:
     ///Active wait calls which specifically requested to wait on this process
     ///wait on this condition variable
     ConditionVariable waiting;
-    int exitCode; ///< Contains -1 if process is running, or the exit code
+    bool zombie; ///< True for terminated not yet joined processes
+    short int exitCode; ///< Contains the exit code
     
     ///Maps the pid to the Process instance. Includes zombie processes
     static std::map<pid_t,Process *> processes;
-    ///Terminated but not joined processes, the key is the parent's pid
-    static std::multimap<pid_t,Process *> zombies;
+    static std::list<Process *> kernelChilds;
+    static std::list<Process *> kernelZombies;
     ///Used to assign a new pid to a process
     static pid_t pidCounter;
     ///Uset to guard access to processes and pidCounter
-- 
GitLab