All right, I see. I thought that because both threads are in the
atomic block when the exception is thrown both threads (aka
transactions) would be rolled back. I was mistaken there, I guess.
But I have a better one now. There is first my test class ValueHolder
where things work as expected. I made use of Semaphores and
CountDownLatches to make sure that the critical situation to test the
behavior of DeuceSTM is created. Then I found out that this doesn't
work since the Semaphores and stuff are then within the atomic block.
So I changed to inserting breakpoints. Let the threads run to the
respective breakpoints, let one thread continue after in the debugger
to create the problem situation, etc. See the sysouts in
ValueHolder.changeValues(...) and ValueMap.changeValues(...) that
comment where to suspend threads and how to continue.
Problem is this, that things work well with the test class ValueHolder
where the variables that are changed are instance variables. I get the
output:
breakpoint 1: only 1 thread allowed to pass
breakpoint 2: both threads have to get here before they may continue
breakpoint 1: only 1 thread allowed to pass
changeValues foo: 6
changeValues bar: 11
foo: 6
bar: 11
breakpoint 1: only 1 thread allowed to pass
breakpoint 2: both threads have to get here before they may continue
changeValues foo: 9
changeValues bar: 12
But things don't work well with class ValueMap where the variables
changed are not instance variables but values in a map stored in an
instance variable. For the test class ValueMap I get some other
output:
breakpoint 1: only 1 thread allowed to pass
breakpoint 1: only 1 thread allowed to pass
breakpoint 2: both threads have to get here before they may continue
changeValues foo: 9
changeValues bar: 12
breakpoint 2: both threads have to get here before they may continue
changeValues foo: 9
changeValues bar: 14
foo: 9
bar: 14
When I step through the debugger through ValueHolder.changeValues(...)
I can see that when the second thread runs through this method a retry
is done, e.g. the given thread jumps back on the stack and runs
ValueHolder.changeValues(...) again. This behavior I can't see when
running the ValueMap test case.
Find the code for these classes below. It's an awful lot of code. My
apologies. I stripped things as much down as I could, though. For my
problem the number of instance variables of some class to be guarded
with an atomic block is not known at compile time. This is why I made
this ValueMap test class in addition.
Maybe you see the problem. I hope I did the instrumentation right also
for the ValueMap test class. I think I did. Find below the code.
Maybe you have some idea.
Thanks and cheers, Oliver
---------------- BEGIN ValueHolder ----------------
public class ValueHolder
{
int foo = 0;
int bar = 0;
public ValueHolder() {
super();
}
public ValueHolder(int foo, int bar) {
super();
this.foo = foo;
this.bar = bar;
}
@Atomic
public void changeValues(int value) throws InterruptedException
{
System.out.println("breakpoint 1: only 1 thread allowed to
pass" );
int tempFoo = foo;
foo = tempFoo + value;
System.out.println("breakpoint 2: both threads have to get
here before they may continue" );
bar = foo + value;
System.out.println("changeValues foo: " + foo);
System.out.println("changeValues bar: " + bar);
}
public static void main(String[] args) throws InterruptedException
{
changeValuesBreakpoints();
}
protected static void changeValuesBreakpoints() throws
InterruptedException
{
ValueHolder valueHolder = new ValueHolder(1, 2);
Thread th1 = new Thread(new
ValueChangerBreakpoints(valueHolder, 3));
Thread th2 = new Thread(new
ValueChangerBreakpoints(valueHolder, 5));
th1.start();
th2.start();
// wait very long till done with debugging or put breakpoint here
Thread.sleep(50000);
System.out.println("foo: " + valueHolder.foo);
System.out.println("bar: " + valueHolder.bar);
}
static class ValueChangerBreakpoints implements Runnable
{
ValueHolder valueHolder = null;
int value = 0;
public ValueChangerBreakpoints() { }
public ValueChangerBreakpoints(ValueHolder valueHolder, int
value) {
super();
this.valueHolder = valueHolder;
this.value = value;
}
public void run() {
try {
valueHolder.changeValues(value);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
---------------- END ValueHolder ----------------
---------------- BEGIN ValueMap ----------------
public class ValueMap
{
public Map<String, Integer> values = new HashMap<String,
Integer>();
public ValueMap()
{
super();
}
public ValueMap(int foo, int bar)
{
super();
values.put("foo", foo);
values.put("bar", bar);
}
@Atomic
public void changeValues(int value) throws InterruptedException
{
System.out.println("breakpoint 1: only 1 thread allowed to
pass");
int tempFoo = values.get("foo");
values.put("foo", tempFoo + value);
System.out.println("breakpoint 2: both threads have to get
here before they may continue");
values.put("bar", values.get("foo") + value);
System.out.println("changeValues foo: " + values.get("foo"));
System.out.println("changeValues bar: " + values.get("bar"));
}
public static void main(String[] args) throws InterruptedException
{
changeValuesBreakpoints();
}
protected static void changeValuesBreakpoints() throws
InterruptedException
{
ValueMap valueMap = new ValueMap(1, 2);
Thread th1 = new Thread(new ValueChangerBreakpoints(valueMap,
3));
Thread th2 = new Thread(new ValueChangerBreakpoints(valueMap,
5));
th1.start();
th2.start();
// wait very long till done with debugging or put breakpoint here
Thread.sleep(50000);
System.out.println("foo: " + valueMap.values.get("foo"));
System.out.println("bar: " + valueMap.values.get("bar"));
}
static class ValueChangerBreakpoints implements Runnable
{
ValueMap valueMap = null;
int value = 0;
public ValueChangerBreakpoints()
{
}
public ValueChangerBreakpoints(ValueMap valueMap, int value)
{
super();
this.valueMap = valueMap;
this.value = value;
}
public void run()
{
try
{
valueMap.changeValues(value);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
---------------- END ValueMap ----------------