From d427cbd33206cbcc88ac4a0f0c29ef831d4bb5b5 Mon Sep 17 00:00:00 2001
From: judds <judds@google.com>
Date: Mon, 11 Dec 2017 12:00:05 -0800
Subject: [PATCH] Fix a race causing an NPE when obtaining load paths for
 resources.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178651500
---
 .../java/com/bumptech/glide/Registry.java     |  6 ++-
 .../glide/provider/LoadPathCache.java         | 48 ++++++++++++++-----
 2 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/library/src/main/java/com/bumptech/glide/Registry.java b/library/src/main/java/com/bumptech/glide/Registry.java
index c2d589552b..d531f09713 100644
--- a/library/src/main/java/com/bumptech/glide/Registry.java
+++ b/library/src/main/java/com/bumptech/glide/Registry.java
@@ -1,5 +1,6 @@
 package com.bumptech.glide;
 
+import android.support.annotation.Nullable;
 import android.support.v4.util.Pools.Pool;
 import com.bumptech.glide.load.Encoder;
 import com.bumptech.glide.load.ImageHeaderParser;
@@ -446,11 +447,14 @@ public <Model, Data> Registry replace(
     return this;
   }
 
+  @Nullable
   public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
       Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
     LoadPath<Data, TResource, Transcode> result =
         loadPathCache.get(dataClass, resourceClass, transcodeClass);
-    if (result == null && !loadPathCache.contains(dataClass, resourceClass, transcodeClass)) {
+    if (loadPathCache.isEmptyLoadPath(result)) {
+      return null;
+    } else if (result == null) {
       List<DecodePath<Data, TResource, Transcode>> decodePaths =
           getDecodePaths(dataClass, resourceClass, transcodeClass);
       // It's possible there is no way to decode or transcode to the desired types from a given
diff --git a/library/src/main/java/com/bumptech/glide/provider/LoadPathCache.java b/library/src/main/java/com/bumptech/glide/provider/LoadPathCache.java
index 6cc5d96301..d4b24a8919 100644
--- a/library/src/main/java/com/bumptech/glide/provider/LoadPathCache.java
+++ b/library/src/main/java/com/bumptech/glide/provider/LoadPathCache.java
@@ -2,8 +2,11 @@
 
 import android.support.annotation.Nullable;
 import android.support.v4.util.ArrayMap;
+import com.bumptech.glide.load.engine.DecodePath;
 import com.bumptech.glide.load.engine.LoadPath;
+import com.bumptech.glide.load.resource.transcode.UnitTranscoder;
 import com.bumptech.glide.util.MultiClassKey;
+import java.util.Collections;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -11,19 +14,38 @@
  * {@link com.bumptech.glide.load.engine.LoadPath}s capable of decoding with the requested types.
  */
 public class LoadPathCache {
+  private static final LoadPath<?, ?, ?> NO_PATHS_SIGNAL =
+      new LoadPath<>(
+          Object.class,
+          Object.class,
+          Object.class,
+          Collections.singletonList(
+              new DecodePath<>(
+                  Object.class,
+                  Object.class,
+                  Object.class,
+                  Collections.emptyList(),
+                  new UnitTranscoder<>(),
+                  /*listPool=*/ null)),
+          /*listPool=*/ null);
+
   private final ArrayMap<MultiClassKey, LoadPath<?, ?, ?>> cache = new ArrayMap<>();
   private final AtomicReference<MultiClassKey> keyRef = new AtomicReference<>();
 
-  public boolean contains(Class<?> dataClass, Class<?> resourceClass, Class<?> transcodeClass) {
-    MultiClassKey key = getKey(dataClass, resourceClass, transcodeClass);
-    boolean result;
-    synchronized (cache) {
-      result = cache.containsKey(key);
-    }
-    keyRef.set(key);
-    return result;
+  /**
+   * Returns {@code} true if the given {@link LoadPath} is the signal object returned from
+   * {@link #get(Class, Class, Class)} that indicates that we've previously found that there are
+   * no available paths to load the requested resources and {@code false} otherwise.
+   */
+  public boolean isEmptyLoadPath(@Nullable LoadPath<?, ?, ?> path) {
+    return NO_PATHS_SIGNAL.equals(path);
   }
 
+  /**
+   * May return {@link #NO_PATHS_SIGNAL} to indicate that we've previously found that there are 0
+   * available load paths for the requested types. Callers must check using
+   * {@link #isEmptyLoadPath(LoadPath)} before using any load path returned by this method.
+   */
   @SuppressWarnings("unchecked")
   @Nullable
   public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> get(
@@ -38,10 +60,14 @@ public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> get(
     return (LoadPath<Data, TResource, Transcode>) result;
   }
 
-  public void put(Class<?> dataClass, Class<?> resourceClass, Class<?> transcodeClass,
-      LoadPath<?, ?, ?> loadPath) {
+  public void put(
+      Class<?> dataClass, Class<?> resourceClass,
+      Class<?> transcodeClass,
+      @Nullable LoadPath<?, ?, ?> loadPath) {
     synchronized (cache) {
-      cache.put(new MultiClassKey(dataClass, resourceClass, transcodeClass), loadPath);
+      cache.put(
+          new MultiClassKey(dataClass, resourceClass, transcodeClass),
+          loadPath != null ? loadPath : NO_PATHS_SIGNAL);
     }
   }