about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libstore/gc.cc26
-rw-r--r--src/libstore/gc.hh4
-rw-r--r--src/libstore/pathlocks.cc5
-rw-r--r--tests/Makefile.am8
-rw-r--r--tests/gc-concurrent.builder.sh5
5 files changed, 39 insertions, 9 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 4c6a944b89ce..e7321449e7a1 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 838188adeb82..c8f908b152aa 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 a92b2225a51c..3beb49aac803 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 7c823a046a79..c491aa64dd17 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 561c2292e98f..2d327f902182 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