/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2005-2006, * @author JBoss Inc. */ /* * Copyright (C) 1998, 1999, 2000, 2001, * * Arjuna Solutions Limited, * Newcastle upon Tyne, * Tyne and Wear, * UK. * * $Id: LockManager.java 2342 2006-03-30 13:06:17Z $ */ package org.jboss.stm.internal.optimistic; import java.io.IOException; import com.arjuna.ats.arjuna.ObjectModel; import com.arjuna.ats.arjuna.ObjectStatus; import com.arjuna.ats.arjuna.ObjectType; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.arjuna.coordinator.*; import com.arjuna.ats.arjuna.logging.tsLogger; import com.arjuna.ats.arjuna.state.InputObjectState; import com.arjuna.ats.arjuna.state.OutputObjectState; import com.arjuna.ats.internal.arjuna.abstractrecords.PersistenceRecord; import com.arjuna.ats.internal.arjuna.abstractrecords.RecoveryRecord; import com.arjuna.ats.internal.arjuna.common.UidHelper; import com.arjuna.ats.internal.txoj.abstractrecords.LockRecord; import com.arjuna.ats.txoj.ConflictType; import com.arjuna.ats.txoj.Lock; import com.arjuna.ats.txoj.LockManager; import com.arjuna.ats.txoj.LockMode; import com.arjuna.ats.txoj.LockResult; import com.arjuna.ats.txoj.exceptions.LockStoreException; import com.arjuna.ats.txoj.logging.txojLogger; public class OptimisticLockManager extends LockManager { /* * Some of these methods are copied directly from LockManager and do 99% the same thing. * The only way they differ is with the types of Locks or LockRecords that they create. * Probably should refactor later to reduce the amount of copied code. */ /* * All of this is here to prevent us grabbing a copy of the state of the object when we lock it. * Optimistic and pessimistic locks cannot be used in the same transaction on the same object. * * WARNING HERE BE DRAGONS. * * (non-Javadoc) * @see com.arjuna.ats.txoj.LockManager#setlock(com.arjuna.ats.txoj.Lock, int, int) */ public int setlock (Lock toSet, int retry, int sleepTime) { if (txojLogger.logger.isTraceEnabled()) { txojLogger.logger.trace("OptimisticLockManager::setlock(" + toSet + ", " + retry + ", " + sleepTime + ")"); } int conflict = ConflictType.CONFLICT; int returnStatus = LockResult.REFUSED; LockRecord newLockR = null; boolean modifyRequired = false; BasicAction currAct = null; if (toSet == null) { txojLogger.i18NLogger.warn_LockManager_2(); return LockResult.REFUSED; } if (!(toSet instanceof OptimisticLock)) return LockResult.REFUSED; initialise(); currAct = BasicAction.Current(); if (currAct != null) { ActionHierarchy ah = currAct.getHierarchy(); if (ah != null) toSet.changeHierarchy(ah); else { txojLogger.i18NLogger.warn_LockManager_3(); toSet = null; return LockResult.REFUSED; } } if (super.loadObjectState()) super.setupStore(); while ((conflict == ConflictType.CONFLICT) && ((retry >= 0) || ((retry == LockManager.waitTotalTimeout) && (sleepTime > 0)))) { Object syncObject = ((currAct == null) ? getMutex() : currAct); synchronized (super.lockStore.getClass()) { synchronized (syncObject) { synchronized (locksHeldLockObject) { conflict = ConflictType.CONFLICT; if (loadState()) { conflict = lockConflict(toSet); } else { txojLogger.i18NLogger.warn_LockManager_4(); } if (conflict != ConflictType.CONFLICT) { /* * When here the conflict was resolved or the retry limit * expired. */ /* no conflict so set lock */ modifyRequired = toSet.modifiesObject(); /* trigger object load from store */ if (super.activate()) { returnStatus = LockResult.GRANTED; if (conflict == ConflictType.COMPATIBLE) { int lrStatus = AddOutcome.AR_ADDED; if (currAct != null) { /* add new lock record to action list */ newLockR = new OptimisticLockRecord(this, (modifyRequired ? false : true), currAct, true); if ((lrStatus = currAct.add(newLockR)) != AddOutcome.AR_ADDED) { newLockR = null; if (lrStatus == AddOutcome.AR_REJECTED) { returnStatus = LockResult.REFUSED; } } } if (returnStatus == LockResult.GRANTED) { locksHeld.insert(toSet); /* * add to local lock * list */ } } else { if (modifyRequired) returnStatus = LockResult.GRANTED; } } else { /* activate failed - refuse request */ txojLogger.i18NLogger.warn_LockManager_5(); returnStatus = LockResult.REFUSED; } } /* * Unload internal state into lock store only if lock list was * modified if this fails claim the setlock failed. If we are * using the lock daemon we can arbitrarily throw the lock away * as the daemon has it. */ if ((returnStatus == LockResult.GRANTED) && (conflict == ConflictType.COMPATIBLE)) { if (!unloadState()) { txojLogger.i18NLogger.warn_LockManager_6(); returnStatus = LockResult.REFUSED; } } else freeState(); /* * Postpone call on modified to here so that semaphore will have * been released. This means when modified invokes save_state * that routine may set another lock without blocking. */ if (returnStatus == LockResult.GRANTED) { if (modifyRequired) { if (super.modified()) { hasBeenLocked = true; } else { conflict = ConflictType.CONFLICT; returnStatus = LockResult.REFUSED; } } } /* * Make sure we free state while we still have the lock. */ if (conflict == ConflictType.CONFLICT) freeState(); } } } if (conflict == ConflictType.CONFLICT) { if (retry != 0) { if (sleepTime > 0) { sleepTime -= conflictManager.wait(retry, sleepTime); } else retry = 0; } if (retry != LockManager.waitTotalTimeout) retry--; } } return returnStatus; } /* * Lock and load the concurrency control state. First we grab the semaphore * to ensure exclusive access and then we build the held lock list by * retreiving the locks from the lock repository. If there is only one * server we do not bother doing this since all the locks can stay in the * server's memory. This is yet another consequence of not having * multi-threaded servers. Does not require synchronized since it can only * be called from other synchronized methods. */ protected final boolean loadState () { if (txojLogger.logger.isTraceEnabled()) { txojLogger.logger.trace("LockManager::loadState()"); } if (super.objectModel == ObjectModel.SINGLE) { stateLoaded = true; return true; } else { InputObjectState S = null; if ((systemKey == null) && !initialise()) { return false; /* init failed */ } if ((mutex == null) || (!mutex.tryLock())) { return false; } stateLoaded = false; objectLocked = true; /* * An exception indicates some form of error and NOT that the state * cannot be found, which is indicated by S being null. */ try { S = lockStore.read_state(get_uid(), type()); /* Pick returned state apart again */ if (S != null) { Uid u = null; /* * avoid system calls in Uid * creation */ Lock current = null; int count = 0; try { count = S.unpackInt(); boolean cleanLoad = true; if (txojLogger.logger.isTraceEnabled()) { txojLogger.logger.trace("LockManager::loadState() loading " + count + " lock(s)"); } /* * Carefully rebuild the internal state - if we fail * throw it away and return. */ for (int i = 0; (i < count) && cleanLoad; i++) { try { u = UidHelper.unpackFrom(S); current = new OptimisticLock(u); if (current != null) { if (current.restore_state(S, ObjectType.ANDPERSISTENT)) { locksHeld.push(current); } else { current = null; cleanLoad = false; } } else { cleanLoad = false; } } catch (IOException e) { cleanLoad = false; } } if (cleanLoad) stateLoaded = true; else { while ((current = locksHeld.pop()) != null) current = null; } } catch (IOException e) { } S = null; } else stateLoaded = true; } catch (LockStoreException e) { txojLogger.logger.warn(e); } } if (!stateLoaded) { if (mutex != null) // means object model != SINGLE { mutex.unlock(); // and exit mutual exclusion } objectLocked = false; } return stateLoaded; } protected boolean doRelease (Uid u, boolean all) { synchronized (super.lockStore.getClass()) { return super.doRelease(u, all); } } public boolean propagate (Uid from, Uid to) { synchronized (super.lockStore.getClass()) { return super.propagate(from, to); } } /** * Overload StateManager.type() */ public String type () { return "StateManager/LockManager/OptimisticLockManager"; } protected OptimisticLockManager () { super(); } protected OptimisticLockManager (int ot) { super(ot); } protected OptimisticLockManager (int ot, int om) { super(ot, om); } protected OptimisticLockManager (Uid u) { super(u); } protected OptimisticLockManager (Uid u, int objectModel) { super(u, ObjectType.ANDPERSISTENT, objectModel); } }