/*
* JVSTM: a Java library for Software Transactional Memory
* Copyright (C) 2005 INESC-ID Software Engineering Group
* http://www.esw.inesc-id.pt
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author's contact:
* INESC-ID Software Engineering Group
* Rua Alves Redol 9
* 1000 - 029 Lisboa
* Portugal
*/
package jvstm;
import java.util.IdentityHashMap;
import java.util.Map;
import jvstm.util.Cons;
/**
* Linear Nested Transaction, meaning that it its guaranteed that is the only
* active transaction in its nesting tree. The name was preserved for backwards
* compatibility.
* This nested transaction is able to write in-place even if its root top-level
* ancestor also did so in some VBox.
* @author nmld
*
*/
public class NestedTransaction extends ReadWriteTransaction {
protected Cons<VBox> overwrittenAncestorWriteSet = Cons.<VBox>empty();
public NestedTransaction(ReadWriteTransaction parent) {
super(parent);
// start with parent's read-set
this.bodiesRead = parent.bodiesRead;
this.arraysRead = parent.arraysRead;
this.next = parent.next;
// start with parent write-set of boxes written in place (useful to commit to parent a little faster)
this.boxesWrittenInPlace = parent.boxesWrittenInPlace;
// use the parent Orec, which will be necessarily the root top-level tx's orec
super.ancVersions = ReadWriteTransaction.EMPTY_VERSIONS;
}
@Override
public Transaction makeUnsafeMultithreaded() {
throw new Error("An Unsafe Parallel Transaction may only be spawned by another Unsafe or a Top-Level transaction");
}
@Override
public void abortTx() {
// do not call super, we do not want to make the Orec of the ancestor
// aborted (at least not yet, it might happen depending on the nature
// of the abort)
for (VBox vbox : overwrittenAncestorWriteSet) {
// revert the in-place entry that had overwritten
vbox.inplace = vbox.inplace.next;
}
this.orec.version = OwnershipRecord.ABORTED;
for (OwnershipRecord mergedLinear : linearNestedOrecs) {
mergedLinear.version = OwnershipRecord.ABORTED;
}
for (ParallelNestedTransaction mergedTx : mergedTxs) {
mergedTx.orec.version = OwnershipRecord.ABORTED;
}
// give the read set arrays, which were used exclusively by this nested or its children, back to the thread pool
Cons<VBox[]> parentArrays = this.getRWParent().bodiesRead;
Cons<VBox[]> myArrays = this.bodiesRead;
while (myArrays != parentArrays) {
returnToPool(myArrays.first());
myArrays = myArrays.rest();
}
bodiesRead = null;
boxesWritten = null;
boxesWrittenInPlace = null;
perTxValues = null;
overwrittenAncestorWriteSet = null;
mergedTxs = null;
linearNestedOrecs = null;
current.set(this.getParent());
}
protected boolean isAncestor(Transaction tx) {
Transaction nextParent = parent;
while (nextParent != null) {
if (nextParent == tx) {
return true;
}
nextParent = nextParent.parent;
}
return false;
}
// Differs from the super method because it registers overwritten entries
@Override
public <T> void setBoxValue(VBox<T> vbox, T value) {
InplaceWrite<T> inplaceWrite = vbox.inplace;
OwnershipRecord currentOwner = inplaceWrite.orec;
if (currentOwner.owner == this) {
inplaceWrite.tempValue = (value == null ? (T)NULL_VALUE : value);
return;
}
// differs here
if (isAncestor(currentOwner.owner)) {
vbox.inplace = new InplaceWrite<T>(this.orec, (value == null ? (T) NULL_VALUE : value), inplaceWrite);
overwrittenAncestorWriteSet = overwrittenAncestorWriteSet.cons(vbox);
return;
}
do {
if (currentOwner.version != 0 && currentOwner.version <= this.number) {
if (inplaceWrite.CASowner(currentOwner, this.orec)) {
inplaceWrite.tempValue = (value == null ? (T) NULL_VALUE : value);
boxesWrittenInPlace = boxesWrittenInPlace.cons(vbox);
return;
} else {
currentOwner = inplaceWrite.orec;
continue;
}
} else {
if (boxesWritten == EMPTY_MAP) {
boxesWritten = new IdentityHashMap<VBox, Object>();
}
boxesWritten.put(vbox, value == null ? NULL_VALUE : value);
return;
}
} while (true);
}
@Override
protected Transaction commitAndBeginTx(boolean readOnly) {
commitTx(true);
return beginWithActiveRecord(readOnly, null);
}
@Override
protected void finish() {
bodiesRead = null;
boxesWritten = null;
perTxValues = null;
overwrittenAncestorWriteSet = null;
boxesWrittenInPlace = null;
mergedTxs = null;
linearNestedOrecs = null;
}
@Override
protected void doCommit() {
tryCommit();
bodiesRead = Cons.empty();
boxesWritten = EMPTY_MAP;
perTxValues = EMPTY_MAP;
overwrittenAncestorWriteSet = Cons.empty();
boxesWrittenInPlace = Cons.empty();
mergedTxs = Cons.empty();
linearNestedOrecs = Cons.empty();
}
@Override
protected void tryCommit() {
ReadWriteTransaction parent = getRWParent();
// update parent's read-set
parent.bodiesRead = this.bodiesRead;
parent.next = this.next;
// update parent's write-set
// first, add boxesWritten to parent. Warning: 'this.boxesWritten'
// may overwrite values of vboxes in parent.boxesWrittenInPlace, so
// care must be taken to check for this case. Also we could have
// written to this.boxesWritten as well as simultaneously to the
// VBox.tempValue, so check for that as well.
for (Map.Entry<VBox, Object> entry : this.boxesWritten.entrySet()) {
VBox vbox = entry.getKey();
Object value = entry.getValue();
if (vbox.inplace.orec.owner == this) {
// if this nested also wrote in-place, then it was after this private write
continue;
} else {
if (parent.boxesWritten == EMPTY_MAP) {
parent.boxesWritten = new IdentityHashMap<VBox, Object>();
}
parent.boxesWritten.put(vbox, value);
}
}
// pass the orecs of linear nested transactions
orec.owner = parent;
Cons<OwnershipRecord> linearNestedAlreadyMerged = parent.linearNestedOrecs.cons(this.orec);
for (OwnershipRecord linearNestedToMerge : this.linearNestedOrecs) {
linearNestedToMerge.owner = parent;
linearNestedAlreadyMerged = linearNestedAlreadyMerged.cons(linearNestedToMerge);
}
parent.linearNestedOrecs = linearNestedAlreadyMerged;
// pass parallel nested transactions
Cons<ParallelNestedTransaction> txsAlreadyMerged = parent.mergedTxs;
for (ParallelNestedTransaction mergedTx : mergedTxs) {
mergedTx.orec.owner = parent;
txsAlreadyMerged = txsAlreadyMerged.cons(mergedTx);
}
parent.mergedTxs = txsAlreadyMerged;
// now, pass the boxes to the parent
parent.boxesWrittenInPlace = this.boxesWrittenInPlace;
if (parent.perTxValues == EMPTY_MAP) {
parent.perTxValues = perTxValues;
} else {
parent.perTxValues.putAll(perTxValues);
}
parent.arraysRead = this.arraysRead;
if (parent.arrayWrites == EMPTY_MAP) {
parent.arrayWrites = arrayWrites;
parent.arrayWritesCount = arrayWritesCount;
} else {
// Propagate arrayWrites and correctly update the parent's arrayWritebacks counter
for (VArrayEntry<?> entry : arrayWrites.values()) {
if (parent.arrayWrites.put(entry, entry) != null) continue;
// Count number of writes to the array
Integer writeCount = parent.arrayWritesCount.get(entry.array);
if (writeCount == null) writeCount = 0;
parent.arrayWritesCount.put(entry.array, writeCount + 1);
}
}
}
}