提交 f1b35267 authored 作者: Andrei Tokar's avatar Andrei Tokar

MVTable.lastModificationId race condition

上级 e9b9edb6
......@@ -344,6 +344,10 @@ public abstract class Query extends Prepared {
if (!cacheableChecked) {
long max = getMaxDataModificationId();
noCache = max == Long.MAX_VALUE;
if (!isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR) ||
!isEverything(ExpressionVisitor.INDEPENDENT_VISITOR)) {
noCache = true;
}
cacheableChecked = true;
}
if (noCache) {
......@@ -356,18 +360,10 @@ public abstract class Query extends Prepared {
return false;
}
}
if (!isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR) ||
!isEverything(ExpressionVisitor.INDEPENDENT_VISITOR)) {
return false;
}
if (db.getModificationDataId() > lastEval &&
getMaxDataModificationId() > lastEval) {
return false;
}
return true;
return getMaxDataModificationId() <= lastEval;
}
public final Value[] getParameterValues() {
private Value[] getParameterValues() {
ArrayList<Parameter> list = getParameters();
if (list == null) {
return new Value[0];
......
......@@ -14,6 +14,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
......@@ -105,7 +106,7 @@ public class MVTable extends TableBase {
private MVPrimaryIndex primaryIndex;
private final ArrayList<Index> indexes = Utils.newSmallArrayList();
private volatile long lastModificationId;
private final AtomicLong lastModificationId = new AtomicLong();
private volatile Session lockExclusiveSession;
// using a ConcurrentHashMap as a set
......@@ -661,7 +662,7 @@ public class MVTable extends TableBase {
@Override
public void removeRow(Session session, Row row) {
lastModificationId = database.getNextModificationDataId();
syncLastModificationIdWithDatabase();
Transaction t = session.getTransaction();
long savepoint = t.setSavepoint();
try {
......@@ -682,7 +683,7 @@ public class MVTable extends TableBase {
@Override
public void truncate(Session session) {
lastModificationId = database.getNextModificationDataId();
syncLastModificationIdWithDatabase();
for (int i = indexes.size() - 1; i >= 0; i--) {
Index index = indexes.get(i);
index.truncate(session);
......@@ -694,7 +695,7 @@ public class MVTable extends TableBase {
@Override
public void addRow(Session session, Row row) {
lastModificationId = database.getNextModificationDataId();
syncLastModificationIdWithDatabase();
Transaction t = session.getTransaction();
long savepoint = t.setSavepoint();
try {
......@@ -715,7 +716,7 @@ public class MVTable extends TableBase {
@Override
public void updateRow(Session session, Row oldRow, Row newRow) {
newRow.setKey(oldRow.getKey());
lastModificationId = database.getNextModificationDataId();
syncLastModificationIdWithDatabase();
Transaction t = session.getTransaction();
long savepoint = t.setSavepoint();
try {
......@@ -782,7 +783,7 @@ public class MVTable extends TableBase {
@Override
public long getMaxDataModificationId() {
return lastModificationId;
return lastModificationId.get();
}
public boolean getContainsLargeObject() {
......@@ -895,8 +896,23 @@ public class MVTable extends TableBase {
*/
public void commit() {
if (database != null) {
lastModificationId = database.getNextModificationDataId();
}
syncLastModificationIdWithDatabase();
}
}
// Field lastModificationId can not be just a volatile, because window of opportunity
// between reading database's modification id and storing this value in the field
// could be exploited by another thread.
// Second thread may do the same with possibly bigger (already advanced) modification id,
// and when first thread finally updates the field, it will result in lastModificationId jumping back.
// This is, of course, unacceptable.
private void syncLastModificationIdWithDatabase() {
long nextModificationDataId = database.getNextModificationDataId();
long currentId;
do {
currentId = lastModificationId.get();
} while (nextModificationDataId > currentId &&
!lastModificationId.compareAndSet(currentId, nextModificationDataId));
}
/**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论