* Acquire a global GC lock to prevent new temporary root files from

being created after the garbage collector has read the temproots
  directory.  This blocks the creation of new processes, but the
  garbage collector could periodically release the GC lock to allow
  them to run.
This commit is contained in:
Eelco Dolstra 2005-01-31 22:23:49 +00:00
parent 89c9bc11ab
commit 32fa82a56a
3 changed files with 36 additions and 19 deletions

View file

@ -12,17 +12,30 @@
#include <unistd.h> #include <unistd.h>
/* Acquire the global GC lock. */ static string gcLockName = "gc.lock";
static AutoCloseFD openGCLock(LockType lockType)
/* Acquire the global GC lock. This is used to prevent new Nix
processes from starting after the temporary root files have been
read. To be precise: when they try to create a new temporary root
file, they will block until the garbage collector has finished /
yielded the GC lock. */
static int openGCLock(LockType lockType)
{ {
#if 0 Path fnGCLock = (format("%1%/%2%")
Path fnGCLock = (format("%1%/%2%/%3%") % nixStateDir % gcLockName).str();
% nixStateDir % tempRootsDir % getpid()).str();
fdTempRoots = open(fnTempRoots.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600); AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
if (fdTempRoots == -1) if (fdGCLock == -1)
throw SysError(format("opening temporary roots file `%1%'") % fnTempRoots); throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
#endif
lockFile(fdGCLock, lockType, true);
/* !!! Restrict read permission on the GC root. Otherwise any
process that can open the file for reading can DoS the
collector. */
return fdGCLock.borrow();
} }
@ -41,11 +54,15 @@ void addTempRoot(const Path & path)
while (1) { while (1) {
fnTempRoots = (format("%1%/%2%/%3%") fnTempRoots = (format("%1%/%2%/%3%")
% nixStateDir % tempRootsDir % getpid()).str(); % nixStateDir % tempRootsDir % getpid()).str();
AutoCloseFD fdGCLock = openGCLock(ltRead);
fdTempRoots = open(fnTempRoots.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600); fdTempRoots = open(fnTempRoots.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
if (fdTempRoots == -1) if (fdTempRoots == -1)
throw SysError(format("opening temporary roots file `%1%'") % fnTempRoots); throw SysError(format("opening temporary roots file `%1%'") % fnTempRoots);
fdGCLock.close();
debug(format("acquiring read lock on `%1%'") % fnTempRoots); debug(format("acquiring read lock on `%1%'") % fnTempRoots);
lockFile(fdTempRoots, ltRead, true); lockFile(fdTempRoots, ltRead, true);
@ -188,14 +205,11 @@ void collectGarbage(const PathSet & roots, GCAction action,
{ {
result.clear(); result.clear();
/* !!! TODO: Acquire the global GC root. This prevents /* Acquire the global GC root. This prevents
a) New roots from being added. a) New roots from being added.
b) Processes from creating new temporary root files. */ b) Processes from creating new temporary root files. */
AutoCloseFD fdGCLock = openGCLock(ltWrite);
/* !!! Restrict read permission on the GC root. Otherwise any
process that can open the file for reading can DoS the
collector. */
/* Determine the live paths which is just the closure of the /* Determine the live paths which is just the closure of the
roots under the `references' relation. */ roots under the `references' relation. */
PathSet livePaths; PathSet livePaths;

View file

@ -36,10 +36,10 @@ nix-pull.sh: dependencies.nix
gc.sh: dependencies.nix gc.sh: dependencies.nix
gc-concurrent.sh: gc-concurrent.nix gc-concurrent2.nix gc-concurrent.sh: gc-concurrent.nix gc-concurrent2.nix
#TESTS = init.sh hash.sh lang.sh simple.sh dependencies.sh locking.sh parallel.sh \ TESTS = init.sh hash.sh lang.sh simple.sh dependencies.sh locking.sh parallel.sh \
# build-hook.sh substitutes.sh substitutes2.sh fallback.sh nix-push.sh gc.sh \ build-hook.sh substitutes.sh substitutes2.sh fallback.sh nix-push.sh gc.sh \
# gc-concurrent.sh verify.sh nix-pull.sh gc-concurrent.sh verify.sh nix-pull.sh
TESTS = init.sh gc-concurrent.sh #TESTS = init.sh gc-concurrent.sh
XFAIL_TESTS = XFAIL_TESTS =

View file

@ -6,6 +6,8 @@ outPath2=$($TOP/src/nix-store/nix-store -q $storeExpr2)
ls -l test-tmp/state/temproots ls -l test-tmp/state/temproots
ln -s $storeExpr2 "$NIX_LOCALSTATE_DIR"/nix/gcroots/foo2
# Start build #1 in the background. It starts immediately. # Start build #1 in the background. It starts immediately.
$TOP/src/nix-store/nix-store -rvv "$storeExpr1" & $TOP/src/nix-store/nix-store -rvv "$storeExpr1" &
pid1=$! pid1=$!
@ -31,4 +33,5 @@ wait $pid2
cat $outPath1/foobar cat $outPath1/foobar
cat $outPath1/input-2/bar cat $outPath1/input-2/bar
# Build #2 should have failed because its derivation got garbage collected.
cat $outPath2/foobar cat $outPath2/foobar