* 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…
Reference in a new issue