/** * Local release. * * @param threadId ID of the thread. * @return Removed candidate. */ @Nullable public GridCacheMvccCandidate releaseLocal(long threadId) { CacheLockCandidates owners = localOwners(); // Release had no effect. if (owners == null) return null; GridCacheMvccCandidate owner = null; for (int i = 0; i < owners.size(); i++) { GridCacheMvccCandidate owner0 = owners.candidate(i); if (owner0.threadId() == threadId) { owner = owner0; break; } } if (owner != null) { owner.setUsed(); remove0(owner.version(), true); return owner; } else return null; }
/** @param entry Entry. */ @SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter"}) void recheck(@Nullable GridCacheEntryEx entry) { if (entry == null) return; if (exchLog.isDebugEnabled()) exchLog.debug("Rechecking entry for completion [entry=" + entry + ", finFut=" + this + ']'); Collection<GridCacheMvccCandidate> cands = pendingLocks.get(entry.txKey()); if (cands != null) { synchronized (cands) { for (Iterator<GridCacheMvccCandidate> it = cands.iterator(); it.hasNext(); ) { GridCacheMvccCandidate cand = it.next(); // Check exclude ID again, as key could have been reassigned. if (cand.removed()) it.remove(); } if (cands.isEmpty()) pendingLocks.remove(entry.txKey()); if (pendingLocks.isEmpty()) { onDone(); if (exchLog.isDebugEnabled()) exchLog.debug("Finish lock future is done: " + this); } } } }
/** * For all remote candidates standing behind the candidate being salvaged marks their transactions * as system invalidate and marks these candidates as owned and used. * * @param ver Version to salvage. */ public void salvageRemote(GridCacheVersion ver) { assert ver != null; GridCacheMvccCandidate cand = candidate(rmts, ver); if (cand != null) { assert rmts != null; assert !rmts.isEmpty(); for (Iterator<GridCacheMvccCandidate> iter = rmts.iterator(); iter.hasNext(); ) { GridCacheMvccCandidate rmt = iter.next(); // For salvaged candidate doneRemote will be called explicitly. if (rmt == cand) break; // Only Near and DHT remote candidates should be released. assert !rmt.nearLocal(); IgniteInternalTx tx = cctx.tm().tx(rmt.version()); if (tx != null) { tx.systemInvalidate(true); rmt.setOwner(); rmt.setUsed(); } else iter.remove(); } } }
/** * @param cacheCtx Cache context. * @param cand Cache lock candidate to add. * @return {@code True} if added as a result of this operation, {@code false} if was previously * added. */ public boolean addNext(GridCacheContext cacheCtx, GridCacheMvccCandidate cand) { assert cand != null; assert !cand.reentry() : "Lock reentries should not be linked: " + cand; // Don't order near candidates by thread as they will be ordered on // DHT node. Also, if candidate is implicit, no point to order him. if (cacheCtx.isNear() || cand.singleImplicit()) return true; LinkedList<GridCacheMvccCandidate> queue = pending.get(); GridCacheMvccCandidate prev = null; if (!queue.isEmpty()) prev = queue.getLast(); queue.add(cand); if (prev != null) { prev.next(cand); cand.previous(prev); } if (log.isDebugEnabled()) log.debug("Linked new candidate: " + cand); return true; }
/** * Removes candidate from the list of near local candidates. * * @param cand Candidate to remove. */ public void removeExplicitLock(GridCacheMvccCandidate cand) { GridCacheExplicitLockSpan span = pendingExplicit.get(cand.threadId()); if (span == null) return; if (span.removeCandidate(cand)) pendingExplicit.remove(cand.threadId(), span); }
/** {@inheritDoc} */ @SuppressWarnings({"unchecked"}) @Override public void onOwnerChanged( GridCacheEntryEx entry, GridCacheMvccCandidate prev, GridCacheMvccCandidate owner) { assert entry != null; assert owner != prev : "New and previous owner are identical instances: " + owner; assert owner == null || prev == null || !owner.version().equals(prev.version()) : "New and previous owners have identical versions [owner=" + owner + ", prev=" + prev + ']'; if (log.isDebugEnabled()) log.debug( "Received owner changed callback [" + entry.key() + ", owner=" + owner + ", prev=" + prev + ']'); if (owner != null && (owner.local() || owner.nearLocal())) { Collection<? extends GridCacheFuture> futCol = futs.get(owner.version()); if (futCol != null) { for (GridCacheFuture fut : futCol) { if (fut instanceof GridCacheMvccFuture && !fut.isDone()) { GridCacheMvccFuture<Boolean> mvccFut = (GridCacheMvccFuture<Boolean>) fut; // Since this method is called outside of entry synchronization, // we can safely invoke any method on the future. // Also note that we don't remove future here if it is done. // The removal is initiated from within future itself. if (mvccFut.onOwnerChanged(entry, owner)) return; } } } } if (log.isDebugEnabled()) log.debug( "Lock future not found for owner change callback (will try transaction futures) [owner=" + owner + ", prev=" + prev + ", entry=" + entry + ']'); // If no future was found, delegate to transaction manager. if (cctx.tm().onOwnerChanged(entry, owner)) { if (log.isDebugEnabled()) log.debug("Found transaction for changed owner: " + owner); } else if (log.isDebugEnabled()) log.debug("Failed to find transaction for changed owner: " + owner); for (FinishLockFuture f : finishFuts) f.recheck(entry); }
/** * Near local candidate. * * @param nodeId Node ID. * @param threadId Thread ID. * @return Remote candidate. */ @Nullable public GridCacheMvccCandidate localCandidate(UUID nodeId, long threadId) { if (locs != null) for (GridCacheMvccCandidate c : locs) if (c.nodeId().equals(nodeId) && c.threadId() == threadId) return c; return null; }
/** * @param nodeId Node ID. * @param threadId Thread ID. * @return Remote candidate. */ @Nullable GridCacheMvccCandidate remoteCandidate(UUID nodeId, long threadId) { if (rmts != null) for (GridCacheMvccCandidate c : rmts) if (c.nodeId().equals(nodeId) && c.threadId() == threadId) return c; return null; }
/** * @param cand Candidate to check. * @return First predecessor that is owner or is not used. */ @Nullable private GridCacheMvccCandidate nonRollbackPrevious(GridCacheMvccCandidate cand) { for (GridCacheMvccCandidate c = cand.previous(); c != null; c = c.previous()) { if (c.owner() || !c.used()) return c; } return null; }
/** * @param cands Candidates to search. * @param ver Version. * @return Candidate for the version. */ @Nullable private GridCacheMvccCandidate candidate( Iterable<GridCacheMvccCandidate> cands, GridCacheVersion ver) { assert ver != null; if (cands != null) for (GridCacheMvccCandidate c : cands) if (c.version().equals(ver)) return c; return null; }
/** * Puts owned versions in front of base. * * @param baseVer Base version. * @param owned Owned list. */ public void markOwned(GridCacheVersion baseVer, GridCacheVersion owned) { if (owned == null) return; if (rmts != null) { GridCacheMvccCandidate baseCand = candidate(rmts, baseVer); if (baseCand != null) baseCand.ownerVersion(owned); } }
/** * @param cand Local candidate added in any of the {@code addLocal(..)} methods. * @return Current lock owner. */ @Nullable public CacheLockCandidates readyLocal(GridCacheMvccCandidate cand) { assert cand.local(); cand.setReady(); reassign(); return allOwners(); }
/** @param cand Remote candidate. */ private void addRemote(GridCacheMvccCandidate cand) { assert !cand.local(); if (log.isDebugEnabled()) log.debug("Adding remote candidate [mvcc=" + this + ", cand=" + cand + "]"); cctx.versions().onReceived(cand.nodeId(), cand.version()); add0(cand); }
/** * @param ver Lock version to acquire or set to ready. * @return Current owner. */ @Nullable public CacheLockCandidates readyLocal(GridCacheVersion ver) { GridCacheMvccCandidate cand = candidate(ver); if (cand == null) return allOwners(); assert cand.local(); return readyLocal(cand); }
/** @return Local candidate only if it's first in the list and is marked as <tt>'owner'</tt>. */ @Nullable GridCacheMvccCandidate localOwner() { if (locs != null) { assert !locs.isEmpty(); GridCacheMvccCandidate first = locs.getFirst(); return first.owner() ? first : null; } return null; }
/** * @param cand Local candidate to remove. * @return {@code True} if removed. */ public boolean removeLocal(GridCacheMvccCandidate cand) { assert cand.key() != null; assert cand.local(); if (cand.dhtLocal() && dhtLocCands.remove(cand)) { if (log.isDebugEnabled()) log.debug("Removed local candidate: " + cand); return true; } return false; }
/** @return Remote candidate only if it's first in the list and is marked as <tt>'used'</tt>. */ @Nullable private GridCacheMvccCandidate remoteOwner() { if (rmts != null) { assert !rmts.isEmpty(); GridCacheMvccCandidate first = rmts.getFirst(); return first.used() && first.owner() ? first : null; } return null; }
/** {@inheritDoc} */ @Override public String toString() { if (!pendingLocks.isEmpty()) { Map<GridCacheVersion, IgniteInternalTx> txs = new HashMap<>(1, 1.0f); for (Collection<GridCacheMvccCandidate> cands : pendingLocks.values()) for (GridCacheMvccCandidate c : cands) txs.put(c.version(), cctx.tm().tx(c.version())); return S.toString( FinishLockFuture.class, this, "txs=" + txs + ", super=" + super.toString()); } else return S.toString(FinishLockFuture.class, this, super.toString()); }
/** * @param lockVer Lock ID. * @param threadId Thread ID. * @return {@code True} if locked by lock ID or thread ID. */ boolean isLocallyOwnedByIdOrThread(GridCacheVersion lockVer, long threadId) { CacheLockCandidates owners = localOwners(); if (owners != null) { for (int i = 0; i < owners.size(); i++) { GridCacheMvccCandidate owner = owners.candidate(i); if ((owner.version().equals(lockVer) || owner.threadId() == threadId)) return true; } } return false; }
/** * @param threadId Thread ID. * @param reentry Reentry flag. * @return Local candidate for the thread. */ @Nullable private GridCacheMvccCandidate localCandidate(long threadId, boolean reentry) { if (locs != null) for (GridCacheMvccCandidate cand : locs) { if (cand.threadId() == threadId) { if (cand.reentry() && !reentry) continue; return cand; } } return null; }
/** * Removes explicit lock for given thread id, key and optional version. * * @param threadId Thread id. * @param key Key. * @param ver Optional version. * @return Candidate. */ public GridCacheMvccCandidate removeExplicitLock( long threadId, KeyCacheObject key, @Nullable GridCacheVersion ver) { assert threadId > 0; GridCacheExplicitLockSpan span = pendingExplicit.get(threadId); if (span == null) return null; GridCacheMvccCandidate cand = span.removeCandidate(key, ver); if (cand != null && span.isEmpty()) pendingExplicit.remove(cand.threadId(), span); return cand; }
/** * Sets remote candidate to done. * * @param ver Version. * @param pending Pending versions. * @param committed Committed versions. * @param rolledback Rolledback versions. * @return Lock owner. */ @Nullable public CacheLockCandidates doneRemote( GridCacheVersion ver, Collection<GridCacheVersion> pending, Collection<GridCacheVersion> committed, Collection<GridCacheVersion> rolledback) { assert ver != null; if (log.isDebugEnabled()) log.debug("Setting remote candidate to done [mvcc=" + this + ", ver=" + ver + "]"); // Check remote candidate. GridCacheMvccCandidate cand = candidate(rmts, ver); if (cand != null) { assert rmts != null; assert !rmts.isEmpty(); assert !cand.local() : "Remote candidate is marked as local: " + cand; assert !cand.nearLocal() : "Remote candidate is marked as near local: " + cand; cand.setOwner(); cand.setUsed(); List<GridCacheMvccCandidate> mvAfter = null; for (ListIterator<GridCacheMvccCandidate> it = rmts.listIterator(); it.hasNext(); ) { GridCacheMvccCandidate c = it.next(); assert !c.nearLocal() : "Remote candidate marked as near local: " + c; if (c == cand) { if (mvAfter != null) for (GridCacheMvccCandidate mv : mvAfter) it.add(mv); break; } else if (!committed.contains(c.version()) && !rolledback.contains(c.version()) && pending.contains(c.version())) { it.remove(); if (mvAfter == null) mvAfter = new LinkedList<>(); mvAfter.add(c); } } } return allOwners(); }
/** * @param threadId Thread ID to check. * @param exclude Versions to ignore. * @return {@code True} if lock is owned by the thread with given ID. */ boolean isLocallyOwnedByThread(long threadId, boolean allowDhtLoc, GridCacheVersion... exclude) { CacheLockCandidates owners = localOwners(); if (owners != null) { for (int i = 0; i < owners.size(); i++) { GridCacheMvccCandidate owner = owners.candidate(i); if (owner.threadId() == threadId && owner.nodeId().equals(cctx.nodeId()) && (allowDhtLoc || !owner.dhtLocal()) && !U.containsObjectArray(exclude, owner.version())) return true; } } return false; }
/** * @param cand Existing candidate. * @param newCand New candidate. * @return {@code False} if new candidate can not be added. */ private boolean compareSerializableVersion( GridCacheMvccCandidate cand, GridCacheMvccCandidate newCand) { assert cand.serializable() && newCand.serializable(); GridCacheVersion candOrder = cand.serializableOrder(); assert candOrder != null : cand; GridCacheVersion newCandOrder = newCand.serializableOrder(); assert newCandOrder != null : newCand; int cmp = SER_VER_COMPARATOR.compare(candOrder, newCandOrder); assert cmp != 0; return cmp < 0; }
/** * Checks if given key is locked by thread with given id or any thread. * * @param key Key to check. * @param threadId Thread id. If -1, all threads will be checked. * @return {@code True} if locked by any or given thread (depending on {@code threadId} value). */ public boolean isLockedByThread(KeyCacheObject key, long threadId) { if (threadId < 0) { for (GridCacheExplicitLockSpan span : pendingExplicit.values()) { GridCacheMvccCandidate cand = span.candidate(key, null); if (cand != null && cand.owner()) return true; } } else { GridCacheExplicitLockSpan span = pendingExplicit.get(threadId); if (span != null) { GridCacheMvccCandidate cand = span.candidate(key, null); return cand != null && cand.owner(); } } return false; }
/** * Removes candidate from collection. * * @param col Collection. * @param ver Version of the candidate to remove. * @return {@code True} if candidate was removed. */ private boolean remove0(Collection<GridCacheMvccCandidate> col, GridCacheVersion ver) { if (col != null) { for (Iterator<GridCacheMvccCandidate> it = col.iterator(); it.hasNext(); ) { GridCacheMvccCandidate cand = it.next(); if (cand.version().equals(ver)) { cand.setUsed(); cand.setRemoved(); it.remove(); reassign(); return true; } } } return false; }
/** * @param col Collection of candidates. * @param reentries Reentry flag. * @param cp Whether to copy or not. * @param excludeVers Exclude versions. * @return Collection of candidates minus the exclude versions. */ private List<GridCacheMvccCandidate> candidates( List<GridCacheMvccCandidate> col, boolean reentries, boolean cp, GridCacheVersion... excludeVers) { if (col == null) return Collections.emptyList(); assert !col.isEmpty(); if (!cp && F.isEmpty(excludeVers)) return col; List<GridCacheMvccCandidate> cands = new ArrayList<>(col.size()); for (GridCacheMvccCandidate c : col) { // Don't include reentries. if ((!c.reentry() || (reentries && c.reentry())) && !U.containsObjectArray(excludeVers, c.version())) cands.add(c); } return cands; }
/** * @param exclude Versions to exclude form check. * @return {@code True} if lock is empty. */ public boolean isEmpty(GridCacheVersion... exclude) { if (locs == null && rmts == null) return true; if (locs != null) { assert !locs.isEmpty(); if (F.isEmpty(exclude)) return false; for (GridCacheMvccCandidate cand : locs) if (!U.containsObjectArray(exclude, cand.version())) return false; } if (rmts != null) { assert !rmts.isEmpty(); if (F.isEmpty(exclude)) return false; for (GridCacheMvccCandidate cand : rmts) if (!U.containsObjectArray(exclude, cand.version())) return false; } return true; }
/** * Adds candidate to the list of near local candidates. * * @param threadId Thread ID. * @param cand Candidate to add. * @param topVer Topology version. */ public void addExplicitLock( long threadId, GridCacheMvccCandidate cand, AffinityTopologyVersion topVer) { while (true) { GridCacheExplicitLockSpan span = pendingExplicit.get(cand.threadId()); if (span == null) { span = new GridCacheExplicitLockSpan(topVer, cand); GridCacheExplicitLockSpan old = pendingExplicit.putIfAbsent(threadId, span); if (old == null) break; else span = old; } // Either span was not empty, or concurrent put did not succeed. if (span.addCandidate(topVer, cand)) break; else pendingExplicit.remove(threadId, span); } }
/** @return All local owners. */ @Nullable public CacheLockCandidates localOwners() { if (locs != null) { assert !locs.isEmpty(); CacheLockCandidates owners = null; GridCacheMvccCandidate first = locs.getFirst(); if (first.read()) { for (GridCacheMvccCandidate cand : locs) { if (cand.owner()) { assert cand.read() : this; if (owners != null) { CacheLockCandidatesList list; if (owners.size() == 1) { GridCacheMvccCandidate owner = owners.candidate(0); owners = list = new CacheLockCandidatesList(); ((CacheLockCandidatesList) owners).add(owner); } else list = ((CacheLockCandidatesList) owners); list.add(cand); } else owners = cand; } if (!cand.read()) break; } } else if (first.owner()) owners = first; return owners; } return null; }