/*
* 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 static jvstm.UtilUnsafe.UNSAFE;
public class VBoxAom<E> extends VBox<E>{
private static final AOMMarker DEFAULT_MARKER = new AOMMarker();
public VBoxAom(){
/**
* The super constructor will initialize the body with null,
* corresponding to the compact layout.
*/
super(DEFAULT_MARKER);
}
/**
* In this case the object will be instantiated in captured memory,
* corresponding to memory allocated inside a transaction that
* cannot escape (i.e., is captured by) its allocating transaction.
*/
public VBoxAom(Transaction owner){
/**
* The super constructor will initialize the body with null,
* corresponding to the compact layout.
*/
super(DEFAULT_MARKER, owner);
}
/**
* The correct constraint for T should be: T extends E & VBox<E>.
* Yet, Java generics does not allow the previous constraint.
*
* During the write-back phase of the commit, when adding a new version to a transactional
* object, AOM must first check if the object is in the extended layout. If it is, then it
* just creates a new version and appends it at the head of the history, as JVSTM did.
* If the object is in the compact layout, however, it must extend the object and add
* the new version. This is done by executing the following three steps:
*
* - (1) create a new VBoxBody instance tagged with version zero, containing the current
* values of the object's fields;
*
* - (2) create a second VBoxBody instance containing the new values produced during the
* transaction and pointing to the previous VBoxBody instance;
*
* - (3) update the object's header so that it points to the previous VBoxBody instance.
*
* The 3rd step is performed with a CAS operation and it could fail in two differents situations:
*
* - (1) an helper transaction already committed the new value.
*
* - (2) the reversion process converted the vbox to the compact layout. But in this case it will try
* to install again the new history overwriting the body field (see the CASbody method).
*
* A problematic situation of ABA could happen when a transaction Tw1 sees the vbox in the compact layout
* and a concurrent helper transaction Tw2 committed first to this vbox and the reversion process Tr reverts
* it back to the compact layout:
*
* Tw1(reads vbox) -> Tw2(commits vbox) -> Tr(revert vbox) -> Tw1(commits)
*
* In this case the CAS performed by Tw1 would succeed because the vbox's body is null, but an ABA
* problem could arise. Yet, it cannot happen the ABA situation because while Tw1 is in execution
* then the corresponding ActiveTxRecord cannot be cleaned by the GC.
*
*/
@Override
public VBoxBody<?> commit(E newValue, int txNumber) {
VBoxBody<E> currentHead = this.body;
VBoxBody<E> existingBody = null;
if (currentHead != null) {
existingBody = currentHead.getBody(txNumber);
}
if (existingBody == null || existingBody.version < txNumber) {
VBoxBody<E> newBody = null;
if(currentHead == null){
/*
* This object is still in the compact layout and it must be extended
* before committing the new value:
* - 1st - The replicate method will instantiate and initialize a new object
* with the corresponding values of the object in the compact layout - like a clone.
* - 2nd - Instantiate a VBoxBody object containing the previously
* initialized object.
*/
newBody = makeNewBody(newValue, txNumber, new VBoxBody<E>(this.replicate(), 0, null));
}
else{
newBody = makeNewBody(newValue, txNumber, currentHead);
}
/*
* - 3rd - if this object was extended in the previous if, then
* this task corresponds to the 3rd step of the object extension.
*/
existingBody = CASbody(currentHead, newBody);
}
// return the existingBody, regardless of whether the CAS succeeded
return existingBody;
}
/* Atomically replace the body with the new one if the current body is the expected.
*
* Return the body that was actually kept.
*/
@Override
protected VBoxBody<E> CASbody(VBoxBody<E> expected, VBoxBody<E> newValue){
do{
if (UNSAFE.compareAndSwapObject(this, Offsets.bodyOffset, expected, newValue)) {
expected = newValue;
} else {
/*
* If the CAS failed the new value must already be there!
* Or the object may be reverted by a concurrent GCTask and
* we will retry to commit the new body.
*/
expected = this.body;
if(expected != null)
expected = expected.getBody(newValue.version);
}
}while(expected == null);
return expected;
}
}