* Wrapper class around pids.

This commit is contained in:
Eelco Dolstra 2004-06-22 09:51:44 +00:00
parent 155d7c8dfa
commit c9fbd2dfd5
4 changed files with 162 additions and 87 deletions

View file

@ -3,11 +3,8 @@
#include <boost/enable_shared_from_this.hpp> #include <boost/enable_shared_from_this.hpp>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include <unistd.h> #include <unistd.h>
#include "normalise.hh" #include "normalise.hh"
@ -60,7 +57,7 @@ protected:
virtual ~Goal() virtual ~Goal()
{ {
debug("goal destroyed"); printMsg(lvlVomit, "goal destroyed");
} }
public: 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; map<Path, Path> inputSucs;
/* The process ID of the builder. */ /* The process ID of the builder. */
pid_t pid; Pid pid;
/* The temporary directory. */ /* The temporary directory. */
Path tmpDir; Path tmpDir;
@ -309,7 +286,6 @@ NormalisationGoal::NormalisationGoal(const Path & _nePath, Worker & _worker)
: Goal(_worker) : Goal(_worker)
{ {
nePath = _nePath; nePath = _nePath;
pid = -1;
state = &NormalisationGoal::init; state = &NormalisationGoal::init;
} }
@ -318,13 +294,6 @@ NormalisationGoal::~NormalisationGoal()
{ {
/* Careful: we should never ever throw an exception from a /* Careful: we should never ever throw an exception from a
destructor. */ destructor. */
if (pid != -1) {
printMsg(lvlError, format("killing child process %1% (%2%)")
% pid % nePath);
killChild(pid);
}
try { try {
deleteTmpDir(false); deleteTmpDir(false);
} catch (Error & e) { } catch (Error & e) {
@ -471,20 +440,16 @@ void NormalisationGoal::buildDone()
{ {
debug(format("build done for `%1%'") % nePath); debug(format("build done for `%1%'") % nePath);
int status;
/* Since we got an EOF on the logger pipe, the builder is presumed /* Since we got an EOF on the logger pipe, the builder is presumed
to have terminated. In fact, the builder could also have to have terminated. In fact, the builder could also have
simply have closed its end of the pipe --- just don't do that simply have closed its end of the pipe --- just don't do that
:-) */ :-) */
/* !!! this could block! */ /* !!! this could block! */
if (waitpid(pid, &status, 0) != pid) pid_t savedPid = pid;
throw SysError(format("builder for `%1%' should have terminated") int status = pid.wait(true);
% nePath);
/* So the child is gone now. */ /* So the child is gone now. */
worker.childTerminated(pid); worker.childTerminated(savedPid);
pid = -1;
/* Close the read side of the logger pipe. */ /* Close the read side of the logger pipe. */
logPipe.readSide.close(); logPipe.readSide.close();
@ -570,7 +535,8 @@ NormalisationGoal::HookReply NormalisationGoal::tryBuildHook()
fromHook.create(); fromHook.create();
/* Fork the hook. */ /* Fork the hook. */
switch (pid = fork()) { pid = fork();
switch (pid) {
case -1: case -1:
throw SysError("unable to fork"); throw SysError("unable to fork");
@ -678,11 +644,9 @@ void NormalisationGoal::terminateBuildHook()
{ {
/* !!! drain stdout of hook */ /* !!! drain stdout of hook */
debug("terminating build hook"); debug("terminating build hook");
int status; pid_t savedPid = pid;
if (waitpid(pid, &status, 0) != pid) pid.wait(true);
printMsg(lvlError, format("process `%1%' missing") % pid); worker.childTerminated(savedPid);
worker.childTerminated(pid);
pid = -1;
fromHook.readSide.close(); fromHook.readSide.close();
toHook.writeSide.close(); toHook.writeSide.close();
fdLogFile.close(); fdLogFile.close();
@ -836,7 +800,8 @@ void NormalisationGoal::startBuilder()
currently use forks to run and wait for the children, it currently use forks to run and wait for the children, it
shouldn't be hard to use threads for this on systems where shouldn't be hard to use threads for this on systems where
fork() is unavailable or inefficient. */ fork() is unavailable or inefficient. */
switch (pid = fork()) { pid = fork();
switch (pid) {
case -1: case -1:
throw SysError("unable to fork"); throw SysError("unable to fork");
@ -885,6 +850,7 @@ void NormalisationGoal::startBuilder()
} }
/* parent */ /* parent */
pid.setSeparatePG(true);
logPipe.writeSide.close(); logPipe.writeSide.close();
worker.childStarted(shared_from_this(), worker.childStarted(shared_from_this(),
pid, logPipe.readSide, true); pid, logPipe.readSide, true);
@ -1199,7 +1165,7 @@ private:
Pipe logPipe; Pipe logPipe;
/* The process ID of the builder. */ /* The process ID of the builder. */
pid_t pid; Pid pid;
/* Lock on the store path. */ /* Lock on the store path. */
PathLocks outputLock; PathLocks outputLock;
@ -1209,7 +1175,6 @@ private:
public: public:
SubstitutionGoal(const Path & _nePath, Worker & _worker); SubstitutionGoal(const Path & _nePath, Worker & _worker);
~SubstitutionGoal();
void work(); void work();
@ -1227,22 +1192,10 @@ SubstitutionGoal::SubstitutionGoal(const Path & _storePath, Worker & _worker)
: Goal(_worker) : Goal(_worker)
{ {
storePath = _storePath; storePath = _storePath;
pid = -1;
state = &SubstitutionGoal::init; 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() void SubstitutionGoal::work()
{ {
(this->*state)(); (this->*state)();
@ -1345,7 +1298,8 @@ void SubstitutionGoal::tryToRun()
deletePath(storePath); deletePath(storePath);
/* Fork the substitute program. */ /* Fork the substitute program. */
switch (pid = fork()) { pid = fork();
switch (pid) {
case -1: case -1:
throw SysError("unable to fork"); throw SysError("unable to fork");
@ -1402,6 +1356,7 @@ void SubstitutionGoal::tryToRun()
} }
/* parent */ /* parent */
pid.setSeparatePG(true);
logPipe.writeSide.close(); logPipe.writeSide.close();
worker.childStarted(shared_from_this(), worker.childStarted(shared_from_this(),
pid, logPipe.readSide, true); pid, logPipe.readSide, true);
@ -1414,18 +1369,14 @@ void SubstitutionGoal::finished()
{ {
debug(format("substitute finished of `%1%'") % storePath); debug(format("substitute finished of `%1%'") % storePath);
int status;
/* Since we got an EOF on the logger pipe, the substitute is /* Since we got an EOF on the logger pipe, the substitute is
presumed to have terminated. */ presumed to have terminated. */
/* !!! this could block! */ /* !!! this could block! */
if (waitpid(pid, &status, 0) != pid) pid_t savedPid = pid;
throw SysError(format("substitute for `%1%' should have terminated") int status = pid.wait(true);
% storePath);
/* So the child is gone now. */ /* So the child is gone now. */
worker.childTerminated(pid); worker.childTerminated(savedPid);
pid = -1;
/* Close the read side of the logger pipe. */ /* Close the read side of the logger pipe. */
logPipe.readSide.close(); logPipe.readSide.close();
@ -1534,7 +1485,7 @@ void Worker::removeGoal(GoalPtr goal)
void Worker::wakeUp(GoalPtr goal) void Worker::wakeUp(GoalPtr goal)
{ {
debug("wake up"); printMsg(lvlVomit, "wake up");
awake.insert(goal); awake.insert(goal);
} }
@ -1593,8 +1544,6 @@ void Worker::run()
while (1) { while (1) {
debug(format("main loop (%1% goals left)") % goals.size());
checkInterrupt(); checkInterrupt();
/* Call every wake goal. */ /* Call every wake goal. */
@ -1602,7 +1551,8 @@ void Worker::run()
Goals awake2(awake); /* !!! why is this necessary? */ Goals awake2(awake); /* !!! why is this necessary? */
awake.clear(); awake.clear();
for (Goals::iterator i = awake2.begin(); i != awake2.end(); ++i) { for (Goals::iterator i = awake2.begin(); i != awake2.end(); ++i) {
debug("goal"); printMsg(lvlVomit,
format("running goal (%1% left)") % goals.size());
checkInterrupt(); checkInterrupt();
GoalPtr goal = *i; GoalPtr goal = *i;
goal->work(); goal->work();
@ -1714,5 +1664,7 @@ void ensurePath(const Path & path)
/* If the path is already valid, we're done. */ /* If the path is already valid, we're done. */
if (isValidPath(path)) return; if (isValidPath(path)) return;
/* !!! add realisation goal */ Worker worker;
worker.addSubstitutionGoal(path, GoalPtr());
worker.run();
} }

View file

@ -127,21 +127,22 @@ void copyPath(const Path & src, const Path & dst)
use a thread). */ use a thread). */
/* Create a pipe. */ /* Create a pipe. */
int fds[2]; Pipe pipe;
if (pipe(fds) == -1) throw SysError("creating pipe"); pipe.create();
/* Fork. */ /* Fork. */
pid_t pid; Pid pid;
switch (pid = fork()) { pid = fork();
switch (pid) {
case -1: case -1:
throw SysError("unable to fork"); throw SysError("unable to fork");
case 0: /* child */ case 0: /* child */
try { try {
close(fds[1]); pipe.writeSide.close();
CopySource source; CopySource source;
source.fd = fds[0]; source.fd = pipe.readSide;
restorePath(dst, source); restorePath(dst, source);
_exit(0); _exit(0);
} catch (exception & e) { } catch (exception & e) {
@ -150,19 +151,16 @@ void copyPath(const Path & src, const Path & dst)
_exit(1); _exit(1);
} }
close(fds[0]);
/* Parent. */ /* Parent. */
pipe.readSide.close();
CopySink sink; CopySink sink;
sink.fd = fds[1]; sink.fd = pipe.writeSide;
dumpPath(src, sink); dumpPath(src, sink);
/* Wait for the child to finish. */ /* Wait for the child to finish. */
int status; int status = pid.wait(true);
if (waitpid(pid, &status, 0) != pid)
throw SysError("waiting for child");
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
throw Error(format("cannot copy `%1% to `%2%': child %3%") throw Error(format("cannot copy `%1% to `%2%': child %3%")
% src % dst % statusToString(status)); % src % dst % statusToString(status));

View file

@ -7,9 +7,11 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include "util.hh" #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) AutoDelete::AutoDelete(const string & p) : path(p)
{ {
del = true; del = true;
@ -353,16 +359,22 @@ void AutoDelete::cancel()
} }
//////////////////////////////////////////////////////////////////////
AutoCloseFD::AutoCloseFD() AutoCloseFD::AutoCloseFD()
{ {
fd = -1; fd = -1;
} }
AutoCloseFD::AutoCloseFD(int fd) AutoCloseFD::AutoCloseFD(int fd)
{ {
this->fd = fd; this->fd = fd;
} }
AutoCloseFD::~AutoCloseFD() AutoCloseFD::~AutoCloseFD()
{ {
try { try {
@ -372,17 +384,20 @@ AutoCloseFD::~AutoCloseFD()
} }
} }
void AutoCloseFD::operator =(int fd) void AutoCloseFD::operator =(int fd)
{ {
if (this->fd != fd) close(); if (this->fd != fd) close();
this->fd = fd; this->fd = fd;
} }
AutoCloseFD::operator int() AutoCloseFD::operator int()
{ {
return fd; return fd;
} }
void AutoCloseFD::close() void AutoCloseFD::close()
{ {
if (fd != -1) { if (fd != -1) {
@ -393,6 +408,7 @@ void AutoCloseFD::close()
} }
} }
bool AutoCloseFD::isOpen() bool AutoCloseFD::isOpen()
{ {
return fd != -1; return fd != -1;
@ -408,32 +424,119 @@ void Pipe::create()
} }
//////////////////////////////////////////////////////////////////////
AutoCloseDir::AutoCloseDir() AutoCloseDir::AutoCloseDir()
{ {
dir = 0; dir = 0;
} }
AutoCloseDir::AutoCloseDir(DIR * dir) AutoCloseDir::AutoCloseDir(DIR * dir)
{ {
this->dir = dir; this->dir = dir;
} }
AutoCloseDir::~AutoCloseDir() AutoCloseDir::~AutoCloseDir()
{ {
if (dir) closedir(dir); if (dir) closedir(dir);
} }
void AutoCloseDir::operator =(DIR * dir) void AutoCloseDir::operator =(DIR * dir)
{ {
this->dir = dir; this->dir = dir;
} }
AutoCloseDir::operator DIR *() AutoCloseDir::operator DIR *()
{ {
return 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; volatile sig_atomic_t _isInterrupted = 0;
void _interrupted() void _interrupted()
@ -448,6 +551,10 @@ void _interrupted()
} }
//////////////////////////////////////////////////////////////////////
string packStrings(const Strings & strings) string packStrings(const Strings & strings)
{ {
string d; string d;

View file

@ -172,6 +172,7 @@ public:
void cancel(); void cancel();
}; };
class AutoCloseFD class AutoCloseFD
{ {
int fd; int fd;
@ -185,6 +186,7 @@ public:
bool isOpen(); bool isOpen();
}; };
class Pipe class Pipe
{ {
public: public:
@ -192,6 +194,7 @@ public:
void create(); void create();
}; };
class AutoCloseDir class AutoCloseDir
{ {
DIR * dir; 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. */ /* User interruption. */
extern volatile sig_atomic_t _isInterrupted; extern volatile sig_atomic_t _isInterrupted;