Here is a second attempt at an NFS workaround.

This patch applies to ccache-2.4. The CCACHE_NFS_WORKAROUND variable 
enables the workaround. This workaround requires the existence of the 
"lockfile" program on the $PATH.

Here's what I think is going on, when NFS corruption occurs:

  * Process X writes object A into the cache.
  * Process Y starts reading object A.
  * Process Z writes a new copy of object A into the cache, renaming 
over the original A, while Y is still reading.

A local filesystem will keep the original object A around until Y closes 
the file. But on NFS, which is a stateless protocol, the server 
immediately forgets that the original A ever existed. When process Y 
comes back requesting the next chunk of the original A, it is gone.

This patch prevents overwriting an object that's already in the cache. 
It seems to work, YMMV.

- John



diff -u ccache-2.4/ccache.c ccache-2.4-fix/ccache.c
--- ccache-2.4/ccache.c Mon Sep 13 06:38:30 2004
+++ ccache-2.4-fix/ccache.c     Sat Mar 24 15:43:45 2007
@@ -158,6 +158,30 @@
        struct stat st1, st2;
        int status;

+       char *workaround = getenv("CCACHE_NFS_WORKAROUND");
+       char *lockfile, *lock_cmd;
+       struct stat st;
+
+       if( workaround )
+       {
+           /* acquire lock -- permission to create the cache entry. */
+           x_asprintf( &lockfile, "%s.lock", hashname );
+           x_asprintf( &lock_cmd, "lockfile -l 600 %s", lockfile );
+           if( 0 != system( lock_cmd ) )
+           {
+               cc_log( "failed to get lockfile %s\n", lockfile );
+               failed();
+           }
+
+           /* got lock -- is the file there now? */
+           if( stat( hashname, &st ) == 0 )
+           {
+               /* another ccache process built it. Just return. */
+               unlink( lockfile );
+               return;
+           }
+       }
+
        x_asprintf(&tmp_stdout, "%s/tmp.stdout.%s", temp_dir, tmp_string());
        x_asprintf(&tmp_stderr, "%s/tmp.stderr.%s", temp_dir, tmp_string());
        x_asprintf(&tmp_hashname, "%s/tmp.hash.%s.o", temp_dir, tmp_string());
@@ -187,6 +211,8 @@
                unlink(tmp_stdout);
                unlink(tmp_stderr);
                unlink(tmp_hashname);
+               if( workaround )
+                   unlink(lockfile);
                failed();
        }
        unlink(tmp_stdout);
@@ -196,6 +222,9 @@
                cc_log("compile of %s gave status = %d\n", output_file, status);
                stats_update(STATS_STATUS);

+               if( workaround )
+                   unlink( lockfile );
+
                fd = open(tmp_stderr, O_RDONLY | O_BINARY);
                if (fd != -1) {
                        if (strcmp(output_file, "/dev/null") == 0 ||
@@ -236,11 +265,20 @@
            rename(tmp_stderr, path_stderr) != 0) {
                cc_log("failed to rename tmp files - %s\n", strerror(errno));
                stats_update(STATS_ERROR);
+               if( workaround )
+                   unlink( lockfile );
                failed();
        }

        cc_log("Placed %s into cache\n", output_file);
        stats_tocache(file_size(&st1) + file_size(&st2));
+
+       if( workaround )
+       {
+           unlink( lockfile );
+           free( lockfile );
+           free( lock_cmd );
+       }

        free(tmp_hashname);
        free(tmp_stderr);

Reply via email to