Issue #2218 - Part 2: Make SurfaceCache free ImageSurfaceCache objects outside of the lock

Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1389479
This commit is contained in:
FranklinDM 2023-04-30 17:48:07 +08:00 • committed by roytam1
commit 93644fd33e

View file

@ -251,12 +251,14 @@ public:
mSurfaces.Put(aSurface->GetSurfaceKey(), aSurface); mSurfaces.Put(aSurface->GetSurfaceKey(), aSurface);
} }
void Remove(NotNull<CachedSurface*> aSurface) already_AddRefed<CachedSurface> Remove(NotNull<CachedSurface*> aSurface)
{ {
MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()), MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()),
"Should not be removing a surface we don't have"); "Should not be removing a surface we don't have");
mSurfaces.Remove(aSurface->GetSurfaceKey()); RefPtr<CachedSurface> surface;
mSurfaces.Remove(aSurface->GetSurfaceKey(), getter_AddRefs(surface));
return surface.forget();
} }
already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey) already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
@ -507,10 +509,14 @@ public:
} }
StopTracking(aSurface, aAutoLock); StopTracking(aSurface, aAutoLock);
cache->Remove(aSurface);
// Remove the per-image cache if it's unneeded now. (Keep it if the image is // Individual surfaces must be freed outside the lock.
// locked, since the per-image cache is where we store that state.) mCachedSurfacesDiscard.AppendElement(cache->Remove(aSurface));
// Remove the per-image cache if it's unneeded now. Keep it if the image is
// locked, since the per-image cache is where we store that state. Note that
// we don't push it into mImageCachesDiscard because all of its surfaces
// have been removed, so it is safe to free while holding the lock.
if (cache->IsEmpty() && !cache->IsLocked()) { if (cache->IsEmpty() && !cache->IsLocked()) {
mImageCaches.Remove(imageKey); mImageCaches.Remove(imageKey);
} }
@ -719,11 +725,12 @@ public:
DoUnlockSurfaces(WrapNotNull(cache), aAutoLock); DoUnlockSurfaces(WrapNotNull(cache), aAutoLock);
} }
void RemoveImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock) already_AddRefed<ImageSurfaceCache>
RemoveImage(const ImageKey aImageKey, const StaticMutexAutoLock& aAutoLock)
{ {
RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey); RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
if (!cache) { if (!cache) {
return; // No cached surfaces for this image, so nothing to do. return nullptr; // No cached surfaces for this image, so nothing to do.
} }
// Discard all of the cached surfaces for this image. // Discard all of the cached surfaces for this image.
@ -738,6 +745,10 @@ public:
// The per-image cache isn't needed anymore, so remove it as well. // The per-image cache isn't needed anymore, so remove it as well.
// This implicitly unlocks the image if it was locked. // This implicitly unlocks the image if it was locked.
mImageCaches.Remove(aImageKey); mImageCaches.Remove(aImageKey);
// Since we did not actually remove any of the surfaces from the cache
// itself, only stopped tracking them, we should free it outside the lock.
return cache.forget();
} }
void DiscardAll(const StaticMutexAutoLock& aAutoLock) void DiscardAll(const StaticMutexAutoLock& aAutoLock)
@ -776,6 +787,13 @@ public:
} }
} }
void TakeDiscard(nsTArray<RefPtr<CachedSurface>>& aDiscard,
const StaticMutexAutoLock& aAutoLock)
{
MOZ_ASSERT(aDiscard.IsEmpty());
aDiscard = Move(mCachedSurfacesDiscard);
}
void LockSurface(NotNull<CachedSurface*> aSurface, void LockSurface(NotNull<CachedSurface*> aSurface,
const StaticMutexAutoLock& aAutoLock) const StaticMutexAutoLock& aAutoLock)
{ {
@ -922,10 +940,12 @@ private:
Remove(WrapNotNull(surface), aAutoLock); Remove(WrapNotNull(surface), aAutoLock);
} }
struct SurfaceTracker : public ExpirationTrackerImpl<CachedSurface, 2, class SurfaceTracker final :
StaticMutex, public ExpirationTrackerImpl<CachedSurface, 2,
StaticMutexAutoLock> StaticMutex,
StaticMutexAutoLock>
{ {
public:
explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS) explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS)
: ExpirationTrackerImpl<CachedSurface, 2, : ExpirationTrackerImpl<CachedSurface, 2,
StaticMutex, StaticMutexAutoLock>( StaticMutex, StaticMutexAutoLock>(
@ -939,23 +959,40 @@ private:
sInstance->Remove(WrapNotNull(aSurface), aAutoLock); sInstance->Remove(WrapNotNull(aSurface), aAutoLock);
} }
void NotifyHandlerEndLocked(const StaticMutexAutoLock& aAutoLock) override
{
sInstance->TakeDiscard(mDiscard, aAutoLock);
}
void NotifyHandlerEnd() override
{
nsTArray<RefPtr<CachedSurface>> discard(Move(mDiscard));
}
StaticMutex& GetMutex() override StaticMutex& GetMutex() override
{ {
return sInstanceMutex; return sInstanceMutex;
} }
nsTArray<RefPtr<CachedSurface>> mDiscard;
}; };
struct MemoryPressureObserver : public nsIObserver class MemoryPressureObserver final : public nsIObserver
{ {
public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_IMETHOD Observe(nsISupports*, NS_IMETHOD Observe(nsISupports*,
const char* aTopic, const char* aTopic,
const char16_t*) override const char16_t*) override
{ {
StaticMutexAutoLock lock(sInstanceMutex); nsTArray<RefPtr<CachedSurface>> discard;
if (sInstance && strcmp(aTopic, "memory-pressure") == 0) { {
sInstance->DiscardForMemoryPressure(lock); StaticMutexAutoLock lock(sInstanceMutex);
if (sInstance && strcmp(aTopic, "memory-pressure") == 0) {
sInstance->DiscardForMemoryPressure(lock);
sInstance->TakeDiscard(discard, lock);
}
} }
return NS_OK; return NS_OK;
} }
@ -967,6 +1004,7 @@ private:
nsTArray<CostEntry> mCosts; nsTArray<CostEntry> mCosts;
nsRefPtrHashtable<nsPtrHashKey<Image>, nsRefPtrHashtable<nsPtrHashKey<Image>,
ImageSurfaceCache> mImageCaches; ImageSurfaceCache> mImageCaches;
nsTArray<RefPtr<CachedSurface>> mCachedSurfacesDiscard;
SurfaceTracker mExpirationTracker; SurfaceTracker mExpirationTracker;
RefPtr<MemoryPressureObserver> mMemoryPressureObserver; RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
nsTArray<RefPtr<image::Image>> mReleasingImagesOnMainThread; nsTArray<RefPtr<image::Image>> mReleasingImagesOnMainThread;
@ -1043,45 +1081,72 @@ SurfaceCache::Initialize()
/* static */ void /* static */ void
SurfaceCache::Shutdown() SurfaceCache::Shutdown()
{ {
StaticMutexAutoLock lock(sInstanceMutex); RefPtr<SurfaceCacheImpl> cache;
MOZ_ASSERT(NS_IsMainThread()); {
MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?"); StaticMutexAutoLock lock(sInstanceMutex);
sInstance = nullptr; MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
cache = sInstance.forget();
}
} }
/* static */ LookupResult /* static */ LookupResult
SurfaceCache::Lookup(const ImageKey aImageKey, SurfaceCache::Lookup(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey) const SurfaceKey& aSurfaceKey)
{ {
StaticMutexAutoLock lock(sInstanceMutex); nsTArray<RefPtr<CachedSurface>> discard;
if (!sInstance) { LookupResult rv(MatchType::NOT_FOUND);
return LookupResult(MatchType::NOT_FOUND);
{
StaticMutexAutoLock lock(sInstanceMutex);
if (!sInstance) {
return rv;
}
rv = sInstance->Lookup(aImageKey, aSurfaceKey, lock);
sInstance->TakeDiscard(discard, lock);
} }
return sInstance->Lookup(aImageKey, aSurfaceKey, lock); return rv;
} }
/* static */ LookupResult /* static */ LookupResult
SurfaceCache::LookupBestMatch(const ImageKey aImageKey, SurfaceCache::LookupBestMatch(const ImageKey aImageKey,
const SurfaceKey& aSurfaceKey) const SurfaceKey& aSurfaceKey)
{ {
StaticMutexAutoLock lock(sInstanceMutex); nsTArray<RefPtr<CachedSurface>> discard;
if (!sInstance) { LookupResult rv(MatchType::NOT_FOUND);
return LookupResult(MatchType::NOT_FOUND);
{
StaticMutexAutoLock lock(sInstanceMutex);
if (!sInstance) {
return rv;
}
rv = sInstance->LookupBestMatch(aImageKey, aSurfaceKey, lock);
sInstance->TakeDiscard(discard, lock);
} }
return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, lock); return rv;
} }
/* static */ InsertOutcome /* static */ InsertOutcome
SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider) SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider)
{ {
StaticMutexAutoLock lock(sInstanceMutex); nsTArray<RefPtr<CachedSurface>> discard;
if (!sInstance) { InsertOutcome rv(InsertOutcome::FAILURE);
return InsertOutcome::FAILURE;
{
StaticMutexAutoLock lock(sInstanceMutex);
if (!sInstance) {
return rv;
}
rv = sInstance->Insert(aProvider, /* aSetAvailable = */ false, lock);
sInstance->TakeDiscard(discard, lock);
} }
return sInstance->Insert(aProvider, /* aSetAvailable = */ false, lock); return rv;
} }
/* static */ bool /* static */ bool
@ -1148,18 +1213,25 @@ SurfaceCache::UnlockEntries(const ImageKey aImageKey)
/* static */ void /* static */ void
SurfaceCache::RemoveImage(const ImageKey aImageKey) SurfaceCache::RemoveImage(const ImageKey aImageKey)
{ {
StaticMutexAutoLock lock(sInstanceMutex); RefPtr<ImageSurfaceCache> discard;
if (sInstance) { {
sInstance->RemoveImage(aImageKey, lock); StaticMutexAutoLock lock(sInstanceMutex);
if (sInstance) {
discard = sInstance->RemoveImage(aImageKey, lock);
}
} }
} }
/* static */ void /* static */ void
SurfaceCache::DiscardAll() SurfaceCache::DiscardAll()
{ {
StaticMutexAutoLock lock(sInstanceMutex); nsTArray<RefPtr<CachedSurface>> discard;
if (sInstance) { {
sInstance->DiscardAll(lock); StaticMutexAutoLock lock(sInstanceMutex);
if (sInstance) {
sInstance->DiscardAll(lock);
sInstance->TakeDiscard(discard, lock);
}
} }
} }