Bug 760067 - Release all OfflineCache custom profile files ASAP after custom profile cache update has finished, r=michal
☠☠ backed out by 981ac887f6e2 ☠ ☠
authorHonza Bambas <honzab.moz@firemni.cz>
Tue, 10 Jul 2012 23:49:17 +0200
changeset 101582 2838ba825479dc2022170933ae23750422c359b8
parent 101581 52e5b438c9115d358a1bdc38e57a994ea22eb456
child 101583 991404facac2f4884dca0257a767595471de1830
push idunknown
push userunknown
push dateunknown
reviewersmichal
bugs760067
milestone16.0a1
Bug 760067 - Release all OfflineCache custom profile files ASAP after custom profile cache update has finished, r=michal
netwerk/cache/nsCacheService.cpp
netwerk/cache/nsCacheService.h
netwerk/cache/nsDiskCacheDeviceSQL.cpp
netwerk/cache/nsDiskCacheDeviceSQL.h
netwerk/test/unit/test_offlinecache_custom-directory.js
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -1583,16 +1583,17 @@ nsCacheService::GetCustomOfflineDevice(n
     nsAutoString profilePath;
     rv = aProfileDir->GetPath(profilePath);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!mCustomOfflineDevices.Get(profilePath, aDevice)) {
         rv = CreateCustomOfflineDevice(aProfileDir, aQuota, aDevice);
         NS_ENSURE_SUCCESS(rv, rv);
 
+        (*aDevice)->SetAutoShutdown();
         mCustomOfflineDevices.Put(profilePath, *aDevice);
     }
 
     return NS_OK;
 }
 
 nsresult
 nsCacheService::CreateOfflineDevice()
@@ -1671,16 +1672,30 @@ nsCacheService::CreateMemoryDevice()
 
     MemoryCacheReporter =
         new NS_MEMORY_REPORTER_NAME(NetworkMemoryCache);
     NS_RegisterMemoryReporter(MemoryCacheReporter);
 
     return rv;
 }
 
+nsresult
+nsCacheService::RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice)
+{
+    nsCOMPtr<nsIFile> profileDir = aDevice->BaseDirectory();
+    if (!profileDir)
+        return NS_ERROR_UNEXPECTED;
+
+    nsAutoString profilePath;
+    nsresult rv = profileDir->GetPath(profilePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mCustomOfflineDevices.Remove(profilePath);
+    return NS_OK;
+}
 
 nsresult
 nsCacheService::CreateRequest(nsCacheSession *   session,
                               const nsACString & clientKey,
                               nsCacheAccessMode  accessRequested,
                               bool               blockingMode,
                               nsICacheListener * listener,
                               nsCacheRequest **  request)
--- a/netwerk/cache/nsCacheService.h
+++ b/netwerk/cache/nsCacheService.h
@@ -194,16 +194,18 @@ private:
 
     nsresult         CreateDiskDevice();
     nsresult         CreateOfflineDevice();
     nsresult         CreateCustomOfflineDevice(nsIFile *aProfileDir,
                                                PRInt32 aQuota,
                                                nsOfflineCacheDevice **aDevice);
     nsresult         CreateMemoryDevice();
 
+    nsresult         RemoveCustomOfflineDevice(nsOfflineCacheDevice *aDevice);
+
     nsresult         CreateRequest(nsCacheSession *   session,
                                    const nsACString & clientKey,
                                    nsCacheAccessMode  accessRequested,
                                    bool               blockingMode,
                                    nsICacheListener * listener,
                                    nsCacheRequest **  request);
 
     nsresult         DoomEntry_Internal(nsCacheEntry * entry,
--- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp
@@ -652,33 +652,42 @@ nsApplicationCache::GetActive(bool *out)
 
 NS_IMETHODIMP
 nsApplicationCache::Activate()
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   mDevice->ActivateCache(mGroup, mClientID);
+
+  if (mDevice->AutoShutdown(this))
+    mDevice = nsnull;
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::Discard()
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
 
   mValid = false;
 
   if (mDevice->IsActiveCache(mGroup, mClientID))
   {
     mDevice->DeactivateGroup(mGroup);
   }
 
-  return mDevice->EvictEntries(mClientID.get());
+  nsresult rv = mDevice->EvictEntries(mClientID.get());
+
+  if (mDevice->AutoShutdown(this))
+    mDevice = nsnull;
+
+  return rv;
 }
 
 NS_IMETHODIMP
 nsApplicationCache::MarkEntry(const nsACString &key,
                               PRUint32 typeBits)
 {
   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
@@ -799,16 +808,17 @@ private:
  */
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(nsOfflineCacheDevice)
 
 nsOfflineCacheDevice::nsOfflineCacheDevice()
   : mDB(nsnull)
   , mCacheCapacity(0)
   , mDeltaCounter(0)
+  , mAutoShutdown(false)
 {
 }
 
 /* static */
 bool
 nsOfflineCacheDevice::GetStrictFileOriginPolicy()
 {
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@@ -2387,8 +2397,28 @@ nsOfflineCacheDevice::SetCacheParentDire
   mCacheDirectory = do_QueryInterface(dir);
 }
 
 void
 nsOfflineCacheDevice::SetCapacity(PRUint32 capacity)
 {
   mCacheCapacity = capacity * 1024;
 }
+
+bool
+nsOfflineCacheDevice::AutoShutdown(nsIApplicationCache * aAppCache)
+{
+  if (!mAutoShutdown)
+    return false;
+
+  mAutoShutdown = false;
+
+  Shutdown();
+
+  nsRefPtr<nsCacheService> cacheService = nsCacheService::GlobalInstance();
+  cacheService->RemoveCustomOfflineDevice(this);
+
+  nsCAutoString clientID;
+  aAppCache->GetClientID(clientID);
+  mCaches.Remove(clientID);
+
+  return true;
+}
--- a/netwerk/cache/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/nsDiskCacheDeviceSQL.h
@@ -162,16 +162,18 @@ public:
                                                char ***keys);
 
   /**
    * Preference accessors
    */
 
   void                    SetCacheParentDirectory(nsIFile * parentDir);
   void                    SetCapacity(PRUint32  capacity);
+  void                    SetAutoShutdown() { mAutoShutdown = true; }
+  bool                    AutoShutdown(nsIApplicationCache * aAppCache);
 
   nsIFile *               BaseDirectory() { return mBaseDirectory; }
   nsIFile *               CacheDirectory() { return mCacheDirectory; }
   PRUint32                CacheCapacity() { return mCacheCapacity; }
   PRUint32                CacheSize();
   PRUint32                EntryCount();
   
 private:
@@ -252,16 +254,17 @@ private:
   nsCOMPtr<mozIStorageStatement>  mStatement_FindClientByNamespace;
   nsCOMPtr<mozIStorageStatement>  mStatement_EnumerateGroups;
   nsCOMPtr<mozIStorageStatement>  mStatement_EnumerateGroupsTimeOrder;
 
   nsCOMPtr<nsIFile>               mBaseDirectory;
   nsCOMPtr<nsIFile>               mCacheDirectory;
   PRUint32                        mCacheCapacity; // in bytes
   PRInt32                         mDeltaCounter;
+  bool                            mAutoShutdown;
 
   nsInterfaceHashtable<nsCStringHashKey, nsIWeakReference> mCaches;
   nsClassHashtable<nsCStringHashKey, nsCString> mActiveCachesByGroup;
   nsTHashtable<nsCStringHashKey> mActiveCaches;
 
   nsCOMPtr<nsIThread> mInitThread;
 };
 
--- a/netwerk/test/unit/test_offlinecache_custom-directory.js
+++ b/netwerk/test/unit/test_offlinecache_custom-directory.js
@@ -59,16 +59,32 @@ function finish_test(customDir)
   do_check_eq(file1.exists(), true);
 
   var file2 = offlineCacheDir.clone();
   file2.append("8");
   file2.append("6");
   file2.append("0B457F75198B29-0");
   do_check_eq(file2.exists(), true);
 
+  // This must not throw an exception.  After the update has finished
+  // the index file can be freely removed.  This way we check this process
+  // is no longer keeping the file open.  Check like this will probably
+  // work only Windows systems.
+
+  // This test could potentially randomaly fail when we start closing
+  // the offline cache database off the main thread.  Tries in a loop
+  // may be a solution then.
+  try {
+    indexSqlFile.remove(false);
+    do_check_true(true);
+  }
+  catch (ex) {
+    do_throw("Could not remove the sqlite.index file, we still keep it open \n" + ex + "\n");
+  }
+
   httpServer.stop(do_test_finished);
 }
 
 function run_test()
 {
   httpServer = new nsHttpServer();
   httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
   httpServer.registerPathHandler("/manifest", manifestHandler);