Skip to content

Commit 53438b9

Browse files
committed
Require ResourceListener in the constructor of EngineResource
ResourceListener is supposed to be effectively final in EngineResource. It was only passed into a separate method to reduce the number of pass throughs. Since we're now loading resources on multiple threads, it's no longer trivially safe to call an additional method to set the listener. Specifically there's a race where the EngineJob exposes the Resource to any new requests for the same image before the Engine has a chance to set the ResourceListener on the EngineResource. The locking to fix this is complicated and would require holding multiple locks simultaneously. Instead this cl moves the ResourceListener into the EngineResource constructor and performs the necessary pass throughs to make that happen. Ideally this is safer and more performant than adjusting the locking. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=234844998
1 parent c6ffb04 commit 53438b9

File tree

8 files changed

+151
-127
lines changed

8 files changed

+151
-127
lines changed

library/src/main/java/com/bumptech/glide/load/engine/ActiveResources.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,12 @@ void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
118118
return;
119119
}
120120
EngineResource<?> newResource =
121-
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
122-
newResource.setResourceListener(ref.key, listener);
121+
new EngineResource<>(
122+
ref.resource,
123+
/*isMemoryCacheable=*/ true,
124+
/*isRecyclable=*/ false,
125+
ref.key,
126+
listener);
123127
listener.onResourceReleased(ref.key, newResource);
124128
}
125129
}
@@ -180,9 +184,9 @@ static final class ResourceWeakReference extends WeakReference<EngineResource<?>
180184
super(referent, queue);
181185
this.key = Preconditions.checkNotNull(key);
182186
this.resource =
183-
referent.isCacheable() && isActiveResourceRetentionAllowed
187+
referent.isMemoryCacheable() && isActiveResourceRetentionAllowed
184188
? Preconditions.checkNotNull(referent.getResource()) : null;
185-
isCacheable = referent.isCacheable();
189+
isCacheable = referent.isMemoryCacheable();
186190
}
187191

188192
void reset() {

library/src/main/java/com/bumptech/glide/load/engine/Engine.java

+19-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.bumptech.glide.load.Key;
1212
import com.bumptech.glide.load.Options;
1313
import com.bumptech.glide.load.Transformation;
14+
import com.bumptech.glide.load.engine.EngineResource.ResourceListener;
1415
import com.bumptech.glide.load.engine.cache.DiskCache;
1516
import com.bumptech.glide.load.engine.cache.DiskCacheAdapter;
1617
import com.bumptech.glide.load.engine.cache.MemoryCache;
@@ -102,7 +103,12 @@ public Engine(
102103
if (engineJobFactory == null) {
103104
engineJobFactory =
104105
new EngineJobFactory(
105-
diskCacheExecutor, sourceExecutor, sourceUnlimitedExecutor, animationExecutor, this);
106+
diskCacheExecutor,
107+
sourceExecutor,
108+
sourceUnlimitedExecutor,
109+
animationExecutor,
110+
/*engineJobListener=*/ this,
111+
/*resourceListener=*/ this);
106112
}
107113
this.engineJobFactory = engineJobFactory;
108114

@@ -276,7 +282,8 @@ private EngineResource<?> getEngineResourceFromCache(Key key) {
276282
// Save an object allocation if we've cached an EngineResource (the typical case).
277283
result = (EngineResource<?>) cached;
278284
} else {
279-
result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);
285+
result = new EngineResource<>(
286+
cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
280287
}
281288
return result;
282289
}
@@ -295,9 +302,7 @@ public synchronized void onEngineJobComplete(
295302
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
296303
// A null resource indicates that the load failed, usually due to an exception.
297304
if (resource != null) {
298-
resource.setResourceListener(key, this);
299-
300-
if (resource.isCacheable()) {
305+
if (resource.isMemoryCacheable()) {
301306
activeResources.activate(key, resource);
302307
}
303308
}
@@ -318,7 +323,7 @@ public void onResourceRemoved(@NonNull final Resource<?> resource) {
318323
@Override
319324
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
320325
activeResources.deactivate(cacheKey);
321-
if (resource.isCacheable()) {
326+
if (resource.isMemoryCacheable()) {
322327
cache.put(cacheKey, resource);
323328
} else {
324329
resourceRecycler.recycle(resource);
@@ -456,7 +461,8 @@ static class EngineJobFactory {
456461
@Synthetic final GlideExecutor sourceExecutor;
457462
@Synthetic final GlideExecutor sourceUnlimitedExecutor;
458463
@Synthetic final GlideExecutor animationExecutor;
459-
@Synthetic final EngineJobListener listener;
464+
@Synthetic final EngineJobListener engineJobListener;
465+
@Synthetic final ResourceListener resourceListener;
460466
@Synthetic final Pools.Pool<EngineJob<?>> pool =
461467
FactoryPools.threadSafe(
462468
JOB_POOL_SIZE,
@@ -468,7 +474,8 @@ public EngineJob<?> create() {
468474
sourceExecutor,
469475
sourceUnlimitedExecutor,
470476
animationExecutor,
471-
listener,
477+
engineJobListener,
478+
resourceListener,
472479
pool);
473480
}
474481
});
@@ -478,12 +485,14 @@ public EngineJob<?> create() {
478485
GlideExecutor sourceExecutor,
479486
GlideExecutor sourceUnlimitedExecutor,
480487
GlideExecutor animationExecutor,
481-
EngineJobListener listener) {
488+
EngineJobListener engineJobListener,
489+
ResourceListener resourceListener) {
482490
this.diskCacheExecutor = diskCacheExecutor;
483491
this.sourceExecutor = sourceExecutor;
484492
this.sourceUnlimitedExecutor = sourceUnlimitedExecutor;
485493
this.animationExecutor = animationExecutor;
486-
this.listener = listener;
494+
this.engineJobListener = engineJobListener;
495+
this.resourceListener = resourceListener;
487496
}
488497

489498
@VisibleForTesting

library/src/main/java/com/bumptech/glide/load/engine/EngineJob.java

+19-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.support.v4.util.Pools;
66
import com.bumptech.glide.load.DataSource;
77
import com.bumptech.glide.load.Key;
8+
import com.bumptech.glide.load.engine.EngineResource.ResourceListener;
89
import com.bumptech.glide.load.engine.executor.GlideExecutor;
910
import com.bumptech.glide.request.ResourceCallback;
1011
import com.bumptech.glide.util.Executors;
@@ -31,9 +32,10 @@ class EngineJob<R> implements DecodeJob.Callback<R>,
3132
final ResourceCallbacksAndExecutors cbs = new ResourceCallbacksAndExecutors();
3233

3334
private final StateVerifier stateVerifier = StateVerifier.newInstance();
35+
private final ResourceListener resourceListener;
3436
private final Pools.Pool<EngineJob<?>> pool;
3537
private final EngineResourceFactory engineResourceFactory;
36-
private final EngineJobListener listener;
38+
private final EngineJobListener engineJobListener;
3739
private final GlideExecutor diskCacheExecutor;
3840
private final GlideExecutor sourceExecutor;
3941
private final GlideExecutor sourceUnlimitedExecutor;
@@ -73,14 +75,16 @@ class EngineJob<R> implements DecodeJob.Callback<R>,
7375
GlideExecutor sourceExecutor,
7476
GlideExecutor sourceUnlimitedExecutor,
7577
GlideExecutor animationExecutor,
76-
EngineJobListener listener,
78+
EngineJobListener engineJobListener,
79+
ResourceListener resourceListener,
7780
Pools.Pool<EngineJob<?>> pool) {
7881
this(
7982
diskCacheExecutor,
8083
sourceExecutor,
8184
sourceUnlimitedExecutor,
8285
animationExecutor,
83-
listener,
86+
engineJobListener,
87+
resourceListener,
8488
pool,
8589
DEFAULT_FACTORY);
8690
}
@@ -91,14 +95,16 @@ class EngineJob<R> implements DecodeJob.Callback<R>,
9195
GlideExecutor sourceExecutor,
9296
GlideExecutor sourceUnlimitedExecutor,
9397
GlideExecutor animationExecutor,
94-
EngineJobListener listener,
98+
EngineJobListener engineJobListener,
99+
ResourceListener resourceListener,
95100
Pools.Pool<EngineJob<?>> pool,
96101
EngineResourceFactory engineResourceFactory) {
97102
this.diskCacheExecutor = diskCacheExecutor;
98103
this.sourceExecutor = sourceExecutor;
99104
this.sourceUnlimitedExecutor = sourceUnlimitedExecutor;
100105
this.animationExecutor = animationExecutor;
101-
this.listener = listener;
106+
this.engineJobListener = engineJobListener;
107+
this.resourceListener = resourceListener;
102108
this.pool = pool;
103109
this.engineResourceFactory = engineResourceFactory;
104110
}
@@ -197,7 +203,7 @@ void cancel() {
197203

198204
isCancelled = true;
199205
decodeJob.cancel();
200-
listener.onEngineJobCancelled(this, key);
206+
engineJobListener.onEngineJobCancelled(this, key);
201207
}
202208

203209
// Exposed for testing.
@@ -231,7 +237,7 @@ void notifyCallbacksOfResult() {
231237
} else if (hasResource) {
232238
throw new IllegalStateException("Already have resource");
233239
}
234-
engineResource = engineResourceFactory.build(resource, isCacheable);
240+
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
235241
// Hold on to resource for duration of our callbacks below so we don't recycle it in the
236242
// middle of notifying if it synchronously released by one of the callbacks. Acquire it under
237243
// a lock here so that any newly added callback that executes before the next locked section
@@ -244,7 +250,7 @@ void notifyCallbacksOfResult() {
244250
localResource = engineResource;
245251
}
246252

247-
listener.onEngineJobComplete(this, localKey, localResource);
253+
engineJobListener.onEngineJobComplete(this, localKey, localResource);
248254

249255
for (final ResourceCallbackAndExecutor entry : copy) {
250256
entry.executor.execute(new CallResourceReady(entry.cb));
@@ -347,7 +353,7 @@ void notifyCallbacksOfException() {
347353
incrementPendingCallbacks(copy.size() + 1);
348354
}
349355

350-
listener.onEngineJobComplete(this, localKey, /*resource=*/ null);
356+
engineJobListener.onEngineJobComplete(this, localKey, /*resource=*/ null);
351357

352358
for (ResourceCallbackAndExecutor entry : copy) {
353359
entry.executor.execute(new CallLoadFailed(entry.cb));
@@ -480,8 +486,10 @@ public int hashCode() {
480486

481487
@VisibleForTesting
482488
static class EngineResourceFactory {
483-
public <R> EngineResource<R> build(Resource<R> resource, boolean isMemoryCacheable) {
484-
return new EngineResource<>(resource, isMemoryCacheable, /*isRecyclable=*/ true);
489+
public <R> EngineResource<R> build(
490+
Resource<R> resource, boolean isMemoryCacheable, Key key, ResourceListener listener) {
491+
return new EngineResource<>(
492+
resource, isMemoryCacheable, /*isRecyclable=*/ true, key, listener);
485493
}
486494
}
487495
}

library/src/main/java/com/bumptech/glide/load/engine/EngineResource.java

+14-12
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,38 @@
1111
* @param <Z> The type of data returned by the wrapped {@link Resource}.
1212
*/
1313
class EngineResource<Z> implements Resource<Z> {
14-
private final boolean isCacheable;
14+
private final boolean isMemoryCacheable;
1515
private final boolean isRecyclable;
1616
private final Resource<Z> resource;
17+
private final ResourceListener listener;
18+
private final Key key;
1719

18-
private ResourceListener listener;
19-
private Key key;
2020
private int acquired;
2121
private boolean isRecycled;
2222

2323
interface ResourceListener {
2424
void onResourceReleased(Key key, EngineResource<?> resource);
2525
}
2626

27-
EngineResource(Resource<Z> toWrap, boolean isCacheable, boolean isRecyclable) {
27+
EngineResource(
28+
Resource<Z> toWrap,
29+
boolean isMemoryCacheable,
30+
boolean isRecyclable,
31+
Key key,
32+
ResourceListener listener) {
2833
resource = Preconditions.checkNotNull(toWrap);
29-
this.isCacheable = isCacheable;
34+
this.isMemoryCacheable = isMemoryCacheable;
3035
this.isRecyclable = isRecyclable;
31-
}
32-
33-
synchronized void setResourceListener(Key key, ResourceListener listener) {
3436
this.key = key;
35-
this.listener = listener;
37+
this.listener = Preconditions.checkNotNull(listener);
3638
}
3739

3840
Resource<Z> getResource() {
3941
return resource;
4042
}
4143

42-
boolean isCacheable() {
43-
return isCacheable;
44+
boolean isMemoryCacheable() {
45+
return isMemoryCacheable;
4446
}
4547

4648
@NonNull
@@ -119,7 +121,7 @@ void release() {
119121
@Override
120122
public synchronized String toString() {
121123
return "EngineResource{"
122-
+ "isCacheable=" + isCacheable
124+
+ "isMemoryCacheable=" + isMemoryCacheable
123125
+ ", listener=" + listener
124126
+ ", key=" + key
125127
+ ", acquired=" + acquired

0 commit comments

Comments
 (0)