chiark / gitweb /
git-cache-proxy: introduce lockfile
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 10 Nov 2013 00:39:17 +0000 (00:39 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 10 Nov 2013 00:39:17 +0000 (00:39 +0000)
scripts/git-cache-proxy

index 520f1e5..b4b2300 100755 (executable)
@@ -157,6 +157,31 @@ for (;;) {
 
 #---------- utility functions ----------
 
+sub lockfile ($$) {
+    my ($fh, $fn, $flockmode) = @_;
+    for (;;) {
+       close $fh;
+       open $fh, '+>', $fn or fail "open/create $fn for lock: $!";
+       if (!flock $fh, $lockmode) {
+           if ($lockmode & LOCK_NB and $! == EWOULDBLOCK) {
+               return 0; # ok then
+           }
+           fail "lock $fn".
+               (($flockmode & ~LOCK_NB) == LOCK_SH ? " (shared)" : "").
+               ": $!";
+       }
+       stat $fh or fail "stat opened $fn: $!";
+       my $fh_ino = ((stat _)[1]);
+       if (!stat $fn) {
+           $! == ENOENT or fail "stat $fn: $!";
+           next;
+       }
+       my $fn_ino = ((stat _)[1]);
+       return 1 if $fn_ino == $fh_ino;
+       # oh dear
+    }
+}
+
 sub xread {
     my $length = shift;
     my $buffer = "";
@@ -236,8 +261,7 @@ sub readcommand () {
 }
 
 sub clonefetch () {
-    open LOCK, "+>", $lock or fail "open/create $lock: $!";
-    flock LOCK, LOCK_EX or fail "lock exclusive $lock: $!";
+    lockfile \*LOCK, $lock, LOCK_EX;
 
     my $exists = lstat $gitd;
     $exists or $!==ENOENT or fail "lstat $gitd: $!";
@@ -315,10 +339,7 @@ sub clonefetch () {
     }
 
     servinfo "sharing";
-    flock LOCK, LOCK_UN or fail "unlock $lock: $!";
-    flock LOCK, LOCK_SH or fail "lock shared $lock: $!";
-    # actually, just relocking as shared would have the same semantics
-    # but it's best to be explicit
+    lockfile \*LOCK, $lock, LOCK_SH; # NB releases and relocks
 
     if (chdir $gitd) {
        return 1;
@@ -356,10 +377,9 @@ sub housekeeping () {
 
 sub housekeepingcheck ($$) {
     my ($dofork, $force) = @_;
-    open HLOCK, "+>", "Housekeeping.lock" 
        or fail "open/create Housekeeping.lock: $!";
     if (!$force) {
-       if (flock HLOCK, LOCK_EX|LOCK_NB) {
+       if (!lockfile \*HLOCK, "Housekeeping.lock", LOCK_EX|LOCK_NB) {
            logm 'debug', "housekeeping lock taken, not running";
            close HLOCK;
            return 0;