* Wrapper class around pids.
This commit is contained in:
		
							parent
							
								
									155d7c8dfa
								
							
						
					
					
						commit
						c9fbd2dfd5
					
				
					 4 changed files with 162 additions and 87 deletions
				
			
		|  | @ -3,11 +3,8 @@ | |||
| #include <boost/enable_shared_from_this.hpp> | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/time.h> | ||||
| #include <fcntl.h> | ||||
| #include <signal.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "normalise.hh" | ||||
|  | @ -60,7 +57,7 @@ protected: | |||
| 
 | ||||
|     virtual ~Goal() | ||||
|     { | ||||
|         debug("goal destroyed"); | ||||
|         printMsg(lvlVomit, "goal destroyed"); | ||||
|     } | ||||
| 
 | ||||
| public: | ||||
|  | @ -157,26 +154,6 @@ public: | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| void killChild(pid_t pid) | ||||
| { | ||||
|     /* Send a KILL signal to every process in the child process group
 | ||||
|        (which hopefully includes *all* its children). */ | ||||
|     if (kill(-pid, SIGKILL) != 0) | ||||
|         printMsg(lvlError, format("killing process %1%") % pid); | ||||
|     else { | ||||
|         /* Wait until the child dies, disregarding the exit status. */ | ||||
|         int status; | ||||
|         while (waitpid(pid, &status, 0) == -1) | ||||
|             if (errno != EINTR) printMsg(lvlError, | ||||
|                 format("waiting for process %1%") % pid); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -233,7 +210,7 @@ private: | |||
|     map<Path, Path> inputSucs; | ||||
| 
 | ||||
|     /* The process ID of the builder. */ | ||||
|     pid_t pid; | ||||
|     Pid pid; | ||||
| 
 | ||||
|     /* The temporary directory. */ | ||||
|     Path tmpDir; | ||||
|  | @ -309,7 +286,6 @@ NormalisationGoal::NormalisationGoal(const Path & _nePath, Worker & _worker) | |||
|     : Goal(_worker) | ||||
| { | ||||
|     nePath = _nePath; | ||||
|     pid = -1; | ||||
|     state = &NormalisationGoal::init; | ||||
| } | ||||
| 
 | ||||
|  | @ -318,13 +294,6 @@ NormalisationGoal::~NormalisationGoal() | |||
| { | ||||
|     /* Careful: we should never ever throw an exception from a
 | ||||
|        destructor. */ | ||||
| 
 | ||||
|     if (pid != -1) { | ||||
|         printMsg(lvlError, format("killing child process %1% (%2%)") | ||||
|             % pid % nePath); | ||||
|         killChild(pid); | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         deleteTmpDir(false); | ||||
|     } catch (Error & e) { | ||||
|  | @ -471,20 +440,16 @@ void NormalisationGoal::buildDone() | |||
| { | ||||
|     debug(format("build done for `%1%'") % nePath); | ||||
| 
 | ||||
|     int status; | ||||
| 
 | ||||
|     /* Since we got an EOF on the logger pipe, the builder is presumed
 | ||||
|        to have terminated.  In fact, the builder could also have | ||||
|        simply have closed its end of the pipe --- just don't do that | ||||
|        :-) */ | ||||
|     /* !!! this could block! */ | ||||
|     if (waitpid(pid, &status, 0) != pid) | ||||
|         throw SysError(format("builder for `%1%' should have terminated") | ||||
|             % nePath); | ||||
|     pid_t savedPid = pid; | ||||
|     int status = pid.wait(true); | ||||
| 
 | ||||
|     /* So the child is gone now. */ | ||||
|     worker.childTerminated(pid); | ||||
|     pid = -1; | ||||
|     worker.childTerminated(savedPid); | ||||
| 
 | ||||
|     /* Close the read side of the logger pipe. */ | ||||
|     logPipe.readSide.close(); | ||||
|  | @ -570,7 +535,8 @@ NormalisationGoal::HookReply NormalisationGoal::tryBuildHook() | |||
|     fromHook.create(); | ||||
| 
 | ||||
|     /* Fork the hook. */ | ||||
|     switch (pid = fork()) { | ||||
|     pid = fork(); | ||||
|     switch (pid) { | ||||
|          | ||||
|     case -1: | ||||
|         throw SysError("unable to fork"); | ||||
|  | @ -678,11 +644,9 @@ void NormalisationGoal::terminateBuildHook() | |||
| { | ||||
|     /* !!! drain stdout of hook */ | ||||
|     debug("terminating build hook"); | ||||
|     int status; | ||||
|     if (waitpid(pid, &status, 0) != pid) | ||||
|         printMsg(lvlError, format("process `%1%' missing") % pid); | ||||
|     worker.childTerminated(pid); | ||||
|     pid = -1; | ||||
|     pid_t savedPid = pid; | ||||
|     pid.wait(true); | ||||
|     worker.childTerminated(savedPid); | ||||
|     fromHook.readSide.close(); | ||||
|     toHook.writeSide.close(); | ||||
|     fdLogFile.close(); | ||||
|  | @ -836,7 +800,8 @@ void NormalisationGoal::startBuilder() | |||
|        currently use forks to run and wait for the children, it | ||||
|        shouldn't be hard to use threads for this on systems where | ||||
|        fork() is unavailable or inefficient. */ | ||||
|     switch (pid = fork()) { | ||||
|     pid = fork(); | ||||
|     switch (pid) { | ||||
| 
 | ||||
|     case -1: | ||||
|         throw SysError("unable to fork"); | ||||
|  | @ -885,6 +850,7 @@ void NormalisationGoal::startBuilder() | |||
|     } | ||||
| 
 | ||||
|     /* parent */ | ||||
|     pid.setSeparatePG(true); | ||||
|     logPipe.writeSide.close(); | ||||
|     worker.childStarted(shared_from_this(), | ||||
|         pid, logPipe.readSide, true); | ||||
|  | @ -1199,7 +1165,7 @@ private: | |||
|     Pipe logPipe; | ||||
| 
 | ||||
|     /* The process ID of the builder. */ | ||||
|     pid_t pid; | ||||
|     Pid pid; | ||||
| 
 | ||||
|     /* Lock on the store path. */ | ||||
|     PathLocks outputLock; | ||||
|  | @ -1209,7 +1175,6 @@ private: | |||
| 
 | ||||
| public: | ||||
|     SubstitutionGoal(const Path & _nePath, Worker & _worker); | ||||
|     ~SubstitutionGoal(); | ||||
| 
 | ||||
|     void work(); | ||||
| 
 | ||||
|  | @ -1227,22 +1192,10 @@ SubstitutionGoal::SubstitutionGoal(const Path & _storePath, Worker & _worker) | |||
|     : Goal(_worker) | ||||
| { | ||||
|     storePath = _storePath; | ||||
|     pid = -1; | ||||
|     state = &SubstitutionGoal::init; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| SubstitutionGoal::~SubstitutionGoal() | ||||
| { | ||||
|     /* !!! turn this into a destructor for pids */ | ||||
|     if (pid != -1) { | ||||
|         printMsg(lvlError, format("killing child process %1% (%2%)") | ||||
|             % pid % storePath); | ||||
|         killChild(pid); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void SubstitutionGoal::work() | ||||
| { | ||||
|     (this->*state)(); | ||||
|  | @ -1345,7 +1298,8 @@ void SubstitutionGoal::tryToRun() | |||
|         deletePath(storePath); | ||||
| 
 | ||||
|     /* Fork the substitute program. */ | ||||
|     switch (pid = fork()) { | ||||
|     pid = fork(); | ||||
|     switch (pid) { | ||||
|          | ||||
|     case -1: | ||||
|         throw SysError("unable to fork"); | ||||
|  | @ -1402,6 +1356,7 @@ void SubstitutionGoal::tryToRun() | |||
|     } | ||||
|      | ||||
|     /* parent */ | ||||
|     pid.setSeparatePG(true); | ||||
|     logPipe.writeSide.close(); | ||||
|     worker.childStarted(shared_from_this(), | ||||
|         pid, logPipe.readSide, true); | ||||
|  | @ -1414,18 +1369,14 @@ void SubstitutionGoal::finished() | |||
| { | ||||
|     debug(format("substitute finished of `%1%'") % storePath); | ||||
| 
 | ||||
|     int status; | ||||
| 
 | ||||
|     /* Since we got an EOF on the logger pipe, the substitute is
 | ||||
|        presumed to have terminated.  */ | ||||
|     /* !!! this could block! */ | ||||
|     if (waitpid(pid, &status, 0) != pid) | ||||
|         throw SysError(format("substitute for `%1%' should have terminated") | ||||
|             % storePath); | ||||
|     pid_t savedPid = pid; | ||||
|     int status = pid.wait(true); | ||||
| 
 | ||||
|     /* So the child is gone now. */ | ||||
|     worker.childTerminated(pid); | ||||
|     pid = -1; | ||||
|     worker.childTerminated(savedPid); | ||||
| 
 | ||||
|     /* Close the read side of the logger pipe. */ | ||||
|     logPipe.readSide.close(); | ||||
|  | @ -1534,7 +1485,7 @@ void Worker::removeGoal(GoalPtr goal) | |||
| 
 | ||||
| void Worker::wakeUp(GoalPtr goal) | ||||
| { | ||||
|     debug("wake up"); | ||||
|     printMsg(lvlVomit, "wake up"); | ||||
|     awake.insert(goal); | ||||
| } | ||||
| 
 | ||||
|  | @ -1593,8 +1544,6 @@ void Worker::run() | |||
| 
 | ||||
|     while (1) { | ||||
| 
 | ||||
|         debug(format("main loop (%1% goals left)") % goals.size()); | ||||
| 
 | ||||
|         checkInterrupt(); | ||||
| 
 | ||||
|         /* Call every wake goal. */ | ||||
|  | @ -1602,7 +1551,8 @@ void Worker::run() | |||
|             Goals awake2(awake); /* !!! why is this necessary? */ | ||||
|             awake.clear(); | ||||
|             for (Goals::iterator i = awake2.begin(); i != awake2.end(); ++i) { | ||||
|                 debug("goal"); | ||||
|                 printMsg(lvlVomit, | ||||
|                     format("running goal (%1% left)") % goals.size()); | ||||
|                 checkInterrupt(); | ||||
|                 GoalPtr goal = *i; | ||||
|                 goal->work(); | ||||
|  | @ -1714,5 +1664,7 @@ void ensurePath(const Path & path) | |||
|     /* If the path is already valid, we're done. */ | ||||
|     if (isValidPath(path)) return; | ||||
| 
 | ||||
|     /* !!! add realisation goal */ | ||||
|     Worker worker; | ||||
|     worker.addSubstitutionGoal(path, GoalPtr()); | ||||
|     worker.run(); | ||||
| } | ||||
|  |  | |||
|  | @ -127,21 +127,22 @@ void copyPath(const Path & src, const Path & dst) | |||
|        use a thread). */ | ||||
| 
 | ||||
|     /* Create a pipe. */ | ||||
|     int fds[2]; | ||||
|     if (pipe(fds) == -1) throw SysError("creating pipe"); | ||||
|     Pipe pipe; | ||||
|     pipe.create(); | ||||
| 
 | ||||
|     /* Fork. */ | ||||
|     pid_t pid; | ||||
|     switch (pid = fork()) { | ||||
|     Pid pid; | ||||
|     pid = fork(); | ||||
|     switch (pid) { | ||||
| 
 | ||||
|     case -1: | ||||
|         throw SysError("unable to fork"); | ||||
| 
 | ||||
|     case 0: /* child */ | ||||
|         try { | ||||
|             close(fds[1]); | ||||
|             pipe.writeSide.close(); | ||||
|             CopySource source; | ||||
|             source.fd = fds[0]; | ||||
|             source.fd = pipe.readSide; | ||||
|             restorePath(dst, source); | ||||
|             _exit(0); | ||||
|         } catch (exception & e) { | ||||
|  | @ -150,19 +151,16 @@ void copyPath(const Path & src, const Path & dst) | |||
|         _exit(1);         | ||||
|     } | ||||
| 
 | ||||
|     close(fds[0]); | ||||
|      | ||||
|     /* Parent. */ | ||||
| 
 | ||||
|     pipe.readSide.close(); | ||||
|      | ||||
|     CopySink sink; | ||||
|     sink.fd = fds[1]; | ||||
|     sink.fd = pipe.writeSide; | ||||
|     dumpPath(src, sink); | ||||
| 
 | ||||
|     /* Wait for the child to finish. */ | ||||
|     int status; | ||||
|     if (waitpid(pid, &status, 0) != pid) | ||||
|         throw SysError("waiting for child"); | ||||
| 
 | ||||
|     int status = pid.wait(true); | ||||
|     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | ||||
|         throw Error(format("cannot copy `%1% to `%2%': child %3%") | ||||
|             % src % dst % statusToString(status)); | ||||
|  |  | |||
|  | @ -7,9 +7,11 @@ | |||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| #include <dirent.h> | ||||
| #include <fcntl.h> | ||||
| #include <signal.h> | ||||
| 
 | ||||
| #include "util.hh" | ||||
| 
 | ||||
|  | @ -337,6 +339,10 @@ void writeFull(int fd, const unsigned char * buf, size_t count) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| AutoDelete::AutoDelete(const string & p) : path(p) | ||||
| { | ||||
|     del = true; | ||||
|  | @ -353,16 +359,22 @@ void AutoDelete::cancel() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseFD::AutoCloseFD() | ||||
| { | ||||
|     fd = -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseFD::AutoCloseFD(int fd) | ||||
| { | ||||
|     this->fd = fd; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseFD::~AutoCloseFD() | ||||
| { | ||||
|     try { | ||||
|  | @ -372,17 +384,20 @@ AutoCloseFD::~AutoCloseFD() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void AutoCloseFD::operator =(int fd) | ||||
| { | ||||
|     if (this->fd != fd) close(); | ||||
|     this->fd = fd; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseFD::operator int() | ||||
| { | ||||
|     return fd; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void AutoCloseFD::close() | ||||
| { | ||||
|     if (fd != -1) { | ||||
|  | @ -393,6 +408,7 @@ void AutoCloseFD::close() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool AutoCloseFD::isOpen() | ||||
| { | ||||
|     return fd != -1; | ||||
|  | @ -408,32 +424,119 @@ void Pipe::create() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseDir::AutoCloseDir() | ||||
| { | ||||
|     dir = 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseDir::AutoCloseDir(DIR * dir) | ||||
| { | ||||
|     this->dir = dir; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseDir::~AutoCloseDir() | ||||
| { | ||||
|     if (dir) closedir(dir); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void AutoCloseDir::operator =(DIR * dir) | ||||
| { | ||||
|     this->dir = dir; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| AutoCloseDir::operator DIR *() | ||||
| { | ||||
|     return dir; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| Pid::Pid() | ||||
| { | ||||
|     pid = -1; | ||||
|     separatePG = false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Pid::~Pid() | ||||
| { | ||||
|     kill(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Pid::operator =(pid_t pid) | ||||
| { | ||||
|     if (this->pid != pid) kill(); | ||||
|     this->pid = pid; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Pid::operator pid_t() | ||||
| { | ||||
|     return pid; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Pid::kill() | ||||
| { | ||||
|     if (pid == -1) return; | ||||
|      | ||||
|     printMsg(lvlError, format("killing child process %1%") % pid); | ||||
| 
 | ||||
|     /* Send a KILL signal to the child.  If it has its own process
 | ||||
|        group, send the signal to every process in the child process | ||||
|        group (which hopefully includes *all* its children). */ | ||||
|     if (::kill(separatePG ? -pid : pid, SIGKILL) != 0) | ||||
|         printMsg(lvlError, format("killing process %1%") % pid); | ||||
|     else { | ||||
|         /* Wait until the child dies, disregarding the exit status. */ | ||||
|         int status; | ||||
|         while (waitpid(pid, &status, 0) == -1) | ||||
|             if (errno != EINTR) printMsg(lvlError, | ||||
|                 format("waiting for process %1%") % pid); | ||||
|     } | ||||
| 
 | ||||
|     pid = -1; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int Pid::wait(bool block) | ||||
| { | ||||
|     while (1) { | ||||
|         int status; | ||||
|         int res = waitpid(pid, &status, block ? 0 : WNOHANG); | ||||
|         if (res == pid) { | ||||
|             pid = -1; | ||||
|             return status; | ||||
|         } | ||||
|         if (res == 0 && !block) return -1; | ||||
|         if (errno != EINTR) | ||||
|             throw SysError("cannot get child exit status"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void Pid::setSeparatePG(bool separatePG) | ||||
| { | ||||
|     this->separatePG = separatePG; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| volatile sig_atomic_t _isInterrupted = 0; | ||||
| 
 | ||||
| void _interrupted() | ||||
|  | @ -448,6 +551,10 @@ void _interrupted() | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| 
 | ||||
| string packStrings(const Strings & strings) | ||||
| { | ||||
|     string d; | ||||
|  |  | |||
|  | @ -172,6 +172,7 @@ public: | |||
|     void cancel(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class AutoCloseFD | ||||
| { | ||||
|     int fd; | ||||
|  | @ -185,6 +186,7 @@ public: | |||
|     bool isOpen(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class Pipe | ||||
| { | ||||
| public: | ||||
|  | @ -192,6 +194,7 @@ public: | |||
|     void create(); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class AutoCloseDir | ||||
| { | ||||
|     DIR * dir; | ||||
|  | @ -204,6 +207,21 @@ public: | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class Pid | ||||
| { | ||||
|     pid_t pid; | ||||
|     bool separatePG; | ||||
| public: | ||||
|     Pid(); | ||||
|     ~Pid(); | ||||
|     void operator =(pid_t pid); | ||||
|     operator pid_t(); | ||||
|     void kill(); | ||||
|     int wait(bool block); | ||||
|     void setSeparatePG(bool separatePG); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* User interruption. */ | ||||
| 
 | ||||
| extern volatile sig_atomic_t _isInterrupted; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue