diff --git a/src/main/java/net/minecraftforge/common/util/Lazy.java b/src/main/java/net/minecraftforge/common/util/Lazy.java index fe1c4e96e..58368d65a 100644 --- a/src/main/java/net/minecraftforge/common/util/Lazy.java +++ b/src/main/java/net/minecraftforge/common/util/Lazy.java @@ -78,7 +78,7 @@ public interface Lazy extends Supplier */ final class Concurrent implements Lazy { - private static final Object lock = new Object(); + private volatile Object lock = new Object(); private volatile Supplier supplier; private volatile T instance; @@ -91,14 +91,20 @@ public interface Lazy extends Supplier @Override public final T get() { + // Copy the lock to a local variable to prevent NPEs if the lock field is set to null between the + // null-check and the synchronization + Object localLock = this.lock; if (supplier != null) { - synchronized (lock) + // localLock is not null here because supplier was non-null after we copied the lock and both of them + // are volatile + synchronized (localLock) { if (supplier != null) { instance = supplier.get(); supplier = null; + this.lock = null; } } } diff --git a/src/main/java/net/minecraftforge/common/util/NonNullLazy.java b/src/main/java/net/minecraftforge/common/util/NonNullLazy.java index 23a652703..cbed18822 100644 --- a/src/main/java/net/minecraftforge/common/util/NonNullLazy.java +++ b/src/main/java/net/minecraftforge/common/util/NonNullLazy.java @@ -20,8 +20,7 @@ package net.minecraftforge.common.util; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.function.Supplier; +import java.util.Objects; /** * Proxy object for a value that is calculated on first access. @@ -36,7 +35,8 @@ public interface NonNullLazy extends NonNullSupplier */ static NonNullLazy of(@Nonnull NonNullSupplier supplier) { - return new NonNullLazy.Fast<>(supplier); + Lazy lazy = Lazy.of(supplier::get); + return () -> Objects.requireNonNull(lazy.get()); } /** @@ -45,65 +45,7 @@ public interface NonNullLazy extends NonNullSupplier */ static NonNullLazy concurrentOf(@Nonnull NonNullSupplier supplier) { - return new NonNullLazy.Concurrent<>(supplier); - } - - /** - * Non-thread-safe implementation. - */ - final class Fast implements NonNullLazy - { - private NonNullSupplier supplier; - private T instance; - - private Fast(NonNullSupplier supplier) - { - this.supplier = supplier; - } - - @Nonnull - @Override - public final T get() - { - if (supplier != null) - { - instance = supplier.get(); - supplier = null; - } - return instance; - } - } - - /** - * Thread-safe implementation. - */ - final class Concurrent implements NonNullLazy - { - private static final Object lock = new Object(); - private volatile NonNullSupplier supplier; - private volatile T instance; - - private Concurrent(NonNullSupplier supplier) - { - this.supplier = supplier; - } - - @Nonnull - @Override - public final T get() - { - if (supplier != null) - { - synchronized (lock) - { - if (supplier != null) - { - instance = supplier.get(); - supplier = null; - } - } - } - return instance; - } + Lazy lazy = Lazy.concurrentOf(supplier::get); + return () -> Objects.requireNonNull(lazy.get()); } }