forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			569 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			569 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| HTTP Cache
 | ||
| ==========
 | ||
| 
 | ||
| This document describes the **HTTP cache implementation**.
 | ||
| 
 | ||
| The code resides in `/netwerk/cache2 (searchfox)
 | ||
| <https://searchfox.org/mozilla-central/source/netwerk/cache2>`_
 | ||
| 
 | ||
| API
 | ||
| ---
 | ||
| 
 | ||
| Here is a detailed description of the HTTP cache v2 API, examples
 | ||
| included.  This document only contains what cannot be found or may not
 | ||
| be clear directly from the `IDL files <https://searchfox.org/mozilla-central/search?q=&path=cache2%2FnsICache&case=false®exp=false>`_ comments.
 | ||
| 
 | ||
| -  The cache API is **completely thread-safe** and **non-blocking**.
 | ||
| -  There is **no IPC support**.  It's only accessible on the default
 | ||
|    chrome process.
 | ||
| -  When there is no profile the new HTTP cache works, but everything is
 | ||
|    stored only in memory not obeying any particular limits.
 | ||
| 
 | ||
| .. _nsICacheStorageService:
 | ||
| 
 | ||
| nsICacheStorageService
 | ||
| ----------------------
 | ||
| 
 | ||
| -  The HTTP cache entry-point. Accessible as a service only, fully
 | ||
|    thread-safe, scriptable.
 | ||
| 
 | ||
| -  `nsICacheStorageService.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheStorageService.idl>`_
 | ||
| 
 | ||
| -   \ ``"@mozilla.org/netwerk/cache-storage-service;1"``
 | ||
| 
 | ||
| -  Provides methods accessing "storage" objects – see `nsICacheStorage` below – giving further access to cache entries – see :ref:`nsICacheEntry <nsICacheEntry>` more below – per specific URL.
 | ||
| 
 | ||
| -  Currently we have 3 types of storages, all the access methods return
 | ||
|    an :ref:`nsICacheStorage <nsICacheStorage>` object:
 | ||
| 
 | ||
|    -  **memory-only** (``memoryCacheStorage``): stores data only in a
 | ||
|       memory cache, data in this storage are never put to disk
 | ||
| 
 | ||
|    -  **disk** (``diskCacheStorage``): stores data on disk, but for
 | ||
|       existing entries also looks into the memory-only storage; when
 | ||
|       instructed via a special argument also primarily looks into
 | ||
|       application caches
 | ||
| 
 | ||
|    .. note::
 | ||
| 
 | ||
|       **application cache** (``appCacheStorage``): when a consumer has a
 | ||
|       specific ``nsIApplicationCache`` (i.e. a particular app cache
 | ||
|       version in a group) in hands, this storage will provide read and
 | ||
|       write access to entries in that application cache; when the app
 | ||
|       cache is not specified, this storage will operate over all
 | ||
|       existing app caches. **This kind of storage is deprecated and will be removed** in `bug 1694662 <https://bugzilla.mozilla.org/show_bug.cgi?id=1694662>`_
 | ||
| 
 | ||
| -  The service also provides methods to clear the whole disk and memory
 | ||
|    cache content or purge any intermediate memory structures:
 | ||
| 
 | ||
|    -  ``clear``– after it returns, all entries are no longer accessible
 | ||
|       through the cache APIs; the method is fast to execute and
 | ||
|       non-blocking in any way; the actual erase happens in background
 | ||
| 
 | ||
|    -  ``purgeFromMemory``– removes (schedules to remove) any
 | ||
|       intermediate cache data held in memory for faster access (more
 | ||
|       about the :ref:`Intermediate_Memory_Caching <Intermediate_Memory_Caching>` below)
 | ||
| 
 | ||
| .. _nsILoadContextInfo:
 | ||
| 
 | ||
| nsILoadContextInfo
 | ||
| ------------------
 | ||
| 
 | ||
| -  Distinguishes the scope of the storage demanded to open.
 | ||
| 
 | ||
| -  Mandatory argument to ``*Storage`` methods of :ref:`nsICacheStorageService <nsICacheStorageService>`.
 | ||
| 
 | ||
| -  `nsILoadContextInfo.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/base/nsILoadContextInfo.idl>`_
 | ||
| 
 | ||
| 
 | ||
| -  It is a helper interface wrapping following four arguments into a single one:
 | ||
| 
 | ||
|    -  **private-browsing** boolean flag
 | ||
|    -  **anonymous load** boolean flag
 | ||
|    -  **origin attributes** js value
 | ||
| 
 | ||
|    .. note::
 | ||
| 
 | ||
|       Helper functions to create nsILoadContextInfo objects:
 | ||
| 
 | ||
|       -  C++ consumers: functions at ``LoadContextInfo.h`` exported
 | ||
|          header
 | ||
| 
 | ||
|       -  JS consumers: ``Services.loadContextInfo`` which is an instance of ``nsILoadContextInfoFactory``.
 | ||
| 
 | ||
| -  Two storage objects created with the same set of
 | ||
|    ``nsILoadContextInfo``\ arguments are identical, containing the same
 | ||
|    cache entries.
 | ||
| 
 | ||
| -  Two storage objects created with in any way different
 | ||
|    ``nsILoadContextInfo``\ arguments are strictly and completely
 | ||
|    distinct and cache entries in them do not overlap even when having
 | ||
|    the same URIs.
 | ||
| 
 | ||
| .. _nsICacheStorage:
 | ||
| 
 | ||
| nsICacheStorage
 | ||
| ---------------
 | ||
| 
 | ||
| -  `nsICacheStorage.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheStorage.idl>`_
 | ||
| 
 | ||
| -  Obtained from call to one of the ``*Storage`` methods on
 | ||
|    :ref:`nsICacheStorageService <nsICacheStorageService>`.
 | ||
| 
 | ||
| -  Represents a distinct storage area (or scope) to put and get cache
 | ||
|    entries mapped by URLs into and from it.
 | ||
| 
 | ||
| -  *Similarity with the old cache*\ : this interface may be with some
 | ||
|    limitations considered as a mirror to ``nsICacheSession``, but less
 | ||
|    generic and not inclining to abuse.
 | ||
| 
 | ||
| nsICacheEntryOpenCallback
 | ||
| -------------------------
 | ||
| 
 | ||
| -  `nsICacheEntryOpenCallback.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheEntryOpenCallback.idl>`_
 | ||
| 
 | ||
| -  The result of ``nsICacheStorage.asyncOpenURI`` is always and only
 | ||
|    sent to callbacks on this interface.
 | ||
| 
 | ||
| -  These callbacks are ensured to be invoked when ``asyncOpenURI``
 | ||
|    returns ``NS_OK``.
 | ||
| 
 | ||
| -
 | ||
| 
 | ||
|    .. note::
 | ||
| 
 | ||
|       When the
 | ||
|       cache entry object is already present in memory or open as
 | ||
|       "force-new" (a.k.a "open-truncate") this callback is invoked
 | ||
|       sooner then the ``asyncOpenURI``\ method returns (i.e.
 | ||
|       immediately); there is currently no way to opt out of this feature
 | ||
|       (see `bug
 | ||
|       938186 <https://bugzilla.mozilla.org/show_bug.cgi?id=938186>`__).
 | ||
| 
 | ||
| .. _nsICacheEntry:
 | ||
| 
 | ||
| nsICacheEntry
 | ||
| -------------
 | ||
| 
 | ||
| -  `nsICacheEntry.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheEntry.idl>`_
 | ||
| 
 | ||
| -  Obtained asynchronously or pseudo-asynchronously by a call to
 | ||
|    ``nsICacheStorage.asyncOpenURI``.
 | ||
| 
 | ||
| -  Provides access to a cached entry data and meta data for reading or
 | ||
|    writing or in some cases both, see below.
 | ||
| 
 | ||
| Lifetime of a new entry
 | ||
| -----------------------
 | ||
| 
 | ||
| -  Such entry is initially empty (no data or meta data is stored in it).
 | ||
| 
 | ||
| -  The ``aNew``\ argument in ``onCacheEntryAvailable`` is ``true`` for
 | ||
|    and only for new entries.
 | ||
| 
 | ||
| -  Only one consumer (the so called "*writer*") may have such an entry
 | ||
|    available (obtained via ``onCacheEntryAvailable``).
 | ||
| 
 | ||
| -  Other parallel openers of the same cache entry are blocked (wait) for
 | ||
|    invocation of their ``onCacheEntryAvailable`` until one of the
 | ||
|    following occurs:
 | ||
| 
 | ||
|    -  The *writer* simply throws the entry away: other waiting opener in
 | ||
|       line gets the entry again as "*new*", the cycle repeats.
 | ||
| 
 | ||
|       .. note::
 | ||
| 
 | ||
|          This applies in general, writers throwing away the cache entry
 | ||
|          means a failure to write the cache entry and a new writer is
 | ||
|          being looked for again, the cache entry remains empty (a.k.a.
 | ||
|          "new").
 | ||
| 
 | ||
|    -  The *writer* stored all necessary meta data in the cache entry and
 | ||
|       called ``metaDataReady`` on it: other consumers now get the entry
 | ||
|       and may examine and potentially modify the meta data and read the
 | ||
|       data (if any) of the cache entry.
 | ||
|    -  When the *writer* has data (i.e. the response payload) to write to
 | ||
|       the cache entry, it **must** open the output stream on it
 | ||
|       **before** it calls ``metaDataReady``.
 | ||
| 
 | ||
| -  When the *writer* still keeps the cache entry and has open and keeps
 | ||
|    open the output stream on it, other consumers may open input streams
 | ||
|    on the entry. The data will be available as the *writer* writes data
 | ||
|    to the cache entry's output stream immediately, even before the
 | ||
|    output stream is closed. This is called :ref:`concurrent
 | ||
|    read/write <Concurrent_read_and_write>`.
 | ||
| 
 | ||
| .. _Concurrent_read_and_write:
 | ||
| 
 | ||
| Concurrent read and write
 | ||
| -------------------------
 | ||
| 
 | ||
| The cache supports reading a cache entry data while it is still being
 | ||
| written by the first consumer - the *writer*.
 | ||
| This can only be engaged for resumable responses that (`bug
 | ||
| 960902 <https://bugzilla.mozilla.org/show_bug.cgi?id=960902#c17>`__)
 | ||
| don't need revalidation. Reason is that when the writer is interrupted
 | ||
| (by e.g. external canceling of the loading channel) concurrent readers
 | ||
| would not be able to reach the remaining unread content.
 | ||
| 
 | ||
| .. note::
 | ||
| 
 | ||
|    This could be improved by keeping the network load running and being
 | ||
|    stored to the cache entry even after the writing channel has been
 | ||
|    canceled.
 | ||
| 
 | ||
| When the *writer* is interrupted, the first concurrent *reader* in line
 | ||
| does a range request for the rest of the data - and becomes that way a
 | ||
| new *writer*. The rest of the *readers* are still concurrently reading
 | ||
| the content since output stream for the cache entry is again open and
 | ||
| kept by the current *writer*.
 | ||
| 
 | ||
| Lifetime of an existing entry with only a partial content
 | ||
| ---------------------------------------------------------
 | ||
| 
 | ||
| -  Such a cache entry is first examined in the
 | ||
|    ``nsICacheEntryOpenCallback.onCacheEntryCheck`` callback, where it
 | ||
|    has to be checked for completeness.
 | ||
| -  In this case, the ``Content-Length`` (or different indicator) header
 | ||
|    doesn't equal to the data size reported by the cache entry.
 | ||
| -  The consumer then indicates the cache entry needs to be revalidated
 | ||
|    by returning ``ENTRY_NEEDS_REVALIDATION``\ from
 | ||
|    ``onCacheEntryCheck``.
 | ||
| -  This consumer, from the point of view the cache, takes a role of the
 | ||
|    *writer*.
 | ||
| -  Other parallel consumers, if any, are blocked until the *writer*
 | ||
|    calls ``setValid`` on the cache entry.
 | ||
| -  The consumer is then responsible to validate the partial content
 | ||
|    cache entry with the network server and attempt to load the rest of
 | ||
|    the data.
 | ||
| -  When the server responds positively (in case of an HTTP server with a
 | ||
|    206 response code) the *writer* (in this order) opens the output
 | ||
|    stream on the cache entry and calls ``setValid`` to unblock other
 | ||
|    pending openers.
 | ||
| -  Concurrent read/write is engaged.
 | ||
| 
 | ||
| Lifetime of an existing entry that doesn't pass server revalidation
 | ||
| -------------------------------------------------------------------
 | ||
| 
 | ||
| -  Such a cache entry is first examined in the
 | ||
|    ``nsICacheEntryOpenCallback.onCacheEntryCheck`` callback, where the
 | ||
|    consumer finds out it must be revalidated with the server before use.
 | ||
| -  The consumer then indicates the cache entry needs to be revalidated
 | ||
|    by returning ``ENTRY_NEEDS_REVALIDATION``\ from
 | ||
|    ``onCacheEntryCheck``.
 | ||
| -  This consumer, from the point of view the cache, takes a role of the
 | ||
|    *writer*.
 | ||
| -  Other parallel consumers, if any, are blocked until the *writer*
 | ||
|    calls ``setValid`` on the cache entry.
 | ||
| -  The consumer is then responsible to validate the partial content
 | ||
|    cache entry with the network server.
 | ||
| -  The server responses with a 200 response which means the cached
 | ||
|    content is no longer valid and a new version must be loaded from the
 | ||
|    network.
 | ||
| -  The *writer* then calls ``recreate``\ on the cache entry. This
 | ||
|    returns a new empty entry to write the meta data and data to, the
 | ||
|    *writer* exchanges its cache entry by this new one and handles it as
 | ||
|    a new one.
 | ||
| -  The *writer* then (in this order) fills the necessary meta data of
 | ||
|    the cache entry, opens the output stream on it and calls
 | ||
|    ``metaDataReady`` on it.
 | ||
| -  Any other pending openers, if any, are now given this new entry to
 | ||
|    examine and read as an existing entry.
 | ||
| 
 | ||
| Adding a new storage
 | ||
| --------------------
 | ||
| 
 | ||
| Should there be a need to add a new distinct storage for which the
 | ||
| current scoping model would not be sufficient - use one of the two
 | ||
| following ways:
 | ||
| 
 | ||
| #. *[preferred]* Add a new ``<Your>Storage`` method on
 | ||
|    :ref:`nsICacheStorageService <nsICacheStorageService>` and if needed give it any arguments to
 | ||
|    specify the storage scope even more.  Implementation only should need
 | ||
|    to enhance the context key generation and parsing code and enhance
 | ||
|    current - or create new when needed - :ref:`nsICacheStorage <nsICacheStorage>`
 | ||
|    implementations to carry any additional information down to the cache
 | ||
|    service.
 | ||
| #. *[*\ **not**\ *preferred]* Add a new argument to
 | ||
|    :ref:`nsILoadContextInfo <nsILoadContextInfo>`; **be careful
 | ||
|    here**, since some arguments on the context may not be known during
 | ||
|    the load time, what may lead to inter-context data leaking or
 | ||
|    implementation problems. Adding more distinction to
 | ||
|    :ref:`nsILoadContextInfo <nsILoadContextInfo>` also affects all existing storages which may
 | ||
|    not be always desirable.
 | ||
| 
 | ||
| See context keying details for more information.
 | ||
| 
 | ||
| Threading
 | ||
| ---------
 | ||
| 
 | ||
| The cache API is fully thread-safe.
 | ||
| 
 | ||
| The cache is using a single background thread where any IO operations
 | ||
| like opening, reading, writing and erasing happen.  Also memory pool
 | ||
| management, eviction, visiting loops happen on this thread.
 | ||
| 
 | ||
| The thread supports several priority levels. Dispatching to a level with
 | ||
| a lower number is executed sooner then dispatching to higher number
 | ||
| layers; also any loop on lower levels yields to higher levels so that
 | ||
| scheduled deletion of 1000 files will not block opening cache entries.
 | ||
| 
 | ||
| #. **OPEN_PRIORITY:** except opening priority cache files also file
 | ||
|    dooming happens here to prevent races
 | ||
| #. **READ_PRIORITY:** top level documents and head blocking script cache
 | ||
|    files are open and read as the first
 | ||
| #. **OPEN**
 | ||
| #. **READ:** any normal priority content, such as images are open and
 | ||
|    read here
 | ||
| #. **WRITE:** writes are processed as last, we cache data in memory in
 | ||
|    the mean time
 | ||
| #. **MANAGEMENT:** level for the memory pool and CacheEntry background
 | ||
|    operations
 | ||
| #. **CLOSE:** file closing level
 | ||
| #. **INDEX:** index is being rebuild here
 | ||
| #. **EVICT:** files overreaching the disk space consumption limit are
 | ||
|    being evicted here
 | ||
| 
 | ||
| NOTE: Special case for eviction - when an eviction is scheduled on the
 | ||
| IO thread, all operations pending on the OPEN level are first merged to
 | ||
| the OPEN_PRIORITY level. The eviction preparation operation - i.e.
 | ||
| clearing of the internal IO state - is then put to the end of the
 | ||
| OPEN_PRIORITY level.  All this happens atomically.
 | ||
| 
 | ||
| Storage and entries scopes
 | ||
| --------------------------
 | ||
| 
 | ||
| A *scope key* string used to map the storage scope is based on the
 | ||
| arguments of :ref:`nsILoadContextInfo <nsILoadContextInfo>`. The form is following (currently
 | ||
| pending in `bug
 | ||
| 968593 <https://bugzilla.mozilla.org/show_bug.cgi?id=968593>`__):
 | ||
| 
 | ||
| .. code::
 | ||
| 
 | ||
|    a,b,i1009,p,
 | ||
| 
 | ||
| -  Regular expression: ``(.([-,]+)?,)*``
 | ||
| -  The first letter is an identifier, identifiers are to be
 | ||
|    alphabetically sorted and always terminate with ','
 | ||
| -  a - when present the scope is belonging to an **anonymous** load
 | ||
| -  b - when present the scope is **in browser element** load
 | ||
| -  i - when present must have a decimal integer value that represents an
 | ||
|    app ID the scope belongs to, otherwise there is no app (app ID is
 | ||
|    considered ``0``)
 | ||
| -  p - when present the scope is of a **private browsing** load, this
 | ||
|    never persists
 | ||
| 
 | ||
| ``CacheStorageService``\ keeps a global hashtable mapped by the *scope
 | ||
| key*. Elements in this global hashtable are hashtables of cache entries.
 | ||
| The cache entries are mapped by concantation of Enhance ID and URI
 | ||
| passed to ``nsICacheStorage.asyncOpenURI``.  So that when an entry is
 | ||
| being looked up, first the global hashtable is searched using the
 | ||
| *scope key*. An entries hashtable is found. Then this entries hashtable
 | ||
| is searched using <enhance-id:><uri> string. The elements in this
 | ||
| hashtable are CacheEntry classes, see below.
 | ||
| 
 | ||
| The hash tables keep a strong reference to ``CacheEntry`` objects. The
 | ||
| only way to remove ``CacheEntry`` objects from memory is by exhausting a
 | ||
| memory limit for :ref:`Intermediate_Memory_Caching <Intermediate_Memory_Caching>`, what triggers a background
 | ||
| process of purging expired and then least used entries from memory.
 | ||
| Another way is to directly call the
 | ||
| ``nsICacheStorageService.purge``\ method. That method is also called
 | ||
| automatically on the ``"memory-pressure"`` indication.
 | ||
| 
 | ||
| Access to the hashtables is protected by a global lock. We also - in a
 | ||
| thread-safe manner - count the number of consumers keeping a reference
 | ||
| on each entry. The open callback actually doesn't give the consumer
 | ||
| directly the ``CacheEntry`` object but a small wrapper class that
 | ||
| manages the 'consumer reference counter' on its cache entry. This both
 | ||
| mechanisms ensure thread-safe access and also inability to have more
 | ||
| then a single instance of a ``CacheEntry`` for a single
 | ||
| <scope+enhanceID+URL> key.
 | ||
| 
 | ||
| ``CacheStorage``, implementing the :ref:`nsICacheStorage <nsICacheStorage>` interface, is
 | ||
| forwarding all calls to internal methods of ``CacheStorageService``
 | ||
| passing itself as an argument.  ``CacheStorageService`` then generates
 | ||
| the *scope key* using the ``nsILoadContextInfo`` of the storage. Note:
 | ||
| CacheStorage keeps a thread-safe copy of ``nsILoadContextInfo`` passed
 | ||
| to a ``*Storage`` method on ``nsICacheStorageService``.
 | ||
| 
 | ||
| Invoking open callbacks
 | ||
| -----------------------
 | ||
| 
 | ||
| ``CacheEntry``, implementing the ``nsICacheEntry`` interface, is
 | ||
| responsible for managing the cache entry internal state and to properly
 | ||
| invoke ``onCacheEntryCheck`` and ``onCacheEntryAvaiable`` callbacks to
 | ||
| all callers of ``nsICacheStorage.asyncOpenURI``.
 | ||
| 
 | ||
| -  Keeps a FIFO of all openers.
 | ||
| -  Keeps its internal state like NOTLOADED, LOADING, EMPTY, WRITING,
 | ||
|    READY, REVALIDATING.
 | ||
| -  Keeps the number of consumers keeping a reference to it.
 | ||
| -  Refers a ``CacheFile`` object that holds actual data and meta data
 | ||
|    and, when told to, persists it to the disk.
 | ||
| 
 | ||
| The openers FIFO is an array of ``CacheEntry::Callback`` objects.
 | ||
| ``CacheEntry::Callback`` keeps a strong reference to the opener plus the
 | ||
| opening flags.  ``nsICacheStorage.asyncOpenURI`` forwards to
 | ||
| ``CacheEntry::AsyncOpen`` and triggers the following pseudo-code:
 | ||
| 
 | ||
| **CacheStorage::AsyncOpenURI** - the API entry point:
 | ||
| 
 | ||
| -  globally atomic:
 | ||
| 
 | ||
|    -  look a given ``CacheEntry`` in ``CacheStorageService`` hash tables
 | ||
|       up
 | ||
|    -  if not found: create a new one, add it to the proper hash table
 | ||
|       and set its state to NOTLOADED
 | ||
|    -  consumer reference ++
 | ||
| 
 | ||
| -  call to `CacheEntry::AsyncOpen`
 | ||
| -  consumer reference --
 | ||
| 
 | ||
| **CacheEntry::AsyncOpen** (entry atomic):
 | ||
| 
 | ||
| -  the opener is added to FIFO, consumer reference ++ (dropped back
 | ||
|    after an opener is removed from the FIFO)
 | ||
| -  state == NOTLOADED:
 | ||
| 
 | ||
|    -  state = LOADING
 | ||
|    -  when OPEN_TRUNCATE flag was used:
 | ||
| 
 | ||
|       -  ``CacheFile`` is created as 'new', state = EMPTY
 | ||
| 
 | ||
|    -  otherwise:
 | ||
| 
 | ||
|       -  ``CacheFile`` is created and load on it started
 | ||
|       -  ``CacheEntry::OnFileReady`` notification is now expected
 | ||
| 
 | ||
| -  state == LOADING: just do nothing and exit
 | ||
| -  call to `CacheEntry::InvokeCallbacks`
 | ||
| 
 | ||
| **CacheEntry::InvokeCallbacks** (entry atomic):
 | ||
| 
 | ||
| -  called on:
 | ||
| 
 | ||
|    -  a new opener has been added to the FIFO via an ``AsyncOpen`` call
 | ||
|    -  asynchronous result of CacheFile open ``CacheEntry::OnFileReady>``
 | ||
|    -  the writer throws the entry away - ``CacheEntry::OnHandleClosed``
 | ||
|    -  the **output stream** of the entry has been **opened** or
 | ||
|       **closed**
 | ||
|    -  ``metaDataReady``\ or ``setValid``\ on the entry has been called
 | ||
|    -  the entry has been **doomed**
 | ||
| 
 | ||
| -  state == EMPTY:
 | ||
| 
 | ||
|    -  on OPER_READONLY flag use: onCacheEntryAvailable with
 | ||
|       ``null``\ for the cache entry
 | ||
|    -  otherwise:
 | ||
| 
 | ||
|       -  state = WRITING
 | ||
|       -  opener is removed from the FIFO and remembered as the current
 | ||
|          '*writer*'
 | ||
|       -  onCacheEntryAvailable with ``aNew = true``\ and this entry is
 | ||
|          invoked (on the caller thread) for the *writer*
 | ||
| 
 | ||
| -  state == READY:
 | ||
| 
 | ||
|    -  onCacheEntryCheck with the entry is invoked on the first opener in
 | ||
|       FIFO - on the caller thread if demanded
 | ||
|    -  result == RECHECK_AFTER_WRITE_FINISHED:
 | ||
| 
 | ||
|       -  opener is left in the FIFO with a flag ``RecheckAfterWrite``
 | ||
|       -  such openers are skipped until the output stream on the entry
 | ||
|          is closed, then ``onCacheEntryCheck`` is re-invoked on them
 | ||
|       -  Note: here is a potential for endless looping when
 | ||
|          RECHECK_AFTER_WRITE_FINISHED is abused
 | ||
| 
 | ||
|    -  result == ENTRY_NEEDS_REVALIDATION:
 | ||
| 
 | ||
|       -  state = REVALIDATING, this prevents invocation of any callback
 | ||
|          until ``CacheEntry::SetValid`` is called
 | ||
|       -  continue as in state ENTRY_WANTED (just below)
 | ||
| 
 | ||
|    -  result == ENTRY_WANTED:
 | ||
| 
 | ||
|       -  consumer reference ++ (dropped back when the consumer releases
 | ||
|          the entry)
 | ||
|       -  onCacheEntryAvailable is invoked on the opener with
 | ||
|          ``aNew = false``\ and the entry
 | ||
|       -  opener is removed from the FIFO
 | ||
| 
 | ||
|    -  result == ENTRY_NOT_WANTED:
 | ||
| 
 | ||
|       -  ``onCacheEntryAvailable`` is invoked on the opener with
 | ||
|          ``null``\ for the entry
 | ||
|       -  opener is removed from the FIFO
 | ||
| 
 | ||
| -  state == WRITING or REVALIDATING:
 | ||
| 
 | ||
|    -  do nothing and exit
 | ||
| 
 | ||
| -  any other value of state is unexpected here (assertion failure)
 | ||
| -  loop this process while there are openers in the FIFO
 | ||
| 
 | ||
| **CacheEntry::OnFileReady** (entry atomic):
 | ||
| 
 | ||
| -  load result == failure or the file has not been found on disk (is
 | ||
|    new): state = EMPTY
 | ||
| -  otherwise: state = READY since the cache file has been found and is
 | ||
|    usable containing meta data and data of the entry
 | ||
| -  call to ``CacheEntry::InvokeCallbacks``
 | ||
| 
 | ||
| **CacheEntry::OnHandleClosed** (entry atomic):
 | ||
| 
 | ||
| -  Called when any consumer throws the cache entry away
 | ||
| -  If the handle is not the handle given to the current *writer*, then
 | ||
|    exit
 | ||
| -  state == WRITING: the writer failed to call ``metaDataReady`` on the
 | ||
|    entry - state = EMPTY
 | ||
| -  state == REVALIDATING: the writer failed the re-validation process
 | ||
|    and failed to call ``setValid`` on the entry - state = READY
 | ||
| -  call to ``CacheEntry::InvokeCallbacks``
 | ||
| 
 | ||
| **All consumers release the reference:**
 | ||
| 
 | ||
| -  the entry may now be purged (removed) from memory when found expired
 | ||
|    or least used on overrun of the :ref:`memory
 | ||
|    pool <Intermediate_Memory_Caching>` limit
 | ||
| -  when this is a disk cache entry, its cached data chunks are released
 | ||
|    from memory and only meta data is kept
 | ||
| 
 | ||
| .. _Intermediate_Memory_Caching:
 | ||
| 
 | ||
| Intermediate memory caching
 | ||
| ---------------------------
 | ||
| 
 | ||
| Intermediate memory caching of frequently used metadata (a.k.a. disk cache memory pool).
 | ||
| 
 | ||
| For the disk cache entries we keep some of the most recent and most used
 | ||
| cache entries' meta data in memory for immediate zero-thread-loop
 | ||
| opening. The default size of this meta data memory pool is only 250kB
 | ||
| and is controlled by a new ``browser.cache.disk.metadata_memory_limit``
 | ||
| preference. When the limit is exceeded, we purge (throw away) first
 | ||
| **expired** and then **least used** entries to free up memory again.
 | ||
| 
 | ||
| Only ``CacheEntry`` objects that are already loaded and filled with data
 | ||
| and having the 'consumer reference == 0' (`bug
 | ||
| 942835 <https://bugzilla.mozilla.org/show_bug.cgi?id=942835#c3>`__) can
 | ||
| be purged.
 | ||
| 
 | ||
| The 'least used' entries are recognized by the lowest value of
 | ||
| `frecency <https://wiki.mozilla.org/User:Jesse/NewFrecency?title=User:Jesse/NewFrecency>`__
 | ||
| we re-compute for each entry on its every access. The decay time is
 | ||
| controlled by the ``browser.cache.frecency_half_life_hours`` preference
 | ||
| and defaults to 6 hours. The best decay time will be based on results of
 | ||
| `an experiment <https://bugzilla.mozilla.org/show_bug.cgi?id=986728>`__.
 | ||
| 
 | ||
| The memory pool is represented by two lists (strong referring ordered
 | ||
| arrays) of ``CacheEntry`` objects:
 | ||
| 
 | ||
| #. Sorted by expiration time (that default to 0xFFFFFFFF)
 | ||
| #. Sorted by frecency (defaults to 0)
 | ||
| 
 | ||
| We have two such pools, one for memory-only entries actually
 | ||
| representing the memory-only cache and one for disk cache entries for
 | ||
| which we only keep the meta data.  Each pool has a different limit
 | ||
| checking - the memory cache pool is controlled by
 | ||
| ``browser.cache.memory.capacity``, the disk entries pool is already
 | ||
| described above. The pool can be accessed and modified only on the cache
 | ||
| background thread.
 | 
