From 668ae4ddd3f79926a47d552e8e7b825550bdc09f Mon Sep 17 00:00:00 2001 From: Nuno Diegues Date: Thu, 29 May 2014 11:49:17 +0200 Subject: [PATCH] Parallel nested transactions had a bug on the abort procedure. A test was added that explains the problem along with the fix on the corresponding file. In summary, more than one depth of nesting could cause a top level transaction to incorrectly take ownership of a VBox from another top level transaction. --- .../simple/AbortedParallelTxTest.java | 154 +++++++++++++++++++++ src/main/java/jvstm/ParallelNestedTransaction.java | 20 +-- 2 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 examples/parallel-nesting/simple/AbortedParallelTxTest.java diff --git a/examples/parallel-nesting/simple/AbortedParallelTxTest.java b/examples/parallel-nesting/simple/AbortedParallelTxTest.java new file mode 100644 index 0000000..192843b --- /dev/null +++ b/examples/parallel-nesting/simple/AbortedParallelTxTest.java @@ -0,0 +1,154 @@ +package simple; + +import java.util.ArrayList; +import java.util.List; + +import jvstm.CommitException; +import jvstm.ParallelTask; +import jvstm.Transaction; +import jvstm.VBox; + +/** + * Test the following scenario + * A writes x=1 + * A spawns B + * B spawns C e D + * C writes x=2 + * C reads y=0 + * D writes y=1 + * D commits + * C aborts + * at this point, another top level transaction Z tries to write to x and acquires ownership of the VBox + * when in fact the ownership belonged to A + * + * + * @author nmld + * + */ +public class AbortedParallelTxTest { + + protected static final VBox x = new VBox(0); + protected static final VBox y = new VBox(0); + + public static class TopLevelA extends Thread { + + public void run() { + Transaction.begin(false); + try { + int valX = x.get(); + x.put(valX + 1); + + List> tasks = new ArrayList>(); + tasks.add(new NestedB()); + Transaction.current().manageNestedParallelTxs(tasks); + + Transaction.commit(); + + throw new AssertionError("TopLevelA should not commit because of Z"); + + } catch (CommitException ce) { + Transaction.abort(); + } + } + } + + public static class NestedB extends ParallelTask { + + @Override + public Void execute() throws Throwable { + List> tasks = new ArrayList>(); + tasks.add(new NestedC()); + tasks.add(new NestedD()); + Transaction.current().manageNestedParallelTxs(tasks); + return null; + } + + } + + public static class NestedC extends ParallelTask { + + private boolean isRepeating = false; + + @Override + public Void execute() throws Throwable { + if (isRepeating) { + Thread.sleep(3000); + } + + int valX = x.get(); + x.put(valX + 1); + int valY = y.get(); + + if (valX != 1) { + throw new AssertionError("NestedC read valX != 1: " + valX); + } + if (!isRepeating && valY != 0) { + throw new AssertionError("NestedC was not yet aborted, yet it read valY != 0: " + valY); + } + if (isRepeating && valY != 1) { + throw new AssertionError("NestedC was already aborted, yet it read valY != 1: " + valY); + } + + isRepeating = true; + + Thread.sleep(750); + + return null; + } + + } + + public static class NestedD extends ParallelTask { + + @Override + public Void execute() throws Throwable { + Thread.sleep(500); + + int valY = y.get(); + y.put(valY + 1); + + if (valY != 0) { + throw new AssertionError("NestedD read valY != 0: " + valY); + } + + return null; + } + + } + + public static class TopLevelZ extends Thread { + + public void run() { + try { + + Transaction.begin(false); + Thread.sleep(2000); + + int valX = x.get(); + x.put(valX + 1); + Transaction.commit(); + + } catch (InterruptedException e) { + } + } + + } + + public static void main(String[] args) throws InterruptedException { + TopLevelA A = new TopLevelA(); + TopLevelZ Z = new TopLevelZ(); + A.start(); + Z.start(); + A.join(); + Z.join(); + if (x.get() != 1) { + throw new AssertionError("Check in the end verified that x != 1"); + } + if (y.get() != 0) { + throw new AssertionError("Check in the end verified that y != 0"); + } + + System.out.println("Successful test"); + } + +} diff --git a/src/main/java/jvstm/ParallelNestedTransaction.java b/src/main/java/jvstm/ParallelNestedTransaction.java index 57754b5..1f299d6 100644 --- a/src/main/java/jvstm/ParallelNestedTransaction.java +++ b/src/main/java/jvstm/ParallelNestedTransaction.java @@ -141,14 +141,18 @@ public class ParallelNestedTransaction extends ReadWriteTransaction { } private void manualAbort() { - for (ParallelNestedTransaction mergedIntoParent : getRWParent().mergedTxs) { - for (VBox vboxMergedIntoParent : mergedIntoParent.boxesWrittenInPlace) { - revertOverwrite(vboxMergedIntoParent); - } - } - for (VBox vboxMergedIntoParent : getRWParent().boxesWrittenInPlace) { - revertOverwrite(vboxMergedIntoParent); - } + ReadWriteTransaction parent = getRWParent(); + while (parent != null) { + for (ParallelNestedTransaction mergedIntoParent : parent.mergedTxs) { + for (VBox vboxMergedIntoParent : mergedIntoParent.boxesWrittenInPlace) { + revertOverwrite(vboxMergedIntoParent); + } + } + for (VBox vboxMergedIntoParent : parent.boxesWrittenInPlace) { + revertOverwrite(vboxMergedIntoParent); + } + parent = parent.getRWParent(); + } this.orec.version = OwnershipRecord.ABORTED; for (ReadWriteTransaction child : mergedTxs) { -- 2.11.4.GIT