/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import org.junit.Assert;
import org.objectfabric.generated.SimpleClass;
public abstract class ConcurrentClient {
// Client flags
public static final int USE_TWO_INTEGERS = 1 << 0;
public static final int USE_ONE_INTEGER_PER_CLIENT = 1 << 1;
public static final int USE_ABORTS = 1 << 2;
public static final int NO_WRITE = 1 << 3;
public static final int CROSS = 1 << 4;
public static final int TRANSFER = 1 << 5;
public static final int USE_ALL = (1 << 6) - 1;
static {
int value = USE_TWO_INTEGERS;
value |= USE_ONE_INTEGER_PER_CLIENT;
value |= USE_ABORTS;
value |= NO_WRITE;
value |= CROSS;
value |= TRANSFER;
Debug.assertAlways(USE_ALL == value);
}
private ConcurrentClient() {
}
protected void onStart() {
}
@SuppressWarnings("unused")
public static void loop(final SimpleClass object, final int client, int count, final int flags) {
for (int i = 0; i < count; i++) {
final int step = i;
ExpectedExceptionThrower.executeAndReturnException(new Runnable() {
@Override
public void run() {
object.workspace().atomic(new Runnable() {
@Override
public void run() {
step(object, client, step, flags);
}
});
}
});
// Assert no memory leak
if (i == count / 2 || i == count * 9 / 10) {
TestsHelper.assertMemory("at i=" + i);
/*
* Otherwise sometimes threads run one after the other and there is no
* transaction aborted, which breaks the asserts.
*/
try {
Thread.sleep(1);
} catch (java.lang.InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
}
private static void step(SimpleClass object, int client, int step, int flags) {
if ((flags & NO_WRITE) == 0) {
if ((flags & TRANSFER) != 0) {
if (Platform.get().randomDouble() < 0.99)
Transfer.between0And1(object);
else
Transfer.to2(object);
} else if ((flags & USE_ONE_INTEGER_PER_CLIENT) != 0) {
switch (client) {
case 0:
object.int0(object.int0() + 1);
break;
case 1:
object.int1(object.int1() + 1);
break;
case 2:
object.int2(object.int2() + 1);
break;
case 3:
object.int3(object.int3() + 1);
break;
default:
Debug.fail();
}
} else if (((flags & CROSS) != 0) && Platform.get().randomBoolean()) {
int temp = object.int0();
object.int0(object.int1() + 1);
object.int1(temp);
} else {
if (((flags & USE_TWO_INTEGERS) == 0) || step % 2 == 0)
object.int0(object.int0() + 1);
else
object.int1(object.int1() + 1);
}
}
if (((flags & USE_ABORTS) != 0) && Platform.get().randomBoolean() && step != 0) {
ExpectedExceptionThrower.expectException();
ExpectedExceptionThrower.throwRuntimeException("abort");
}
}
public static String writeClientFlags(int flags) {
StringBuilder sb = new StringBuilder();
if ((flags & USE_TWO_INTEGERS) != 0)
sb.append("USE_TWO_INTEGERS, ");
if ((flags & USE_ONE_INTEGER_PER_CLIENT) != 0)
sb.append("USE_ONE_INTEGER_PER_CLIENT, ");
if ((flags & USE_ABORTS) != 0)
sb.append("USE_ABORTS, ");
if ((flags & NO_WRITE) != 0)
sb.append("NO_WRITE, ");
if ((flags & CROSS) != 0)
sb.append("CROSS, ");
if ((flags & TRANSFER) != 0)
sb.append("TRANSFER, ");
return sb.toString();
}
public static final class Transfer {
public static final int TOTAL = 1000;
private static final int COEF = 10;
public static final void between0And1(SimpleClass object) {
int transfer;
if (Platform.get().randomBoolean()) {
transfer = Platform.get().randomInt(object.int0() / COEF + 1);
object.int0(object.int0() - transfer);
object.int1(object.int1() + transfer);
} else {
transfer = Platform.get().randomInt(object.int1() / COEF + 1);
object.int1(object.int1() - transfer);
object.int0(object.int0() + transfer);
}
object.int3(transfer);
assertTotal(object);
}
public static final void to2(SimpleClass object) {
int transfer;
if (Platform.get().randomBoolean()) {
transfer = Platform.get().randomInt(object.int0() / COEF + 1);
object.int0(object.int0() - transfer);
object.int2(object.int2() + transfer);
} else {
transfer = Platform.get().randomInt(object.int1() / COEF + 1);
object.int1(object.int1() - transfer);
object.int2(object.int2() + transfer);
}
object.int3(transfer);
assertTotal(object);
}
public static final void assertTotal(final SimpleClass object) {
object.workspace().atomicRead(new Runnable() {
@Override
public void run() {
int total = object.int0() + object.int1() + object.int2();
Log.write("assertTotal: " + object.int0() + ", " + object.int1() + ", " + object.int2() + " (" + total + "), transfer: " + object.int3());
Debug.assertAlways(total == TOTAL);
Assert.assertTrue(object.int0() + object.int1() + object.int2() == TOTAL);
}
});
}
}
}