mirror of
https://github.com/NixOS/nix.git
synced 2025-11-25 19:51:00 +01:00
Merged R9217
This commit is contained in:
parent
ca3d96222a
commit
a94ea0fd61
6 changed files with 114 additions and 17 deletions
|
|
@ -183,6 +183,9 @@ private:
|
||||||
WeakGoalMap derivationGoals;
|
WeakGoalMap derivationGoals;
|
||||||
WeakGoalMap substitutionGoals;
|
WeakGoalMap substitutionGoals;
|
||||||
|
|
||||||
|
/* Goals waiting for busy paths to be unlocked. */
|
||||||
|
WeakGoals waitingForAnyGoal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Worker();
|
Worker();
|
||||||
|
|
@ -224,6 +227,10 @@ public:
|
||||||
get info about `storePath' (with --query-info). We combine
|
get info about `storePath' (with --query-info). We combine
|
||||||
substituter invocations to reduce overhead. */
|
substituter invocations to reduce overhead. */
|
||||||
void waitForInfo(GoalPtr goal, Path sub, Path storePath);
|
void waitForInfo(GoalPtr goal, Path sub, Path storePath);
|
||||||
|
|
||||||
|
/* Wait for any goal to finish. Pretty indiscriminate way to
|
||||||
|
wait for some resource that some other goal is holding. */
|
||||||
|
void waitForAnyGoal(GoalPtr goal);
|
||||||
|
|
||||||
/* Loop until the specified top-level goals have finished. */
|
/* Loop until the specified top-level goals have finished. */
|
||||||
void run(const Goals & topGoals);
|
void run(const Goals & topGoals);
|
||||||
|
|
@ -654,7 +661,7 @@ private:
|
||||||
void buildDone();
|
void buildDone();
|
||||||
|
|
||||||
/* Is the build hook willing to perform the build? */
|
/* Is the build hook willing to perform the build? */
|
||||||
typedef enum {rpAccept, rpDecline, rpPostpone, rpDone} HookReply;
|
typedef enum {rpAccept, rpDecline, rpPostpone, rpDone, rpRestart} HookReply;
|
||||||
HookReply tryBuildHook();
|
HookReply tryBuildHook();
|
||||||
|
|
||||||
/* Synchronously wait for a build hook to finish. */
|
/* Synchronously wait for a build hook to finish. */
|
||||||
|
|
@ -663,9 +670,13 @@ private:
|
||||||
/* Acquires locks on the output paths and gathers information
|
/* Acquires locks on the output paths and gathers information
|
||||||
about the build (e.g., the input closures). During this
|
about the build (e.g., the input closures). During this
|
||||||
process its possible that we find out that the build is
|
process its possible that we find out that the build is
|
||||||
unnecessary, in which case we return false (this is not an
|
unnecessary, in which case we return prDone. It's also
|
||||||
error condition!). */
|
possible that some other goal is already building/substituting
|
||||||
bool prepareBuild();
|
the output paths, in which case we return prRestart (go back to
|
||||||
|
the haveDerivation() state). Otherwise, prProceed is
|
||||||
|
returned. */
|
||||||
|
typedef enum {prProceed, prDone, prRestart} PrepareBuildReply;
|
||||||
|
PrepareBuildReply prepareBuild();
|
||||||
|
|
||||||
/* Start building a derivation. */
|
/* Start building a derivation. */
|
||||||
void startBuilder();
|
void startBuilder();
|
||||||
|
|
@ -805,6 +816,20 @@ void DerivationGoal::haveDerivation()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If this is a fixed-output derivation, it is possible that some
|
||||||
|
other goal is already building the output paths. (The case
|
||||||
|
where some other process is building it is handled through
|
||||||
|
normal locking mechanisms.) So if any output paths are already
|
||||||
|
being built, put this goal to sleep. */
|
||||||
|
for (PathSet::iterator i = invalidOutputs.begin();
|
||||||
|
i != invalidOutputs.end(); ++i)
|
||||||
|
if (pathIsLockedByMe(*i)) {
|
||||||
|
/* Wait until any goal finishes (hopefully the one that is
|
||||||
|
locking *i), then retry haveDerivation(). */
|
||||||
|
worker.waitForAnyGoal(shared_from_this());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* We are first going to try to create the invalid output paths
|
/* We are first going to try to create the invalid output paths
|
||||||
through substitutes. If that doesn't work, we'll build
|
through substitutes. If that doesn't work, we'll build
|
||||||
them. */
|
them. */
|
||||||
|
|
@ -898,6 +923,12 @@ void DerivationGoal::tryToBuild()
|
||||||
/* Somebody else did it. */
|
/* Somebody else did it. */
|
||||||
amDone(ecSuccess);
|
amDone(ecSuccess);
|
||||||
return;
|
return;
|
||||||
|
case rpRestart:
|
||||||
|
/* Somebody else is building this output path.
|
||||||
|
Restart from haveDerivation(). */
|
||||||
|
state = &DerivationGoal::haveDerivation;
|
||||||
|
worker.waitForAnyGoal(shared_from_this());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure that we are allowed to start a build. */
|
/* Make sure that we are allowed to start a build. */
|
||||||
|
|
@ -908,9 +939,14 @@ void DerivationGoal::tryToBuild()
|
||||||
|
|
||||||
/* Acquire locks and such. If we then see that the build has
|
/* Acquire locks and such. If we then see that the build has
|
||||||
been done by somebody else, we're done. */
|
been done by somebody else, we're done. */
|
||||||
if (!prepareBuild()) {
|
PrepareBuildReply preply = prepareBuild();
|
||||||
|
if (preply == prDone) {
|
||||||
amDone(ecSuccess);
|
amDone(ecSuccess);
|
||||||
return;
|
return;
|
||||||
|
} else if (preply == prRestart) {
|
||||||
|
state = &DerivationGoal::haveDerivation;
|
||||||
|
worker.waitForAnyGoal(shared_from_this());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Okay, we have to build. */
|
/* Okay, we have to build. */
|
||||||
|
|
@ -1209,11 +1245,12 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||||
|
|
||||||
/* Acquire locks and such. If we then see that the output
|
/* Acquire locks and such. If we then see that the output
|
||||||
paths are now valid, we're done. */
|
paths are now valid, we're done. */
|
||||||
if (!prepareBuild()) {
|
PrepareBuildReply preply = prepareBuild();
|
||||||
|
if (preply == prDone || preply == prRestart) {
|
||||||
/* Tell the hook to exit. */
|
/* Tell the hook to exit. */
|
||||||
writeLine(toHook.writeSide, "cancel");
|
writeLine(toHook.writeSide, "cancel");
|
||||||
terminateBuildHook();
|
terminateBuildHook();
|
||||||
return rpDone;
|
return preply == prDone ? rpDone : rpRestart;
|
||||||
}
|
}
|
||||||
|
|
||||||
printMsg(lvlInfo, format("running hook to build path(s) %1%")
|
printMsg(lvlInfo, format("running hook to build path(s) %1%")
|
||||||
|
|
@ -1289,11 +1326,27 @@ void DerivationGoal::terminateBuildHook(bool kill)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DerivationGoal::prepareBuild()
|
DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild()
|
||||||
{
|
{
|
||||||
|
/* Check for the possibility that some other goal in this process
|
||||||
|
has locked the output since we checked in haveDerivation().
|
||||||
|
(It can't happen between here and the lockPaths() call below
|
||||||
|
because we're not allowing multi-threading.) */
|
||||||
|
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||||
|
i != drv.outputs.end(); ++i)
|
||||||
|
if (pathIsLockedByMe(i->second.path)) {
|
||||||
|
debug(format("restarting derivation `%1%' because `%2%' is locked by another goal")
|
||||||
|
% drvPath % i->second.path);
|
||||||
|
return prRestart;
|
||||||
|
}
|
||||||
|
|
||||||
/* Obtain locks on all output paths. The locks are automatically
|
/* Obtain locks on all output paths. The locks are automatically
|
||||||
released when we exit this function or Nix crashes. */
|
released when we exit this function or Nix crashes. */
|
||||||
/* !!! BUG: this could block, which is not allowed. */
|
/* !!! BUG: this could block, which is not allowed. */
|
||||||
|
/* !!! and once we make this non-blocking, we should carefully
|
||||||
|
consider the case where some but not all locks are required; we
|
||||||
|
should then release the acquired locks so that the other
|
||||||
|
processes and the pathIsLockedByMe() test don't get confused. */
|
||||||
outputLocks.lockPaths(outputPaths(drv.outputs),
|
outputLocks.lockPaths(outputPaths(drv.outputs),
|
||||||
(format("waiting for lock on %1%") % showPaths(outputPaths(drv.outputs))).str());
|
(format("waiting for lock on %1%") % showPaths(outputPaths(drv.outputs))).str());
|
||||||
|
|
||||||
|
|
@ -1309,7 +1362,7 @@ bool DerivationGoal::prepareBuild()
|
||||||
debug(format("skipping build of derivation `%1%', someone beat us to it")
|
debug(format("skipping build of derivation `%1%', someone beat us to it")
|
||||||
% drvPath);
|
% drvPath);
|
||||||
outputLocks.setDeletion(true);
|
outputLocks.setDeletion(true);
|
||||||
return false;
|
return prDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validPaths.size() > 0) {
|
if (validPaths.size() > 0) {
|
||||||
|
|
@ -1380,7 +1433,7 @@ bool DerivationGoal::prepareBuild()
|
||||||
allPaths.insert(inputPaths.begin(), inputPaths.end());
|
allPaths.insert(inputPaths.begin(), inputPaths.end());
|
||||||
allStatePaths.insert(inputStatePaths.begin(), inputStatePaths.end());
|
allStatePaths.insert(inputStatePaths.begin(), inputStatePaths.end());
|
||||||
|
|
||||||
return true;
|
return prProceed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2171,6 +2224,17 @@ void SubstitutionGoal::tryToRun()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Maybe a derivation goal has already locked this path
|
||||||
|
(exceedingly unlikely, since it should have used a substitute
|
||||||
|
first, but let's be defensive). */
|
||||||
|
outputLock.reset(); // make sure this goal's lock is gone
|
||||||
|
if (pathIsLockedByMe(storePath)) {
|
||||||
|
debug(format("restarting substitution of `%1%' because it's locked by another goal")
|
||||||
|
% storePath);
|
||||||
|
worker.waitForAnyGoal(shared_from_this());
|
||||||
|
return; /* restart in the tryToRun() state when another goal finishes */
|
||||||
|
}
|
||||||
|
|
||||||
/* Acquire a lock on the output path. */
|
/* Acquire a lock on the output path. */
|
||||||
outputLock = boost::shared_ptr<PathLocks>(new PathLocks);
|
outputLock = boost::shared_ptr<PathLocks>(new PathLocks);
|
||||||
outputLock->lockPaths(singleton<PathSet>(storePath),
|
outputLock->lockPaths(singleton<PathSet>(storePath),
|
||||||
|
|
@ -2400,6 +2464,16 @@ void Worker::removeGoal(GoalPtr goal)
|
||||||
if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
|
if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
|
||||||
topGoals.clear();
|
topGoals.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wake up goals waiting for any goal to finish. */
|
||||||
|
for (WeakGoals::iterator i = waitingForAnyGoal.begin();
|
||||||
|
i != waitingForAnyGoal.end(); ++i)
|
||||||
|
{
|
||||||
|
GoalPtr goal = i->lock();
|
||||||
|
if (goal) wakeUp(goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitingForAnyGoal.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2486,6 +2560,13 @@ void Worker::waitForInfo(GoalPtr goal, Path sub, Path storePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Worker::waitForAnyGoal(GoalPtr goal)
|
||||||
|
{
|
||||||
|
debug("wait for any goal");
|
||||||
|
waitingForAnyGoal.insert(goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Worker::getInfo()
|
void Worker::getInfo()
|
||||||
{
|
{
|
||||||
for (map<Path, PathSet>::iterator i = requestedInfo.begin();
|
for (map<Path, PathSet>::iterator i = requestedInfo.begin();
|
||||||
|
|
|
||||||
|
|
@ -223,5 +223,12 @@ void PathLocks::setDeletion(bool deletePaths)
|
||||||
this->deletePaths = deletePaths;
|
this->deletePaths = deletePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool pathIsLockedByMe(const Path & path)
|
||||||
|
{
|
||||||
|
Path lockPath = path + ".lock";
|
||||||
|
return lockedPaths.find(lockPath) != lockedPaths.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool pathIsLockedByMe(const Path & path);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,12 @@ typedef enum {
|
||||||
#define STDERR_ERROR 0x63787470
|
#define STDERR_ERROR 0x63787470
|
||||||
|
|
||||||
|
|
||||||
/* The default location of the daemon socket, relative to
|
/* The default location of the daemon socket, relative to nixStateDir.
|
||||||
nixStateDir. */
|
The socket is in a directory to allow you to control access to the
|
||||||
#define DEFAULT_SOCKET_PATH "/daemon.socket"
|
Nix daemon by setting the mode/ownership of the directory
|
||||||
|
appropriately. (This wouldn't work on the socket itself since it
|
||||||
|
must be deleted and recreated on startup.) */
|
||||||
|
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
|
||||||
|
|
||||||
|
|
||||||
Path readStorePath(Source & from);
|
Path readStorePath(Source & from);
|
||||||
|
|
|
||||||
|
|
@ -753,6 +753,8 @@ static void daemonLoop()
|
||||||
|
|
||||||
string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
|
string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
|
||||||
|
|
||||||
|
createDirs(dirOf(socketPath));
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
if (socketPath.size() >= sizeof(addr.sun_path))
|
if (socketPath.size() >= sizeof(addr.sun_path))
|
||||||
|
|
@ -762,7 +764,8 @@ static void daemonLoop()
|
||||||
unlink(socketPath.c_str());
|
unlink(socketPath.c_str());
|
||||||
|
|
||||||
/* Make sure that the socket is created with 0666 permission
|
/* Make sure that the socket is created with 0666 permission
|
||||||
(everybody can connect). */
|
(everybody can connect --- provided they have access to the
|
||||||
|
directory containing the socket). */
|
||||||
mode_t oldMode = umask(0111);
|
mode_t oldMode = umask(0111);
|
||||||
int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
|
int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
|
||||||
umask(oldMode);
|
umask(oldMode);
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ rec {
|
||||||
# Test for building two derivations in parallel that produce the
|
# Test for building two derivations in parallel that produce the
|
||||||
# same output path because they're fixed-output derivations.
|
# same output path because they're fixed-output derivations.
|
||||||
parallelSame = [
|
parallelSame = [
|
||||||
(f2 "foo" ./fixed.builder2.sh "flat" "md5" "3670af73070fa14077ad74e0f5ea4e42")
|
(f2 "foo" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
|
||||||
(f2 "bar" ./fixed.builder2.sh "flat" "md5" "3670af73070fa14077ad74e0f5ea4e42")
|
(f2 "bar" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue