diff options
author | Matt Holgate <matt@emobix.co.uk> | 2014-06-18 11:10:44 +0100 |
---|---|---|
committer | Matt Holgate <matt@emobix.co.uk> | 2014-06-18 11:10:44 +0100 |
commit | 13e3327167cf47aa7c202ed83835bf43169ac8c1 (patch) | |
tree | de4cd06f1f0dcda15bef6d642928fe85ec578a37 | |
parent | d5a22b7c76c7c0f67f9350b2a7a5dc030b212421 (diff) | |
download | mupdf-13e3327167cf47aa7c202ed83835bf43169ac8c1.tar.xz |
Fix for bug #694405 - iOS Pdf Crash
If an iOS app uses too much memory, the OS asks it to free up some space.
If it doesn't do so in a timely manner, it will get a second warning before
being killed by the OS.
In other platforms, where malloc() return NULL in OOM, the store scavenger
releases memory when mallocs fail. In iOS, mallocs usually never return NULL
because the app is killed before this can happen. Therefore, we need to
initiate a scavenge from the low memory notification instead.
We evict the store to 50% of its current size when a memory warning occurs
when it is in the foreground, and 0% when a memory warning occurs whilst
it is in the background. Having said this, I didn't manage to get a background
warning to occur, presumably because we don't request background execution
Therefore, I think in practice the OS just kills the process. However, this
will be useful if we ever add background execution.
-rw-r--r-- | include/mupdf/fitz/store.h | 11 | ||||
-rw-r--r-- | platform/ios/Classes/MuAppDelegate.h | 6 | ||||
-rw-r--r-- | platform/ios/Classes/MuAppDelegate.m | 13 | ||||
-rw-r--r-- | source/fitz/store.c | 38 |
4 files changed, 65 insertions, 3 deletions
diff --git a/include/mupdf/fitz/store.h b/include/mupdf/fitz/store.h index dc832b54..f954e3b8 100644 --- a/include/mupdf/fitz/store.h +++ b/include/mupdf/fitz/store.h @@ -184,6 +184,17 @@ void fz_empty_store(fz_context *ctx); int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase); /* + fz_shrink_store: Evict items from the store until the total size of + the objects in the store is reduced to a given percentage of its + current size. + + percent: %age of current size to reduce the store to. + + Returns non zero if we managed to free enough memory, zero otherwise. +*/ +int fz_shrink_store(fz_context *ctx, unsigned int percent); + +/* fz_print_store: Dump the contents of the store for debugging. */ #ifndef NDEBUG diff --git a/platform/ios/Classes/MuAppDelegate.h b/platform/ios/Classes/MuAppDelegate.h index a20233db..34074c87 100644 --- a/platform/ios/Classes/MuAppDelegate.h +++ b/platform/ios/Classes/MuAppDelegate.h @@ -2,6 +2,12 @@ #import "MuLibraryController.h" +enum +{ + // use at most 128M for resource cache + ResourceCacheMaxSize = 128<<20 // use at most 128M for resource cache +}; + @interface MuAppDelegate : NSObject <UIApplicationDelegate, UINavigationControllerDelegate> { UIWindow *window; diff --git a/platform/ios/Classes/MuAppDelegate.m b/platform/ios/Classes/MuAppDelegate.m index 2c6e1df3..b88de1cb 100644 --- a/platform/ios/Classes/MuAppDelegate.m +++ b/platform/ios/Classes/MuAppDelegate.m @@ -1,8 +1,12 @@ #include "common.h" +#include "mupdf/fitz.h" #import "MuAppDelegate.h" @implementation MuAppDelegate +{ + BOOL _isInBackground; +} - (BOOL) application: (UIApplication*)application didFinishLaunchingWithOptions: (NSDictionary*)launchOptions { @@ -10,8 +14,7 @@ queue = dispatch_queue_create("com.artifex.mupdf.queue", NULL); - // use at most 128M for resource cache - ctx = fz_new_context(NULL, NULL, 128<<20); + ctx = fz_new_context(NULL, NULL, ResourceCacheMaxSize); fz_register_document_handlers(ctx); screenScale = [[UIScreen mainScreen] scale]; @@ -57,11 +60,13 @@ { printf("applicationDidEnterBackground!\n"); [[NSUserDefaults standardUserDefaults] synchronize]; + _isInBackground = YES; } - (void)applicationWillEnterForeground:(UIApplication *)application { printf("applicationWillEnterForeground!\n"); + _isInBackground = NO; } - (void)applicationDidBecomeActive:(UIApplication *)application @@ -77,7 +82,9 @@ - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { - printf("applicationDidReceiveMemoryWarning\n"); + NSLog(@"applicationDidReceiveMemoryWarning"); + int success = fz_shrink_store(ctx, _isInBackground ? 0 : 50); + NSLog(@"fz_shrink_store: success = %d", success); } - (void) dealloc diff --git a/source/fitz/store.c b/source/fitz/store.c index 609f10dc..61561862 100644 --- a/source/fitz/store.c +++ b/source/fitz/store.c @@ -636,3 +636,41 @@ int fz_store_scavenge(fz_context *ctx, unsigned int size, int *phase) #endif return 0; } + +int +fz_shrink_store(fz_context *ctx, unsigned int percent) +{ + int success; + fz_store *store; + + if (ctx == NULL) + return 0; + + if (percent >= 100) + return 1; + + store = ctx->store; + if (store == NULL) + return 0; + +#ifdef DEBUG_SCAVENGING + fprintf(stderr, "fz_shrink_store: %d\n", store->size/(1024*1024)); +#endif + fz_lock(ctx, FZ_LOCK_ALLOC); + + unsigned int maxSize = (unsigned int)(((uint64_t)store->size * percent) / 100); + if (store->size > maxSize) + { + unsigned int toFree = store->size - maxSize; + scavenge(ctx, store->size - maxSize); + } + + success = (store->size <= maxSize) ? 1 : 0; + fz_unlock(ctx, FZ_LOCK_ALLOC); +#ifdef DEBUG_SCAVENGING + fprintf(stderr, "fz_shrink_store after: %d\n", store->size/(1024*1024)); +#endif + + return success; +} + |