/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite.internal.processors.cache.distributed.near; import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.IgniteInternalFuture; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTopologyFuture; import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey; import org.apache.ignite.internal.util.GridConcurrentHashSet; import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.lang.GridPlainRunnable; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.CI1; import org.apache.ignite.internal.util.typedef.internal.S; import org.jetbrains.annotations.Nullable; /** * */ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearTxPrepareFutureAdapter { /** * @param cctx Context. * @param tx Transaction. */ protected GridNearOptimisticTxPrepareFutureAdapter(GridCacheSharedContext cctx, GridNearTxLocal tx) { super(cctx, tx); assert tx.optimistic() : tx; } /** {@inheritDoc} */ @Override public final void prepare() { // Obtain the topology version to use. long threadId = Thread.currentThread().getId(); AffinityTopologyVersion topVer = cctx.mvcc().lastExplicitLockTopologyVersion(threadId); // If there is another system transaction in progress, use it's topology version to prevent deadlock. if (topVer == null && tx.system()) { topVer = cctx.tm().lockedTopologyVersion(threadId, tx); if (topVer == null) topVer = tx.topologyVersionSnapshot(); } if (topVer != null) { tx.topologyVersion(topVer); cctx.mvcc().addFuture(this); prepare0(false, true); return; } prepareOnTopology(false, null); } /** * Acquires topology read lock. * * @return Topology ready future. */ protected final GridDhtTopologyFuture topologyReadLock() { return tx.txState().topologyReadLock(cctx, this); } /** * Releases topology read lock. */ protected final void topologyReadUnlock() { tx.txState().topologyReadUnlock(cctx); } /** * @param remap Remap flag. * @param c Optional closure to run after map. */ protected final void prepareOnTopology(final boolean remap, @Nullable final Runnable c) { GridDhtTopologyFuture topFut = topologyReadLock(); AffinityTopologyVersion topVer = null; try { if (topFut == null) { assert isDone(); return; } if (topFut.isDone()) { topVer = topFut.topologyVersion(); if (remap) tx.onRemap(topVer); else tx.topologyVersion(topVer); if (!remap) cctx.mvcc().addFuture(this); } } finally { topologyReadUnlock(); } if (topVer != null) { IgniteCheckedException err = tx.txState().validateTopology( cctx, tx.writeMap().isEmpty(), topFut); if (err != null) { onDone(err); return; } prepare0(remap, false); if (c != null) c.run(); } else { topFut.listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() { @Override public void apply(final IgniteInternalFuture<AffinityTopologyVersion> fut) { cctx.kernalContext().closure().runLocalSafe(new GridPlainRunnable() { @Override public void run() { try { fut.get(); prepareOnTopology(remap, c); } catch (IgniteCheckedException e) { onDone(e); } finally { cctx.txContextReset(); } } }); } }); } } /** * @param remap Remap flag. * @param topLocked {@code True} if thread already acquired lock preventing topology change. */ protected abstract void prepare0(boolean remap, boolean topLocked); /** * Keys lock future. */ protected static class KeyLockFuture extends GridFutureAdapter<GridNearTxPrepareResponse> { /** */ @GridToStringInclude protected Collection<IgniteTxKey> lockKeys = new GridConcurrentHashSet<>(); /** */ private volatile boolean allKeysAdded; /** * @param key Key to track for locking. */ protected void addLockKey(IgniteTxKey key) { assert !allKeysAdded; lockKeys.add(key); } /** * @param key Locked keys. */ protected void onKeyLocked(IgniteTxKey key) { lockKeys.remove(key); checkLocks(); } /** * Moves future to the ready state. */ protected void onAllKeysAdded() { allKeysAdded = true; checkLocks(); } /** * @return {@code True} if all locks are owned. */ private boolean checkLocks() { boolean locked = lockKeys.isEmpty(); if (locked && allKeysAdded) { if (log.isDebugEnabled()) log.debug("All locks are acquired for near prepare future: " + this); onDone((GridNearTxPrepareResponse)null); } else { if (log.isDebugEnabled()) log.debug("Still waiting for locks [fut=" + this + ", keys=" + lockKeys + ']'); } return locked; } /** {@inheritDoc} */ @Override public String toString() { return S.toString(KeyLockFuture.class, this, super.toString()); } } }