Skip to content

Commit 603357c

Browse files
falhassenglide-copybara-robot
authored andcommitted
Fix deadlock in SingleRequest.clear by using the Engine as the requestLock.
This occurs because CallResourceReady only acquires 2/3 of the necessary locks to perform its runnable (SingleRequest.requestLock & EngineJob.this), and is missing the Engine.this lock. In this change, I changed SingleRequest.requestLock to always be Engine.this. Note that change only affects uses of SingleRequest built through RequestBuilder. Users of the public API SingleRequest.obtain are unaffected by this change. If this fix works as intended, we can consider providing a new SingleRequest.obtain API with the requestLock param removed and deprecating the current SingleRequest.obtain. PiperOrigin-RevId: 817821083
1 parent 2f55cce commit 603357c

File tree

5 files changed

+34
-3
lines changed

5 files changed

+34
-3
lines changed

instrumentation/src/androidTest/java/com/bumptech/glide/MultiRequestTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public void thumbnail_onResourceReady_forPrimary_isComplete_whenRequestListenerI
5353
Glide.init(
5454
context,
5555
new GlideBuilder()
56-
.setSourceExecutor(GlideExecutor.newSourceBuilder().setThreadCount(1).build()));
56+
.setSourceExecutor(GlideExecutor.newSourceBuilder().setThreadCount(1).build())
57+
.setFixSingleRequestClearDeadlock(true));
5758

5859
AtomicBoolean isPrimaryRequestComplete = new AtomicBoolean(false);
5960
CountDownLatch countDownLatch = new CountDownLatch(1);

instrumentation/src/androidTest/java/com/bumptech/glide/RequestTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ public void setUp() {
5050
// Some emulators only have a single resize thread, so waiting on a latch will block them
5151
// forever.
5252
Glide.init(
53-
context, new GlideBuilder().setSourceExecutor(GlideExecutor.newUnlimitedSourceExecutor()));
53+
context,
54+
new GlideBuilder()
55+
.setSourceExecutor(GlideExecutor.newUnlimitedSourceExecutor())
56+
.setFixSingleRequestClearDeadlock(true));
5457
}
5558

5659
@Test

library/src/main/java/com/bumptech/glide/GlideBuilder.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,19 @@ public GlideBuilder setDisableHardwareBitmapsOnO(boolean disableHardwareBitmapsO
539539
return this;
540540
}
541541

542+
/**
543+
* Fix single request clear deadlock mentioned in <a
544+
* href="https://github.com/bumptech/glide/issues/5617">#5617</a>.
545+
*
546+
* <p>This flag changes {@link com.bumptech.glide.request.SingleRequest} to lock on underlying
547+
* {@link Engine} to avoid deadlocks in calling code that also requires a lock on the {@link
548+
* Engine}.
549+
*/
550+
public GlideBuilder setFixSingleRequestClearDeadlock(boolean isEnabled) {
551+
glideExperimentsBuilder.update(new FixSingleRequestClearDeadlock(), isEnabled);
552+
return this;
553+
}
554+
542555
void setRequestManagerFactory(@Nullable RequestManagerFactory factory) {
543556
this.requestManagerFactory = factory;
544557
}
@@ -653,4 +666,7 @@ public static final class OverrideGlideThreadPriority implements Experiment {}
653666

654667
/** See {@link #setUseMediaStoreOpenFileApisIfPossible(boolean)}. */
655668
public static final class UseMediaStoreOpenFileApisIfPossible implements Experiment {}
669+
670+
/** See {@link #setFixSingleRequestClearDeadlock(boolean)}. */
671+
public static final class FixSingleRequestClearDeadlock implements Experiment {}
656672
}

library/src/main/java/com/bumptech/glide/RequestBuilder.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import androidx.annotation.NonNull;
1717
import androidx.annotation.Nullable;
1818
import androidx.annotation.RawRes;
19+
import com.bumptech.glide.GlideBuilder.FixSingleRequestClearDeadlock;
1920
import com.bumptech.glide.load.DataSource;
2021
import com.bumptech.glide.load.Transformation;
2122
import com.bumptech.glide.load.engine.DiskCacheStrategy;
@@ -68,6 +69,7 @@ public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBui
6869
private final Class<TranscodeType> transcodeClass;
6970
private final Glide glide;
7071
private final GlideContext glideContext;
72+
private final GlideExperiments experiments;
7173

7274
@NonNull
7375
@SuppressWarnings("unchecked")
@@ -99,6 +101,7 @@ protected RequestBuilder(
99101
this.context = context;
100102
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
101103
this.glideContext = glide.getGlideContext();
104+
this.experiments = glide.getGlideContext().getExperiments();
102105

103106
initRequestListeners(requestManager.getDefaultRequestListeners());
104107
apply(requestManager.getDefaultRequestOptions());
@@ -1166,8 +1169,13 @@ private Request buildRequest(
11661169
@Nullable RequestListener<TranscodeType> targetListener,
11671170
BaseRequestOptions<?> requestOptions,
11681171
Executor callbackExecutor) {
1172+
GlideExperiments experiments = glideContext.getExperiments();
1173+
Object requestLock =
1174+
glideContext.getExperiments().isEnabled(FixSingleRequestClearDeadlock.class)
1175+
? glideContext.getEngine()
1176+
: new Object();
11691177
return buildRequestRecursive(
1170-
/* requestLock= */ new Object(),
1178+
requestLock,
11711179
target,
11721180
targetListener,
11731181
/* parentCoordinator= */ null,

library/test/src/test/java/com/bumptech/glide/request/SingleRequestTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import android.graphics.drawable.Drawable;
2424
import androidx.annotation.NonNull;
2525
import androidx.annotation.Nullable;
26+
import com.bumptech.glide.GlideBuilder.FixSingleRequestClearDeadlock;
2627
import com.bumptech.glide.GlideContext;
2728
import com.bumptech.glide.GlideExperiments;
2829
import com.bumptech.glide.Priority;
@@ -1071,6 +1072,8 @@ static final class SingleRequestBuilder {
10711072
private final Map<Class<?>, Transformation<?>> transformations = new HashMap<>();
10721073

10731074
SingleRequestBuilder() {
1075+
GlideExperiments glideExperiments = mock(GlideExperiments.class);
1076+
when(glideExperiments.isEnabled(FixSingleRequestClearDeadlock.class)).thenReturn(true);
10741077
when(glideContext.getExperiments()).thenReturn(mock(GlideExperiments.class));
10751078
when(requestCoordinator.getRoot()).thenReturn(requestCoordinator);
10761079
when(requestCoordinator.canSetImage(any(Request.class))).thenReturn(true);

0 commit comments

Comments
 (0)