LCOV - code coverage report
Current view: directory - startupcache - StartupCache.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 327 263 80.4 %
Date: 2012-06-02 Functions: 73 56 76.7 %

       1                 : /* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Startup Cache.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * The Mozilla Foundation <http://www.mozilla.org/>.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Benedict Hsieh <bhsieh@mozilla.com>
      24                 :  *  Taras Glek <tglek@mozilla.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "prio.h"
      41                 : #include "prtypes.h"
      42                 : #include "pldhash.h"
      43                 : #include "nsXPCOMStrings.h"
      44                 : #include "mozilla/scache/StartupCache.h"
      45                 : 
      46                 : #include "nsAutoPtr.h"
      47                 : #include "nsClassHashtable.h"
      48                 : #include "nsComponentManagerUtils.h"
      49                 : #include "nsDirectoryServiceUtils.h"
      50                 : #include "nsIClassInfo.h"
      51                 : #include "nsIFile.h"
      52                 : #include "nsILocalFile.h"
      53                 : #include "nsIMemoryReporter.h"
      54                 : #include "nsIObserver.h"
      55                 : #include "nsIObserverService.h"
      56                 : #include "nsIOutputStream.h"
      57                 : #include "nsIStartupCache.h"
      58                 : #include "nsIStorageStream.h"
      59                 : #include "nsIStreamBufferAccess.h"
      60                 : #include "nsIStringStream.h"
      61                 : #include "nsISupports.h"
      62                 : #include "nsITimer.h"
      63                 : #include "nsIZipWriter.h"
      64                 : #include "nsIZipReader.h"
      65                 : #include "nsWeakReference.h"
      66                 : #include "nsZipArchive.h"
      67                 : #include "mozilla/Omnijar.h"
      68                 : #include "prenv.h"
      69                 : #include "mozilla/FunctionTimer.h"
      70                 : #include "mozilla/Telemetry.h"
      71                 : #include "nsThreadUtils.h"
      72                 : #include "nsXULAppAPI.h"
      73                 : #include "nsIProtocolHandler.h"
      74                 : 
      75                 : #ifdef IS_BIG_ENDIAN
      76                 : #define SC_ENDIAN "big"
      77                 : #else
      78                 : #define SC_ENDIAN "little"
      79                 : #endif
      80                 : 
      81                 : #if PR_BYTES_PER_WORD == 4
      82                 : #define SC_WORDSIZE "4"
      83                 : #else
      84                 : #define SC_WORDSIZE "8"
      85                 : #endif
      86                 : 
      87                 : namespace mozilla {
      88                 : namespace scache {
      89                 : 
      90                 : static PRInt64
      91               3 : GetStartupCacheMappingSize()
      92                 : {
      93               3 :     mozilla::scache::StartupCache* sc = mozilla::scache::StartupCache::GetSingleton();
      94               3 :     return sc ? sc->SizeOfMapping() : 0;
      95                 : }
      96                 : 
      97             699 : NS_MEMORY_REPORTER_IMPLEMENT(StartupCacheMapping,
      98                 :                              "explicit/startup-cache/mapping",
      99                 :                              KIND_NONHEAP,
     100                 :                              nsIMemoryReporter::UNITS_BYTES,
     101                 :                              GetStartupCacheMappingSize,
     102                 :                              "Memory used to hold the mapping of the startup "
     103                 :                              "cache from file.  This memory is likely to be "
     104            2127 :                              "swapped out shortly after start-up.")
     105                 : 
     106               0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StartupCacheDataMallocSizeOf, "startup-cache/data")
     107                 : 
     108                 : static PRInt64
     109               0 : GetStartupCacheDataSize()
     110                 : {
     111               0 :     mozilla::scache::StartupCache* sc = mozilla::scache::StartupCache::GetSingleton();
     112               0 :     return sc ? sc->HeapSizeOfIncludingThis(StartupCacheDataMallocSizeOf) : 0;
     113                 : }
     114                 : 
     115             696 : NS_MEMORY_REPORTER_IMPLEMENT(StartupCacheData,
     116                 :                              "explicit/startup-cache/data",
     117                 :                              KIND_HEAP,
     118                 :                              nsIMemoryReporter::UNITS_BYTES,
     119                 :                              GetStartupCacheDataSize,
     120                 :                              "Memory used by the startup cache for things "
     121            2127 :                              "other than the file mapping.")
     122                 : 
     123                 : static const char sStartupCacheName[] = "startupCache." SC_WORDSIZE "." SC_ENDIAN;
     124                 : static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
     125                 : 
     126                 : StartupCache*
     127           22689 : StartupCache::GetSingleton() 
     128                 : {
     129           22689 :   if (!gStartupCache)
     130            6515 :     StartupCache::InitSingleton();
     131                 : 
     132           22689 :   return StartupCache::gStartupCache;
     133                 : }
     134                 : 
     135                 : void
     136            1419 : StartupCache::DeleteSingleton()
     137                 : {
     138            1419 :   delete StartupCache::gStartupCache;
     139            1419 : }
     140                 : 
     141                 : nsresult
     142            6515 : StartupCache::InitSingleton() 
     143                 : {
     144                 :   nsresult rv;
     145            6515 :   StartupCache::gStartupCache = new StartupCache();
     146                 : 
     147            6515 :   rv = StartupCache::gStartupCache->Init();
     148            6515 :   if (NS_FAILED(rv)) {
     149            5828 :     delete StartupCache::gStartupCache;
     150            5828 :     StartupCache::gStartupCache = nsnull;
     151                 :   }
     152            6515 :   return rv;
     153                 : }
     154                 : 
     155                 : StartupCache* StartupCache::gStartupCache;
     156                 : bool StartupCache::gShutdownInitiated;
     157                 : enum StartupCache::TelemetrifyAge StartupCache::gPostFlushAgeAction = StartupCache::IGNORE_AGE;
     158                 : 
     159            6515 : StartupCache::StartupCache() 
     160                 :   : mArchive(NULL), mStartupWriteInitiated(false), mWriteThread(NULL),
     161            6515 :     mMappingMemoryReporter(nsnull), mDataMemoryReporter(nsnull) { }
     162                 : 
     163           13030 : StartupCache::~StartupCache() 
     164                 : {
     165            6515 :   if (mTimer) {
     166             661 :     mTimer->Cancel();
     167                 :   }
     168                 : 
     169                 :   // Generally, the in-memory table should be empty here,
     170                 :   // but an early shutdown means either mTimer didn't run 
     171                 :   // or the write thread is still running.
     172            6515 :   WaitOnWriteThread();
     173            6515 :   WriteToDisk();
     174            6515 :   gStartupCache = nsnull;
     175            6515 :   (void)::NS_UnregisterMemoryReporter(mMappingMemoryReporter);
     176            6515 :   (void)::NS_UnregisterMemoryReporter(mDataMemoryReporter);
     177            6515 :   mMappingMemoryReporter = nsnull;
     178            6515 :   mDataMemoryReporter = nsnull;
     179            6515 : }
     180                 : 
     181                 : nsresult
     182            6515 : StartupCache::Init() 
     183                 : {
     184            6515 :   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     185               0 :     NS_WARNING("Startup cache is only available in the chrome process");
     186               0 :     return NS_ERROR_NOT_AVAILABLE;
     187                 :   }
     188                 :   // workaround for bug 653936
     189           13030 :   nsCOMPtr<nsIProtocolHandler> jarInitializer(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar"));
     190                 :   
     191                 :   nsresult rv;
     192            6515 :   mTable.Init();
     193                 : #ifdef DEBUG
     194            6515 :   mWriteObjectMap.Init();
     195                 : #endif
     196                 : 
     197                 :   // This allows to override the startup cache filename
     198                 :   // which is useful from xpcshell, when there is no ProfLDS directory to keep cache in.
     199            6515 :   char *env = PR_GetEnv("MOZ_STARTUP_CACHE");
     200            6515 :   if (env) {
     201               0 :     rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(mFile));
     202                 :   } else {
     203           13030 :     nsCOMPtr<nsIFile> file;
     204                 :     rv = NS_GetSpecialDirectory("ProfLDS",
     205            6515 :                                 getter_AddRefs(file));
     206            6515 :     if (NS_FAILED(rv)) {
     207                 :       // return silently, this will fail in mochitests's xpcshell process.
     208            5828 :       return rv;
     209                 :     }
     210                 : 
     211             687 :     rv = file->AppendNative(NS_LITERAL_CSTRING("startupCache"));
     212             687 :     NS_ENSURE_SUCCESS(rv, rv);
     213                 : 
     214                 :     // Try to create the directory if it's not there yet
     215             687 :     rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
     216             687 :     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
     217               0 :       return rv;
     218                 : 
     219             687 :     rv = file->AppendNative(NS_LITERAL_CSTRING(sStartupCacheName));
     220                 : 
     221             687 :     NS_ENSURE_SUCCESS(rv, rv);
     222                 :     
     223            7202 :     mFile = do_QueryInterface(file);
     224                 :   }
     225                 : 
     226             687 :   NS_ENSURE_TRUE(mFile, NS_ERROR_UNEXPECTED);
     227                 : 
     228             687 :   mObserverService = do_GetService("@mozilla.org/observer-service;1");
     229                 :   
     230             687 :   if (!mObserverService) {
     231               0 :     NS_WARNING("Could not get observerService.");
     232               0 :     return NS_ERROR_UNEXPECTED;
     233                 :   }
     234                 :   
     235             687 :   mListener = new StartupCacheListener();  
     236             687 :   rv = mObserverService->AddObserver(mListener, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
     237             687 :                                      false);
     238             687 :   NS_ENSURE_SUCCESS(rv, rv);
     239             687 :   rv = mObserverService->AddObserver(mListener, "startupcache-invalidate",
     240             687 :                                      false);
     241             687 :   NS_ENSURE_SUCCESS(rv, rv);
     242                 :   
     243             687 :   rv = LoadArchive(RECORD_AGE);
     244                 :   
     245                 :   // Sometimes we don't have a cache yet, that's ok.
     246                 :   // If it's corrupted, just remove it and start over.
     247             687 :   if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
     248               0 :     NS_WARNING("Failed to load startupcache file correctly, removing!");
     249               0 :     InvalidateCache();
     250                 :   }
     251                 : 
     252             687 :   mMappingMemoryReporter = new NS_MEMORY_REPORTER_NAME(StartupCacheMapping);
     253             687 :   mDataMemoryReporter    = new NS_MEMORY_REPORTER_NAME(StartupCacheData);
     254             687 :   (void)::NS_RegisterMemoryReporter(mMappingMemoryReporter);
     255             687 :   (void)::NS_RegisterMemoryReporter(mDataMemoryReporter);
     256                 : 
     257             687 :   return NS_OK;
     258                 : }
     259                 : 
     260                 : /** 
     261                 :  * LoadArchive can be called from the main thread or while reloading cache on write thread.
     262                 :  */
     263                 : nsresult
     264            1843 : StartupCache::LoadArchive(enum TelemetrifyAge flag)
     265                 : {
     266                 :   bool exists;
     267            1843 :   mArchive = NULL;
     268            1843 :   nsresult rv = mFile->Exists(&exists);
     269            1843 :   if (NS_FAILED(rv) || !exists)
     270            1237 :     return NS_ERROR_FILE_NOT_FOUND;
     271                 :   
     272             606 :   mArchive = new nsZipArchive();
     273             606 :   rv = mArchive->OpenArchive(mFile);
     274             606 :   if (NS_FAILED(rv) || flag == IGNORE_AGE)
     275             604 :     return rv;
     276                 : 
     277               4 :   nsCString comment;
     278               2 :   if (!mArchive->GetComment(comment)) {
     279               0 :     return rv;
     280                 :   }
     281                 : 
     282                 :   const char *data;
     283               2 :   size_t len = NS_CStringGetData(comment, &data);
     284                 :   PRTime creationStamp;
     285                 :   // We might not have a comment if the startup cache file was created
     286                 :   // before we started recording creation times in the comment.
     287               2 :   if (len == sizeof(creationStamp)) {
     288               2 :     memcpy(&creationStamp, data, len);
     289               2 :     PRTime current = PR_Now();
     290               2 :     PRInt64 diff = current - creationStamp;
     291                 : 
     292                 :     // We can't use AccumulateTimeDelta here because we have no way of
     293                 :     // reifying a TimeStamp from creationStamp.
     294               2 :     PRInt64 usec_per_hour = PR_USEC_PER_SEC * PRInt64(3600);
     295               2 :     PRInt64 hour_diff = (diff + usec_per_hour - 1) / usec_per_hour;
     296                 :     mozilla::Telemetry::Accumulate(Telemetry::STARTUP_CACHE_AGE_HOURS,
     297               2 :                                    hour_diff);
     298                 :   }
     299                 : 
     300               2 :   return rv;
     301                 : }
     302                 : 
     303                 : namespace {
     304                 : 
     305                 : nsresult
     306           22381 : GetBufferFromZipArchive(nsZipArchive *zip, bool doCRC, const char* id,
     307                 :                         char** outbuf, PRUint32* length)
     308                 : {
     309           22381 :   if (!zip)
     310           22379 :     return NS_ERROR_NOT_AVAILABLE;
     311                 : 
     312               4 :   nsZipItemPtr<char> zipItem(zip, id, doCRC);
     313               2 :   if (!zipItem)
     314               1 :     return NS_ERROR_NOT_AVAILABLE;
     315                 : 
     316               1 :   *outbuf = zipItem.Forget();
     317               1 :   *length = zipItem.Length();
     318               1 :   return NS_OK;
     319                 : }
     320                 : 
     321                 : } /* anonymous namespace */
     322                 : 
     323                 : // NOTE: this will not find a new entry until it has been written to disk!
     324                 : // Consumer should take ownership of the resulting buffer.
     325                 : nsresult
     326            8105 : StartupCache::GetBuffer(const char* id, char** outbuf, PRUint32* length) 
     327                 : {
     328            8105 :   NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
     329            8105 :   WaitOnWriteThread();
     330            8105 :   if (!mStartupWriteInitiated) {
     331                 :     CacheEntry* entry; 
     332           16206 :     nsDependentCString idStr(id);
     333            8103 :     mTable.Get(idStr, &entry);
     334            8103 :     if (entry) {
     335            1288 :       *outbuf = new char[entry->size];
     336             644 :       memcpy(*outbuf, entry->data, entry->size);
     337             644 :       *length = entry->size;
     338             644 :       return NS_OK;
     339                 :     }
     340                 :   }
     341                 : 
     342            7461 :   nsresult rv = GetBufferFromZipArchive(mArchive, true, id, outbuf, length);
     343            7461 :   if (NS_SUCCEEDED(rv))
     344               1 :     return rv;
     345                 : 
     346           14920 :   nsRefPtr<nsZipArchive> omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
     347                 :   // no need to checksum omnijarred entries
     348            7460 :   rv = GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
     349            7460 :   if (NS_SUCCEEDED(rv))
     350               0 :     return rv;
     351                 : 
     352            7460 :   omnijar = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
     353                 :   // no need to checksum omnijarred entries
     354            7460 :   return GetBufferFromZipArchive(omnijar, false, id, outbuf, length);
     355                 : }
     356                 : 
     357                 : // Makes a copy of the buffer, client retains ownership of inbuf.
     358                 : nsresult
     359            7379 : StartupCache::PutBuffer(const char* id, const char* inbuf, PRUint32 len) 
     360                 : {
     361            7379 :   NS_ASSERTION(NS_IsMainThread(), "Startup cache only available on main thread");
     362            7379 :   WaitOnWriteThread();
     363            7379 :   if (StartupCache::gShutdownInitiated) {
     364               0 :     return NS_ERROR_NOT_AVAILABLE;
     365                 :   }
     366                 : 
     367           22137 :   nsAutoArrayPtr<char> data(new char[len]);
     368            7379 :   memcpy(data, inbuf, len);
     369                 : 
     370           14758 :   nsDependentCString idStr(id);
     371                 :   // Cache it for now, we'll write all together later.
     372                 :   CacheEntry* entry; 
     373                 :   
     374                 : #ifdef DEBUG
     375            7379 :   mTable.Get(idStr, &entry);
     376            7379 :   NS_ASSERTION(entry == nsnull, "Existing entry in StartupCache.");
     377                 :   
     378            7379 :   if (mArchive) {
     379               0 :     nsZipItem* zipItem = mArchive->GetItem(id);
     380               0 :     NS_ASSERTION(zipItem == nsnull, "Existing entry in disk StartupCache.");
     381                 :   }
     382                 : #endif
     383                 :   
     384           14758 :   entry = new CacheEntry(data.forget(), len);
     385            7379 :   mTable.Put(idStr, entry);
     386            7379 :   return ResetStartupWriteTimer();
     387                 : }
     388                 : 
     389                 : size_t
     390               3 : StartupCache::SizeOfMapping() 
     391                 : {
     392               3 :     return mArchive ? mArchive->SizeOfMapping() : 0;
     393                 : }
     394                 : 
     395                 : size_t
     396               0 : StartupCache::HeapSizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
     397                 : {
     398                 :     // This function could measure more members, but they haven't been found by
     399                 :     // DMD to be significant.  They can be added later if necessary.
     400               0 :     return aMallocSizeOf(this) +
     401               0 :            mTable.SizeOfExcludingThis(SizeOfEntryExcludingThis, aMallocSizeOf);
     402                 : }
     403                 : 
     404                 : /* static */ size_t
     405               0 : StartupCache::SizeOfEntryExcludingThis(const nsACString& key, const nsAutoPtr<CacheEntry>& data,
     406                 :                                        nsMallocSizeOfFun mallocSizeOf, void *)
     407                 : {
     408               0 :     return data->SizeOfExcludingThis(mallocSizeOf);
     409                 : }
     410                 : 
     411                 : struct CacheWriteHolder
     412            1212 : {
     413                 :   nsCOMPtr<nsIZipWriter> writer;
     414                 :   nsCOMPtr<nsIStringInputStream> stream;
     415                 :   PRTime time;
     416                 : };
     417                 : 
     418                 : PLDHashOperator
     419            5142 : CacheCloseHelper(const nsACString& key, nsAutoPtr<CacheEntry>& data, 
     420                 :                  void* closure) 
     421                 : {
     422                 :   nsresult rv;
     423                 :  
     424            5142 :   CacheWriteHolder* holder = (CacheWriteHolder*) closure;  
     425            5142 :   nsIStringInputStream* stream = holder->stream;
     426            5142 :   nsIZipWriter* writer = holder->writer;
     427                 : 
     428            5142 :   stream->ShareData(data->data, data->size);
     429                 : 
     430                 : #ifdef DEBUG
     431                 :   bool hasEntry;
     432            5142 :   rv = writer->HasEntry(key, &hasEntry);
     433            5142 :   NS_ASSERTION(NS_SUCCEEDED(rv) && hasEntry == false, 
     434                 :                "Existing entry in disk StartupCache.");
     435                 : #endif
     436            5142 :   rv = writer->AddEntryStream(key, holder->time, true, stream, false);
     437                 :   
     438            5142 :   if (NS_FAILED(rv)) {
     439               0 :     NS_WARNING("cache entry deleted but not written to disk.");
     440                 :   }
     441            5142 :   return PL_DHASH_REMOVE;
     442                 : }
     443                 : 
     444                 : 
     445                 : /** 
     446                 :  * WriteToDisk writes the cache out to disk. Callers of WriteToDisk need to call WaitOnWriteThread
     447                 :  * to make sure there isn't a write happening on another thread
     448                 :  */
     449                 : void
     450            6517 : StartupCache::WriteToDisk() 
     451                 : {
     452                 :   nsresult rv;
     453            6517 :   mStartupWriteInitiated = true;
     454                 : 
     455            6517 :   if (mTable.Count() == 0)
     456            5908 :     return;
     457                 : 
     458            1218 :   nsCOMPtr<nsIZipWriter> zipW = do_CreateInstance("@mozilla.org/zipwriter;1");
     459             609 :   if (!zipW)
     460                 :     return;
     461                 : 
     462             609 :   rv = zipW->Open(mFile, PR_RDWR | PR_CREATE_FILE);
     463             609 :   if (NS_FAILED(rv)) {
     464               3 :     NS_WARNING("could not open zipfile for write");
     465                 :     return;
     466                 :   } 
     467                 : 
     468                 :   // If we didn't have an mArchive member, that means that we failed to
     469                 :   // open the startup cache for reading.  Therefore, we need to record
     470                 :   // the time of creation in a zipfile comment; this will be useful for
     471                 :   // Telemetry statistics.
     472             606 :   PRTime now = PR_Now();
     473             606 :   if (!mArchive) {
     474            1212 :     nsCString comment;
     475             606 :     comment.Assign((char *)&now, sizeof(now));
     476             606 :     zipW->SetComment(comment);
     477                 :   }
     478                 : 
     479                 :   nsCOMPtr<nsIStringInputStream> stream 
     480            1212 :     = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
     481             606 :   if (NS_FAILED(rv)) {
     482               0 :     NS_WARNING("Couldn't create string input stream.");
     483                 :     return;
     484                 :   }
     485                 : 
     486            1212 :   CacheWriteHolder holder;
     487             606 :   holder.stream = stream;
     488             606 :   holder.writer = zipW;
     489             606 :   holder.time = now;
     490                 : 
     491             606 :   mTable.Enumerate(CacheCloseHelper, &holder);
     492                 : 
     493                 :   // Close the archive so Windows doesn't choke.
     494             606 :   mArchive = NULL;
     495             606 :   zipW->Close();
     496                 : 
     497                 :   // Our reader's view of the archive is outdated now, reload it.
     498             606 :   LoadArchive(gPostFlushAgeAction);
     499                 :   
     500                 :   return;
     501                 : }
     502                 : 
     503                 : void
     504             550 : StartupCache::InvalidateCache() 
     505                 : {
     506             550 :   WaitOnWriteThread();
     507             550 :   mTable.Clear();
     508             550 :   mArchive = NULL;
     509             550 :   mFile->Remove(false);
     510             550 :   LoadArchive(gPostFlushAgeAction);
     511             550 : }
     512                 : 
     513                 : /*
     514                 :  * WaitOnWriteThread() is called from a main thread to wait for the worker
     515                 :  * thread to finish. However since the same code is used in the worker thread and
     516                 :  * main thread, the worker thread can also call WaitOnWriteThread() which is a no-op.
     517                 :  */
     518                 : void
     519           23339 : StartupCache::WaitOnWriteThread()
     520                 : {
     521           23339 :   NS_ASSERTION(NS_IsMainThread(), "Startup cache should only wait for io thread on main thread");
     522           23339 :   if (!mWriteThread || mWriteThread == PR_GetCurrentThread())
     523           23337 :     return;
     524                 : 
     525                 :   NS_TIME_FUNCTION_MIN(30);
     526               2 :   PR_JoinThread(mWriteThread);
     527               2 :   mWriteThread = NULL;
     528                 : }
     529                 : 
     530                 : void 
     531               2 : StartupCache::ThreadedWrite(void *aClosure)
     532                 : {
     533               2 :   gStartupCache->WriteToDisk();
     534               2 : }
     535                 : 
     536                 : /*
     537                 :  * The write-thread is spawned on a timeout(which is reset with every write). This
     538                 :  * can avoid a slow shutdown. After writing out the cache, the zipreader is
     539                 :  * reloaded on the worker thread.
     540                 :  */
     541                 : void
     542               2 : StartupCache::WriteTimeout(nsITimer *aTimer, void *aClosure)
     543                 : {
     544                 :   gStartupCache->mWriteThread = PR_CreateThread(PR_USER_THREAD,
     545                 :                                                 StartupCache::ThreadedWrite,
     546                 :                                                 NULL,
     547                 :                                                 PR_PRIORITY_NORMAL,
     548                 :                                                 PR_LOCAL_THREAD,
     549                 :                                                 PR_JOINABLE_THREAD,
     550               2 :                                                 0);
     551               2 : }
     552                 : 
     553                 : // We don't want to refcount StartupCache, so we'll just
     554                 : // hold a ref to this and pass it to observerService instead.
     555            9738 : NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheListener, nsIObserver)
     556                 : 
     557                 : nsresult
     558            1232 : StartupCacheListener::Observe(nsISupports *subject, const char* topic, const PRUnichar* data)
     559                 : {
     560            1232 :   StartupCache* sc = StartupCache::GetSingleton();
     561            1232 :   if (!sc)
     562               0 :     return NS_OK;
     563                 : 
     564            1232 :   if (strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
     565                 :     // Do not leave the thread running past xpcom shutdown
     566             688 :     sc->WaitOnWriteThread();
     567             688 :     StartupCache::gShutdownInitiated = true;
     568             544 :   } else if (strcmp(topic, "startupcache-invalidate") == 0) {
     569             544 :     sc->InvalidateCache();
     570                 :   }
     571            1232 :   return NS_OK;
     572                 : } 
     573                 : 
     574                 : nsresult
     575            7375 : StartupCache::GetDebugObjectOutputStream(nsIObjectOutputStream* aStream,
     576                 :                                          nsIObjectOutputStream** aOutStream) 
     577                 : {
     578            7375 :   NS_ENSURE_ARG_POINTER(aStream);
     579                 : #ifdef DEBUG
     580                 :   StartupCacheDebugOutputStream* stream
     581            7375 :     = new StartupCacheDebugOutputStream(aStream, &mWriteObjectMap);
     582            7375 :   NS_ADDREF(*aOutStream = stream);
     583                 : #else
     584                 :   NS_ADDREF(*aOutStream = aStream);
     585                 : #endif
     586                 :   
     587            7375 :   return NS_OK;
     588                 : }
     589                 : 
     590                 : nsresult
     591            7381 : StartupCache::ResetStartupWriteTimer()
     592                 : {
     593            7381 :   mStartupWriteInitiated = false;
     594                 :   nsresult rv;
     595            7381 :   if (!mTimer)
     596             661 :     mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     597                 :   else
     598            6720 :     rv = mTimer->Cancel();
     599            7381 :   NS_ENSURE_SUCCESS(rv, rv);
     600                 :   // Wait for 10 seconds, then write out the cache.
     601            7381 :   mTimer->InitWithFuncCallback(StartupCache::WriteTimeout, this, 60000,
     602            7381 :                                nsITimer::TYPE_ONE_SHOT);
     603            7381 :   return NS_OK;
     604                 : }
     605                 : 
     606                 : nsresult
     607               1 : StartupCache::RecordAgesAlways()
     608                 : {
     609               1 :   gPostFlushAgeAction = RECORD_AGE;
     610               1 :   return NS_OK;
     611                 : }
     612                 : 
     613                 : // StartupCacheDebugOutputStream implementation
     614                 : #ifdef DEBUG
     615           66375 : NS_IMPL_ISUPPORTS3(StartupCacheDebugOutputStream, nsIObjectOutputStream, 
     616                 :                    nsIBinaryOutputStream, nsIOutputStream)
     617                 : 
     618                 : bool
     619            7375 : StartupCacheDebugOutputStream::CheckReferences(nsISupports* aObject)
     620                 : {
     621                 :   nsresult rv;
     622                 :   
     623           14750 :   nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(aObject);
     624            7375 :   if (!classInfo) {
     625               0 :     NS_ERROR("aObject must implement nsIClassInfo");
     626               0 :     return false;
     627                 :   }
     628                 :   
     629                 :   PRUint32 flags;
     630            7375 :   rv = classInfo->GetFlags(&flags);
     631            7375 :   NS_ENSURE_SUCCESS(rv, false);
     632            7375 :   if (flags & nsIClassInfo::SINGLETON)
     633            7375 :     return true;
     634                 :   
     635               0 :   nsISupportsHashKey* key = mObjectMap->GetEntry(aObject);
     636               0 :   if (key) {
     637                 :     NS_ERROR("non-singleton aObject is referenced multiple times in this" 
     638               0 :                   "serialization, we don't support that.");
     639               0 :     return false;
     640                 :   }
     641                 : 
     642               0 :   mObjectMap->PutEntry(aObject);
     643               0 :   return true;
     644                 : }
     645                 : 
     646                 : // nsIObjectOutputStream implementation
     647                 : nsresult
     648            7375 : StartupCacheDebugOutputStream::WriteObject(nsISupports* aObject, bool aIsStrongRef)
     649                 : {
     650           14750 :   nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
     651                 :   
     652            7375 :   NS_ASSERTION(rootObject.get() == aObject,
     653                 :                "bad call to WriteObject -- call WriteCompoundObject!");
     654            7375 :   bool check = CheckReferences(aObject);
     655            7375 :   NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
     656            7375 :   return mBinaryStream->WriteObject(aObject, aIsStrongRef);
     657                 : }
     658                 : 
     659                 : nsresult
     660               0 : StartupCacheDebugOutputStream::WriteSingleRefObject(nsISupports* aObject)
     661                 : {
     662               0 :   nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
     663                 :   
     664               0 :   NS_ASSERTION(rootObject.get() == aObject,
     665                 :                "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
     666               0 :   bool check = CheckReferences(aObject);
     667               0 :   NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
     668               0 :   return mBinaryStream->WriteSingleRefObject(aObject);
     669                 : }
     670                 : 
     671                 : nsresult
     672               0 : StartupCacheDebugOutputStream::WriteCompoundObject(nsISupports* aObject,
     673                 :                                                 const nsIID& aIID,
     674                 :                                                 bool aIsStrongRef)
     675                 : {
     676               0 :   nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
     677                 :   
     678               0 :   nsCOMPtr<nsISupports> roundtrip;
     679               0 :   rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
     680               0 :   NS_ASSERTION(roundtrip.get() == aObject,
     681                 :                "bad aggregation or multiple inheritance detected by call to "
     682                 :                "WriteCompoundObject!");
     683                 : 
     684               0 :   bool check = CheckReferences(aObject);
     685               0 :   NS_ENSURE_TRUE(check, NS_ERROR_FAILURE);
     686               0 :   return mBinaryStream->WriteCompoundObject(aObject, aIID, aIsStrongRef);
     687                 : }
     688                 : 
     689                 : nsresult
     690               0 : StartupCacheDebugOutputStream::WriteID(nsID const& aID) 
     691                 : {
     692               0 :   return mBinaryStream->WriteID(aID);
     693                 : }
     694                 : 
     695                 : char*
     696               0 : StartupCacheDebugOutputStream::GetBuffer(PRUint32 aLength, PRUint32 aAlignMask)
     697                 : {
     698               0 :   return mBinaryStream->GetBuffer(aLength, aAlignMask);
     699                 : }
     700                 : 
     701                 : void
     702               0 : StartupCacheDebugOutputStream::PutBuffer(char* aBuffer, PRUint32 aLength)
     703                 : {
     704               0 :   mBinaryStream->PutBuffer(aBuffer, aLength);
     705               0 : }
     706                 : #endif //DEBUG
     707                 : 
     708                 : StartupCacheWrapper* StartupCacheWrapper::gStartupCacheWrapper = nsnull;
     709                 : 
     710              40 : NS_IMPL_THREADSAFE_ISUPPORTS1(StartupCacheWrapper, nsIStartupCache)
     711                 : 
     712               1 : StartupCacheWrapper* StartupCacheWrapper::GetSingleton() 
     713                 : {
     714               1 :   if (!gStartupCacheWrapper)
     715               1 :     gStartupCacheWrapper = new StartupCacheWrapper();
     716                 : 
     717               1 :   NS_ADDREF(gStartupCacheWrapper);
     718               1 :   return gStartupCacheWrapper;
     719                 : }
     720                 : 
     721                 : nsresult 
     722               5 : StartupCacheWrapper::GetBuffer(const char* id, char** outbuf, PRUint32* length) 
     723                 : {
     724               5 :   StartupCache* sc = StartupCache::GetSingleton();
     725               5 :   if (!sc) {
     726               0 :     return NS_ERROR_NOT_INITIALIZED;
     727                 :   }
     728               5 :   return sc->GetBuffer(id, outbuf, length);
     729                 : }
     730                 : 
     731                 : nsresult
     732               4 : StartupCacheWrapper::PutBuffer(const char* id, const char* inbuf, PRUint32 length) 
     733                 : {
     734               4 :   StartupCache* sc = StartupCache::GetSingleton();
     735               4 :   if (!sc) {
     736               0 :     return NS_ERROR_NOT_INITIALIZED;
     737                 :   }
     738               4 :   return sc->PutBuffer(id, inbuf, length);
     739                 : }
     740                 : 
     741                 : nsresult
     742               6 : StartupCacheWrapper::InvalidateCache() 
     743                 : {
     744               6 :   StartupCache* sc = StartupCache::GetSingleton();
     745               6 :   if (!sc) {
     746               0 :     return NS_ERROR_NOT_INITIALIZED;
     747                 :   }
     748               6 :   sc->InvalidateCache();
     749               6 :   return NS_OK;
     750                 : }
     751                 : 
     752                 : nsresult 
     753               0 : StartupCacheWrapper::GetDebugObjectOutputStream(nsIObjectOutputStream* stream,
     754                 :                                                 nsIObjectOutputStream** outStream) 
     755                 : {
     756               0 :   StartupCache* sc = StartupCache::GetSingleton();
     757               0 :   if (!sc) {
     758               0 :     return NS_ERROR_NOT_INITIALIZED;
     759                 :   }
     760               0 :   return sc->GetDebugObjectOutputStream(stream, outStream);
     761                 : }
     762                 : 
     763                 : nsresult
     764             102 : StartupCacheWrapper::StartupWriteComplete(bool *complete)
     765                 : {
     766             102 :   StartupCache* sc = StartupCache::GetSingleton();
     767             102 :   if (!sc) {
     768               0 :     return NS_ERROR_NOT_INITIALIZED;
     769                 :   }
     770             102 :   sc->WaitOnWriteThread();
     771             102 :   *complete = sc->mStartupWriteInitiated && sc->mTable.Count() == 0;
     772             102 :   return NS_OK;
     773                 : }
     774                 : 
     775                 : nsresult
     776               2 : StartupCacheWrapper::ResetStartupWriteTimer()
     777                 : {
     778               2 :   StartupCache* sc = StartupCache::GetSingleton();
     779               2 :   return sc ? sc->ResetStartupWriteTimer() : NS_ERROR_NOT_INITIALIZED;
     780                 : }
     781                 : 
     782                 : nsresult
     783               1 : StartupCacheWrapper::GetObserver(nsIObserver** obv) {
     784               1 :   StartupCache* sc = StartupCache::GetSingleton();
     785               1 :   if (!sc) {
     786               0 :     return NS_ERROR_NOT_INITIALIZED;
     787                 :   }
     788               1 :   NS_ADDREF(*obv = sc->mListener);
     789               1 :   return NS_OK;
     790                 : }
     791                 : 
     792                 : nsresult
     793               1 : StartupCacheWrapper::RecordAgesAlways() {
     794               1 :   StartupCache *sc = StartupCache::GetSingleton();
     795               1 :   return sc ? sc->RecordAgesAlways() : NS_ERROR_NOT_INITIALIZED;
     796                 : }
     797                 : 
     798                 : } // namespace scache
     799                 : } // namespace mozilla

Generated by: LCOV version 1.7