|
import java.util.Optional; |
|
import java.util.concurrent.*; |
|
import java.util.concurrent.atomic.AtomicReference; |
|
import java.util.function.Supplier; |
|
|
|
/** |
|
* This is a container for shared instance allowing monitoring of sharing and safe manipulation. |
|
* |
|
* A new instance is provided by the supplier when the container is created. Every execution of {@link Supplier#get()} must return new unique instance. |
|
* The instance is provided to the borrowers by using {@link #share()} method. The number of borrowers is unlimited, |
|
* but every execution of {@link #share()} method must be eventually followed by the execution of {@link #unshare(T)} method |
|
* when the instance is no longer used by a borrower. After execution of {@link #unshare(T)} method the borrower must not |
|
* manipulate with the instance and should not retain the reference on it. |
|
* The instance is considered unshared when the number of execution of {@link #share()} and {@link #unshare(T)} methods is equal. |
|
* |
|
* The instance can be taken (removed) when is no longer shared and replaced with the new one by using {@link #takeSafely()}. |
|
* The same supplier is used for obtaining the new instance. If this method is executed and the instance is still shared |
|
* then it will wait until all borrowers will call {@link #unshare(T)} method or until timeout. |
|
* However the new instance become available immediately and can be shared even when the previous instance is still shared. |
|
*/ |
|
public class SharedInstance<T> { |
|
|
|
private final Supplier<? extends T> supplier; |
|
|
|
private final AtomicReference<ManagedInstance<T>> previous; |
|
|
|
private volatile ManagedInstance<T> current; |
|
|
|
public SharedInstance(Supplier<? extends T> supplier) { |
|
this.supplier = supplier; |
|
this.previous = new AtomicReference<>(); |
|
this.current = new ManagedInstance<>(supplier.get()); |
|
} |
|
|
|
public T share() { |
|
Optional<T> instOpt = current.get(); |
|
if (!instOpt.isPresent()) { |
|
synchronized (this) { |
|
instOpt = current.get(); |
|
} |
|
if (!instOpt.isPresent()) { |
|
throw new IllegalStateException("The shared instance is in the corrupted state because of illegal execution of unshare method"); |
|
} |
|
} |
|
|
|
return instOpt.get(); |
|
} |
|
|
|
public boolean unshare(T instance) { |
|
ManagedInstance<T> pObj = null; |
|
ManagedInstance<T> prev = previous.get(); |
|
if ((prev != null) && prev.contains(instance)) { |
|
pObj = prev; |
|
} else if (current.contains(instance)) { |
|
pObj = current; |
|
} |
|
if (pObj != null) { |
|
pObj.release(); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
public synchronized Future<T> takeSafely() { |
|
previous.set(current); |
|
current = new ManagedInstance<>(supplier.get()); |
|
ManagedInstance<T> prev = previous.get(); |
|
return new FutureTask<T>(() -> prev.closeAndGetAfterAllReleased()) { |
|
@Override protected void done() { |
|
previous.compareAndSet(prev, null); |
|
} |
|
}; |
|
} |
|
|
|
private static class ManagedInstance<T> { |
|
|
|
private final T instance; |
|
|
|
private final Phaser phaser; |
|
|
|
public ManagedInstance(T instance) { |
|
this.instance = instance; |
|
this.phaser = new Phaser(1); |
|
} |
|
|
|
public Optional<T> get() { |
|
if (phaser.register() != 0) { |
|
return Optional.empty(); |
|
} |
|
|
|
return Optional.of(instance); |
|
} |
|
|
|
public void release() { |
|
phaser.arrive(); |
|
} |
|
|
|
public T closeAndGetAfterAllReleased() { |
|
phaser.arriveAndAwaitAdvance(); |
|
return instance; |
|
} |
|
|
|
public boolean contains(T instance) { |
|
return (this.instance == instance); |
|
} |
|
} |
|
} |