Author: rhuijben
Date: Mon Jul  5 21:25:07 2010
New Revision: 960708

URL: http://svn.apache.org/viewvc?rev=960708&view=rev
Log:
Following up on r960635, use the new dirent data to optimize the most
common paths through the status walker to skip the per file stat
operation previously required to check if a file was modified.

This should at least avoid a kernel invocation for every unmodified
file in a working copy, but I expect a bigger difference on larger
working copies and working copies stored on a network drive.

* subversion/include/svn_io.h
  (svn_io_stat_dirent): Add argument to help handling enoent.

* subversion/libsvn_subr/io.c
  (svn_io_stat_dirent): Handle all entry not found errors.

* subversion/libsvn_wc/status.c
  (assemble_status): Take dirent instead of special_path boolean. If
     dirent contains size and last_mod time use these to check the file
     for modifications before falling back to calling
     svn_wc__internal_text_modified_p(). Also make sure text_modified_p
     is set on an access denied error.
  (send_status_structure,
   handle_dir_entry): Forward dirent instead of special_path boolean.
  (get_dir_status): Use svn_io_get_dirents3 to obtain more advanced
     dirents.
  (internal_status): Obtain dirent instead of just path info and pass
     that to assemble_status()

Modified:
    subversion/trunk/subversion/include/svn_io.h
    subversion/trunk/subversion/libsvn_subr/io.c
    subversion/trunk/subversion/libsvn_wc/status.c

Modified: subversion/trunk/subversion/include/svn_io.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_io.h?rev=960708&r1=960707&r2=960708&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_io.h (original)
+++ subversion/trunk/subversion/include/svn_io.h Mon Jul  5 21:25:07 2010
@@ -1491,11 +1491,15 @@ svn_io_get_dirents(apr_hash_t **dirents,
 /** Create a svn_io_dirent2_t instance for path. Specialized variant of
  * svn_io_stat() that directly translates node_kind and special.
  *
+ * If @a ignore_enoent is set to @c TRUE, set *dirent_p->kind to
+ * svn_node_none instead of returning an error.
+ *
  * @since New in 1.7.
  */
 svn_error_t *
 svn_io_stat_dirent(const svn_io_dirent2_t **dirent_p,
                    const char *path,
+                   svn_boolean_t ignore_enoent,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool);
 

Modified: subversion/trunk/subversion/libsvn_subr/io.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/io.c?rev=960708&r1=960707&r2=960708&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/io.c (original)
+++ subversion/trunk/subversion/libsvn_subr/io.c Mon Jul  5 21:25:07 2010
@@ -2104,16 +2104,38 @@ svn_io_get_dirents3(apr_hash_t **dirents
 svn_error_t *
 svn_io_stat_dirent(const svn_io_dirent2_t **dirent_p,
                    const char *path,
+                   svn_boolean_t ignore_enoent,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
   apr_finfo_t finfo;
   svn_io_dirent2_t *dirent;
+  svn_error_t *err;
+
+  err = svn_io_stat(&finfo, path,
+                    APR_FINFO_TYPE | APR_FINFO_NAME | APR_FINFO_LINK
+                    | APR_FINFO_SIZE | APR_FINFO_MTIME,
+                    scratch_pool));
+
+  if (err && ignore_enoent && 
+      (APR_STATUS_IS_ENOENT(err->apr_err)
+       || APR_STATUS_IS_ENOTDIR(err->apr_err)
+#ifdef WIN32
+           /* On Windows, APR_STATUS_IS_ENOTDIR includes several kinds of
+            * invalid-pathname error but not this one, so we include it. */
+           /* ### This fix should go into APR. */
+           || (APR_TO_OS_ERROR(err->apr_err) == ERROR_INVALID_NAME)
+#endif
+      ))
+    {
+      svn_error_clear(err);
+      dirent = svn_io_dirent2_create(result_pool);
+      SVN_ERR_ASSERT(dirent->kind == svn_node_none);
 
-  SVN_ERR(svn_io_stat(&finfo, path,
-                      APR_FINFO_TYPE | APR_FINFO_NAME | APR_FINFO_LINK
-                      | APR_FINFO_SIZE | APR_FINFO_MTIME,
-                      scratch_pool));
+      *dirent_p = dirent;
+      return SVN_NO_ERROR;
+    }
+  SVN_ERR(err);
 
   dirent = svn_io_dirent2_create(result_pool);
   map_apr_finfo_to_node_kind(&(dirent->kind), &(dirent->special), &finfo);

Modified: subversion/trunk/subversion/libsvn_wc/status.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/status.c?rev=960708&r1=960707&r2=960708&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/status.c (original)
+++ subversion/trunk/subversion/libsvn_wc/status.c Mon Jul  5 21:25:07 2010
@@ -249,7 +249,9 @@ internal_status(svn_wc_status3_t **statu
    LOCAL_ABSPATH doesn't have a versioned parent directory.
 
    PATH_KIND is the node kind of LOCAL_ABSPATH as determined by the caller.
-   PATH_SPECIAL indicates whether the entry is a special file.
+   DIRENT contains more details about the in-wc node of LOCAL_ABSPATH,
+   including whether it is a symlink. If DIRENT is NULL, don't handle
+   LOCAL_ABSPATH as a symlink and assume no cached data.
 
    If GET_ALL is zero, and ENTRY is not locally modified, then *STATUS
    will be set to NULL.  If GET_ALL is non-zero, then *STATUS will be
@@ -263,7 +265,7 @@ assemble_status(svn_wc_status3_t **statu
                 const char *parent_repos_root_url,
                 const char *parent_repos_relpath,
                 svn_node_kind_t path_kind,
-                svn_boolean_t path_special,
+                const svn_io_dirent2_t *dirent,
                 svn_boolean_t get_all,
                 const svn_lock_t *repos_lock,
                 apr_pool_t *result_pool,
@@ -285,6 +287,8 @@ assemble_status(svn_wc_status3_t **statu
   svn_boolean_t have_base;
   svn_boolean_t conflicted;
   svn_boolean_t copied = FALSE;
+  svn_filesize_t translated_size;
+  apr_time_t last_mod_time;
   svn_depth_t depth;
   svn_error_t *err;
 
@@ -296,9 +300,9 @@ assemble_status(svn_wc_status3_t **statu
   SVN_ERR(svn_wc__db_read_info(&db_status, &db_kind, &revision,
                                &repos_relpath, &repos_root_url, NULL,
                                &changed_rev, &changed_date,
-                               &changed_author, NULL, &depth, NULL, NULL,
-                               NULL, &changelist, NULL, NULL, NULL, NULL,
-                               &prop_modified_p, &have_base, NULL,
+                               &changed_author, &last_mod_time, &depth, NULL,
+                               &translated_size, NULL, &changelist, NULL, NULL,
+                               NULL, NULL, &prop_modified_p, &have_base, NULL,
                                &conflicted, &lock, db, local_abspath,
                                result_pool, scratch_pool));
 
@@ -484,29 +488,44 @@ assemble_status(svn_wc_status3_t **statu
       if ((db_kind == svn_wc__db_kind_file
            || db_kind == svn_wc__db_kind_symlink)
 #ifdef HAVE_SYMLINK
-          && (wc_special == path_special)
+          && (wc_special == (dirent && dirent->special))
 #endif /* HAVE_SYMLINK */
           )
         {
-          err = svn_wc__internal_text_modified_p(&text_modified_p,
-                                                 db, local_abspath,
-                                                 FALSE, TRUE,
-                                                 scratch_pool);
-
-          if (err)
+          /* If the on-disk dirent exactly matches the expected state
+             skip all operations in svn_wc__internal_text_modified_p()
+             to avoid an extra filestat for every file, which can be
+             expensive on network drives as a filestat usually can't
+             be cached there */
+          if (dirent
+              && dirent->filesize != SVN_INVALID_FILESIZE
+              && dirent->mtime != 0
+              && translated_size == dirent->filesize
+              && last_mod_time == dirent->mtime)
+            text_modified_p = FALSE;
+          else
             {
-              if (!APR_STATUS_IS_EACCES(err->apr_err))
-                return svn_error_return(err);
+              err = svn_wc__internal_text_modified_p(&text_modified_p,
+                                                     db, local_abspath,
+                                                     FALSE, TRUE,
+                                                     scratch_pool);
 
-              /* An access denied is very common on Windows when another
-                 application has the file open.  Previously we ignored
-                 this error in svn_wc__text_modified_internal_p, where it
-                 should have really errored. */
-              svn_error_clear(err);
+              if (err)
+                {
+                  if (!APR_STATUS_IS_EACCES(err->apr_err))
+                    return svn_error_return(err);
+
+                  /* An access denied is very common on Windows when another
+                     application has the file open.  Previously we ignored
+                     this error in svn_wc__text_modified_internal_p, where it
+                     should have really errored. */
+                  svn_error_clear(err);
+                  text_modified_p = TRUE;
+                }
             }
         }
 #ifdef HAVE_SYMLINK
-      else if ( wc_special != path_special)
+      else if (wc_special != (dirent && dirent->special))
         node_status = svn_wc_status_obstructed;
 #endif /* HAVE_SYMLINK */
 
@@ -730,7 +749,7 @@ send_status_structure(const struct walk_
                       const char *parent_repos_root_url,
                       const char *parent_repos_relpath,
                       svn_node_kind_t path_kind,
-                      svn_boolean_t path_special,
+                      const svn_io_dirent2_t *dirent,
                       svn_boolean_t get_all,
                       svn_wc_status_func4_t status_func,
                       void *status_baton,
@@ -779,7 +798,7 @@ send_status_structure(const struct walk_
 
   SVN_ERR(assemble_status(&statstruct, wb->db, local_abspath,
                           parent_repos_root_url, parent_repos_relpath,
-                          path_kind, path_special, get_all,
+                          path_kind, dirent, get_all,
                           repos_lock, scratch_pool, scratch_pool));
 
   if (statstruct && status_func)
@@ -962,7 +981,7 @@ handle_dir_entry(const struct walk_statu
                  const char *dir_repos_root_url,
                  const char *dir_repos_relpath,
                  svn_node_kind_t path_kind,
-                 svn_boolean_t path_special,
+                 svn_io_dirent2_t *dirent,
                  const apr_array_header_t *ignores,
                  svn_depth_t depth,
                  svn_boolean_t get_all,
@@ -1002,7 +1021,7 @@ handle_dir_entry(const struct walk_statu
           SVN_ERR(send_status_structure(wb, local_abspath,
                                         dir_repos_root_url,
                                         dir_repos_relpath, svn_node_dir,
-                                        FALSE /* path_special */, get_all,
+                                        dirent, get_all,
                                         status_func, status_baton, pool));
         }
     }
@@ -1012,7 +1031,7 @@ handle_dir_entry(const struct walk_statu
       SVN_ERR(send_status_structure(wb, local_abspath,
                                     dir_repos_root_url,
                                     dir_repos_relpath, path_kind,
-                                    path_special, get_all,
+                                    dirent, get_all,
                                     status_func, status_baton, pool));
     }
 
@@ -1129,7 +1148,7 @@ get_dir_status(const struct walk_status_
     SVN_ERR(svn_hash_from_cstring_keys(&nodes, child_nodes, subpool));
   }
 
-  SVN_ERR(svn_io_get_dirents2(&dirents, local_abspath, subpool));
+  SVN_ERR(svn_io_get_dirents3(&dirents, local_abspath, FALSE, subpool, 
subpool));
 
   SVN_ERR(svn_wc__db_read_info(&dir_status, NULL, NULL, &dir_repos_relpath,
                                &dir_repos_root_url, NULL, NULL, NULL, NULL,
@@ -1227,7 +1246,7 @@ get_dir_status(const struct walk_status_
       const void *key;
       apr_ssize_t klen;
       const char *node_abspath;
-      svn_io_dirent_t *dirent_p;
+      svn_io_dirent2_t *dirent_p;
 
       svn_pool_clear(iterpool);
 
@@ -1273,7 +1292,7 @@ get_dir_status(const struct walk_status_
                                        dir_repos_relpath,
                                        dirent_p ? dirent_p->kind
                                                 : svn_node_none,
-                                       dirent_p ? dirent_p->special : FALSE,
+                                       dirent_p,
                                        ignore_patterns,
                                        depth == svn_depth_infinity
                                                            ? depth
@@ -2510,9 +2529,8 @@ internal_status(svn_wc_status3_t **statu
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
 {
-  svn_node_kind_t path_kind;
+  const svn_io_dirent2_t *dirent;
   svn_wc__db_kind_t node_kind;
-  svn_boolean_t path_special;
   const char *parent_repos_relpath;
   const char *parent_repos_root_url;
   svn_wc__db_status_t node_status;
@@ -2520,8 +2538,8 @@ internal_status(svn_wc_status3_t **statu
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
-  SVN_ERR(svn_io_check_special_path(local_abspath, &path_kind, &path_special,
-                                    scratch_pool));
+  SVN_ERR(svn_io_stat_dirent(&dirent, local_abspath, TRUE,
+                             scratch_pool, scratch_pool));
 
   err = svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL, NULL, NULL,
                              NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -2556,7 +2574,7 @@ internal_status(svn_wc_status3_t **statu
   if (node_kind == svn_wc__db_kind_unknown)
     return svn_error_return(assemble_unversioned(status,
                                                  db, local_abspath,
-                                                 path_kind,
+                                                 dirent->kind,
                                                  FALSE /* is_ignored */,
                                                  result_pool, scratch_pool));
 
@@ -2601,8 +2619,9 @@ internal_status(svn_wc_status3_t **statu
 
   return svn_error_return(assemble_status(status, db, local_abspath,
                                           parent_repos_root_url,
-                                          parent_repos_relpath, path_kind,
-                                          path_special,
+                                          parent_repos_relpath,
+                                          dirent->kind,
+                                          dirent,
                                           TRUE /* get_all */,
                                           NULL /* repos_lock */,
                                           result_pool, scratch_pool));


Reply via email to