aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2005-01-31 22:01:55 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2005-01-31 22:01:55 +0000
commit89c9bc11abc02cc746838bfef101e5fecc59a6c5 (patch)
treeff3f905443476b0c41be7560851cd90b2823b8df
parent207bdcbe86bac5fb9e650d26b22ae33c667151e4 (diff)
* Add a test for a more subtle race: a process starting after the
temporary root files have been read but creating outputs before the store directory has been read.
-rw-r--r--src/libstore/gc.cc22
-rw-r--r--tests/Makefile.am10
-rw-r--r--tests/gc-concurrent.sh39
-rw-r--r--tests/gc-concurrent2.builder.sh9
-rw-r--r--tests/gc-concurrent2.nix.in25
5 files changed, 85 insertions, 20 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index af40e0b92..c89f7a8a1 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -12,10 +12,24 @@
#include <unistd.h>
+/* Acquire the global GC lock. */
+static AutoCloseFD openGCLock(LockType lockType)
+{
+#if 0
+ Path fnGCLock = (format("%1%/%2%/%3%")
+ % nixStateDir % tempRootsDir % getpid()).str();
+
+ fdTempRoots = open(fnTempRoots.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (fdTempRoots == -1)
+ throw SysError(format("opening temporary roots file `%1%'") % fnTempRoots);
+#endif
+}
+
+
static string tempRootsDir = "temproots";
/* The file to which we write our temporary roots. */
-Path fnTempRoots;
+static Path fnTempRoots;
static AutoCloseFD fdTempRoots;
@@ -213,6 +227,10 @@ void collectGarbage(const PathSet & roots, GCAction action,
else
tempRootsClosed.insert(*i);
+ /* For testing - see tests/gc-concurrent.sh. */
+ if (getenv("NIX_DEBUG_GC_WAIT"))
+ sleep(2);
+
/* After this point the set of roots or temporary roots cannot
increase, since we hold locks on everything. So everything
that is not currently in in `livePaths' or `tempRootsClosed'
@@ -231,7 +249,7 @@ void collectGarbage(const PathSet & roots, GCAction action,
/* !!! when we have multiple output paths per derivation, this
will not work anymore because we get cycles. */
storePaths = topoSort(storePaths2);
-
+
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
debug(format("considering deletion of `%1%'") % *i);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c491aa64d..e1c158500 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -34,12 +34,12 @@ fallback.sh: fallback.nix
nix-push.sh: dependencies.nix
nix-pull.sh: dependencies.nix
gc.sh: dependencies.nix
-gc-concurrent.sh: gc-concurrent.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 \
- 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.sh b/tests/gc-concurrent.sh
index c41475fb6..fd329b457 100644
--- a/tests/gc-concurrent.sh
+++ b/tests/gc-concurrent.sh
@@ -1,21 +1,34 @@
-storeExpr=$($TOP/src/nix-instantiate/nix-instantiate gc-concurrent.nix)
-outPath=$($TOP/src/nix-store/nix-store -q $storeExpr)
+storeExpr1=$($TOP/src/nix-instantiate/nix-instantiate gc-concurrent.nix)
+outPath1=$($TOP/src/nix-store/nix-store -q $storeExpr1)
+
+storeExpr2=$($TOP/src/nix-instantiate/nix-instantiate gc-concurrent2.nix)
+outPath2=$($TOP/src/nix-store/nix-store -q $storeExpr2)
ls -l test-tmp/state/temproots
+# Start build #1 in the background. It starts immediately.
+$TOP/src/nix-store/nix-store -rvv "$storeExpr1" &
+pid1=$!
-# Start a build in the background.
-$TOP/src/nix-store/nix-store -rvv "$storeExpr" &
-pid=$!
+# Start build #2 in the background after 3 seconds.
+(sleep 3 && $TOP/src/nix-store/nix-store -rvv "$storeExpr2") &
+pid2=$!
-# Run the garbage collector while the build is running.
+# Run the garbage collector while the build is running. Note: the GC
+# sleeps for *another* 2 seconds after acquiring the GC lock. This
+# checks whether build #1
sleep 2
-$NIX_BIN_DIR/nix-collect-garbage -vvvvv
+NIX_DEBUG_GC_WAIT=1 $NIX_BIN_DIR/nix-collect-garbage -vvvvv
+
+# Wait for build #1/#2 to finish.
+echo waiting for pid $pid1 to finish...
+wait $pid1
+echo waiting for pid $pid2 to finish...
+wait $pid2
-# Wait for the build to finish.
-echo waiting for pid $pid to finish...
-wait $pid
+# Check that the root of build #1 and its dependencies haven't been
+# deleted.
+cat $outPath1/foobar
+cat $outPath1/input-2/bar
-# Check that the root and its dependencies haven't been deleted.
-cat $outPath/foobar
-cat $outPath/input-2/bar
+cat $outPath2/foobar
diff --git a/tests/gc-concurrent2.builder.sh b/tests/gc-concurrent2.builder.sh
new file mode 100644
index 000000000..d94e46971
--- /dev/null
+++ b/tests/gc-concurrent2.builder.sh
@@ -0,0 +1,9 @@
+export PATH=/bin:/usr/bin:$PATH
+
+mkdir $out
+echo $(cat $input1/foo)$(cat $input2/bar)xyzzy > $out/foobar
+
+# Check that the GC hasn't deleted the lock on our output.
+test -e "$out.lock"
+
+sleep 3 \ No newline at end of file
diff --git a/tests/gc-concurrent2.nix.in b/tests/gc-concurrent2.nix.in
new file mode 100644
index 000000000..e6050f1bb
--- /dev/null
+++ b/tests/gc-concurrent2.nix.in
@@ -0,0 +1,25 @@
+let {
+
+ input1 = derivation {
+ name = "dependencies-input-1";
+ system = "@system@";
+ builder = "@shell@";
+ args = ["-e" "-x" ./dependencies.builder1.sh];
+ };
+
+ input2 = derivation {
+ name = "dependencies-input-2";
+ system = "@system@";
+ builder = "@shell@";
+ args = ["-e" "-x" ./dependencies.builder2.sh];
+ };
+
+ body = derivation {
+ name = "gc-concurrent2";
+ system = "@system@";
+ builder = "@shell@";
+ args = ["-e" "-x" ./gc-concurrent2.builder.sh];
+ inherit input1 input2;
+ };
+
+} \ No newline at end of file