allow upgrading promise to lock
package com.orientechnologies.common.concur.lock;
import com.orientechnologies.orient.core.id.ORID;
+import com.orientechnologies.orient.core.tx.OTransactionId;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+// Allows creating a promise and upgrading it to a lock.
public class OSimplePromiseManager {
private final Lock lock = new ReentrantLock();
- private final Map<ORID, Integer> map = new ConcurrentHashMap<>();
+ private final Map<ORID, Promise> map = new ConcurrentHashMap<>();
- public void promise(ORID rid, int version) {
+ // todo: could use a timeout
+ // todo: should I use Request ID instead of transaction ID?
+ public void promise(ORID rid, int version, OTransactionId txId) {
lock.lock();
try {
- Integer v = map.get(rid);
- if (v != null) {
- throw new OLockException(String.format("RID '%s' is promised with version %d", rid, v));
+ Promise p = map.get(rid);
+ if (p != null) {
+ throw new OLockException(
+ String.format(
+ "RID '%s' is promised with version %d to tx '%s'",
+ rid, p.getVersion(), p.getTxId()));
}
- map.put(rid, version);
+ map.put(rid, new Promise(version, txId));
} finally {
lock.unlock();
}
}
- public void release(ORID rid, int version) {
+ public OTransactionId lock(ORID rid, int version, OTransactionId txId, boolean force) {
lock.lock();
try {
- Integer v = map.get(rid);
- if (v == null || v != version) {
- // safe to ignore?
+ Promise p = map.get(rid);
+ if (p == null) {
+ p = new Promise(version, txId);
+ p.lock();
+ map.put(rid, p);
+ } else {
+ if (p.isLocked()) {
+ throw new OLockException(String.format("Cannot acquire lock for resource: '%s'", rid));
+ }
+ if (force) {
+ // todo: must check version
+ OTransactionId kickedOut = p.getTxId();
+ p = new Promise(version, txId);
+ p.lock();
+ map.put(rid, p);
+ return kickedOut;
+ } else {
+ if (p.txId.equals(txId)) {
+ p.lock();
} else {
+ throw new OLockException(
+ String.format(
+ "Resource '%s' is promised with version %d to tx '%s'",
+ rid, p.getVersion(), p.getTxId()));
+ }
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ return null;
+ }
+
+ public void release(ORID rid, int version) {
+ lock.lock();
+ try {
+ Promise p = map.get(rid);
+ // assert p != null;
+ // or safe to ignore?
+ if (p.getVersion() == version) {
map.remove(rid);
}
} finally {
@@ -46,4 +89,31 @@ public class OSimplePromiseManager {
lock.unlock();
}
}
+
+ public static class Promise {
+ private final Integer version;
+ private final OTransactionId txId;
+ private boolean locked = false;
+
+ public Promise(Integer version, OTransactionId txId) {
+ this.version = version;
+ this.txId = txId;
+ }
+
+ public Integer getVersion() {
+ return version;
+ }
+
+ public OTransactionId getTxId() {
+ return txId;
+ }
+
+ public void lock() {
+ locked = true;
+ }
+
+ public boolean isLocked() {
+ return locked;
+ }
+ }
}
