You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2012/03/09 22:23:49 UTC

svn commit: r1299054 - in /subversion/branches/revprop-cache/subversion/libsvn_fs_fs: fs.h fs_fs.c

Author: stefan2
Date: Fri Mar  9 21:23:48 2012
New Revision: 1299054

URL: http://svn.apache.org/viewvc?rev=1299054&view=rev
Log:
On the revprop-cache branch: replace the revprop-gen file with
the use of the new svn_named_atomic API.
!!! Not tested, yet. Just committing things for review. !!!

* subversion/libsvn_fs_fs/fs.h
  (PATH_REVPROP_GEN): drop
  (fs_fs_data_t): remove cached revprop gen value; add shared mem
   access objects
* subversion/libsvn_fs_fs/fs_fs.c
  (REVPROP_CHANGE_TIMEOUT, ATOMIC_REVPROP_GENERATION,
   ATOMIC_REVPROP_TIMEOUT): new constants
  (path_revprop_generation): drop
  (ensure_revprop_generation, ensure_revprop_timeout): new utilities
  (read_revprop_generation): switch from file to shm access
  (begin_revprop_change, end_revprop_change): new revprop update
   communication functions
  (check_revprop_generation, increment_revprop_generation): drop
  (set_revision_proplist): if revprop cache has been enabled, call the
   new update communication functions
  (revision_proplist): if revprop cache has been enabled, call the
   new reader function
  (hotcopy_create_empty_dest, svn_fs_fs__create): drop handling
   of revprop generation files

Modified:
    subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs.h
    subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs_fs.c

Modified: subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs.h?rev=1299054&r1=1299053&r2=1299054&view=diff
==============================================================================
--- subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs.h Fri Mar  9 21:23:48 2012
@@ -34,6 +34,7 @@
 #include "private/svn_fs_private.h"
 #include "private/svn_sqlite.h"
 #include "private/svn_mutex.h"
+#include "private/svn_named_atomic.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -60,8 +61,6 @@ extern "C" {
 #define PATH_LOCKS_DIR        "locks"            /* Directory of locks */
 #define PATH_MIN_UNPACKED_REV "min-unpacked-rev" /* Oldest revision which
                                                     has not been packed. */
-#define PATH_REVPROP_GEN      "revprop-gen"      /* File containing the
-                                                    revprop change counter */
 /* If you change this, look at tests/svn_test_fs.c(maybe_install_fsfs_conf) */
 #define PATH_CONFIG           "fsfs.conf"        /* Configuration */
 
@@ -240,11 +239,13 @@ typedef struct fs_fs_data_t
      rep key (revision/offset) to svn_string_t. */
   svn_cache__t *fulltext_cache;
 
-  /* Revprop "generation" that is valid for this svn_fs_t.
-     It is the revprop change counter read once from "revprop-gen".
-     Will be read upon first access.  0 means that the value has not
-     been read from disk, yet. */
-  apr_int64_t revprop_generation;
+  /* Access object to the revprop "generation". Will be NULL until
+     the first access. */
+  svn_named_atomic__t *revprop_generation;
+  
+  /* Access object to the revprop update timeout. Will be NULL until
+     the first access. */
+  svn_named_atomic__t *revprop_timeout;
   
   /* Revision property cache.  Maps from (rev,generation) to apr_hash_t. */
   svn_cache__t *revprop_cache;

Modified: subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs_fs.c?rev=1299054&r1=1299053&r2=1299054&view=diff
==============================================================================
--- subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/revprop-cache/subversion/libsvn_fs_fs/fs_fs.c Fri Mar  9 21:23:48 2012
@@ -92,6 +92,16 @@
    Values < 1 disable deltification. */
 #define SVN_FS_FS_MAX_DELTIFICATION_WALK 1023
 
+/* Give writing processes 10 seconds to replace an existing revprop
+   file with a new one. After that time, we assume that the writing
+   process got aborted and that we have re-read revprops. */
+#define REVPROP_CHANGE_TIMEOUT 10 * 1000000
+
+/* The following are names of atomics that will be used to communicate
+ * revprop updates across all processes on this machine. */
+#define ATOMIC_REVPROP_GENERATION "RevPropGeneration"
+#define ATOMIC_REVPROP_TIMEOUT    "RevPropTimeout"
+
 /* Following are defines that specify the textual elements of the
    native filesystem directories and revision files. */
 
@@ -225,12 +235,6 @@ path_lock(svn_fs_t *fs, apr_pool_t *pool
 }
 
 static const char *
-path_revprop_generation(svn_fs_t *fs, apr_pool_t *pool)
-{
-  return svn_dirent_join(fs->path, PATH_REVPROP_GEN, pool);
-}
-
-static const char *
 path_rev_packed(svn_fs_t *fs, svn_revnum_t rev, const char *kind,
                 apr_pool_t *pool)
 {
@@ -2707,103 +2711,129 @@ svn_fs_fs__rev_get_root(svn_fs_id_t **ro
   return SVN_NO_ERROR;
 }
 
-/* Reads the revprop_gen file and writes the content into the
-   REVPROP_GENERATION member of FS.  Use pool for allocations. */
+/* Make sure the revprop_generation member in FS is set. */
 static svn_error_t *
-read_revprop_generation(svn_fs_t *fs,
-                        apr_pool_t *pool)
+ensure_revprop_generation(svn_fs_t *fs)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   
-  const char *path = path_revprop_generation(fs, pool);
-  svn_error_t *err;
-  char buf[80];
-  int i;
+  return ffd->revprop_generation == NULL
+    ? svn_named_atomic__get(&ffd->revprop_generation,
+                            ATOMIC_REVPROP_GENERATION,
+                            TRUE)
+    : SVN_NO_ERROR;
+}
 
-  /* Read the raw data from the file, if it exists. If it does
-     not, set the generation to "1" and return.
-     We don't want to have this function fail.  So, patiently
-     retry a couple of times the case the OS denied us access. */
-  apr_pool_t *iterpool = svn_pool_create(pool);
-  for (i = 0; i < RECOVERABLE_RETRY_COUNT; ++i)
-    {
-      apr_file_t *file;
-      apr_size_t len = sizeof(buf);
-      
-      svn_pool_clear(iterpool);
+/* Make sure the revprop_timeout member in FS is set. */
+static svn_error_t *
+ensure_revprop_timeout(svn_fs_t *fs)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  
+  return ffd->revprop_timeout == NULL
+    ? svn_named_atomic__get(&ffd->revprop_timeout,
+                            ATOMIC_REVPROP_TIMEOUT,
+                            TRUE)
+    : SVN_NO_ERROR;
+}
 
-      err = svn_io_file_open(&file, path, APR_READ | APR_BUFFERED,
-                            APR_OS_DEFAULT, iterpool);
-      if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+/* Read the current revprop generation and return it in *GENERATION.
+   Also, detect aborted / crashed writers and recover from that.
+   Use the access object in FS to set the shared mem values. */
+static svn_error_t *
+read_revprop_generation(svn_fs_t *fs,
+                        apr_int64_t *generation)
+{
+  apr_int64_t current = 0;
+  fs_fs_data_t *ffd = fs->fsap_data;
+  
+  /* read the current revprop generation number */
+  SVN_ERR(ensure_revprop_generation(fs));
+  SVN_ERR(svn_named_atomic__read(&current, ffd->revprop_generation));
+
+  /* is an unfinished revprop write under the way? */
+  if (current % 2)
+    {
+      apr_int64_t timeout = 0;
+
+      /* read timeout for the write operation */
+      SVN_ERR(ensure_revprop_timeout(fs));
+      SVN_ERR(svn_named_atomic__read(&timeout, ffd->revprop_timeout));
+
+      /* has the writer process been aborted,
+       * i.e. has the timeout been reached?
+       */
+      if (apr_time_now() > timeout)
         {
-          /* No-one changed a revprop -> we are still at gen 1. */
-          ffd->revprop_generation = 1;
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
+          /* Cause everyone to re-read revprops upon their next access.
+           * Keep in mind that we may not be the only one trying to do it.
+           */
+          while (current % 2)
+            SVN_ERR(svn_named_atomic__add(&current,
+                                          1,
+                                          ffd->revprop_generation));
         }
-      svn_error_clear(err);
-
-      RETRY_RECOVERABLE(err, file,
-                        svn_io_read_length_line(file,
-                                                buf,
-                                                &len,
-                                                iterpool));
-      IGNORE_RECOVERABLE(err, svn_io_file_close(file,
-                                                iterpool));
-
-      break;
     }
-  SVN_ERR(err);
-
-  svn_pool_destroy(iterpool);
-
-  /* Check that the first line contains only digits. */
-  SVN_ERR(check_file_buffer_numeric(buf, 0, path, 
-                                    "Revprop generations", pool));
-  SVN_ERR(svn_cstring_atoi64(&ffd->revprop_generation, buf));
-
-  /* Graceful behavior in case someone put a "0" in the file. */
-  if (ffd->revprop_generation <= 0)
-    ffd->revprop_generation = 1;
 
+  /* return the value we just got */
+  *generation = current;
   return SVN_NO_ERROR;
 }
 
+/* Set the revprop generation to the next odd number to indicate that
+   there is a revprop write process under way. If that times out,
+   readers shall recover from that state & re-read revprops.
+   Use the access object in FS to set the shared mem value. */
 static svn_error_t *
-check_revprop_generation(svn_fs_t *fs,
-                         apr_pool_t *pool)
+begin_revprop_change(svn_fs_t *fs)
 {
+  apr_int64_t current;
   fs_fs_data_t *ffd = fs->fsap_data;
   
-  return ffd->revprop_generation == 0
-    ? read_revprop_generation(fs, pool)
-    : SVN_NO_ERROR;
+  /* set the timeout for the write operation */
+  SVN_ERR(ensure_revprop_timeout(fs));
+  SVN_ERR(svn_named_atomic__write(NULL,
+                                  apr_time_now() + REVPROP_CHANGE_TIMEOUT,
+                                  ffd->revprop_timeout));
+
+  /* set the revprop generation to an odd value to indicate
+   * that a write is in progress
+   */
+  SVN_ERR(ensure_revprop_generation(fs));
+  do
+    {
+      SVN_ERR(svn_named_atomic__add(&current,
+                                    1,
+                                    ffd->revprop_generation));
+    }
+  while (current % 2 == 0);
+
+  return SVN_NO_ERROR;
 }
 
+/* Set the revprop generation to the next even number to indicate that
+   a) readers shall re-read revprops, and
+   b) the write process has been completed (no recovery required)
+   Use the access object in FS to set the shared mem value. */
 static svn_error_t *
-increment_revprop_generation(svn_fs_t *fs,
-                             apr_pool_t *pool)
+end_revprop_change(svn_fs_t *fs)
 {
+  apr_int64_t current = 1;
   fs_fs_data_t *ffd = fs->fsap_data;
   
-  const char *path = path_revprop_generation(fs, pool);
-  const char *tmp_filename;
-  svn_string_t *generation;
-
-  SVN_ERR(read_revprop_generation(fs, pool));
-
-  /* Increment the key and add a trailing \n to the string so the
-     txn-current file has a newline in it. */
-  ++ffd->revprop_generation;
-  generation = svn_string_createf(pool, "%" APR_INT64_T_FMT "\n",
-                                  ffd->revprop_generation);
+  /* set the revprop generation to an even value to indicate
+   * that a write has been completed
+   */
+  SVN_ERR(ensure_revprop_generation(fs));
+  do
+    {
+      SVN_ERR(svn_named_atomic__add(&current,
+                                    1,
+                                    ffd->revprop_generation));
+    }
+  while (current % 2);
 
-  SVN_ERR(svn_io_write_unique(&tmp_filename,
-                              svn_dirent_dirname(path, pool),
-                              generation->data, generation->len,
-                              svn_io_file_del_none, pool));
-  return move_into_place(tmp_filename, path, 
-                         svn_fs_fs__path_current(fs, pool), pool);
+  return SVN_NO_ERROR;
 }
 
 /* Set the revision property list of revision REV in filesystem FS to
@@ -2822,10 +2852,12 @@ set_revision_proplist(svn_fs_t *fs,
       const char *tmp_path;
       const char *perms_reference;
       svn_stream_t *stream;
-      svn_node_kind_t kind;
+      svn_node_kind_t kind = svn_node_none;
+      fs_fs_data_t *ffd = fs->fsap_data;
 
       /* test whether revprops already exist for this revision */
-      SVN_ERR(svn_io_check_path(final_path, &kind, pool));
+      if (ffd->revprop_cache)
+        SVN_ERR(svn_io_check_path(final_path, &kind, pool));
 
       /* ### do we have a directory sitting around already? we really shouldn't
          ### have to get the dirname here. */
@@ -2840,13 +2872,17 @@ set_revision_proplist(svn_fs_t *fs,
          file won't exist and therefore can't serve as its own reference.
          (Whereas the rev file should already exist at this point.) */
       SVN_ERR(svn_fs_fs__path_rev_absolute(&perms_reference, fs, rev, pool));
+
+      /* Now, we are actually (potentially) replacing revprops. Make sure
+         that all other threads and processes will know about this. */
+      if (kind != svn_node_none)
+        SVN_ERR(begin_revprop_change(fs));
+
       SVN_ERR(move_into_place(tmp_path, final_path, perms_reference, pool));
 
-      /* Invalidate all cached revprops for this FS and for all other
-         users that haven't read any revprops, YET.  Since writing revprops
-         implies a write lock, there can be no races. */
+      /* Indicate that the update (if relevant) has been completed. */
       if (kind != svn_node_none)
-        SVN_ERR(increment_revprop_generation(fs, pool));
+        SVN_ERR(end_revprop_change(fs));
 
       return SVN_NO_ERROR;
     }
@@ -2865,15 +2901,18 @@ revision_proplist(apr_hash_t **proplist_
   const char *key;
 
   SVN_ERR(ensure_revision_exists(fs, rev, pool));
-  SVN_ERR(check_revprop_generation(fs, pool));
 
   /* Try cache lookup first. */
-  key = svn_fs_fs__combine_two_numbers(rev, ffd->revprop_generation, pool);
   if (ffd->revprop_cache)
     {
+      apr_int64_t generation;
       svn_boolean_t is_cached;
+
+      SVN_ERR(read_revprop_generation(fs, &generation));
+
+      key = svn_fs_fs__combine_two_numbers(rev, generation, pool);
       SVN_ERR(svn_cache__get((void **) proplist_p, &is_cached,
-                              ffd->revprop_cache, key, pool));
+                             ffd->revprop_cache, key, pool));
       if (is_cached)
         return SVN_NO_ERROR;
     }
@@ -6909,9 +6948,6 @@ svn_fs_fs__create(svn_fs_t *fs,
                                  "", pool));
     }
 
-  /* Create the revprop generation tracking file. */
-  SVN_ERR(increment_revprop_generation(fs, pool));
-
   /* This filesystem is ready.  Stamp it with a format number. */
   SVN_ERR(write_format(path_format(fs, pool),
                        ffd->format, ffd->max_files_per_dir, FALSE, pool));
@@ -9026,14 +9062,6 @@ hotcopy_create_empty_dest(svn_fs_t *src_
                                  "", pool));
     }
 
-  /* Copy the revprop generation file if it exists in SRC_FS. */
-  SVN_ERR(svn_io_check_path(path_revprop_generation(src_fs, pool),
-                            &kind, pool));
-  if (kind == svn_node_file)
-    SVN_ERR(svn_io_copy_file(path_revprop_generation(src_fs, pool),
-                             path_revprop_generation(dst_fs, pool),
-                             TRUE, pool));
-    
   dst_ffd->youngest_rev_cache = 0;
   return SVN_NO_ERROR;
 }