/* * 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 jvstm.util.Cons; /* An inevitable transaction is accomplished as follows: 1) first it tries to enqueue a commit * record using a compare-and-swap; 2) when it succeeds it ensures that the previous record is * committed; 3) then it can execute without interference from other write transactions. The commit * does not need to validate the transaction, because it will read from the immediately previous * state. * * There are two catches: 1) inevitable transactions must still keep the write-set for the following * transactions to validate against this commit; 2) the write-set cannot be made accessible until * the end of the transaction, thus a special InevitableTransactionsRecord is used that doesn't * return the write-set until it has been computed, even though its commit record may already exist. */ public class InevitableTransaction extends TopLevelTransaction { private Cons<VBox> vboxesWrittenBack = Cons.empty(); public InevitableTransaction(ActiveTransactionsRecord activeRecord) { super(activeRecord); } @Override public void start() { ActiveTransactionsRecord latestRecord = this.activeTxRecord; // start by enqueueing the request do { latestRecord = findLatestRecord(latestRecord); setCommitTxRecord(new InevitableActiveTransactionsRecord(latestRecord.transactionNumber + 1)); } while (!latestRecord.trySetNext(getCommitTxRecord())); ensureCommitStatus(); upgradeTx(latestRecord); // once we get here, we may already increment the transaction number. // This is also required to allow setBoxValue to immediately write to // the vbox.body setNumber(getCommitTxRecord().transactionNumber); super.start(); } // Note that this method differs from the one in the superclass. The loop guard doesn't include // our own record. We don't want to commit it just yet. @Override protected void ensureCommitStatus() { ActiveTransactionsRecord recordToCommit = Transaction.mostRecentCommittedRecord.getNext(); while ((recordToCommit != null) && (recordToCommit.transactionNumber < getCommitTxRecord().transactionNumber)) { helpCommit(recordToCommit); recordToCommit = recordToCommit.getNext(); } } protected ActiveTransactionsRecord findLatestRecord(ActiveTransactionsRecord from) { ActiveTransactionsRecord latest = from; for (ActiveTransactionsRecord aux; (aux = latest.getNext()) != null; latest = aux) { ; } return latest; } // Also, InevitableTransactions cannot abort because their commit record as already been created @Override public void abortTx() { commitTx(true); //throw new Error("An Inevitable transaction cannot abort. I've committed it instead."); } @Override public Transaction makeNestedTransaction(boolean readOnly) { throw new Error(getClass().getSimpleName() + " doesn't support nesting yet"); } @Override public <T> T getBoxValue(VBox<T> vbox) { // we don't keep a read-set because this transaction will be valid for sure return vbox.body.value; } // Always store in place, given that commits are not ocurring // concurrently, but still keep the write-set to enable other transactions // to validate against this one. @Override public <T> void setBoxValue(VBox<T> vbox, T value) { VBoxBody<T> body = vbox.body; if ((body != null) && (body.version == this.number)) { /* * If the head of the versioned history corresponds to the body * created by this transaction then there is no chance of this * object being reverted. */ body.value = value; } else { VBoxBody<T> newBody; if(body == null){ newBody = VBox.makeNewBody(value, number, vbox instanceof VBoxAom? new VBoxBody<T>(vbox.replicate(), 0, null) : null); }else{ newBody = VBox.makeNewBody(value, number, body); } this.vboxesWrittenBack = this.vboxesWrittenBack.cons(vbox); /* * We must prevent from concurrent reversions * The following CAS of the VBoxAom retries if the object has been reverted. */ vbox.CASbody(body, newBody); // vbox.body = newBody; } } @Override public <T> T getPerTxValue(PerTxBox<T> box, T initial) { throw new Error(getClass().getSimpleName() + " doesn't support PerTxBoxes yet"); } @Override public <T> void setPerTxValue(PerTxBox<T> box, T value) { throw new Error(getClass().getSimpleName() + " doesn't support PerTxBoxes yet"); } @Override public WriteSet makeWriteSet() { return new WriteSet(vboxesWrittenBack); } @Override protected void tryCommit() { ActiveTransactionsRecord commitRecord = getCommitTxRecord(); // we know we're valid and we're already enqueued. just set the writeset ((InevitableActiveTransactionsRecord)commitRecord).setWriteSet(makeWriteSet()); helpCommit(commitRecord); upgradeTx(commitRecord); } @Override public <T> T getArrayValue(VArrayEntry<T> entry) { // Read directly from array return entry.array.values.get(entry.index); } @Override public <T> void setArrayValue(VArrayEntry<T> entry, T value) { throw new Error(getClass().getSimpleName() + " doesn't support writing to VArrays yet"); } }