Fix (NonNull)Lazy.Concurrent using a global lock (#7403)
* Fix Lazy.Concurrent using a global lock, thus preventing multiple threads from resolving independent Lazy's at the same time * Use Lazy with an added null check to implement NonNullLazy
This commit is contained in:
parent
53eedb0f10
commit
06fe9ccda0
2 changed files with 13 additions and 65 deletions
|
@ -78,7 +78,7 @@ public interface Lazy<T> extends Supplier<T>
|
|||
*/
|
||||
final class Concurrent<T> implements Lazy<T>
|
||||
{
|
||||
private static final Object lock = new Object();
|
||||
private volatile Object lock = new Object();
|
||||
private volatile Supplier<T> supplier;
|
||||
private volatile T instance;
|
||||
|
||||
|
@ -91,14 +91,20 @@ public interface Lazy<T> extends Supplier<T>
|
|||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T> extends NonNullSupplier<T>
|
|||
*/
|
||||
static <T> NonNullLazy<T> of(@Nonnull NonNullSupplier<T> supplier)
|
||||
{
|
||||
return new NonNullLazy.Fast<>(supplier);
|
||||
Lazy<T> lazy = Lazy.of(supplier::get);
|
||||
return () -> Objects.requireNonNull(lazy.get());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,65 +45,7 @@ public interface NonNullLazy<T> extends NonNullSupplier<T>
|
|||
*/
|
||||
static <T> NonNullLazy<T> concurrentOf(@Nonnull NonNullSupplier<T> supplier)
|
||||
{
|
||||
return new NonNullLazy.Concurrent<>(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-thread-safe implementation.
|
||||
*/
|
||||
final class Fast<T> implements NonNullLazy<T>
|
||||
{
|
||||
private NonNullSupplier<T> supplier;
|
||||
private T instance;
|
||||
|
||||
private Fast(NonNullSupplier<T> 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<T> implements NonNullLazy<T>
|
||||
{
|
||||
private static final Object lock = new Object();
|
||||
private volatile NonNullSupplier<T> supplier;
|
||||
private volatile T instance;
|
||||
|
||||
private Concurrent(NonNullSupplier<T> 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<T> lazy = Lazy.concurrentOf(supplier::get);
|
||||
return () -> Objects.requireNonNull(lazy.get());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue