/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.transaction.xa; import org.infinispan.marshall.Ids; import org.infinispan.remoting.transport.Address; import org.infinispan.util.Util; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collection; import java.util.HashSet; import java.util.Set; import static java.util.Collections.emptySet; /** * This class is used when deadlock detection is enabled. * * @author Mircea.Markus@jboss.com * @author Pedro Ruivo * @author Sebastiano Peluso */ public class DldGlobalTransaction extends GlobalTransaction { private static final Log log = LogFactory.getLog(DldGlobalTransaction.class); private static final boolean trace = log.isTraceEnabled(); protected volatile long coinToss; protected volatile boolean isMarkedForRollback; protected transient volatile Object localLockIntention; protected volatile Collection<Object> remoteLockIntention = emptySet(); //the same above, but to readSet in serializable isolation level protected volatile Collection<Object> remoteReadLockIntention = emptySet(); protected volatile Set<Object> locksAtOrigin = emptySet(); //the same above, but to readSet in serializable isolation level protected volatile Set<Object> readLocksAtOrigin = emptySet(); public DldGlobalTransaction() { } public DldGlobalTransaction(Address addr, boolean remote) { super(addr, remote); } /** * Sets the random number that defines the coin toss. A coin toss is a random number that is used when a deadlock is * detected for deciding which transaction should commit and which should rollback. */ public void setCoinToss(long coinToss) { this.coinToss = coinToss; } public long getCoinToss() { return coinToss; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof DldGlobalTransaction)) return false; if (!super.equals(o)) return false; DldGlobalTransaction that = (DldGlobalTransaction) o; if (coinToss != that.coinToss) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (int) (coinToss ^ (coinToss >>> 32)); return result; } @Override public String toString() { return "DldGlobalTransaction{" + "coinToss=" + coinToss + ", isMarkedForRollback=" + isMarkedForRollback + ", lockIntention=" + localLockIntention + ", affectedKeys=" + remoteLockIntention + ", locksAtOrigin=" + locksAtOrigin + "} " + super.toString(); } /** * Returns the key this transaction intends to lock. */ public Object getLockIntention() { return localLockIntention; } public void setLockIntention(Object lockIntention) { if (trace) log.tracef("Setting local lock intention to %s", lockIntention); this.localLockIntention = lockIntention; } public boolean wouldLose(DldGlobalTransaction other) { return this.coinToss < other.coinToss; } public void setRemoteLockIntention(Collection<Object> remoteLockIntention) { if (trace) { log.tracef("Setting the remote lock intention: %s", remoteLockIntention); } this.remoteLockIntention = remoteLockIntention; } public Collection<Object> getRemoteLockIntention() { return remoteLockIntention; } public boolean hasLockAtOrigin(Collection<Object> remoteLockIntention) { log.tracef("Our(%s) locks at origin are: %s. Others remote lock intention is: %s", this, locksAtOrigin, remoteLockIntention); for (Object key : remoteLockIntention) { if (this.locksAtOrigin.contains(key)) { return true; } } return false; } public void setLocksHeldAtOrigin(Set<Object> locksAtOrigin) { if (trace) log.tracef("Setting locks at origin for (%s) to %s", this, locksAtOrigin); this.locksAtOrigin = locksAtOrigin; } public Set<Object> getLocksHeldAtOrigin() { return this.locksAtOrigin; } public void setReadLocksHeldAtOrigin(Set<Object> readLocksAtOrigin) { if (trace) { log.tracef("Setting *read* locks at origin for (%s) to %s", this, locksAtOrigin); } this.readLocksAtOrigin = readLocksAtOrigin; } public void setRemoteReadLockIntention(Set<Object> remoteReadLockIntention) { if (trace) { log.tracef("Setting the remote read lock intention for (%s) to %s", this, remoteReadLockIntention); } this.remoteReadLockIntention = remoteReadLockIntention; } public boolean hasLockAtOrigin(DldGlobalTransaction other) { Set<Object> conflictingLocks = new HashSet<Object>(locksAtOrigin); conflictingLocks.addAll(readLocksAtOrigin); conflictingLocks.retainAll(other.remoteLockIntention); conflictingLocks.retainAll(other.remoteReadLockIntention); for(Object lock : conflictingLocks) { if(locksAtOrigin.contains(lock) || other.remoteLockIntention.contains(lock)) { return true; //conflict and one of them it is a write lock } } return false; } public static class Externalizer extends GlobalTransaction.AbstractGlobalTxExternalizer<DldGlobalTransaction> { @Override protected DldGlobalTransaction createGlobalTransaction() { return (DldGlobalTransaction) TransactionFactory.TxFactoryEnum.DLD_NORECOVERY_XA.newGlobalTransaction(); } @Override public void writeObject(ObjectOutput output, DldGlobalTransaction ddGt) throws IOException { super.writeObject(output, ddGt); output.writeLong(ddGt.getCoinToss()); if (ddGt.locksAtOrigin.isEmpty()) { output.writeObject(null); } else { output.writeObject(ddGt.locksAtOrigin); } if(ddGt.readLocksAtOrigin.isEmpty()) { output.writeObject(null); } else { output.writeObject(ddGt.readLocksAtOrigin); } } @Override @SuppressWarnings("unchecked") public DldGlobalTransaction readObject(ObjectInput input) throws IOException, ClassNotFoundException { DldGlobalTransaction ddGt = super.readObject(input); ddGt.setCoinToss(input.readLong()); Object locksAtOriginObj = input.readObject(); Object readLockAtOriginObj = input.readObject(); if (locksAtOriginObj == null) { ddGt.setLocksHeldAtOrigin(emptySet()); } else { ddGt.setLocksHeldAtOrigin((Set<Object>) locksAtOriginObj); } if(readLockAtOriginObj == null) { ddGt.setReadLocksHeldAtOrigin(emptySet()); } else { ddGt.setReadLocksHeldAtOrigin((Set<Object>) readLockAtOriginObj); } return ddGt; } @Override public Integer getId() { return Ids.DEADLOCK_DETECTING_GLOBAL_TRANSACTION; } @Override public Set<Class<? extends DldGlobalTransaction>> getTypeClasses() { return Util.<Class<? extends DldGlobalTransaction>>asSet(DldGlobalTransaction.class); } } }