*/
package org.opendaylight.controller.cluster.databroker.actors.dds;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
-import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import java.util.function.Function;
+import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
import org.opendaylight.yangtools.concepts.Identifiable;
private static final AtomicReferenceFieldUpdater<AbstractClientHandle, State> STATE_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(AbstractClientHandle.class, State.class, "state");
- private final TransactionIdentifier transactionId;
- private final AbstractClientHistory parent;
+ private final @NonNull TransactionIdentifier transactionId;
+ private final @NonNull AbstractClientHistory parent;
private volatile State<T> state = new State<>();
// Hidden to prevent outside instantiation
AbstractClientHandle(final AbstractClientHistory parent, final TransactionIdentifier transactionId) {
- this.transactionId = Preconditions.checkNotNull(transactionId);
- this.parent = Preconditions.checkNotNull(parent);
+ this.transactionId = requireNonNull(transactionId);
+ this.parent = requireNonNull(parent);
}
@Override
+ // Non-final for mocking
public TransactionIdentifier getIdentifier() {
return transactionId;
}
*
* @return True if this transaction became closed during this call
*/
+ // Non-final for mocking
public boolean abort() {
if (commonAbort()) {
parent.onTransactionAbort(this);
}
private boolean commonAbort() {
- final Collection<T> toClose = ensureClosed();
+ final Map<Long, T> toClose = ensureClosed();
if (toClose == null) {
return false;
}
- toClose.forEach(AbstractProxyTransaction::abort);
+ toClose.values().forEach(AbstractProxyTransaction::abort);
+ parent.onTransactionShardsBound(transactionId, toClose.keySet());
return true;
}
* Make sure this snapshot is closed. If it became closed as the effect of this call, return a collection of
* {@link AbstractProxyTransaction} handles which need to be closed, too.
*
- * @return null if this snapshot has already been closed, otherwise a collection of proxies, which need to be
+ * @return null if this snapshot has already been closed, otherwise a State with of proxies, which need to be
* closed, too.
*/
- final @Nullable Collection<T> ensureClosed() {
- @SuppressWarnings("unchecked")
- final State<T> local = STATE_UPDATER.getAndSet(this, null);
- return local == null ? null : local.values();
+ final @Nullable Map<Long, T> ensureClosed() {
+ // volatile read and a conditional CAS. This ends up being better in the typical case when we are invoked more
+ // than once (see ClientBackedTransaction) than performing a STATE_UPDATER.getAndSet().
+ final State<T> local = state;
+ return local != null && STATE_UPDATER.compareAndSet(this, local, null) ? local : null;
}
- final T ensureProxy(final YangInstanceIdentifier path, final Function<Long, T> createProxy) {
- final Map<Long, T> local = getState();
+ final T ensureProxy(final YangInstanceIdentifier path) {
+ final State<T> local = getState();
final Long shard = parent.resolveShardForPath(path);
- return local.computeIfAbsent(shard, createProxy);
+ return local.computeIfAbsent(shard, this::createProxy);
}
final AbstractClientHistory parent() {
return parent;
}
+ abstract @NonNull T createProxy(@NonNull Long shard);
+
private State<T> getState() {
final State<T> local = state;
- Preconditions.checkState(local != null, "Transaction %s is closed", transactionId);
+ checkState(local != null, "Transaction %s is closed", transactionId);
return local;
}