Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b5adbee1
Unverified
Commit
b5adbee1
authored
May 24, 2022
by
Yang Chao
Committed by
GitHub
May 24, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix an issue that clearing the image cache may cause resource leaks (#104527)
parent
a56c5e51
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
38 additions
and
12 deletions
+38
-12
image_cache.dart
packages/flutter/lib/src/painting/image_cache.dart
+11
-12
image_cache_test.dart
packages/flutter/test/painting/image_cache_test.dart
+27
-0
No files found.
packages/flutter/lib/src/painting/image_cache.dart
View file @
b5adbee1
...
...
@@ -397,16 +397,16 @@ class ImageCache {
if
(!
kReleaseMode
)
{
listenerTask
=
TimelineTask
(
parent:
timelineTask
)..
start
(
'listener'
);
}
//
If we're doing tracing, we need to make sure that we don't try to finish
//
the trace entry multiple times if we get re-entrant calls from a multi-
//
frame provider here.
//
A multi-frame provider may call the listener more than once. We need do make
//
sure that some cleanup works won't run multiple times, such as finishing the
//
tracing task or removing the listeners
bool
listenedOnce
=
false
;
// We shouldn't use the _pendingImages map if the cache is disabled, but we
// will have to listen to the image at least once so we don't leak it in
// the live image tracking.
// If the cache is disabled, this variable will be set.
_PendingImage
?
untrackedP
endingImage
;
final
bool
trackPendingImage
=
maximumSize
>
0
&&
maximumSizeBytes
>
0
;
late
_PendingImage
p
endingImage
;
void
listener
(
ImageInfo
?
info
,
bool
syncCall
)
{
int
?
sizeBytes
;
if
(
info
!=
null
)
{
...
...
@@ -421,14 +421,14 @@ class ImageCache {
_trackLiveImage
(
key
,
result
,
sizeBytes
);
// Only touch if the cache was enabled when resolve was initially called.
if
(
untrackedPendingImage
==
null
)
{
if
(
trackPendingImage
)
{
_touch
(
key
,
image
,
listenerTask
);
}
else
{
image
.
dispose
();
}
final
_PendingImage
?
pendingImage
=
untrackedPendingImage
??
_pendingImages
.
remove
(
key
);
if
(
pendingImage
!=
null
)
{
_pendingImages
.
remove
(
key
);
if
(
!
listenedOnce
)
{
pendingImage
.
removeListener
();
}
if
(!
kReleaseMode
&&
!
listenedOnce
)
{
...
...
@@ -445,10 +445,9 @@ class ImageCache {
}
final
ImageStreamListener
streamListener
=
ImageStreamListener
(
listener
);
if
(
maximumSize
>
0
&&
maximumSizeBytes
>
0
)
{
_pendingImages
[
key
]
=
_PendingImage
(
result
,
streamListener
);
}
else
{
untrackedPendingImage
=
_PendingImage
(
result
,
streamListener
);
pendingImage
=
_PendingImage
(
result
,
streamListener
);
if
(
trackPendingImage
)
{
_pendingImages
[
key
]
=
pendingImage
;
}
// Listener is removed in [_PendingImage.removeListener].
result
.
addListener
(
streamListener
);
...
...
packages/flutter/test/painting/image_cache_test.dart
View file @
b5adbee1
...
...
@@ -332,6 +332,33 @@ void main() {
expect
(
imageCache
.
liveImageCount
,
0
);
});
test
(
'Clearing image cache does not leak live images'
,
()
async
{
imageCache
.
maximumSize
=
1
;
final
ui
.
Image
testImage1
=
await
createTestImage
(
width:
8
,
height:
8
);
final
ui
.
Image
testImage2
=
await
createTestImage
(
width:
10
,
height:
10
);
final
TestImageStreamCompleter
completer1
=
TestImageStreamCompleter
();
final
TestImageStreamCompleter
completer2
=
TestImageStreamCompleter
()..
testSetImage
(
testImage2
);
imageCache
.
putIfAbsent
(
testImage1
,
()
=>
completer1
);
expect
(
imageCache
.
statusForKey
(
testImage1
).
pending
,
true
);
expect
(
imageCache
.
statusForKey
(
testImage1
).
live
,
true
);
imageCache
.
clear
();
expect
(
imageCache
.
statusForKey
(
testImage1
).
pending
,
false
);
expect
(
imageCache
.
statusForKey
(
testImage1
).
live
,
true
);
completer1
.
testSetImage
(
testImage1
);
expect
(
imageCache
.
statusForKey
(
testImage1
).
keepAlive
,
true
);
expect
(
imageCache
.
statusForKey
(
testImage1
).
live
,
false
);
imageCache
.
putIfAbsent
(
testImage2
,
()
=>
completer2
);
expect
(
imageCache
.
statusForKey
(
testImage1
).
tracked
,
false
);
// evicted
expect
(
imageCache
.
statusForKey
(
testImage2
).
tracked
,
true
);
});
test
(
'Evicting a pending image clears the live image by default'
,
()
async
{
final
ui
.
Image
testImage
=
await
createTestImage
(
width:
8
,
height:
8
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment