/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.store;
import org.h2.test.TestBase;
/**
* Test using volatile fields to ensure we don't read from a version that is
* concurrently written to.
*/
public class TestSpinLock extends TestBase {
/**
* The version to use for writing.
*/
volatile int writeVersion;
/**
* The current data object.
*/
volatile Data data = new Data(0, null);
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws Exception {
final TestSpinLock obj = new TestSpinLock();
Thread t = new Thread() {
@Override
public void run() {
while (!isInterrupted()) {
for (int i = 0; i < 10000; i++) {
Data d = obj.copyOnWrite();
obj.data = d;
d.write(i);
d.writing = false;
}
}
}
};
t.start();
try {
for (int i = 0; i < 100000; i++) {
Data d = obj.getImmutable();
int z = d.x + d.y;
if (z != 0) {
String error = i + " result: " + z + " now: " + d.x + " "
+ d.y;
System.out.println(error);
throw new Exception(error);
}
}
} finally {
t.interrupt();
t.join();
}
}
/**
* Clone the data object if necessary (if the write version is newer than
* the current version).
*
* @return the data object
*/
Data copyOnWrite() {
Data d = data;
d.writing = true;
int w = writeVersion;
if (w <= data.version) {
return d;
}
Data d2 = new Data(w, data);
d2.writing = true;
d.writing = false;
return d2;
}
/**
* Get an immutable copy of the data object.
*
* @return the immutable object
*/
private Data getImmutable() {
Data d = data;
++writeVersion;
// wait until writing is done,
// but only for the current write operation:
// a bit like a spin lock
while (d.writing) {
// Thread.yield() is not required, specially
// if there are multiple cores
// but getImmutable() doesn't
// need to be that fast actually
Thread.yield();
}
return d;
}
/**
* The data class - represents the root page.
*/
static class Data {
/**
* The version.
*/
final int version;
/**
* The values.
*/
int x, y;
/**
* Whether a write operation is in progress.
*/
volatile boolean writing;
/**
* Create a copy of the data.
*
* @param version the new version
* @param old the old data or null
*/
Data(int version, Data old) {
this.version = version;
if (old != null) {
this.x = old.x;
this.y = old.y;
}
}
/**
* Write to the fields in an unsynchronized way.
*
* @param value the new value
*/
void write(int value) {
this.x = value;
this.y = -value;
}
}
}