Bug 724203 - Optimize nsLocalFile::IsDirectory on Windows by 50% giving 5ms startup improvement. r=neil.
authorBrian R. Bondy <netzen@gmail.com>
Wed, 08 Feb 2012 16:48:23 -0500
changeset 87977 475090373d31025356da9c42d33088003c8c269a
parent 87976 094e4a9472a1d1bba36cd5b7533f6cc929ab93c9
child 87978 f4a4146c56839add74f2760404024cfa3c2b6983
push id975
push userffxbld
push dateTue, 13 Mar 2012 21:39:16 +0000
treeherdermozilla-aurora@99faebf9dc36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersneil
bugs724203
milestone13.0a1
Bug 724203 - Optimize nsLocalFile::IsDirectory on Windows by 50% giving 5ms startup improvement. r=neil.
xpcom/io/nsLocalFileWin.cpp
xpcom/io/nsLocalFileWin.h
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -689,16 +689,17 @@ NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsIS
 
 
 //-----------------------------------------------------------------------------
 // nsLocalFile <public>
 //-----------------------------------------------------------------------------
 
 nsLocalFile::nsLocalFile()
   : mDirty(true)
+  , mResolveDirty(true)
   , mFollowSymlinks(false)
 {
 }
 
 nsresult
 nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
 {
     NS_ENSURE_ARG_POINTER(aInstancePtr);
@@ -730,16 +731,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFil
 
 
 //-----------------------------------------------------------------------------
 // nsLocalFile <private>
 //-----------------------------------------------------------------------------
 
 nsLocalFile::nsLocalFile(const nsLocalFile& other)
   : mDirty(true)
+  , mResolveDirty(true)
   , mFollowSymlinks(other.mFollowSymlinks)
   , mWorkingPath(other.mWorkingPath)
 {
 }
 
 // Resolve the shortcut file from mWorkingPath and write the path 
 // it points to into mResolvedPath.
 nsresult
@@ -792,39 +794,85 @@ nsLocalFile::ResolveAndStat()
         return rv;
 
     // if this isn't a shortcut file or we aren't following symlinks then we're done 
     if (!mFollowSymlinks 
         || mFileInfo64.type != PR_FILE_FILE 
         || !IsShortcutPath(mWorkingPath))
     {
         mDirty = false;
+        mResolveDirty = false;
         return NS_OK;
     }
 
     // we need to resolve this shortcut to what it points to, this will
     // set mResolvedPath. Even if it fails we need to have the resolved
     // path equal to working path for those functions that always use
     // the resolved path.
     rv = ResolveShortcut();
     if (NS_FAILED(rv))
     {
         mResolvedPath.Assign(mWorkingPath);
         return rv;
     }
+    mResolveDirty = false;
 
     // get the details of the resolved path
     rv = GetFileInfo(mResolvedPath, &mFileInfo64);
     if (NS_FAILED(rv))
         return rv;
 
     mDirty = false;
     return NS_OK;
 }
 
+/**
+ * Fills the mResolvedPath member variable with the file or symlink target
+ * if follow symlinks is on.  This is a copy of the Resolve parts from
+ * ResolveAndStat. ResolveAndStat is much slower though because of the stat.
+ *
+ * @return NS_OK on success.
+*/
+nsresult
+nsLocalFile::Resolve()
+{
+  // if we aren't dirty then we are already done
+  if (!mResolveDirty) {
+    return NS_OK;
+  }
+
+  // we can't resolve/stat anything that isn't a valid NSPR addressable path
+  if (mWorkingPath.IsEmpty()) {
+    return NS_ERROR_FILE_INVALID_PATH;
+  }
+  
+  // this is usually correct
+  mResolvedPath.Assign(mWorkingPath);
+
+  // if this isn't a shortcut file or we aren't following symlinks then
+  // we're done.
+  if (!mFollowSymlinks || 
+      !IsShortcutPath(mWorkingPath)) {
+    mResolveDirty = false;
+    return NS_OK;
+  }
+
+  // we need to resolve this shortcut to what it points to, this will
+  // set mResolvedPath. Even if it fails we need to have the resolved
+  // path equal to working path for those functions that always use
+  // the resolved path.
+  nsresult rv = ResolveShortcut();
+  if (NS_FAILED(rv)) {
+    mResolvedPath.Assign(mWorkingPath);
+    return rv;
+  }
+
+  mResolveDirty = false;
+  return NS_OK;
+}
 
 //-----------------------------------------------------------------------------
 // nsLocalFile::nsIFile,nsILocalFile
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsLocalFile::Clone(nsIFile **file)
 {
@@ -2486,37 +2534,41 @@ nsLocalFile::IsExecutable(bool *_retval)
 
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsLocalFile::IsDirectory(bool *_retval)
 {
-    NS_ENSURE_ARG(_retval);
-
-    nsresult rv = ResolveAndStat();
-    if (NS_FAILED(rv))
-        return rv;
-
-    *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY); 
-    return NS_OK;
+  nsresult rv = IsFile(_retval);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  *_retval = !*_retval;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocalFile::IsFile(bool *_retval)
 {
-    NS_ENSURE_ARG(_retval);
-
-    nsresult rv = ResolveAndStat();
-    if (NS_FAILED(rv))
-        return rv;
-
-    *_retval = (mFileInfo64.type == PR_FILE_FILE); 
-    return NS_OK;
+  NS_ENSURE_ARG(_retval);
+  nsresult rv = Resolve();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  DWORD attributes = GetFileAttributes(mResolvedPath.get());
+  if (INVALID_FILE_ATTRIBUTES == attributes) {
+    return NS_ERROR_FILE_NOT_FOUND;
+  }
+
+  *_retval = !(attributes & FILE_ATTRIBUTE_DIRECTORY);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLocalFile::IsHidden(bool *_retval)
 {
     return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
 }
 
--- a/xpcom/io/nsLocalFileWin.h
+++ b/xpcom/io/nsLocalFileWin.h
@@ -90,34 +90,41 @@ public:
     static void GlobalInit();
     static void GlobalShutdown();
 
 private:
     nsLocalFile(const nsLocalFile& other);
     ~nsLocalFile() {}
 
     bool mDirty;            // cached information can only be used when this is false
+    bool mResolveDirty;
     bool mFollowSymlinks;   // should we follow symlinks when working on this file
     
     // this string will always be in native format!
     nsString mWorkingPath;
     
     // this will be the resolved path of shortcuts, it will *NEVER* 
     // be returned to the user
     nsString mResolvedPath;
 
     // this string, if not empty, is the *short* pathname that represents
     // mWorkingPath
     nsString mShortWorkingPath;
 
     PRFileInfo64 mFileInfo64;
 
-    void MakeDirty() { mDirty = true; mShortWorkingPath.Truncate(); }
+    void MakeDirty() 
+    { 
+      mDirty = true;
+      mResolveDirty = true;
+      mShortWorkingPath.Truncate();
+    }
 
     nsresult ResolveAndStat();
+    nsresult Resolve();
     nsresult ResolveShortcut();
 
     void EnsureShortPath();
     
     nsresult CopyMove(nsIFile *newParentDir, const nsAString &newName,
                       bool followSymlinks, bool move);
     nsresult CopySingleFile(nsIFile *source, nsIFile* dest,
                             const nsAString &newName,