Bug 588873 - Crashfix, catch EXCEPTION_IN_PAGE_ERROR, use file io for checksum on Windows for more safety+speed r=benh a=blocking-betaN
authorTaras Glek <tglek@mozilla.com>
Fri, 03 Dec 2010 10:16:10 -0800
changeset 58560 9ddbf8ab23a5ce28bf9f152e3fd814a983de6eb0
parent 58559 2357fce3dceb983eb62aa78c07406658c9a29e5a
child 58561 b67f6a46781a00fe92f71e6a5c5e395f355d9a88
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersbenh, blocking-betaN
bugs588873
milestone2.0b8pre
Bug 588873 - Crashfix, catch EXCEPTION_IN_PAGE_ERROR, use file io for checksum on Windows for more safety+speed r=benh a=blocking-betaN
xpcom/io/nsFastLoadFile.cpp
xpcom/io/nsFastLoadFile.h
--- a/xpcom/io/nsFastLoadFile.cpp
+++ b/xpcom/io/nsFastLoadFile.cpp
@@ -56,16 +56,33 @@
 
 #include "nsBinaryStream.h"
 #include "nsFastLoadFile.h"
 #include "nsInt64.h"
 #ifdef XP_UNIX
 #include <sys/mman.h>
 #endif
 
+#ifdef XP_WIN
+#include <windows.h>
+#include "private/pprio.h"  // To get PR_ImportFile
+
+#define MOZ_WIN_MEM_TRY_BEGIN __try {
+#define MOZ_WIN_MEM_TRY_CATCH(cmd) }                                \
+  __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ?            \
+           EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)   \
+  {                                                                 \
+    NS_WARNING("EXCEPTION_IN_PAGE_ERROR in " __FUNCTION__);         \
+    cmd;                                                            \
+  }
+#else
+#define MOZ_WIN_MEM_TRY_BEGIN {
+#define MOZ_WIN_MEM_TRY_CATCH(cmd) }
+#endif
+
 #ifdef DEBUG_brendan
 # define METERING
 # define DEBUG_MUX
 #endif
 
 #ifdef METERING
 # define METER(x)       x
 #else
@@ -563,17 +580,19 @@ nsFastLoadFileReader::Read(char* aBuffer
             NS_ASSERTION(entry->mBytesLeft >= 8, "demux segment length botch!");
             entry->mBytesLeft -= 8;
         }
     }
     if (!mFileData)
         return NS_BASE_STREAM_CLOSED;
 
     PRUint32 count = PR_MIN(mFileLen - mFilePos, aCount);
+MOZ_WIN_MEM_TRY_BEGIN
     memcpy(aBuffer, mFileData+mFilePos, count);
+MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
     *aBytesRead = count;
     mFilePos += count;
     if (entry) {
         NS_ASSERTION(entry->mBytesLeft >= *aBytesRead, "demux Read underflow!");
         entry->mBytesLeft -= *aBytesRead;
 
 #ifdef NS_DEBUG
         // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
@@ -593,19 +612,21 @@ nsFastLoadFileReader::ReadSegments(nsWri
     NS_ASSERTION(!entry || (!entry->mNeedToSeek && entry->mBytesLeft != 0),
                  "ReadSegments called from above nsFastLoadFileReader layer?!");
 
     if (!mFileData)
         return NS_BASE_STREAM_CLOSED;
 
     PRUint32 count = PR_MIN(mFileLen - mFilePos, aCount);
 
+MOZ_WIN_MEM_TRY_BEGIN
     // Errors returned from the writer get ignored.
     aWriter(this, aClosure, (char*)(mFileData + mFilePos), 0,
             count, aResult);
+MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
     mFilePos += count;
     if (entry) {
         NS_ASSERTION(entry->mBytesLeft >= *aResult,
                      "demux ReadSegments underflow!");
         entry->mBytesLeft -= *aResult;
 
 #ifdef NS_DEBUG
         // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
@@ -617,20 +638,46 @@ nsFastLoadFileReader::ReadSegments(nsWri
 }
 
 NS_IMETHODIMP
 nsFastLoadFileReader::ComputeChecksum(PRUint32 *aResult)
 {
     PRUint32 checksum = 0;
     // Skip first 2 fields.
     PRUint32 pos = offsetof(nsFastLoadHeader, mVersion);
+#ifdef XP_WIN
+    if (pos != PR_Seek(mFD, pos, PR_SEEK_SET))
+        return NS_ERROR_FAILURE;
+    PRUint32 len, rem = 0;
+    char buf[64 * 1024];
+    while ((len = PR_Read(mFD, buf + rem, sizeof(buf) - rem)) && len > 0) {
+        len += rem;
+        rem = NS_AccumulateFastLoadChecksum(&checksum,
+                                            reinterpret_cast<PRUint8*>(buf),
+                                            len,
+                                            PR_FALSE);
+        if (rem)
+            memcpy(buf, buf + len - rem, rem);
+    }
+    if (len < 0)
+        return NS_ERROR_FAILURE;
+
+    if (rem) {
+        NS_AccumulateFastLoadChecksum(&checksum,
+                                      reinterpret_cast<PRUint8*>(buf),
+                                      rem,
+                                      PR_TRUE);
+    }
+
+#else
     NS_AccumulateFastLoadChecksum(&checksum,
                                   mFileData + pos,
                                   mFileLen - pos,
                                   PR_TRUE);
+#endif
     *aResult = checksum;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator* *aDependencies)
 {
     return NS_NewArrayEnumerator(aDependencies, mFooter.mDependencies);
@@ -858,24 +905,37 @@ nsFastLoadFileReader::ReadMuxedDocumentI
     aInfo->mURISpec = ToNewCString(spec);
     return NS_OK;
 }
 
 nsresult
 nsFastLoadFileReader::Open()
 {
     nsresult rv;
+    PRFileDesc *fd;    // OS file-descriptor
+    {    
+#ifdef XP_WIN
+    nsAutoString name;
+    rv = mFile->GetPath(name);
+    NS_ENSURE_SUCCESS(rv, rv);
+    // By not using OpenNSPRFileDesc can pass FILE_FLAG_SEQUENTIAL_SCAN so Windows reads in the file faster
+    HANDLE winFD = ::CreateFileW(name.get(), GENERIC_READ, FILE_SHARE_READ,
+                                 NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+    if (winFD == INVALID_HANDLE_VALUE)
+        return NS_ERROR_FAILURE;
+    fd = PR_ImportFile((PROsfd) winFD);
+#else
     nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mFile, &rv);
     if (NS_FAILED(rv))
         return rv;
-    PRFileDesc *fd;    // OS file-descriptor
     rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
     if (NS_FAILED(rv))
         return rv;
-
+#endif
+    }
     PRInt64 size = PR_Available64(fd);
     if (size >= PR_INT32_MAX) {
         PR_Close(fd);
         return NS_ERROR_FILE_TOO_BIG;
     }
 
     mFileLen = (PRUint32) size;
     if (mFileLen < sizeof(nsFastLoadHeader)) {
@@ -885,47 +945,55 @@ nsFastLoadFileReader::Open()
 
     mFileMap = PR_CreateFileMap(fd, mFileLen, PR_PROT_READONLY);
     if (!mFileMap) {
         PR_Close(fd);
         return NS_ERROR_FAILURE;
     }
 
     mFileData = (PRUint8*) PR_MemMap(mFileMap, 0, mFileLen);
-    // At this point the non-mmap file descriptor is no longer needed
+#ifdef XP_WIN
+    mFD = fd;
+#else
+    // At this point the non-mmap file descriptor is no longer needed on non-windows
     PR_Close(fd);
+#endif
 
     if (!mFileData)
         return NS_ERROR_FAILURE;
 
 #if defined(XP_UNIX)
     madvise((char *)mFileData, mFileLen, MADV_WILLNEED);
 #endif
 
+MOZ_WIN_MEM_TRY_BEGIN
     rv = ReadHeader(&mHeader);
     if (NS_FAILED(rv))
         return rv;
 
     PRUint32 checksum;
     rv = ComputeChecksum(&checksum);
     if (NS_FAILED(rv))
         return rv;
-    
+
     if (checksum != mHeader.mChecksum)
         return NS_ERROR_FAILURE;
 
     if (mHeader.mVersion != MFL_FILE_VERSION ||
-        mHeader.mFooterOffset == 0 || 
+        mHeader.mFooterOffset == 0 ||
         memcmp(mHeader.mMagic, magic, MFL_FILE_MAGIC_SIZE))
         return NS_ERROR_UNEXPECTED;
-    
+
     SeekTo(mHeader.mFooterOffset);
 
     rv = ReadFooter(&mFooter);
-    if (NS_FAILED(rv))
+
+MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
+
+   if (NS_FAILED(rv))
         return rv;
 
     SeekTo(sizeof(nsFastLoadHeader));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFastLoadFileReader::Close()
@@ -943,20 +1011,24 @@ nsFastLoadFileReader::Close()
         PR_MemUnmap(mFileData, mFileLen);
         mFileData = nsnull;
     }
 
     if (mFileMap) {
         PR_CloseFileMap(mFileMap);
         mFileMap = nsnull;
     }
+    
+#ifdef XP_WIN
+    mFD = nsnull;
+#endif
 
     mFileLen = 0;
     mFilePos = 0;
-    
+
     if (!mFooter.mObjectMap)
         return NS_OK;
 
     for (PRUint32 i = 0, n = mFooter.mNumSharpObjects; i < n; i++) {
         nsObjectMapEntry* entry = &mFooter.mObjectMap[i];
         entry->mReadObject = nsnull;
     }
     mFooter.mNumSharpObjects = 0;
@@ -1846,17 +1918,17 @@ nsFastLoadFileWriter::Init()
 
     return NS_OK;
 }
 
 nsresult
 nsFastLoadFileWriter::Open()
 {
     nsresult rv;
-    
+
     if (!mSeekableOutput)
         return NS_ERROR_FAILURE;
 
     rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
                                sizeof(nsFastLoadHeader));
     if (NS_FAILED(rv))
         return rv;
 
@@ -1931,17 +2003,17 @@ nsFastLoadFileWriter::Close()
         rv = mBufferAccess->GetUnbufferedStream(getter_AddRefs(output));
         if (NS_FAILED(rv) || !output)
             return NS_ERROR_UNEXPECTED;
 
         nsCOMPtr<nsIInputStream> input;
         rv = mFileIO->GetInputStream(getter_AddRefs(input));
         if (NS_FAILED(rv))
             return rv;
- 
+
         // Seek the input stream to right after checksum/magic.
         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
         rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
                             offsetof(nsFastLoadHeader, mVersion));
         if (NS_FAILED(rv))
             return rv;
 
         char buf[MFL_CHECKSUM_BUFSIZE];
@@ -2153,17 +2225,17 @@ nsFastLoadFileWriter::WriteSingleRefObje
 
 NS_IMETHODIMP
 nsFastLoadFileWriter::WriteCompoundObject(nsISupports* aObject,
                                           const nsIID& aIID,
                                           PRBool aIsStrongRef)
 {
     nsresult rv;
     nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
-    
+
     // We could assert that |rootObject != aObject|, but that would prevent
     // callers who don't know whether they're dealing with the primary
     // nsISupports pointer (e.g., they don't know which implementation of
     // nsIURI they have) from using this function.
 
 #ifdef NS_DEBUG
     nsCOMPtr<nsISupports> roundtrip;
     rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
--- a/xpcom/io/nsFastLoadFile.h
+++ b/xpcom/io/nsFastLoadFile.h
@@ -51,16 +51,17 @@
 #include "nsDebug.h"
 #include "nsID.h"
 #include "nsMemory.h"
 
 #include "nsIFastLoadFileControl.h"
 #include "nsIFastLoadService.h"
 #include "nsISeekableStream.h"
 #include "nsISupportsArray.h"
+#include "mozilla/FileUtils.h"
 
 /**
  * FastLoad file Object ID (OID) is an identifier for multiply and cyclicly
  * connected objects in the serialized graph of all reachable objects.
  *
  * Holy Mixed Metaphors: JS, after Common Lisp, uses #n= to define a "sharp
  * variable" naming an object that's multiply or cyclicly connected, and #n#
  * to stand for a connection to an already-defined object.  We too call any
@@ -395,16 +396,22 @@ class nsFastLoadFileReader
     nsDocumentMapReadEntry* mCurrentDocumentMapEntry;
 
     friend class nsFastLoadFileUpdater;
     nsIFile *mFile;     // .mfasl file
     PRUint32 mFileLen;  // length of file
     PRUint32 mFilePos;  // current position within file
     PRFileMap *mFileMap;// nspr datastructure for mmap
     PRUint8 *mFileData; // pointer to mmaped file
+#ifdef XP_WIN
+    // Using a descriptor avoids having to check for EXCEPTION_IN_PAGE_ERROR
+    // in ComputeChecksum. Combined with FILE_FLAG_SEQUENTIAL_SCAN
+    // this speeds up cold IO via kernel readahead.
+    mozilla::AutoFDClose mFD;
+#endif
 };
 
 NS_COM nsresult
 NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult NS_OUTPARAM,
                          nsIFile* aFile);
 
 /**
  * Inherit from the concrete class nsBinaryInputStream, which inherits from