* normaliseFState() now locks all output paths prior to building, thus
ensuring that simultaneous invocations of Nix don't clobber each other's builds. * Fixed a bug in `make install'.
This commit is contained in:
parent
9df93f30bd
commit
545145cd58
5 changed files with 151 additions and 30 deletions
|
@ -22,7 +22,7 @@ noinst_LIBRARIES = libnix.a libshared.a
|
||||||
|
|
||||||
libnix_a_SOURCES = util.cc hash.cc archive.cc md5.c \
|
libnix_a_SOURCES = util.cc hash.cc archive.cc md5.c \
|
||||||
store.cc fstate.cc normalise.cc exec.cc \
|
store.cc fstate.cc normalise.cc exec.cc \
|
||||||
globals.cc db.cc references.cc
|
globals.cc db.cc references.cc pathlocks.cc
|
||||||
|
|
||||||
libshared_a_SOURCES = shared.cc
|
libshared_a_SOURCES = shared.cc
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ install-data-local:
|
||||||
$(INSTALL) -d $(localstatedir)/nix
|
$(INSTALL) -d $(localstatedir)/nix
|
||||||
$(INSTALL) -d $(localstatedir)/nix/db
|
$(INSTALL) -d $(localstatedir)/nix/db
|
||||||
$(INSTALL) -d $(localstatedir)/nix/links
|
$(INSTALL) -d $(localstatedir)/nix/links
|
||||||
|
rm -f $(prefix)/current
|
||||||
ln -sf $(localstatedir)/nix/links/current $(prefix)/current
|
ln -sf $(localstatedir)/nix/links/current $(prefix)/current
|
||||||
$(INSTALL) -d $(localstatedir)/log/nix
|
$(INSTALL) -d $(localstatedir)/log/nix
|
||||||
$(INSTALL) -d $(prefix)/store
|
$(INSTALL) -d $(prefix)/store
|
||||||
|
|
109
src/normalise.cc
109
src/normalise.cc
|
@ -4,6 +4,7 @@
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
#include "db.hh"
|
#include "db.hh"
|
||||||
#include "exec.hh"
|
#include "exec.hh"
|
||||||
|
#include "pathlocks.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +24,29 @@ static FSId storeSuccessor(const FSId & id1, ATerm sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
typedef set<FSId> FSIdSet;
|
static FSId useSuccessor(const FSId & id)
|
||||||
|
{
|
||||||
|
string idSucc;
|
||||||
|
if (nixDB.queryString(noTxn, dbSuccessors, id, idSucc)) {
|
||||||
|
debug(format("successor %1% -> %2%") % (string) id % idSucc);
|
||||||
|
return parseHash(idSucc);
|
||||||
|
} else
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef map<string, FSId> OutPaths;
|
||||||
|
typedef map<string, SliceElem> ElemMap;
|
||||||
|
|
||||||
|
|
||||||
|
Strings pathsFromOutPaths(const OutPaths & ps)
|
||||||
|
{
|
||||||
|
Strings ss;
|
||||||
|
for (OutPaths::const_iterator i = ps.begin();
|
||||||
|
i != ps.end(); i++)
|
||||||
|
ss.push_back(i->first);
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FSId normaliseFState(FSId id, FSIdSet pending)
|
FSId normaliseFState(FSId id, FSIdSet pending)
|
||||||
|
@ -32,19 +55,66 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||||
|
|
||||||
/* Try to substitute $id$ by any known successors in order to
|
/* Try to substitute $id$ by any known successors in order to
|
||||||
speed up the rewrite process. */
|
speed up the rewrite process. */
|
||||||
string idSucc;
|
id = useSuccessor(id);
|
||||||
while (nixDB.queryString(noTxn, dbSuccessors, id, idSucc)) {
|
|
||||||
debug(format("successor %1% -> %2%") % (string) id % idSucc);
|
|
||||||
id = parseHash(idSucc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the fstate expression. */
|
/* Get the fstate expression. */
|
||||||
FState fs = parseFState(termFromId(id));
|
FState fs = parseFState(termFromId(id));
|
||||||
|
|
||||||
/* It this is a normal form (i.e., a slice) we are done. */
|
/* If this is a normal form (i.e., a slice) we are done. */
|
||||||
if (fs.type == FState::fsSlice) return id;
|
if (fs.type == FState::fsSlice) return id;
|
||||||
|
if (fs.type != FState::fsDerive) abort();
|
||||||
|
|
||||||
/* Otherwise, it's a derivation. */
|
|
||||||
|
/* Otherwise, it's a derive expression, and we have to build it to
|
||||||
|
determine its normal form. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Some variables. */
|
||||||
|
|
||||||
|
/* Output paths, with their ids. */
|
||||||
|
OutPaths outPaths;
|
||||||
|
|
||||||
|
/* Input paths, with their slice elements. */
|
||||||
|
ElemMap inMap;
|
||||||
|
|
||||||
|
/* Referencable paths (i.e., input and output paths). */
|
||||||
|
Strings refPaths;
|
||||||
|
|
||||||
|
/* The environment to be passed to the builder. */
|
||||||
|
Environment env;
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse the outputs. */
|
||||||
|
for (DeriveOutputs::iterator i = fs.derive.outputs.begin();
|
||||||
|
i != fs.derive.outputs.end(); i++)
|
||||||
|
{
|
||||||
|
debug(format("building %1% in `%2%'") % (string) i->second % i->first);
|
||||||
|
outPaths[i->first] = i->second;
|
||||||
|
refPaths.push_back(i->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain locks on all output paths. The locks are automatically
|
||||||
|
released when we exit this function or Nix crashes. */
|
||||||
|
PathLocks outputLock(pathsFromOutPaths(outPaths));
|
||||||
|
|
||||||
|
/* Now check again whether there is a successor. This is because
|
||||||
|
another process may have started building in parallel. After
|
||||||
|
it has finished and released the locks, we can (and should)
|
||||||
|
reuse its results. (Strictly speaking the first successor
|
||||||
|
check above can be omitted, but that would be less efficient.)
|
||||||
|
Note that since we now hold the locks on the output paths, no
|
||||||
|
other process can build this expression, so no further checks
|
||||||
|
are necessary. */
|
||||||
|
{
|
||||||
|
FSId id2 = useSuccessor(id);
|
||||||
|
if (id2 != id) {
|
||||||
|
FState fs = parseFState(termFromId(id2));
|
||||||
|
debug(format("skipping build of %1%, someone beat us to it")
|
||||||
|
% (string) id);
|
||||||
|
if (fs.type != FState::fsSlice) abort();
|
||||||
|
return id2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Right platform? */
|
/* Right platform? */
|
||||||
if (fs.derive.platform != thisSystem)
|
if (fs.derive.platform != thisSystem)
|
||||||
|
@ -52,14 +122,12 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||||
% fs.derive.platform % thisSystem);
|
% fs.derive.platform % thisSystem);
|
||||||
|
|
||||||
/* Realise inputs (and remember all input paths). */
|
/* Realise inputs (and remember all input paths). */
|
||||||
typedef map<string, SliceElem> ElemMap;
|
|
||||||
|
|
||||||
ElemMap inMap;
|
|
||||||
|
|
||||||
for (FSIds::iterator i = fs.derive.inputs.begin();
|
for (FSIds::iterator i = fs.derive.inputs.begin();
|
||||||
i != fs.derive.inputs.end(); i++) {
|
i != fs.derive.inputs.end(); i++) {
|
||||||
FSId nf = normaliseFState(*i, pending);
|
FSId nf = normaliseFState(*i, pending);
|
||||||
realiseSlice(nf, pending);
|
realiseSlice(nf, pending);
|
||||||
|
/* !!! nf should be a root of the garbage collector while we
|
||||||
|
are building */
|
||||||
FState fs = parseFState(termFromId(nf));
|
FState fs = parseFState(termFromId(nf));
|
||||||
if (fs.type != FState::fsSlice) abort();
|
if (fs.type != FState::fsSlice) abort();
|
||||||
for (SliceElems::iterator j = fs.slice.elems.begin();
|
for (SliceElems::iterator j = fs.slice.elems.begin();
|
||||||
|
@ -67,27 +135,14 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||||
inMap[j->path] = *j;
|
inMap[j->path] = *j;
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings inPaths;
|
|
||||||
for (ElemMap::iterator i = inMap.begin(); i != inMap.end(); i++)
|
for (ElemMap::iterator i = inMap.begin(); i != inMap.end(); i++)
|
||||||
inPaths.push_back(i->second.path);
|
refPaths.push_back(i->second.path);
|
||||||
|
|
||||||
/* Build the environment. */
|
/* Build the environment. */
|
||||||
Environment env;
|
|
||||||
for (StringPairs::iterator i = fs.derive.env.begin();
|
for (StringPairs::iterator i = fs.derive.env.begin();
|
||||||
i != fs.derive.env.end(); i++)
|
i != fs.derive.env.end(); i++)
|
||||||
env[i->first] = i->second;
|
env[i->first] = i->second;
|
||||||
|
|
||||||
/* Parse the outputs. */
|
|
||||||
typedef map<string, FSId> OutPaths;
|
|
||||||
OutPaths outPaths;
|
|
||||||
for (DeriveOutputs::iterator i = fs.derive.outputs.begin();
|
|
||||||
i != fs.derive.outputs.end(); i++)
|
|
||||||
{
|
|
||||||
debug(format("building %1% in `%2%'") % (string) i->second % i->first);
|
|
||||||
outPaths[i->first] = i->second;
|
|
||||||
inPaths.push_back(i->first);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We can skip running the builder if we can expand all output
|
/* We can skip running the builder if we can expand all output
|
||||||
paths from their ids. */
|
paths from their ids. */
|
||||||
bool fastBuild = true;
|
bool fastBuild = true;
|
||||||
|
@ -132,7 +187,7 @@ FSId normaliseFState(FSId id, FSIdSet pending)
|
||||||
registerPath(path, i->second);
|
registerPath(path, i->second);
|
||||||
fs.slice.roots.push_back(i->second);
|
fs.slice.roots.push_back(i->second);
|
||||||
|
|
||||||
Strings refs = filterReferences(path, inPaths);
|
Strings refs = filterReferences(path, refPaths);
|
||||||
|
|
||||||
SliceElem elem;
|
SliceElem elem;
|
||||||
elem.path = path;
|
elem.path = path;
|
||||||
|
|
48
src/pathlocks.cc
Normal file
48
src/pathlocks.cc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "pathlocks.hh"
|
||||||
|
|
||||||
|
|
||||||
|
PathLocks::PathLocks(const Strings & _paths)
|
||||||
|
{
|
||||||
|
/* Note that `fds' is built incrementally so that the destructor
|
||||||
|
will only release those locks that we have already acquired. */
|
||||||
|
|
||||||
|
/* Sort the paths. This assures that locks are always acquired in
|
||||||
|
the same order, thus preventing deadlocks. */
|
||||||
|
Strings paths(_paths);
|
||||||
|
paths.sort();
|
||||||
|
|
||||||
|
/* Acquire the lock for each path. */
|
||||||
|
for (Strings::iterator i = paths.begin(); i != paths.end(); i++) {
|
||||||
|
string path = *i;
|
||||||
|
string lockPath = path + ".lock";
|
||||||
|
|
||||||
|
debug(format("locking path `%1%'") % path);
|
||||||
|
|
||||||
|
/* Open/create the lock file. */
|
||||||
|
int fd = open(lockPath.c_str(), O_WRONLY | O_CREAT, 0666);
|
||||||
|
if (fd == -1)
|
||||||
|
throw SysError(format("opening lock file `%1%'") % lockPath);
|
||||||
|
|
||||||
|
fds.push_back(fd);
|
||||||
|
|
||||||
|
/* Lock it. */
|
||||||
|
struct flock lock;
|
||||||
|
lock.l_type = F_WRLCK; /* exclusive lock */
|
||||||
|
lock.l_whence = SEEK_SET;
|
||||||
|
lock.l_start = 0;
|
||||||
|
lock.l_len = 0; /* entire file */
|
||||||
|
|
||||||
|
while (fcntl(fd, F_SETLKW, &lock) == -1)
|
||||||
|
if (errno != EINTR)
|
||||||
|
throw SysError(format("acquiring lock on `%1%'") % lockPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PathLocks::~PathLocks()
|
||||||
|
{
|
||||||
|
for (list<int>::iterator i = fds.begin(); i != fds.end(); i++)
|
||||||
|
close(*i);
|
||||||
|
}
|
18
src/pathlocks.hh
Normal file
18
src/pathlocks.hh
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef __PATHLOCKS_H
|
||||||
|
#define __PATHLOCKS_H
|
||||||
|
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
|
|
||||||
|
class PathLocks
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
list<int> fds;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PathLocks(const Strings & _paths);
|
||||||
|
~PathLocks();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !__PATHLOCKS_H */
|
|
@ -260,7 +260,6 @@ void addToStore(string srcPath, string & dstPath, FSId & id,
|
||||||
dstPath = canonPath(nixStore + "/" + (string) id + "-" + baseName);
|
dstPath = canonPath(nixStore + "/" + (string) id + "-" + baseName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/* !!! should not use the substitutes! */
|
|
||||||
dstPath = expandId(id, deterministicName ? dstPath : "",
|
dstPath = expandId(id, deterministicName ? dstPath : "",
|
||||||
nixStore, FSIdSet(), true);
|
nixStore, FSIdSet(), true);
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue