diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 4c6a944b89..e7321449e7 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -194,9 +194,35 @@ void collectGarbage(const PathSet & roots, GCAction action, debug(format("dead path `%1%'") % path); result.insert(path); + AutoCloseFD fdLock; + if (action == gcDeleteDead) { printMsg(lvlInfo, format("deleting `%1%'") % path); + + /* Only delete a lock file if we can acquire a write lock + on it. That means that it's either stale, or the + process that created it hasn't locked it yet. In the + latter case the other process will detect that we + deleted the lock, and retry (see pathlocks.cc). */ + if (path.size() >= 5 && string(path, path.size() - 5) == ".lock") { + + fdLock = open(path.c_str(), O_RDWR); + if (fdLock == -1) { + if (errno == ENOENT) continue; + throw SysError(format("opening lock file `%1%'") % path); + } + + if (!lockFile(fdLock, ltWrite, false)) { + debug(format("skipping active lock `%1%'") % path); + continue; + } + } + deleteFromStore(path); + + if (fdLock != -1) + /* Write token to stale (deleted) lock file. */ + writeFull(fdLock, (const unsigned char *) "d", 1); } /* Only delete lock files if the path is belongs to doesn't diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh index 838188adeb..c8f908b152 100644 --- a/src/libstore/gc.hh +++ b/src/libstore/gc.hh @@ -16,7 +16,9 @@ void collectGarbage(const PathSet & roots, GCAction action, PathSet & result); /* Register a temporary GC root. This root will automatically - disappear when this process exits. */ + disappear when this process exits. WARNING: this function should + not be called inside a BDB transaction, otherwise we can + deadlock. */ void addTempRoot(const Path & path); diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index a92b2225a5..3beb49aac8 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -127,9 +127,8 @@ PathLocks::~PathLocks() /* Write a (meaningless) token to the file to indicate to other processes waiting on this lock that the lock is stale (deleted). */ - if (write(i->first, "d", 1) == 1) { - unlink(i->second.c_str()); - } + unlink(i->second.c_str()); + writeFull(i->first, (const unsigned char *) "d", 1); /* Note that the result of unlink() is ignored; removing the lock file is an optimisation, not a necessity. */ } diff --git a/tests/Makefile.am b/tests/Makefile.am index 7c823a046a..c491aa64dd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -36,10 +36,10 @@ nix-pull.sh: dependencies.nix gc.sh: dependencies.nix gc-concurrent.sh: gc-concurrent.nix -#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 \ -# gc-concurrent.sh verify.sh nix-pull.sh -TESTS = init.sh gc-concurrent.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 \ + gc-concurrent.sh verify.sh nix-pull.sh +#TESTS = init.sh gc-concurrent.sh XFAIL_TESTS = diff --git a/tests/gc-concurrent.builder.sh b/tests/gc-concurrent.builder.sh index 561c2292e9..2d327f9021 100644 --- a/tests/gc-concurrent.builder.sh +++ b/tests/gc-concurrent.builder.sh @@ -6,4 +6,7 @@ echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar sleep 5 mkdir $out || true -ln -s $input2 $out/input-2 \ No newline at end of file +# Check that the GC hasn't deleted the lock on our output. +test -e "$out.lock" + +ln -s $input2 $out/input-2