Bug 878577 - Part 1: Setup hard limit for DiscardTracker. r=seth

This commit is contained in:
Shih-Chiang Chien 2013-12-16 19:31:00 +08:00
parent 739dce166a
commit 0a3d019d9c
5 changed files with 66 additions and 26 deletions

View File

@ -305,6 +305,8 @@ pref("image.mem.decodeondraw", true);
pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
pref("image.mem.min_discard_timeout_ms", 86400000); /* 24h, we rely on the out of memory hook */
pref("image.mem.max_decoded_image_kb", 30000); /* 30MB seems reasonable */
// 65MB seems reasonable and layout/reftests/bugs/370629-1.html requires more than 62MB
pref("image.mem.hard_limit_decoded_image_kb", 66560);
pref("image.onload.decode.limit", 24); /* don't decode more than 24 images eagerly */
// XXX this isn't a good check for "are touch events supported", but

View File

@ -19,9 +19,10 @@ static const char* sDiscardTimeoutPref = "image.mem.min_discard_timeout_ms";
/* static */ bool DiscardTracker::sInitialized = false;
/* static */ bool DiscardTracker::sTimerOn = false;
/* static */ Atomic<bool> DiscardTracker::sDiscardRunnablePending(false);
/* static */ int64_t DiscardTracker::sCurrentDecodedImageBytes = 0;
/* static */ uint64_t DiscardTracker::sCurrentDecodedImageBytes = 0;
/* static */ uint32_t DiscardTracker::sMinDiscardTimeoutMs = 10000;
/* static */ uint32_t DiscardTracker::sMaxDecodedImageKB = 42 * 1024;
/* static */ uint32_t DiscardTracker::sHardLimitDecodedImageKB = 0;
/* static */ PRLock * DiscardTracker::sAllocationLock = nullptr;
/* static */ mozilla::Mutex* DiscardTracker::sNodeListMutex = nullptr;
/* static */ Atomic<bool> DiscardTracker::sShutdown(false);
@ -138,21 +139,39 @@ DiscardTracker::DiscardAll()
DisableTimer();
}
void
DiscardTracker::InformAllocation(int64_t bytes)
/* static */ bool
DiscardTracker::TryAllocation(uint64_t aBytes)
{
MOZ_ASSERT(sInitialized);
PR_Lock(sAllocationLock);
bool enoughSpace =
!sHardLimitDecodedImageKB ||
(sHardLimitDecodedImageKB * 1024) - sCurrentDecodedImageBytes >= aBytes;
if (enoughSpace) {
sCurrentDecodedImageBytes += aBytes;
}
PR_Unlock(sAllocationLock);
// If we're using too much memory for decoded images, MaybeDiscardSoon will
// enqueue a callback to discard some images.
MaybeDiscardSoon();
return enoughSpace;
}
/* static */ void
DiscardTracker::InformDeallocation(uint64_t aBytes)
{
// This function is called back e.g. from RasterImage::Discard(); be careful!
MOZ_ASSERT(sInitialized);
PR_Lock(sAllocationLock);
sCurrentDecodedImageBytes += bytes;
MOZ_ASSERT(sCurrentDecodedImageBytes >= 0);
MOZ_ASSERT(aBytes <= sCurrentDecodedImageBytes);
sCurrentDecodedImageBytes -= aBytes;
PR_Unlock(sAllocationLock);
// If we're using too much memory for decoded images, MaybeDiscardSoon will
// enqueue a callback to discard some images.
MaybeDiscardSoon();
}
/**
@ -169,6 +188,9 @@ DiscardTracker::Initialize()
"image.mem.max_decoded_image_kb",
50 * 1024);
Preferences::AddUintVarCache(&sHardLimitDecodedImageKB,
"image.mem.hard_limit_decoded_image_kb",
0);
// Create the timer.
sTimer = do_CreateInstance("@mozilla.org/timer;1");
@ -278,7 +300,7 @@ DiscardTracker::DiscardNow()
sCurrentDecodedImageBytes > sMaxDecodedImageKB * 1024) {
// Discarding the image should cause sCurrentDecodedImageBytes to
// decrease via a call to InformAllocation().
// decrease via a call to InformDeallocation().
node->img->Discard();
// Careful: Discarding may have caused the node to have been removed

View File

@ -82,12 +82,20 @@ class DiscardTracker
static void DiscardAll();
/**
* Inform the discard tracker that we've allocated or deallocated some
* memory for a decoded image. We use this to determine when we've
* allocated too much memory and should discard some images. This function
* can be called from any thread and is thread-safe.
* Inform the discard tracker that we are going to allocate some memory
* for a decoded image. We use this to determine when we've allocated
* too much memory and should discard some images. This function can be
* called from any thread and is thread-safe. If this function succeeds, the
* caller is now responsible for ensuring that InformDeallocation is called.
*/
static void InformAllocation(int64_t bytes);
static bool TryAllocation(uint64_t aBytes);
/**
* Inform the discard tracker that we've deallocated some memory for a
* decoded image. This function can be called from any thread and is
* thread-safe.
*/
static void InformDeallocation(uint64_t aBytes);
private:
/**
@ -116,9 +124,10 @@ class DiscardTracker
static bool sInitialized;
static bool sTimerOn;
static mozilla::Atomic<bool> sDiscardRunnablePending;
static int64_t sCurrentDecodedImageBytes;
static uint64_t sCurrentDecodedImageBytes;
static uint32_t sMinDiscardTimeoutMs;
static uint32_t sMaxDecodedImageKB;
static uint32_t sHardLimitDecodedImageKB;
// Lock for safegarding the 64-bit sCurrentDecodedImageBytes
static PRLock *sAllocationLock;
static mozilla::Mutex* sNodeListMutex;

View File

@ -170,7 +170,7 @@ imgFrame::~imgFrame()
mPalettedImageData = nullptr;
if (mInformedDiscardTracker) {
DiscardTracker::InformAllocation(-4 * mSize.height * mSize.width);
DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width);
}
}
@ -203,6 +203,11 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
NS_WARNING("moz_malloc for paletted image data should succeed");
NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
} else {
// Inform the discard tracker that we are going to allocate some memory.
if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) {
NS_WARNING("Exceed the hard limit of decode image size");
return NS_ERROR_OUT_OF_MEMORY;
}
// For Windows, we must create the device surface first (if we're
// going to) so that the image surface can wrap it. Can't be done
// the other way around.
@ -238,9 +243,15 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
} else if (!mImageSurface->CairoStatus()) {
NS_WARNING("gfxImageSurface should have good CairoStatus");
}
// Image surface allocation is failed, need to return
// the booked buffer size.
DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
return NS_ERROR_OUT_OF_MEMORY;
}
mInformedDiscardTracker = true;
#ifdef XP_MACOSX
if (!ShouldUseImageSurfaces()) {
mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
@ -248,14 +259,6 @@ nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
#endif
}
// Inform the discard tracker that we've allocated some memory, but only if
// we're not a paletted image (paletted images are not usually large and are
// used only for animated frames, which we don't discard).
if (!mPalettedImageData) {
DiscardTracker::InformAllocation(4 * mSize.width * mSize.height);
mInformedDiscardTracker = true;
}
return NS_OK;
}
@ -314,7 +317,7 @@ nsresult imgFrame::Optimize()
// We just dumped most of our allocated memory, so tell the discard
// tracker that we're not using any at all.
if (mInformedDiscardTracker) {
DiscardTracker::InformAllocation(-4 * mSize.width * mSize.height);
DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
mInformedDiscardTracker = false;
}

View File

@ -3918,6 +3918,10 @@ pref("image.mem.max_ms_before_yield", 5);
// might keep around more than this, but we'll try to get down to this value).
pref("image.mem.max_decoded_image_kb", 51200);
// Hard limit for the amount of decoded image data, 0 means we don't have the
// hard limit for it.
pref("image.mem.hard_limit_decoded_image_kb", 0);
// Minimum timeout for expiring unused images from the surface cache, in
// milliseconds. This controls how long we store cached temporary surfaces.
pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60ms