From 644fc616b3b79d7301aeabb3a86910e10a5606d5 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Thu, 29 May 2014 14:00:13 +0100 Subject: attempt to purge low level cache on out of memory during fetch --- content/hlcache.c | 2 +- content/llcache.c | 102 ++++++++++++++++++++++++++++++++---------------------- content/llcache.h | 10 ++++-- 3 files changed, 68 insertions(+), 46 deletions(-) (limited to 'content') diff --git a/content/hlcache.c b/content/hlcache.c index 23fb79562..e7c4cd84d 100644 --- a/content/hlcache.c +++ b/content/hlcache.c @@ -143,7 +143,7 @@ static void hlcache_clean(void *ignored) } /* Attempt to clean the llcache */ - llcache_clean(); + llcache_clean(false); /* Re-schedule ourselves */ guit->browser->schedule(hlcache->params.bg_clean_time, hlcache_clean, NULL); diff --git a/content/llcache.c b/content/llcache.c index 0c9b865d7..13ef77f3b 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -1916,9 +1916,43 @@ static nserror llcache_fetch_notmodified(llcache_object *object, * \param len Byte length of data * \return NSERROR_OK on success, appropriate error otherwise. */ -static nserror llcache_fetch_process_data(llcache_object *object, const uint8_t *data, - size_t len) +static nserror +llcache_fetch_process_data(llcache_object *object, + const uint8_t *data, + size_t len) { + if (object->fetch.state != LLCACHE_FETCH_DATA) { + /* On entry into this state, check if we need to + * invalidate the cache control data. We are guaranteed + * to have received all response headers. + * + * There are two cases in which we want to suppress + * cacheing of an object: + * + * 1) The HTTP response code is not 200 or 203 + * 2) The request URI had a query string and the + * response headers did not provide an explicit + * object expiration time. + */ + long http_code = fetch_http_code(object->fetch.fetch); + + if ((http_code != 200 && http_code != 203) || + (nsurl_has_component(object->url, NSURL_QUERY) && + (object->cache.max_age == INVALID_AGE && + object->cache.expires == 0))) { + /* Invalidate cache control data */ + llcache_invalidate_cache_control_data(object); + } + + /* Release candidate, if any */ + if (object->candidate != NULL) { + object->candidate->candidate_count--; + object->candidate = NULL; + } + + object->fetch.state = LLCACHE_FETCH_DATA; + } + /* Resize source buffer if it's too small */ if (object->source_len + len >= object->source_alloc) { const size_t new_len = object->source_len + len + 64 * 1024; @@ -2310,38 +2344,6 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) /* Normal 2xx state machine */ case FETCH_DATA: /* Received some data */ - if (object->fetch.state != LLCACHE_FETCH_DATA) { - /* On entry into this state, check if we need to - * invalidate the cache control data. We are guaranteed - * to have received all response headers. - * - * There are two cases in which we want to suppress - * cacheing of an object: - * - * 1) The HTTP response code is not 200 or 203 - * 2) The request URI had a query string and the - * response headers did not provide an explicit - * object expiration time. - */ - long http_code = fetch_http_code(object->fetch.fetch); - - if ((http_code != 200 && http_code != 203) || - (nsurl_has_component(object->url, NSURL_QUERY) && - (object->cache.max_age == INVALID_AGE && - object->cache.expires == 0))) { - /* Invalidate cache control data */ - llcache_invalidate_cache_control_data(object); - } - - /* Release candidate, if any */ - if (object->candidate != NULL) { - object->candidate->candidate_count--; - object->candidate = NULL; - } - } - - object->fetch.state = LLCACHE_FETCH_DATA; - error = llcache_fetch_process_data(object, msg->data.header_or_data.buf, msg->data.header_or_data.len); @@ -2445,6 +2447,14 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) /* Deal with any errors reported by event handlers */ if (error != NSERROR_OK) { + if (error == NSERROR_NOMEM) { + /* attempt to purge the cache to free some + * memory. will not help this fetch, but may + * allow the UI to report errors etc. + */ + llcache_clean(true); + } + if (object->fetch.fetch != NULL) { fetch_abort(object->fetch.fetch); object->fetch.fetch = NULL; @@ -2454,7 +2464,6 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) object->fetch.state = LLCACHE_FETCH_COMPLETE; } - return; } } @@ -2821,20 +2830,29 @@ total_object_size(llcache_object *object) * Public API * ******************************************************************************/ -/** +/* * Attempt to clean the cache * * The memory cache cleaning discards objects in order of increasing value. + * + * Exported interface documented in llcache.h */ -/* Exported interface documented in llcache.h */ -void llcache_clean(void) +void llcache_clean(bool purge) { llcache_object *object, *next; uint32_t llcache_size = 0; int remaining_lifetime; + uint32_t limit; LLCACHE_LOG(("Attempting cache clean")); + /* If the cache is being purged set the size limit to zero. */ + if (purge) { + limit = 0; + } else { + limit = llcache->limit; + } + /* Uncacheable objects with no users or fetches */ for (object = llcache->uncached_objects; object != NULL; @@ -2892,7 +2910,7 @@ void llcache_clean(void) * persistant so their RAM can be reclaimed in the next * step */ - if (llcache->limit < llcache_size) { + if (limit < llcache_size) { llcache_persist(NULL); } @@ -2901,7 +2919,7 @@ void llcache_clean(void) * cache exceeds the configured size. */ for (object = llcache->cached_objects; - ((llcache->limit < llcache_size) && (object != NULL)); + ((limit < llcache_size) && (object != NULL)); object = next) { next = object->next; if ((object->users == NULL) && @@ -2925,7 +2943,7 @@ void llcache_clean(void) * the configured size. Efectively just the object metadata. */ for (object = llcache->cached_objects; - ((llcache->limit < llcache_size) && (object != NULL)); + ((limit < llcache_size) && (object != NULL)); object = next) { next = object->next; if ((object->users == NULL) && @@ -2955,7 +2973,7 @@ void llcache_clean(void) * fetch */ for (object = llcache->cached_objects; - ((llcache->limit < llcache_size) && (object != NULL)); + ((limit < llcache_size) && (object != NULL)); object = next) { next = object->next; diff --git a/content/llcache.h b/content/llcache.h index a9ed1861a..4a3521637 100644 --- a/content/llcache.h +++ b/content/llcache.h @@ -246,11 +246,15 @@ void llcache_finalise(void); nserror llcache_poll(void); /** - * Cause the low-level cache to attempt to perform cleanup. No - * guarantees are made as to whether or not cleanups will take + * Cause the low-level cache to attempt to perform cleanup. + * + * No guarantees are made as to whether or not cleanups will take * place and what, if any, space savings will be made. + * + * \param purge Any objects held in the cache that are safely removable will + * be freed regardless of the configured size limits. */ -void llcache_clean(void); +void llcache_clean(bool purge); /** * Retrieve a handle for a low-level cache object -- cgit v1.2.3