[jboss-svn-commits] JBL Code SVN: r37856 - in labs/jbosstm/workspace/mlittle/STM-Arjuna/src: main/java/org/jboss/stm/optimistic and 2 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Fri Jan 20 08:41:49 EST 2012
Author: mark.little at jboss.com
Date: 2012-01-20 08:41:49 -0500 (Fri, 20 Jan 2012)
New Revision: 37856
Added:
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockManager.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockRecord.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/ConflictType.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/LockConflictManager.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLock.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockManager.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockRecord.java
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/test/java/org/jboss/stm/optimistic/
labs/jbosstm/workspace/mlittle/STM-Arjuna/src/test/java/org/jboss/stm/optimistic/OptimisticUnitTest.java
Log:
Initial optimistic concurrency control approach. Hacked. Shouldn't need to copy most of these classes.
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockManager.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockManager.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockManager.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,94 @@
+/*
+ * 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,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: CadaverLockManager.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package org.jboss.stm.optimistic;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.state.*;
+
+import com.arjuna.ats.txoj.logging.txojLogger;
+import com.arjuna.ats.txoj.logging.FacilityCode;
+
+import com.arjuna.common.util.logging.*;
+
+/*
+ *
+ * Lock concurrency controller
+ *
+ * Lock-base concurrency control management system
+ * Instances of this class are created by CadaverLockRecord class
+ * instances for the sole purpose of lock cleanup due to a locked
+ * object going out of scope prior to action termination.
+ * Serialisability prevents locks being released as scope is exited
+ * thus they must be cleaned up later.
+ *
+ */
+
+class CadaverLockManager extends OptimisticLockManager
+{
+
+ public CadaverLockManager(Uid objUid, String tName)
+ {
+ super(objUid);
+
+ objectTypeName = new String(tName);
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "CadaverLockManager::CadaverLockManager(" + objUid + ")");
+ }
+ }
+
+ /*
+ * Publically inherited functions
+ */
+
+ public boolean restore_state (InputObjectState os, int t)
+ {
+ return false;
+ }
+
+ public boolean save_state (OutputObjectState os, int t)
+ {
+ return false;
+ }
+
+ public String type ()
+ {
+ return objectTypeName;
+ }
+
+ private String objectTypeName;
+
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockRecord.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockRecord.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/CadaverLockRecord.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,283 @@
+/*
+ * 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: CadaverLockRecord.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package org.jboss.stm.optimistic;
+
+import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.*;
+import com.arjuna.ats.arjuna.coordinator.*;
+import com.arjuna.ats.arjuna.common.Uid;
+
+import com.arjuna.ats.txoj.logging.txojLogger;
+import com.arjuna.ats.txoj.logging.FacilityCode;
+
+import com.arjuna.common.util.logging.*;
+
+import com.arjuna.ats.txoj.lockstore.*;
+import java.io.PrintWriter;
+
+import com.arjuna.ats.arjuna.exceptions.FatalError;
+
+/**
+ * @message com.arjuna.ats.txoj.CadaverLockRecord_1 [com.arjuna.ats.txoj.CadaverLockRecord_1] - CadaverLockRecord::nestedAbort - no Current!
+ * @message com.arjuna.ats.txoj.CadaverLockRecord_2 [com.arjuna.ats.txoj.CadaverLockRecord_2] - CadaverLockRecord::nestedCommit - no Current!
+ * @message com.arjuna.ats.txoj.CadaverLockRecord_3 [com.arjuna.ats.txoj.CadaverLockRecord_3] - CadaverLockRecord::topLevelAbort - no Current!
+ * @message com.arjuna.ats.txoj.CadaverLockRecord_4 [com.arjuna.ats.txoj.CadaverLockRecord_4] - CadaverLockRecord::topLevelCommit - no Current!
+ */
+
+/*
+ *
+ * Cadaver Lock Record Class Implementation
+ *
+ * Instances of this record class are created by LockManager if the
+ * object goes out of scope prior to the end of a manipulating action.
+ * The intention is that the operations of this class will clean up
+ * those locks that get left set as the object goes out of scope but
+ * which must remain held until the action ends otherwise serialisability
+ * is compromised
+ *
+ */
+
+class CadaverLockRecord extends OptimisticLockRecord
+{
+
+ public CadaverLockRecord (LockStore store, OptimisticLockManager lm, BasicAction currAct)
+ {
+ super(lm, currAct);
+
+ cadaverLockStore = store;
+ objectTypeName = new String(lm.type());
+
+ if (((StateManagerAttribute)lm.attributes()).objectModel == ObjectModel.SINGLE)
+ {
+ doRelease = false;
+ }
+ else
+ doRelease = true;
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "CadaverLockRecord::CadaverLockRecord("+store+
+ ", "+((lm != null) ? lm.get_uid() : Uid.nullUid())+")");
+ }
+ }
+
+ /*
+ * Public virtual functions. These are all re-implementations of inherited
+ * functions
+ */
+
+ public boolean propagateOnAbort ()
+ {
+ return true;
+ }
+
+ /*
+ * Atomic action controlled functions. These functions create an instance
+ * of CadaverLockManager to handle the lock manipulation that is needed and
+ * then throw it away when done.
+ */
+
+ public int nestedAbort ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "CadaverLockRecord::nestedAbort() for "+order());
+ }
+
+ if (doRelease)
+ {
+ CadaverLockManager manager = new CadaverLockManager(order(), objectTypeName);
+
+ if (super.actionHandle == null)
+ {
+ if (txojLogger.aitLoggerI18N.isFatalEnabled())
+ {
+ txojLogger.aitLoggerI18N.fatal("CadaverLockRecord_1");
+ }
+
+ throw new FatalError(txojLogger.log_mesg.getString("com.arjuna.ats.txoj.CadaverLockRecord_1"));
+ }
+
+ return (manager.releaseAll(super.actionHandle.get_uid()) ? TwoPhaseOutcome.FINISH_OK : TwoPhaseOutcome.FINISH_ERROR);
+ }
+ else
+ return TwoPhaseOutcome.FINISH_OK;
+ }
+
+ public int nestedCommit ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "CadaverLockRecord::nestedCommit() for "+order());
+ }
+
+ if (doRelease)
+ {
+ /*
+ * Need to change the owner of the locks from the current
+ * committing action to its parent. Since no genuine LockManager
+ * exists at this time create one to take care of this.
+ */
+
+ if (super.actionHandle == null)
+ {
+ if (txojLogger.aitLoggerI18N.isFatalEnabled())
+ {
+ txojLogger.aitLoggerI18N.fatal("com.arjuna.ats.txoj.CadaverLockRecord_2");
+ }
+
+ throw new FatalError(txojLogger.log_mesg.getString("com.arjuna.ats.txoj.CadaverLockRecord_2"));
+ }
+
+ CadaverLockManager manager = new CadaverLockManager(order(), objectTypeName);
+
+ return (manager.propagate(super.actionHandle.get_uid(), super.actionHandle.parent().get_uid()) ? TwoPhaseOutcome.FINISH_OK : TwoPhaseOutcome.FINISH_ERROR);
+ }
+ else
+ return TwoPhaseOutcome.FINISH_OK;
+ }
+
+ public int topLevelAbort ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "CadaverLockRecord::topLevelAbort() for "+order());
+ }
+
+ if (doRelease)
+ {
+ if (super.actionHandle == null)
+ {
+ if (txojLogger.aitLoggerI18N.isFatalEnabled())
+ {
+ txojLogger.aitLoggerI18N.fatal("com.arjuna.ats.txoj.CadaverLockRecord_3");
+ }
+
+ throw new FatalError(txojLogger.log_mesg.getString("com.arjuna.ats.txoj.CadaverLockRecord_3"));
+ }
+
+ CadaverLockManager manager = new CadaverLockManager(order(), objectTypeName);
+
+ return (manager.releaseAll(super.actionHandle.get_uid()) ? TwoPhaseOutcome.FINISH_OK : TwoPhaseOutcome.FINISH_ERROR);
+ }
+ else
+ return TwoPhaseOutcome.FINISH_OK;
+ }
+
+ public int topLevelCommit ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "CadaverLockRecord::topLevelCommit() for "+order());
+ }
+
+ if (doRelease)
+ {
+ if (super.actionHandle == null)
+ {
+ if (txojLogger.aitLoggerI18N.isFatalEnabled())
+ {
+ txojLogger.aitLoggerI18N.fatal("com.arjuna.ats.txoj.CadaverLockRecord_4");
+ }
+
+ throw new FatalError(txojLogger.log_mesg.getString("com.arjuna.ats.txoj.CadaverLockRecord_4"));
+ }
+
+ CadaverLockManager manager = new CadaverLockManager(order(), objectTypeName);
+
+ return (manager.releaseAll(super.actionHandle.get_uid()) ? TwoPhaseOutcome.FINISH_OK : TwoPhaseOutcome.FINISH_ERROR);
+ }
+ else
+ return TwoPhaseOutcome.FINISH_OK;
+ }
+
+ public void print (PrintWriter strm)
+ {
+ strm.println("CadaverLockRecord : ");
+ super.print(strm);
+ }
+
+ public String type ()
+ {
+ return "/StateManager/AbstractRecord/LockRecord/CadaverLockRecord";
+ }
+
+ public boolean shouldReplace (AbstractRecord ar)
+ {
+ return (((order().equals(ar.order())) &&
+ ar.typeIs() == RecordType.LOCK ) ? true : false);
+ }
+
+ /*
+ * Already determined that ar is a LockRecord, otherwise replace would
+ * not have been called.
+ * So, get the type from it before it is deleted!
+ */
+
+ public void replace (AbstractRecord ar)
+ {
+ OptimisticLockRecord lr = (OptimisticLockRecord) ar;
+
+ objectTypeName = lr.lockType();
+ }
+
+ protected CadaverLockRecord ()
+ {
+ super();
+
+ cadaverLockStore = null;
+ objectTypeName = null;
+ doRelease = false;
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PROTECTED,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "CadaverLockRecord::CadaverLockRecord ()");
+ }
+ }
+
+ private LockStore cadaverLockStore;
+ private String objectTypeName;
+ private boolean doRelease;
+
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/ConflictType.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/ConflictType.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/ConflictType.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,72 @@
+/*
+ * 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,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: ConflictType.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package org.jboss.stm.optimistic;
+
+import java.io.PrintWriter;
+
+/**
+ * The various types of lock conflict that can occur when
+ * trying to set a lock.
+ */
+
+class ConflictType
+{
+
+ public static final int CONFLICT = 0;
+ public static final int COMPATIBLE = 1;
+ public static final int PRESENT = 2;
+
+ public static String stringForm (int c)
+ {
+ switch (c)
+ {
+ case CONFLICT:
+ return "ConflictType.CONFLICT";
+ case COMPATIBLE:
+ return "ConflictType.COMPATIBLE";
+ case PRESENT:
+ return "ConflictType.PRESENT";
+ default:
+ return "Unknown";
+ }
+ }
+
+ /**
+ * Print a human-readable form of the conflict type.
+ */
+
+ public static void print (PrintWriter strm, int c)
+ {
+ strm.print(c);
+ }
+
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/LockConflictManager.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/LockConflictManager.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/LockConflictManager.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,144 @@
+/*
+ * 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) 2000, 2001,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: LockConflictManager.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package org.jboss.stm.optimistic;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import java.lang.InterruptedException;
+
+/**
+ * An instance of this class is used to determine what to do in the event of a
+ * lock conflict for a given object. If the timeout and retry values are >=0
+ * then we use them to sleep the thread which tried to get the lock. If the
+ * retry value is -100 (LockManager.waitTotalTimeout) then the thread will block
+ * for up to the total timeout and be signalled either when the timeout occurs,
+ * or when the lock is actually released.
+ */
+
+class LockConflictManager
+{
+
+ LockConflictManager()
+ {
+ _lock = new Object();
+ _signals = 0;
+ }
+
+ /**
+ * Wait for the specified timeout and retry. We may either sleep the thread,
+ * or block it on a mutex. Returns the time taken to wait.
+ */
+
+ int wait (int retry, int waitTime)
+ {
+ /*
+ * If the retry is -1 then we wait on the object as if it were a lock.
+ * Otherwise we do the usual sleep call.
+ */
+
+ if (retry < 0)
+ {
+ /*
+ * Wait for the lock object to be signalled.
+ */
+
+ Date d1 = Calendar.getInstance().getTime();
+
+ synchronized (_lock)
+ {
+ try
+ {
+ /*
+ * Consume an old signal. May cause us to go round the loop
+ * quicker than we should, but its better than synchronizing
+ * signal and wait.
+ */
+
+ if (_signals == 0)
+ {
+ _lock.wait(waitTime);
+ }
+ else
+ {
+ _signals--;
+
+ return waitTime;
+ }
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ Date d2 = Calendar.getInstance().getTime();
+
+ return (int) (d2.getTime() - d1.getTime());
+ }
+ else
+ {
+ try
+ {
+ /* hope things happen in time */
+
+ Thread.sleep(waitTime);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ return 0;
+ }
+ }
+
+ /**
+ * Signal that the lock has been released.
+ */
+
+ void signal ()
+ {
+ synchronized (_lock)
+ {
+ _lock.notifyAll();
+
+ _signals++;
+
+ if (_signals < 0) // check for overflow
+ _signals = 1;
+ }
+ }
+
+ private Object _lock;
+
+ private int _signals;
+
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLock.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLock.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLock.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,101 @@
+/*
+ * 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,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: Lock.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package org.jboss.stm.optimistic;
+
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.txoj.Lock;
+
+/**
+ * Instances of this class (or derived user classes) are used when trying to set
+ * a lock. The default implementation provides a single-write/multiple-reader
+ * policy. However, by overridding the appropriate methods, other, type-specific
+ * concurrency control locks can be implemented.
+ *
+ * @author Mark Little (mark at arjuna.com)
+ * @version $Id: Lock.java 2342 2006-03-30 13:06:17Z $
+ * @since JTS 1.0.
+ */
+
+public class OptimisticLock extends Lock
+{
+ public OptimisticLock ()
+ {
+ super();
+ }
+
+ public OptimisticLock (int lm)
+ {
+ super(lm);
+ }
+
+ public OptimisticLock (final Uid storeId)
+ {
+ super(storeId);
+ }
+
+ /**
+ * Does this lock imply a modification of the object it is applied to? For
+ * example, a READ lock would return false, but a WRITE lock would return
+ * true.
+ *
+ * @return <code>true</code> if this lock implies the object's state will be
+ * modified, <code>false</code> otherwise.
+ */
+
+ public boolean modifiesObject ()
+ {
+ return true;
+ }
+
+ /**
+ * Implementation of Lock conflict check. Returns TRUE if there is conflict
+ * FALSE otherwise. Does not take account of relationship in the atomic
+ * action hierarchy since this is a function of LockManager.
+ *
+ * @return <code>true</code> if this lock conflicts with the parameter,
+ * <code>false</code> otherwise.
+ */
+
+ public boolean conflictsWith (Lock otherLock)
+ {
+ return false; /* no conflict between these locks */
+ }
+
+ /**
+ * Overrides StateManager.type()
+ */
+
+ public String type ()
+ {
+ return "/StateManager/Lock/OptimisticLock";
+ }
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockManager.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockManager.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockManager.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,1515 @@
+/*
+ * 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.optimistic;
+
+import com.arjuna.ats.txoj.Lock;
+import com.arjuna.ats.txoj.LockList;
+import com.arjuna.ats.txoj.LockListIterator;
+import com.arjuna.ats.txoj.LockManagerAttribute;
+import com.arjuna.ats.txoj.LockResult;
+import com.arjuna.ats.txoj.lockstore.*;
+import com.arjuna.ats.txoj.semaphore.*;
+import com.arjuna.ats.txoj.common.Environment;
+import com.arjuna.ats.arjuna.*;
+import com.arjuna.ats.arjuna.common.*;
+import com.arjuna.ats.arjuna.StateManager;
+import com.arjuna.ats.arjuna.gandiva.*;
+
+import com.arjuna.ats.txoj.logging.txojLogger;
+import com.arjuna.ats.txoj.logging.FacilityCode;
+
+import com.arjuna.common.util.logging.*;
+
+import com.arjuna.ats.arjuna.coordinator.*;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.txoj.common.txojPropertyManager;
+import java.io.PrintWriter;
+import java.util.*;
+
+import com.arjuna.ats.txoj.exceptions.LockStoreException;
+import java.lang.InterruptedException;
+import java.io.IOException;
+
+/**
+ * This class provides (transactional) concurrency control for application
+ * objects.
+ *
+ * @author Mark Little (mark at arjuna.com)
+ * @version $Id: LockManager.java 2342 2006-03-30 13:06:17Z $
+ * @since JTS 1.0.
+ * @see com.arjuna.ats.arjuna.StateManager
+ * @message com.arjuna.ats.txoj.LockManager_1 [com.arjuna.ats.txoj.LockManager_1]
+ * - LockManager: lock propagation failed
+ * @message com.arjuna.ats.txoj.LockManager_2
+ * [com.arjuna.ats.txoj.LockManager_2] - LockManager::setlock() no
+ * lock!
+ * @message com.arjuna.ats.txoj.LockManager_3
+ * [com.arjuna.ats.txoj.LockManager_3] - LockManager::setlock() cannot
+ * find action hierarchy
+ * @message com.arjuna.ats.txoj.LockManager_4
+ * [com.arjuna.ats.txoj.LockManager_4] - LockManager::setlock() cannot
+ * load existing lock states
+ * @message com.arjuna.ats.txoj.LockManager_5
+ * [com.arjuna.ats.txoj.LockManager_5] - LockManager::setlock() cannot
+ * activate object
+ * @message com.arjuna.ats.txoj.LockManager_6
+ * [com.arjuna.ats.txoj.LockManager_6] - LockManager::setlock() cannot
+ * save new lock states
+ * @message com.arjuna.ats.txoj.LockManager_7
+ * [com.arjuna.ats.txoj.LockManager_7] - Lockmanager::releaselock()
+ * could not load old lock states
+ * @message com.arjuna.ats.txoj.LockManager_8
+ * [com.arjuna.ats.txoj.LockManager_8] - Lockmanager::releaselock()
+ * could not unload new lock states
+ * @message com.arjuna.ats.txoj.LockManager_10
+ * [com.arjuna.ats.txoj.LockManager_10] - LockManager::unloadState()
+ * failed to remove empty lock state for object {0} of type {1}
+ * @message com.arjuna.ats.txoj.LockManager_11
+ * [com.arjuna.ats.txoj.LockManager_11] - LockManager.unloadState -
+ * could not save lock state: {0}
+ * @message com.arjuna.ats.txoj.LockManager_12
+ * [com.arjuna.ats.txoj.LockManager_12] - LockManager::unloadState()
+ * failed to write new state for object {0} of type {1}
+ * @message com.arjuna.ats.txoj.LockManager_13
+ * [com.arjuna.ats.txoj.LockManager_13] - LockManager::unloadState()
+ * failed to pack up new state for object {0} of type {1}
+ */
+
+public class OptimisticLockManager extends StateManager
+{
+
+ /**
+ * The default retry value which will be used by setlock if no other value
+ * is given.
+ *
+ * @see #setlock
+ */
+
+ public static final int defaultRetry = 100;
+
+ /**
+ * The default timeout value which will be used by setlock if no other value
+ * is given.
+ *
+ * @see #setlock
+ */
+
+ public static final int defaultSleepTime = 250;
+
+ /**
+ * By default, threads which call setlock with conflicting locks will spin
+ * for the specified (or default) number of timeout and retry attempts, and
+ * then return failure if the lock could not be acquired. If the *retry*
+ * period is set to this value, then such threads will sleep for their total
+ * wait period and be signalled if the lock is released within this period
+ * of time.
+ *
+ * @see #setlock
+ * @since JTS 2.1.
+ */
+
+ public static final int waitTotalTimeout = -100;
+
+ /**
+ * Cleanup. Note we grab the semaphore before destroying the the lock store
+ * to ensure the store is deleted cleanly.
+ */
+
+ public void finalize () throws Throwable
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.DESTRUCTORS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager.finalize()");
+ }
+
+ boolean doSignal = false;
+
+ cleanUp();
+
+ if (mutex != null)
+ {
+ if (mutex.lock() == Semaphore.SM_LOCKED)
+ doSignal = true;
+ }
+
+ lmAttributes = null;
+ locksHeld = null;
+ lockStore = null;
+ conflictManager = null;
+
+ if (doSignal) // mutex must be set
+ mutex.unlock();
+
+ mutex = null;
+
+ super.finalize();
+ }
+
+ /**
+ * Change lock ownership as nested action commits. All locks owned by the
+ * committing action have their owners changed to be the parent of the
+ * committing action. BasicAction ensures this is only called at nested
+ * commit. This function works by copying the old LockList pointer and then
+ * creating a new held lock list. Locks are then moved from the old to the
+ * new, propagating en route.
+ */
+
+ public final boolean propagate (Uid from, Uid to)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::propagate(" + from + ", " + to + ")");
+ }
+
+ boolean result = false;
+ int retryCount = 10;
+
+ do
+ {
+ try
+ {
+ synchronized (locksHeldLockObject)
+ {
+ if (loadState())
+ {
+ LockList oldlist = locksHeld;
+ Lock current = null;
+
+ locksHeld = new LockList(); /* create a new one */
+
+ if (locksHeld != null)
+ {
+ /*
+ * scan through old list of held locks and propagate
+ * to parent.
+ */
+
+ while ((current = oldlist.pop()) != null)
+ {
+ if (current.getCurrentOwner().equals(from))
+ {
+ current.propagate();
+ }
+
+ if (!locksHeld.insert(current))
+ {
+ current = null;
+ }
+ }
+
+ oldlist = null; /* get rid of old lock list */
+
+ result = true;
+ }
+ else
+ {
+ /*
+ * Cannot create new locklist - abort and try again.
+ */
+
+ freeState();
+
+ throw new NullPointerException();
+ }
+ }
+
+ if (result)
+ result = unloadState();
+ }
+ }
+ catch (NullPointerException e)
+ {
+ result = false;
+ }
+
+ if (!result)
+ {
+ try
+ {
+ Thread.sleep(OptimisticLockManager.DOZE_TIME);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ }
+ while ((!result) && (--retryCount > 0));
+
+ if (!result)
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.LockManager_1");
+ }
+
+ synchronized (locksHeldLockObject)
+ {
+ freeState();
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Clear out all locks for a given action. Should be triggered automatically
+ * at top-level commit but is also user callable so is potentially
+ * dangerous.
+ */
+
+ public final boolean releaseAll (Uid actionUid)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::releaseAll(" + actionUid + ")");
+ }
+
+ return doRelease(actionUid, true);
+ }
+
+ /**
+ * Release a SINGLE LOCK lock that has the given uid. Breaks two-phase
+ * locking rules so watch out!
+ */
+
+ public final boolean releaselock (Uid lockUid)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::releaseLock(" + lockUid + ")");
+ }
+
+ return doRelease(lockUid, false);
+ }
+
+ /*
+ * This is the main user visible operation. Attempts to set the given lock
+ * on the current object. If lock cannot be set, then the lock attempt is
+ * retried retry times before giving up and returning an error. This gives a
+ * simple handle on deadlock. Use the default timeout and retry values.
+ * @return <code>LockResult</code> indicating outcome.
+ */
+
+ public final int setlock (Lock toSet)
+ {
+ return setlock(toSet, OptimisticLockManager.defaultRetry,
+ OptimisticLockManager.defaultSleepTime);
+ }
+
+ /*
+ * This is the main user visible operation. Attempts to set the given lock
+ * on the current object. If lock cannot be set, then the lock attempt is
+ * retried retry times before giving up and returning an error. This gives a
+ * simple handle on deadlock. Use the default timeout value.
+ * @return <code>LockResult</code> indicating outcome.
+ */
+
+ public final int setlock (Lock toSet, int retry)
+ {
+ return setlock(toSet, retry, OptimisticLockManager.defaultSleepTime);
+ }
+
+ /*
+ * This is the main user visible operation. Attempts to set the given lock
+ * on the current object. If lock cannot be set, then the lock attempt is
+ * retried retry times before giving up and returning an error. This gives a
+ * simple handle on deadlock.
+ * @return <code>LockResult</code> indicating outcome.
+ */
+
+ public final int setlock (Lock toSet, int retry, int sleepTime)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PUBLIC,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::setlock(" + toSet + ", " + retry + ", "
+ + sleepTime + ")");
+ }
+
+ if (!(toSet instanceof OptimisticLock))
+ return LockResult.REFUSED;
+
+ int conflict = ConflictType.CONFLICT;
+ int returnStatus = LockResult.REFUSED;
+ OptimisticLockRecord newLockR = null;
+ boolean modifyRequired = false;
+ BasicAction currAct = null;
+
+ if (toSet == null)
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.LockManager_2");
+ }
+
+ return LockResult.REFUSED;
+ }
+
+ currAct = BasicAction.Current();
+
+ if (currAct != null)
+ {
+ ActionHierarchy ah = currAct.getHierarchy();
+
+ if (ah != null)
+ toSet.changeHierarchy(ah);
+ else
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.LockManager_3");
+ }
+
+ toSet = null;
+
+ return LockResult.REFUSED;
+ }
+ }
+
+ if (super.loadObjectState())
+ super.setupStore();
+
+ while ((conflict == ConflictType.CONFLICT)
+ && ((retry >= 0) || (retry == OptimisticLockManager.waitTotalTimeout)))
+ {
+ synchronized (locksHeldLockObject)
+ {
+ conflict = ConflictType.CONFLICT;
+
+ if (loadState())
+ {
+ conflict = lockConflict(toSet);
+ }
+ else
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.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) && (!modifyRequired))
+ {
+ int lrStatus = AddOutcome.AR_ADDED;
+
+ if (currAct != null)
+ {
+ /* add new lock record to action list */
+
+ newLockR = new OptimisticLockRecord(this,
+ (modifyRequired ? false : true),
+ currAct);
+
+ 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 */
+
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.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())
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.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 != OptimisticLockManager.waitTotalTimeout)
+ retry--;
+ }
+ }
+
+ return returnStatus;
+ }
+
+ protected synchronized boolean modified ()
+ {
+ BasicAction currAct = BasicAction.Current();
+ boolean modifyRequired = true;
+ int lrStatus;
+ boolean returnStatus = true;
+
+ if (currAct != null)
+ {
+ /* add new lock record to action list */
+
+ OptimisticLockRecord newLockR = new OptimisticLockRecord(this,
+ (modifyRequired ? false : true),
+ currAct);
+
+ if ((lrStatus = currAct.add(newLockR)) != AddOutcome.AR_ADDED)
+ {
+ newLockR = null;
+
+ if (lrStatus == AddOutcome.AR_REJECTED)
+ returnStatus = false;
+ }
+ }
+
+ if (returnStatus)
+ return super.modified();
+ else
+ return false;
+ }
+
+ /**
+ * Print information about this instance on the specified
+ * <code>PrintWriter</code>.
+ */
+
+ public void print (PrintWriter strm)
+ {
+ LockListIterator next = new LockListIterator(locksHeld);
+ Lock current;
+
+ strm.println("LocalLockManager for object " + get_uid());
+
+ if (!stateLoaded)
+ strm.println("No loaded state");
+ else if (locksHeld != null)
+ {
+ strm.println("\tCurrently holding : " + locksHeld.entryCount()
+ + " locks");
+
+ while ((current = next.iterate()) != null)
+ current.print(strm);
+ }
+ else
+ strm.println("Currently holding : 0 locks");
+ }
+
+ /**
+ * Load state into object prior to doing the printing.
+ */
+
+ public synchronized void printState (PrintWriter strm)
+ {
+ synchronized (locksHeldLockObject)
+ {
+ boolean iDeleteState = false;
+
+ if (!stateLoaded)
+ {
+ loadState();
+ iDeleteState = true;
+ }
+
+ print(strm);
+
+ if (iDeleteState)
+ freeState();
+ }
+ }
+
+ /**
+ * Overload StateManager.type()
+ */
+
+ public String type ()
+ {
+ return "StateManager/LockManager";
+ }
+
+ /**
+ * @return the <code>LockManagerAttribute</code> object for this instance.
+ * Must be returned as an <code>Object</code> because it overrides
+ * StateManager.attributes.
+ * @see LockManagerAttribute
+ */
+
+ public Object attributes ()
+ {
+ return lmAttributes;
+ }
+
+ /*
+ * Pass on some args to StateManager and initialise internal state. The lock
+ * store and semaphore are set up lazily since they depend upon the result
+ * of the type() operation which if run in the constructor always give the
+ * same answer!
+ */
+
+ protected OptimisticLockManager(Uid storeUid)
+ {
+ this(storeUid, ObjectType.ANDPERSISTENT, null);
+ }
+
+ protected OptimisticLockManager(Uid storeUid, ObjectName attr)
+ {
+ this(storeUid, ObjectType.ANDPERSISTENT, attr);
+ }
+
+ protected OptimisticLockManager(Uid storeUid, int ot)
+ {
+ this(storeUid, ot, null);
+ }
+
+ protected OptimisticLockManager(Uid storeUid, int ot, ObjectName attr)
+ {
+ super(storeUid, ot, attr);
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS,
+ VisibilityLevel.VIS_PROTECTED,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::LockManager(" + storeUid + ")");
+ }
+
+ parseObjectName();
+
+ systemKey = null;
+ locksHeld = new LockList();
+ lockStore = null;
+ mutex = null;
+ stateLoaded = false;
+ hasBeenLocked = false;
+ objectLocked = false;
+ conflictManager = new LockConflictManager();
+ }
+
+ /*
+ * Pass on some args to StateManager and initialise internal state. The lock
+ * store and semaphore are set up lazily since they depend upon the result
+ * of the type() operation which if run in the constructor always give the
+ * same answer!
+ */
+
+ protected OptimisticLockManager()
+ {
+ this(ObjectType.RECOVERABLE, null);
+ }
+
+ protected OptimisticLockManager(int ot)
+ {
+ this(ot, null);
+ }
+
+ protected OptimisticLockManager(int ot, ObjectName attr)
+ {
+ super(ot, attr);
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS,
+ VisibilityLevel.VIS_PROTECTED,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::LockManager(" + ot + ")");
+ }
+
+ parseObjectName();
+
+ systemKey = null;
+ locksHeld = new LockList();
+ lockStore = null;
+ mutex = null;
+ stateLoaded = false;
+ hasBeenLocked = false;
+ objectLocked = false;
+ conflictManager = new LockConflictManager();
+ }
+
+ /**
+ * This method *must* be called in the finalizer of every object. It ensures
+ * that any necessary cleanup work is done in the event that the object goes
+ * out of scope within a transaction.
+ */
+
+ protected void terminate ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PROTECTED,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::terminate() for object-id " + get_uid());
+ }
+
+ cleanUp();
+ super.terminate();
+ }
+
+ private final synchronized void cleanUp ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::cleanUp() for object-id " + get_uid());
+ }
+
+ if (hasBeenLocked)
+ {
+ if ((super.smAttributes.objectModel == ObjectModel.MULTIPLE)
+ && (systemKey == null))
+ {
+ initialise();
+ }
+
+ /*
+ * Unlike in the original version of Arjuna, we don't check to see
+ * if the invoking thread is within a transaction. We look at
+ * whether this object has been used within a transaction, and then
+ * act accordingly.
+ */
+
+ synchronized (super.usingActions)
+ {
+ if (super.usingActions != null)
+ {
+ Enumeration e = super.usingActions.keys();
+
+ while (e.hasMoreElements())
+ {
+ BasicAction action = (BasicAction) e.nextElement();
+
+ while (action != null)
+ {
+ /*
+ * Pop actions off using list. Don't check if action
+ * is running below so that cadavers can be created
+ * in commit protocol too.
+ */
+
+ /*
+ * We need to create a cadaver lock record to
+ * maintain the locks because this object is being
+ * deleted.
+ */
+
+ AbstractRecord A = new CadaverLockRecord(lockStore,
+ this, action);
+
+ if (action.add(A) != AddOutcome.AR_ADDED)
+ A = null;
+ }
+ }
+ }
+ }
+
+ hasBeenLocked = false;
+ }
+ }
+
+ /*
+ * doRelease: Does all the hard work of lock release. Either releases all
+ * locks for a given tx uid, or simply one lock with a given uid as
+ * appropriate. Does not require 'synchronized' as it can only be called
+ * from other synchronized methods.
+ */
+
+ private final boolean doRelease (Uid u, boolean all)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::doRelease(" + u + ", " + all + ")");
+ }
+
+ Lock previous = null;
+ Lock current = null;
+ boolean deleted = false;
+ boolean result = false;
+ int retryCount = 10;
+ boolean loaded = false;
+
+ do
+ {
+ synchronized (locksHeldLockObject)
+ {
+ if (loadState())
+ {
+ loaded = true;
+
+ /*
+ * Must declare iterator after loadstate or it sees an empty
+ * list!
+ */
+
+ LockListIterator next = new LockListIterator(locksHeld);
+
+ /*
+ * Now scan through held lock list to find which locks to
+ * release u is either the unique id of the lock owner
+ * (oneOrAll = ALL_LOCKS) or the uid of the actual lock
+ * itself (oneOrAll = SINGLE_LOCK).
+ */
+
+ previous = null;
+
+ while ((current = next.iterate()) != null)
+ {
+ Uid checkUid = null;
+
+ if (all)
+ checkUid = current.getCurrentOwner();
+ else
+ checkUid = current.get_uid();
+
+ /*
+ * Is this the right lock?
+ */
+
+ if (u.equals(checkUid))
+ {
+ locksHeld.forgetNext(previous);
+ current = null;
+ deleted = true;
+
+ if (!all)
+ {
+ break;
+ }
+ }
+ else
+ previous = current;
+ }
+
+ result = true;
+ }
+ else
+ {
+ /*
+ * Free state while we still have the lock.
+ */
+
+ freeState();
+
+ result = false;
+ }
+ }
+
+ if (!result)
+ {
+ try
+ {
+ Thread.sleep(OptimisticLockManager.DOZE_TIME);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+
+ }
+ while ((!result) && (--retryCount > 0));
+
+ boolean releasedOK = false;
+
+ // if (!stateLoaded)
+ if (!loaded)
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.LockManager_7");
+ /*
+ * No need to freeState since we will have done that by now.
+ */
+ }
+ else
+ {
+ if (!deleted)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ " *** CANNOT locate locks ***");
+ }
+ }
+
+ retryCount = 10;
+
+ synchronized (locksHeldLockObject)
+ {
+ do
+ {
+ if (!unloadState())
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ txojLogger.aitLoggerI18N
+ .warn("com.arjuna.ats.txoj.LockManager_8");
+ }
+ else
+ releasedOK = true;
+
+ }
+ while ((--retryCount > 0) && (!releasedOK));
+ }
+ }
+
+ /*
+ * Now signal to any waiting threads that they may try to acquire the
+ * lock.
+ */
+
+ conflictManager.signal();
+
+ return releasedOK;
+ }
+
+ /*
+ * Simply free up the semaphore. We do this if we detect conflict. Since the
+ * list has not been modified it can simply be discarded. Does not need
+ * 'synchronized' as can only be called from synchronized methods.
+ */
+
+ private final void freeState ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::freeState()");
+ }
+
+ if (mutex != null)
+ {
+ /*
+ * If we are working in a shared lock store mode, then clear the
+ * cached lock list. Otherwise, do nothing.
+ */
+
+ if (super.smAttributes.objectModel != ObjectModel.SINGLE)
+ {
+ /* clear out the existing list */
+
+ while (locksHeld.pop() != null)
+ ;
+
+ stateLoaded = false;
+
+ if (objectLocked)
+ {
+ objectLocked = false;
+
+ mutex.unlock();
+ }
+ }
+ else
+ stateLoaded = false;
+ }
+ else
+ {
+ stateLoaded = false;
+ objectLocked = false;
+ }
+ }
+
+ /*
+ * Don't need to protect with a synchronization as this routine can only be
+ * called from within other protected methods. Only called if multiple
+ * object model is used.
+ */
+
+ private final boolean initialise ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::initialise()");
+ }
+
+ boolean result = false;
+
+ if (systemKey == null)
+ {
+ systemKey = type();
+
+ if (mutex == null)
+ {
+ mutex = new Semaphore(systemKey);
+ }
+
+ if (mutex != null)
+ {
+ if (mutex.lock() == Semaphore.SM_LOCKED)
+ {
+ if (lockStore == null)
+ {
+ Object[] param = new Object[3];
+
+ param[0] = lmAttributes.lockStoreType;
+ param[1] = new Integer(ObjectModel.MULTIPLE);
+ param[2] = systemKey;
+
+ lockStore = new LockStore(param);
+
+ param = null;
+ }
+ }
+
+ mutex.unlock();
+ }
+ }
+
+ result = (lockStore != null);
+
+ return result;
+ }
+
+ private final boolean isAncestorOf (Lock heldLock)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::isAncestorOf(" + heldLock.getCurrentOwner()
+ + ")");
+ }
+
+ BasicAction action = BasicAction.Current();
+
+ if (action == null)
+ return false; /* no action no ancestry! */
+
+ return action.isAncestor(heldLock.getCurrentOwner());
+ }
+
+ /*
+ * 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.
+ */
+
+ private final boolean loadState ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::loadState()");
+ }
+
+ if (super.smAttributes.objectModel == ObjectModel.SINGLE)
+ {
+ stateLoaded = true;
+
+ return true;
+ }
+ else
+ {
+ InputObjectState S = null;
+
+ if ((systemKey == null) && !initialise())
+ {
+ return false; /* init failed */
+ }
+
+ if ((mutex == null) || (mutex.lock() != Semaphore.SM_LOCKED))
+ {
+ return false;
+ }
+
+ stateLoaded = false;
+ objectLocked = true;
+
+ /*
+ * An exception indicates some form of error andNOT 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 = new Uid(Uid.nullUid()); /*
+ * avoid system calls in Uid
+ * creation
+ */
+ Lock current = null;
+ int count = 0;
+
+ try
+ {
+ count = S.unpackInt();
+
+ boolean cleanLoad = true;
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "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.unpack(S);
+ current = new Lock(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)
+ {
+ if (txojLogger.aitLogger.isWarnEnabled())
+ txojLogger.aitLogger.warn(e.getMessage());
+ }
+ }
+
+ return stateLoaded;
+ }
+
+ /*
+ * lockconflict: Here we attempt to determine if the provided lock is in
+ * conflict with any of the existing locks. If it is we use nested locking
+ * rules to allow children to lock objects already locked by their
+ * ancestors.
+ */
+
+ private final int lockConflict (Lock otherLock)
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::lockConflict(" + otherLock.get_uid() + ")");
+ }
+
+ boolean matching = false;
+ Lock heldLock = null;
+ LockListIterator next = new LockListIterator(locksHeld);
+
+ while ((heldLock = next.iterate()) != null)
+ {
+ if (heldLock.conflictsWith(otherLock))
+ {
+ if (OptimisticLockManager.nestedLocking)
+ {
+ if (!isAncestorOf(heldLock)) /* not quite Moss's rules */
+ {
+ return ConflictType.CONFLICT;
+ }
+ }
+ else
+ return ConflictType.CONFLICT;
+ }
+ else
+ {
+ if (heldLock.equals(otherLock))
+ matching = true;
+ }
+ }
+
+ return (matching ? ConflictType.PRESENT : ConflictType.COMPATIBLE);
+ }
+
+ /*
+ * Unload the state by writing all the locks to the repository and then
+ * freeing the semaphore.
+ */
+
+ private final boolean unloadState ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::unloadState()");
+ }
+
+ /*
+ * Single object model means we don't need a lock store at all.
+ */
+
+ if (super.smAttributes.objectModel == ObjectModel.SINGLE)
+ {
+ stateLoaded = false;
+
+ return true;
+ }
+ else
+ {
+ boolean unloadOk = false;
+ Lock current = null;
+ String otype = type();
+ Uid u = get_uid();
+ OutputObjectState S = new OutputObjectState(u, otype);
+ int lockCount = locksHeld.entryCount();
+
+ /* destroy old state from lock store */
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS,
+ VisibilityLevel.VIS_PRIVATE,
+ FacilityCode.FAC_CONCURRENCY_CONTROL,
+ "LockManager::unloadState() unloading " + lockCount
+ + " lock(s)");
+ }
+
+ if (lockCount == 0)
+ {
+ if (lockStore.remove_state(u, otype))
+ {
+ unloadOk = true;
+ }
+ else
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N.warn(
+ "com.arjuna.ats.txoj.LockManager_10",
+ new Object[]
+ { u, otype });
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ /* generate new state */
+
+ S.packInt(lockCount);
+
+ while ((current = locksHeld.pop()) != null)
+ {
+ current.get_uid().pack(S);
+
+ if (!current.save_state(S, ObjectType.ANDPERSISTENT))
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N.warn(
+ "com.arjuna.ats.txoj.LockManager_11",
+ new Object[]
+ { current });
+ }
+ unloadOk = false;
+ }
+
+ current = null;
+ }
+
+ if (unloadOk)
+ {
+ /* load image into store */
+
+ if (S.valid() && lockStore.write_committed(u, otype, S))
+ {
+ unloadOk = true;
+ }
+ else
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N.warn(
+ "com.arjuna.ats.txoj.LockManager_12",
+ new Object[]
+ { u, otype });
+ }
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ unloadOk = false;
+
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N.warn(
+ "com.arjuna.ats.txoj.LockManager_13",
+ new Object[]
+ { u, otype });
+ }
+ }
+ }
+
+ stateLoaded = false;
+
+ if (objectLocked)
+ {
+ objectLocked = false;
+
+ if (mutex != null) // means object model != SINGLE
+ mutex.unlock(); // and exit mutual exclusion
+ }
+
+ return unloadOk;
+ }
+ }
+
+ private void parseObjectName ()
+ {
+ lmAttributes = new LockManagerAttribute();
+
+ if (super.objectName != null)
+ {
+ try
+ {
+ /*
+ * Use same attribute name as environment.
+ */
+
+ lmAttributes.lockStoreType = super.objectName
+ .getClassNameAttribute(Environment.LOCKSTORE_TYPE);
+ }
+ catch (Exception e)
+ {
+ // assume not present.
+ }
+
+ // if present should now look for locations as with StateManager
+ }
+ }
+
+ protected LockManagerAttribute lmAttributes;
+
+ private String systemKey;/* used in accessing system resources */
+
+ private LockList locksHeld; /* the actual list of locks set */
+
+ private final Object locksHeldLockObject = new Object(); // mutex for sync
+ // on locksHeld.
+ // Can't use
+ // locksHeld
+ // itself, it's
+ // mutable.
+
+ private LockStore lockStore; /* locks held in shared memory */
+
+ private boolean stateLoaded;
+
+ private boolean hasBeenLocked;/* Locked at least once */
+
+ private boolean objectLocked;/* Semaphore grabbed */
+
+ private Semaphore mutex; /* Controls access to the lock store */
+
+ private LockConflictManager conflictManager;
+
+ private static final int DOZE_TIME = 1000000;
+
+ private static boolean nestedLocking = true;
+
+ static
+ {
+ String nl = txojPropertyManager.propertyManager
+ .getProperty(Environment.ALLOW_NESTED_LOCKING);
+
+ if (nl != null)
+ {
+ if (nl.equals("NO"))
+ nestedLocking = false;
+ }
+ }
+
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockRecord.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockRecord.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/main/java/org/jboss/stm/optimistic/OptimisticLockRecord.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,454 @@
+/*
+ * 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,
+ *
+ * Arjuna Solutions Limited,
+ * Newcastle upon Tyne,
+ * Tyne and Wear,
+ * UK.
+ *
+ * $Id: LockRecord.java 2342 2006-03-30 13:06:17Z $
+ */
+
+package org.jboss.stm.optimistic;
+
+import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
+import com.arjuna.ats.arjuna.ObjectType;
+import com.arjuna.ats.arjuna.state.*;
+import com.arjuna.ats.arjuna.common.Uid;
+
+import com.arjuna.ats.txoj.logging.txojLogger;
+import com.arjuna.ats.txoj.logging.FacilityCode;
+
+import com.arjuna.common.util.logging.*;
+
+import com.arjuna.ats.arjuna.coordinator.*;
+import java.io.*;
+
+/*
+ * Default visibility.
+ */
+
+/*
+ * Use mutex from StateManager?
+ */
+
+/*
+ * Optimistic cc in operation. Grab a copy of the current state so we can check against
+ * the state again when we commit.
+ */
+
+class OptimisticLockRecord extends AbstractRecord
+{
+
+ public OptimisticLockRecord (OptimisticLockManager lm, BasicAction currAct)
+ {
+ super(lm.get_uid(), lm.type(), ObjectType.ANDPERSISTENT);
+
+ actionHandle = currAct;
+
+ managerAddress = lm;
+ readOnly = false;
+ managerType = lm.type();
+ lm.save_state(state, lm.ObjectType());
+ }
+
+ public OptimisticLockRecord (OptimisticLockManager lm, boolean rdOnly, BasicAction currAct)
+ {
+ super(lm.get_uid(), lm.type(), ObjectType.ANDPERSISTENT);
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::LockRecord("+lm.get_uid()+", "
+ +(readOnly ? "PREPARE_READONLY" : "WRITEABLE")+")");
+ }
+
+ actionHandle = currAct;
+
+ managerAddress = lm;
+ readOnly = rdOnly;
+ managerType = lm.type();
+ lm.save_state(state, lm.ObjectType());
+ }
+
+ public int typeIs ()
+ {
+ return RecordType.LOCK;
+ }
+
+ public Object value ()
+ {
+ return (Object) managerAddress;
+ }
+
+ public void setValue (Object o)
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ txojLogger.aitLoggerI18N.warn("com.arjuna.ats.txoj.LockRecord_1");
+ }
+
+ public int nestedAbort ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::nestedAbort() for "+order());
+ }
+
+ /*
+ * Optimistic cc means we just throw away the state.
+ */
+
+ state = null;
+
+ return TwoPhaseOutcome.FINISH_OK;
+ }
+
+ public int nestedCommit ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::nestedCommit() for "+order());
+ }
+
+ /* default constructor problem. */
+
+ if (managerAddress == null)
+ return TwoPhaseOutcome.FINISH_ERROR;
+
+ if (actionHandle != null)
+ {
+ Uid toRelease = actionHandle.get_uid();
+
+ actionHandle = actionHandle.parent();
+
+ return (managerAddress.propagate(toRelease, actionHandle.get_uid()) ? TwoPhaseOutcome.FINISH_OK : TwoPhaseOutcome.FINISH_ERROR);
+ }
+ else
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ txojLogger.aitLoggerI18N.warn("com.arjuna.ats.txoj.LockRecord_4");
+ }
+
+ return TwoPhaseOutcome.FINISH_ERROR;
+ }
+
+ public int nestedPrepare ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::nestedPrepare() for "+order());
+ }
+
+ if (managerAddress == null)
+ return TwoPhaseOutcome.PREPARE_NOTOK;
+
+ if (checkState())
+ return TwoPhaseOutcome.PREPARE_OK;
+ else
+ return TwoPhaseOutcome.PREPARE_NOTOK;
+ }
+
+ public int topLevelAbort ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::topLevelAbort() for "+order());
+ }
+
+ return nestedAbort();
+ }
+
+ public int topLevelCommit ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::topLevelCommit() for "+order());
+ }
+
+ /* default constructor problem. */
+
+ if (managerAddress == null)
+ return TwoPhaseOutcome.FINISH_ERROR;
+
+ if (!checkState())
+ return TwoPhaseOutcome.FINISH_ERROR;
+
+ if (actionHandle != null)
+ {
+ if (!managerAddress.releaseAll(actionHandle.get_uid()))
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N.warn("com.arjuna.ats.txoj.LockRecord_5",
+ new Object[]{actionHandle.get_uid()});
+ }
+
+ return TwoPhaseOutcome.FINISH_ERROR;
+ }
+ }
+ else
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ txojLogger.aitLoggerI18N.warn("com.arjuna.ats.txoj.LockRecord_6");
+
+ return TwoPhaseOutcome.FINISH_ERROR;
+ }
+
+ return TwoPhaseOutcome.FINISH_OK;
+ }
+
+ public int topLevelPrepare ()
+ {
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.FUNCTIONS, VisibilityLevel.VIS_PUBLIC,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::topLevelPrepare() for "+order());
+ }
+
+ if (readOnly)
+ {
+ if (topLevelCommit() == TwoPhaseOutcome.FINISH_OK)
+ return TwoPhaseOutcome.PREPARE_READONLY;
+ else
+ return TwoPhaseOutcome.PREPARE_NOTOK;
+ }
+
+ return TwoPhaseOutcome.PREPARE_OK;
+ }
+
+ public String toString ()
+ {
+ StringWriter strm = new StringWriter();
+
+ print(new PrintWriter(strm));
+
+ return strm.toString();
+ }
+
+ public void print (PrintWriter strm)
+ {
+ super.print(strm);
+ strm.println("OptimisticLockRecord");
+ }
+
+ /*
+ * restore_state and save_state for LockRecords doesn't generally
+ * apply due to object pointers.
+ */
+
+ public boolean restore_state (InputObjectState o, int t)
+ {
+ if (txojLogger.aitLoggerI18N.isWarnEnabled())
+ {
+ txojLogger.aitLoggerI18N.warn("com.arjuna.ats.txoj.LockRecord_7",
+ new Object[]{type(), order()});
+ }
+
+ return false;
+ }
+
+ public boolean save_state (OutputObjectState o, int t)
+ {
+ return true;
+ }
+
+ public String type ()
+ {
+ return "/StateManager/AbstractRecord/OptimisticLockRecord";
+ }
+
+ public final boolean isReadOnly ()
+ {
+ return readOnly;
+ }
+
+ public final String lockType ()
+ {
+ return managerType;
+ }
+
+ public void merge (AbstractRecord a)
+ {
+ }
+
+ public void alter (AbstractRecord a)
+ {
+ }
+
+ public boolean shouldAdd (AbstractRecord a)
+ {
+ return false;
+ }
+
+ public boolean shouldAlter (AbstractRecord a)
+ {
+ return false;
+ }
+
+ public boolean shouldMerge (AbstractRecord a)
+ {
+ return false;
+ }
+
+ public boolean shouldReplace (AbstractRecord ar)
+ {
+ if ((order().equals(ar.order())) && typeIs() == ar.typeIs())
+ {
+ /*
+ * The first test should ensure that ar is a LockRecord.
+ */
+
+ if (((OptimisticLockRecord) ar).isReadOnly() && !readOnly)
+ return true;
+ }
+
+ return false;
+ }
+
+ protected OptimisticLockRecord ()
+ {
+ super();
+
+ if (txojLogger.aitLogger.isDebugEnabled())
+ {
+ txojLogger.aitLogger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PROTECTED,
+ (FacilityCode.FAC_CONCURRENCY_CONTROL | com.arjuna.ats.arjuna.logging.FacilityCode.FAC_ABSTRACT_REC),
+ "LockRecord::LockRecord()");
+ }
+
+ actionHandle = null;
+
+ managerAddress = null;
+ readOnly = false;
+ managerType = null;
+ }
+
+ private boolean checkState ()
+ {
+ /*
+ * If the object is recoverable then we can just check the local state.
+ * If the object is persistent then we have to check the state on disk.
+ */
+
+ OutputObjectState tempState = new OutputObjectState();
+ int objectType = managerAddress.ObjectType();
+
+ /*
+ * If we check the state now, it's possible that some other transactions may be
+ * doing the same concurrently. We need to lock the object at this point. Or
+ * suffer heuristic by checking during commit - though this still leaves a window
+ * of vulnerability.
+ */
+
+ /*
+ * Assume initially that this will only work if the objects are all in the same
+ * address space, since sharing across spaces will impose performance overhead
+ * anyway. In that case, we can maintain a list of all objects that are being
+ * managed optimistically and check them directly as well as lock them.
+ *
+ * Problem is that it's the state that needs to be checked and there may be
+ * multiple instances of the state active in memory at the same time. So would
+ * need to keep each instance per Uid.
+ */
+
+ /*
+ * Could even make this specific to STM and in which case we have even more control.
+ */
+
+ if (objectType == ObjectType.RECOVERABLE)
+ {
+ if (managerAddress.save_state(tempState, objectType))
+ {
+ boolean identical = true;
+
+ if (tempState.length() == state.length())
+ {
+ for (int i = 0; (i < tempState.length()) && identical; i++)
+ {
+ if (tempState.buffer()[i] != state.buffer()[i])
+ identical = false;
+ }
+
+ if (identical)
+ return true;
+ }
+ }
+ }
+
+ if (objectType == ObjectType.ANDPERSISTENT)
+ {
+ /*
+ * Don't need the state - could just check the time of the file update if we are using
+ * a file based object store.
+ */
+
+ try
+ {
+ InputObjectState s = managerAddress.getStore().read_committed(managerAddress.get_uid(), managerAddress.type());
+
+ if (s != null)
+ {
+ boolean identical = true;
+
+ if (s.length() == state.length())
+ {
+ for (int i = 0; (i < s.length()) && identical; i++)
+ {
+ if (s.buffer()[i] != state.buffer()[i])
+ identical = false;
+ }
+
+ if (identical)
+ return true;
+ }
+ }
+ else
+ return true; // no state, so we are first! (check state type instead?)
+ }
+ catch (final Exception ex)
+ {
+ }
+ }
+
+ return false;
+ }
+
+ protected BasicAction actionHandle; // must be changed if we propagate
+
+ private OptimisticLockManager managerAddress;
+ private boolean readOnly;
+ private String managerType;
+ private OutputObjectState state = new OutputObjectState();
+}
Added: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/test/java/org/jboss/stm/optimistic/OptimisticUnitTest.java
===================================================================
--- labs/jbosstm/workspace/mlittle/STM-Arjuna/src/test/java/org/jboss/stm/optimistic/OptimisticUnitTest.java (rev 0)
+++ labs/jbosstm/workspace/mlittle/STM-Arjuna/src/test/java/org/jboss/stm/optimistic/OptimisticUnitTest.java 2012-01-20 13:41:49 UTC (rev 37856)
@@ -0,0 +1,382 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2006, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. 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.jboss.stm.optimistic;
+
+import java.io.IOException;
+import java.util.Random;
+
+import org.jboss.stm.annotations.State;
+import org.jboss.stm.annotations.Transactional;
+import org.jboss.stm.annotations.ReadLock;
+import org.jboss.stm.annotations.WriteLock;
+
+import com.arjuna.ats.arjuna.AtomicAction;
+import com.arjuna.ats.arjuna.ObjectType;
+import com.arjuna.ats.arjuna.common.Uid;
+import com.arjuna.ats.arjuna.coordinator.ActionStatus;
+import com.arjuna.ats.arjuna.coordinator.BasicAction;
+import com.arjuna.ats.arjuna.state.InputObjectState;
+import com.arjuna.ats.arjuna.state.OutputObjectState;
+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 junit.framework.TestCase;
+
+/**
+ * Unit tests for the Class class.
+ *
+ * @author Mark Little
+ */
+
+public class OptimisticUnitTest extends TestCase
+{
+
+ public class AtomicObject extends OptimisticLockManager
+ {
+ public AtomicObject()
+ {
+ super();
+
+ state = 0;
+
+ AtomicAction A = new AtomicAction();
+
+ A.begin();
+
+ if (setlock(new OptimisticLock(LockMode.WRITE), 0) == LockResult.GRANTED)
+ {
+ if (A.commit() == ActionStatus.COMMITTED)
+ System.out.println("Created persistent object " + get_uid());
+ else
+ System.out.println("Action.commit error.");
+ }
+ else
+ {
+ A.abort();
+
+ System.out.println("setlock error.");
+ }
+ }
+
+ /*
+ * In the pessimistic locking case we use Locks to guard against concurrent
+ * access. In the optimistic case we don't. However, this means that multiple
+ * threads acting on the same instance can overwrite state and conflict. So we
+ * need to make these methods thread-safe. Use synchronized keyword for now, but
+ * obviously this could be finer grained. Since these are language constructs they
+ * are not maintained for the duration of the transaction.
+ */
+
+ public synchronized void incr (int value) throws Exception
+ {
+ AtomicAction A = new AtomicAction();
+
+ A.begin();
+
+ /*
+ * setlock will activate the state and create a checkpoint. It will also
+ * add a LockRecord, which takes a snapshot of the state for later comparison.
+ * That is wrong: the LockRecord needs to see the current state and the final
+ * state so that it can compare the current state with the state the object
+ * has at commit time. In fact it probably doesn't need to see the updated state
+ * at all. It could use the updated state as an optimisation: suppose the new
+ * state is different to that which existed at the time setlock was called but is
+ * identical to the state update that this method made, then we probably don't
+ * need to rollback! Someone else made the change for us!
+ */
+
+ if (setlock(new OptimisticLock(LockMode.WRITE), 0) == LockResult.GRANTED)
+ {
+ state += value;
+
+ modified();
+
+ if (A.commit() != ActionStatus.COMMITTED)
+ throw new Exception("Action commit error.");
+ else
+ return;
+ }
+
+ A.abort();
+
+ throw new Exception("Write lock error.");
+ }
+
+ public synchronized void set (int value) throws Exception
+ {
+ AtomicAction A = new AtomicAction();
+
+ A.begin();
+
+ if (setlock(new OptimisticLock(LockMode.WRITE), 0) == LockResult.GRANTED)
+ {
+ state = value;
+
+ modified();
+
+ if (A.commit() != ActionStatus.COMMITTED)
+ throw new Exception("Action commit error.");
+ else
+ return;
+ }
+
+ A.abort();
+
+ throw new Exception("Write lock error.");
+ }
+
+ public synchronized int get () throws Exception
+ {
+ AtomicAction A = new AtomicAction();
+ int value = -1;
+
+ A.begin();
+
+ if (setlock(new OptimisticLock(LockMode.READ), 0) == LockResult.GRANTED)
+ {
+ value = state;
+
+ /*
+ * We don't need to call modified for read locks, but we do need to
+ * check that the state remains unmodified at commit time. This is the job
+ * of the LockRecord. So setlock should add a LockRecord if the lock is read
+ * but should ignore if it is write, because modified must be called later
+ * instead which will do the registration.
+ */
+
+ if (A.commit() == ActionStatus.COMMITTED)
+ return value;
+ else
+ throw new Exception("Action commit error.");
+ }
+
+ A.abort();
+
+ throw new Exception("Read lock error.");
+ }
+
+ public boolean save_state (OutputObjectState os, int ot)
+ {
+ boolean result = super.save_state(os, ot);
+
+ if (!result)
+ return false;
+
+ try
+ {
+ os.packInt(state);
+ }
+ catch (IOException e)
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ public boolean restore_state (InputObjectState os, int ot)
+ {
+ boolean result = super.restore_state(os, ot);
+
+ if (!result)
+ return false;
+
+ try
+ {
+ state = os.unpackInt();
+ }
+ catch (IOException e)
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ public String type ()
+ {
+ return "/StateManager/LockManager/AtomicObject";
+ }
+
+ private int state;
+ }
+
+ public class Worker extends Thread
+ {
+ public Worker (AtomicObject obj)
+ {
+ _obj = obj;
+ }
+
+ public void run ()
+ {
+ Random rand = new Random();
+
+ for (int i = 0; i < 1000; i++)
+ {
+ boolean fault;
+
+ do
+ {
+ fault = false;
+
+ AtomicAction A = new AtomicAction();
+ boolean doCommit = true;
+
+ A.begin();
+
+ try
+ {
+ _obj.incr(i);
+ }
+ catch (final Throwable ex)
+ {
+ ex.printStackTrace();
+
+ doCommit = false;
+ fault = true;
+ }
+
+ if (doCommit)
+ {
+ if (A.commit() != ActionStatus.COMMITTED)
+ {
+ System.err.println("Failed to commit!");
+
+ fault = true;
+ }
+ }
+ else
+ A.abort();
+
+ } while (fault);
+ }
+ }
+
+ private AtomicObject _obj;
+ }
+
+ public void testAtomicObject () throws Exception
+ {
+ AtomicObject obj = new AtomicObject();
+ AtomicAction a = new AtomicAction();
+
+ a.begin();
+
+ obj.set(1234);
+
+ a.commit();
+
+ assertEquals(obj.get(), 1234);
+
+ a = new AtomicAction();
+
+ a.begin();
+
+ obj.incr(1);
+
+ a.abort();
+
+ assertEquals(obj.get(), 1234);
+ }
+
+ public void testMultiSet () throws Exception
+ {
+ AtomicObject obj = new AtomicObject();
+ AtomicAction a = new AtomicAction();
+
+ a.begin();
+
+ obj.set(1234);
+ obj.set(345);
+
+ a.commit();
+
+ assertEquals(obj.get(), 345);
+ }
+
+ public void testNestedAbort () throws Exception
+ {
+ AtomicObject obj = new AtomicObject();
+ AtomicAction a = new AtomicAction();
+ AtomicAction b = new AtomicAction();
+
+ a.begin();
+
+ obj.set(1234);
+
+ b.begin();
+
+ obj.set(345);
+
+ b.abort();
+
+ a.commit();
+
+ assertEquals(obj.get(), 1234);
+ }
+
+ public void testNestedCommit () throws Exception
+ {
+ AtomicObject obj = new AtomicObject();
+ AtomicAction a = new AtomicAction();
+ AtomicAction b = new AtomicAction();
+
+ a.begin();
+
+ obj.set(1234);
+
+ b.begin();
+
+ obj.set(345);
+
+ b.commit();
+
+ a.commit();
+
+ assertEquals(obj.get(), 345);
+ }
+
+ public void testRecoverableHammer () throws Exception
+ {
+ AtomicObject obj = new AtomicObject();
+ Worker worker1 = new Worker(obj);
+ Worker worker2 = new Worker(obj);
+
+ worker1.start();
+ worker2.start();
+
+ try
+ {
+ worker1.join();
+ worker2.join();
+ }
+ catch (final Throwable ex)
+ {
+ }
+
+ assertEquals(obj.get(), 999000); // 2* sum of series 0..1000
+ }
+}
Property changes on: labs/jbosstm/workspace/mlittle/STM-Arjuna/src/test/java/org/jboss/stm/optimistic/OptimisticUnitTest.java
___________________________________________________________________
Added: svn:executable
+ *
More information about the jboss-svn-commits
mailing list