提交 5de4e30b authored 作者: Andrei Tokar's avatar Andrei Tokar

locking of the append buffer

上级 d6b7a8d9
...@@ -51,6 +51,8 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -51,6 +51,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
private final K[] keysBuffer; private final K[] keysBuffer;
private final V[] valuesBuffer; private final V[] valuesBuffer;
private final Object lock = new Object();
private volatile boolean notificationRequested;
/** /**
* Whether the map is closed. Volatile so we don't accidentally write to a * Whether the map is closed. Volatile so we don't accidentally write to a
...@@ -425,7 +427,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -425,7 +427,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
Page emptyRootPage = createEmptyLeaf(); Page emptyRootPage = createEmptyLeaf();
int attempt = 0; int attempt = 0;
do { do {
rootReference = getRoot(); rootReference = getRootInternal();
} while (!updateRoot(rootReference, emptyRootPage, ++attempt)); } while (!updateRoot(rootReference, emptyRootPage, ++attempt));
rootReference.root.removeAllRecursive(); rootReference.root.removeAllRecursive();
} }
...@@ -780,9 +782,15 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -780,9 +782,15 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
public final RootReference getRoot() { public final RootReference getRoot() {
return flushAndGetRoot();
}
private RootReference flushAndGetRoot() {
RootReference rootReference = getRootInternal(); RootReference rootReference = getRootInternal();
return singleWriter && rootReference.getAppendCounter() > 0 ? if (singleWriter && rootReference.getAppendCounter() > 0) {
flushAppendBuffer(rootReference) : rootReference; return flushAppendBuffer(rootReference, false);
}
return rootReference;
} }
private RootReference getRootInternal() { private RootReference getRootInternal() {
...@@ -836,7 +844,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -836,7 +844,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
RootReference updatedRootReference = new RootReference(newRootPage, newVersion, previous, updateCounter, RootReference updatedRootReference = new RootReference(newRootPage, newVersion, previous, updateCounter,
attemptUpdateCounter, false); attemptUpdateCounter);
boolean success = root.compareAndSet(currentRoot, updatedRootReference); boolean success = root.compareAndSet(currentRoot, updatedRootReference);
return success ? updatedRootReference : null; return success ? updatedRootReference : null;
} }
...@@ -969,13 +977,13 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -969,13 +977,13 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @return the number of entries * @return the number of entries
*/ */
public final long sizeAsLong() { public final long sizeAsLong() {
RootReference rootReference = getRoot(); RootReference rootReference = getRootInternal();
return rootReference.root.getTotalCount() + rootReference.getAppendCounter(); return rootReference.root.getTotalCount() + rootReference.getAppendCounter();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
RootReference rootReference = getRoot(); RootReference rootReference = getRootInternal();
Page rootPage = rootReference.root; Page rootPage = rootReference.root;
return rootPage.isLeaf() && rootPage.getKeyCount() == 0 && rootReference.getAppendCounter() == 0; return rootPage.isLeaf() && rootPage.getKeyCount() == 0 && rootReference.getAppendCounter() == 0;
} }
...@@ -1192,83 +1200,139 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1192,83 +1200,139 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* If map was used in append mode, this method will ensure that append buffer * If map was used in append mode, this method will ensure that append buffer
* is flushed - emptied with all entries inserted into map as a new leaf. * is flushed - emptied with all entries inserted into map as a new leaf.
* @param rootReference current RootReference * @param rootReference current RootReference
* @param lockedForUpdate whether rootReference is pre-locked already and
* should stay locked upon return
* @return potentially updated RootReference * @return potentially updated RootReference
*/ */
private RootReference flushAppendBuffer(RootReference rootReference) { private RootReference flushAppendBuffer(RootReference rootReference, boolean lockedForUpdate) {
int attempt = 0; IntValueHolder unsavedMemoryHolder = new IntValueHolder();
int keyCount; RootReference lockedRootReference = lockedForUpdate ? rootReference : null;
while((keyCount = rootReference.getAppendCounter()) > 0) { try {
Page page = Page.createLeaf(this, int attempt = 0;
Arrays.copyOf(keysBuffer, keyCount), int keyCount;
Arrays.copyOf(valuesBuffer, keyCount), while ((keyCount = rootReference.getAppendCounter()) > 0) {
0); if (lockedRootReference == null) {
CursorPos pos = rootReference.root.getAppendCursorPos(null); lockedRootReference = tryLock(rootReference, ++attempt);
assert page.map == this; rootReference = lockedRootReference == null ? getRootInternal() : lockedRootReference;
assert pos != null; continue;
assert page.getKeyCount() > 0;
Object key = page.getKey(0);
assert pos.index < 0 : pos.index;
int index = -pos.index - 1;
assert index == pos.page.getKeyCount() : index + " != " + pos.page.getKeyCount();
Page p = pos.page;
pos = pos.parent;
CursorPos tip = pos;
int unsavedMemory = page.getMemory();
while (true) {
if (pos == null) {
if (p.getKeyCount() == 0) {
p = page;
} else {
Object[] keys = new Object[] { key };
Page.PageReference[] children = new Page.PageReference[] {
new Page.PageReference(p),
new Page.PageReference(page)};
p = Page.createNode(this, keys, children, p.getTotalCount() + page.getTotalCount(), 0);
}
break;
} }
Page c = p;
p = pos.page; Page rootPage = rootReference.root;
index = pos.index;
CursorPos pos = rootPage.getAppendCursorPos(null);
assert pos != null;
assert pos.index < 0 : pos.index;
int index = -pos.index - 1;
assert index == pos.page.getKeyCount() : index + " != " + pos.page.getKeyCount();
Page p = pos.page;
CursorPos tip = pos;
pos = pos.parent; pos = pos.parent;
p = p.copy();
p.setChild(index, page); int remainingBuffer = 0;
p.insertNode(index, key, c); Page page = null;
if ((keyCount = p.getKeyCount()) <= store.getKeysPerPage() && int available = store.getKeysPerPage() - p.getKeyCount();
(p.getMemory() < store.getMaxPageSize() || keyCount <= (p.isLeaf() ? 1 : 2))) { if (available > 0) {
break; p = p.copy();
if (keyCount <= available) {
p.expand(keyCount, keysBuffer, valuesBuffer);
} else {
p.expand(available, keysBuffer, valuesBuffer);
keyCount -= available;
if (lockedForUpdate) {
System.arraycopy(keysBuffer, available, keysBuffer, 0, keyCount);
System.arraycopy(valuesBuffer, available, valuesBuffer, 0, keyCount);
remainingBuffer = keyCount;
} else {
Object[] keys = new Object[keyCount];
Object[] values = new Object[keyCount];
System.arraycopy(keysBuffer, available, keys, 0, keyCount);
System.arraycopy(valuesBuffer, available, values, 0, keyCount);
page = Page.createLeaf(this, keys, values, 0);
}
}
} else {
page = Page.createLeaf(this,
Arrays.copyOf(keysBuffer, keyCount),
Arrays.copyOf(valuesBuffer, keyCount),
0);
} }
int at = keyCount - 2;
key = p.getKey(at); unsavedMemoryHolder.value = 0;
page = p.split(at); if (page != null) {
unsavedMemory += p.getMemory() + page.getMemory(); assert page.map == this;
} assert page.getKeyCount() > 0;
unsavedMemory += p.getMemory(); Object key = page.getKey(0);
while (pos != null) { unsavedMemoryHolder.value += page.getMemory();
Page c = p; while (true) {
p = pos.page; if (pos == null) {
p = p.copy(); if (p.getKeyCount() == 0) {
p.setChild(pos.index, c); p = page;
unsavedMemory += p.getMemory(); } else {
pos = pos.parent; Object[] keys = new Object[]{key};
} Page.PageReference[] children = new Page.PageReference[]{
RootReference updatedRootReference = new RootReference(rootReference, p, ++attempt); new Page.PageReference(p),
if(root.compareAndSet(rootReference, updatedRootReference)) { new Page.PageReference(page)};
while (tip != null) { p = Page.createNode(this, keys, children, p.getTotalCount() + page.getTotalCount(), 0);
tip.page.removePage(); }
tip = tip.parent; break;
}
Page c = p;
p = pos.page;
index = pos.index;
pos = pos.parent;
p = p.copy();
p.setChild(index, page);
p.insertNode(index, key, c);
if ((keyCount = p.getKeyCount()) <= store.getKeysPerPage() &&
(p.getMemory() < store.getMaxPageSize() || keyCount <= (p.isLeaf() ? 1 : 2))) {
break;
}
int at = keyCount - 2;
key = p.getKey(at);
page = p.split(at);
unsavedMemoryHolder.value += p.getMemory() + page.getMemory();
}
} }
if (store.getFileStore() != null) { p = replacePage(pos, p, unsavedMemoryHolder);
store.registerUnsavedPage(unsavedMemory);
RootReference updatedRootReference = new RootReference(rootReference, p, remainingBuffer, lockedForUpdate);
if (root.compareAndSet(rootReference, updatedRootReference)) {
lockedRootReference = null;
while (tip != null) {
tip.page.removePage();
tip = tip.parent;
}
if (store.getFileStore() != null) {
store.registerUnsavedPage(unsavedMemoryHolder.value);
}
assert lockedForUpdate || updatedRootReference.getAppendCounter() == 0;
return updatedRootReference;
} }
assert updatedRootReference.getAppendCounter() == 0; rootReference = getRootInternal();
return updatedRootReference; }
} finally {
if (lockedRootReference != null && !lockedForUpdate) {
assert rootReference.root == lockedRootReference.root;
// assert rootReference.appendCounter == lockedRootReference.appendCounter : rootReference.appendCounter + " != " + lockedRootReference.appendCounter;
rootReference = unlockRoot(lockedRootReference.root, lockedRootReference.appendCounter);
} }
rootReference = getRootInternal();
} }
return rootReference; return rootReference;
} }
private Page replacePage(CursorPos path, Page replacement, IntValueHolder unsavedMemoryHolder) {
int unsavedMemory = replacement.getMemory();
while (path != null) {
Page child = replacement;
replacement = path.page.copy();
replacement.setChild(path.index, child);
unsavedMemory += replacement.getMemory();
path = path.parent;
}
unsavedMemoryHolder.value += unsavedMemory;
return replacement;
}
/** /**
* Appends entry to this map. this method is NOT thread safe and can not be used * Appends entry to this map. this method is NOT thread safe and can not be used
* neither concurrently, nor in combination with any method that updates this map. * neither concurrently, nor in combination with any method that updates this map.
...@@ -1279,22 +1343,28 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1279,22 +1343,28 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/ */
public void append(K key, V value) { public void append(K key, V value) {
int attempt = 0; int attempt = 0;
boolean success = false; RootReference lockedRootReference = null;
while(!success) { boolean success;
do {
RootReference rootReference = getRootInternal(); RootReference rootReference = getRootInternal();
if (lockedRootReference == null) {
lockedRootReference = lockRoot(rootReference, ++attempt);
rootReference = lockedRootReference;
}
int appendCounter = rootReference.getAppendCounter(); int appendCounter = rootReference.getAppendCounter();
if (appendCounter >= keysPerPage) { if (appendCounter >= keysPerPage) {
beforeWrite(); // beforeWrite();
rootReference = flushAppendBuffer(rootReference); rootReference = flushAppendBuffer(rootReference, true);
appendCounter = rootReference.getAppendCounter(); appendCounter = rootReference.getAppendCounter();
assert appendCounter < keysPerPage; assert appendCounter < keysPerPage;
} }
keysBuffer[appendCounter] = key; keysBuffer[appendCounter] = key;
valuesBuffer[appendCounter] = value; valuesBuffer[appendCounter] = value;
RootReference updatedRootReference = new RootReference(rootReference, appendCounter + 1, ++attempt); RootReference updatedRootReference = new RootReference(rootReference, appendCounter + 1, ++attempt, false);
success = root.compareAndSet(rootReference, updatedRootReference); success = root.compareAndSet(rootReference, updatedRootReference);
} } while(!success);
} }
/** /**
...@@ -1304,14 +1374,25 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1304,14 +1374,25 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/ */
public void trimLast() { public void trimLast() {
int attempt = 0; int attempt = 0;
boolean success; RootReference lockedRootReference = null;
do { RootReference rootReference = getRootInternal();
RootReference rootReference = getRootInternal(); boolean success = false;
while(!success) {
int appendCounter = rootReference.getAppendCounter(); int appendCounter = rootReference.getAppendCounter();
if (appendCounter > 0) { if (appendCounter > 0) {
RootReference updatedRootReference = new RootReference(rootReference, appendCounter - 1, ++attempt); if (lockedRootReference == null) {
lockedRootReference = lockRoot(rootReference, ++attempt);
rootReference = lockedRootReference;
continue;
}
// RootReference updatedRootReference = new RootReference(rootReference, appendCounter - 1, ++attempt, rootReference.lockedForUpdate);
RootReference updatedRootReference = new RootReference(rootReference, appendCounter - 1, ++attempt, false);
success = root.compareAndSet(rootReference, updatedRootReference); success = root.compareAndSet(rootReference, updatedRootReference);
rootReference = getRootInternal();
} else { } else {
if (lockedRootReference != null) {
rootReference = unlockRoot(lockedRootReference.root, lockedRootReference.appendCounter);
}
assert rootReference.root.getKeyCount() > 0; assert rootReference.root.getKeyCount() > 0;
Page lastLeaf = rootReference.root.getAppendCursorPos(null).page; Page lastLeaf = rootReference.root.getAppendCursorPos(null).page;
assert lastLeaf.isLeaf(); assert lastLeaf.isLeaf();
...@@ -1320,7 +1401,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1320,7 +1401,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
success = remove(key) != null; success = remove(key) != null;
assert success; assert success;
} }
} while(!success); }
} }
@Override @Override
...@@ -1359,38 +1440,46 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1359,38 +1440,46 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/ */
public final byte appendCounter; public final byte appendCounter;
RootReference(Page root, long version, RootReference previous, RootReference(Page root, long version, RootReference previous, long updateCounter, long updateAttemptCounter) {
long updateCounter, long updateAttemptCounter,
boolean lockedForUpdate) {
this.root = root; this.root = root;
this.version = version; this.version = version;
this.previous = previous; this.previous = previous;
this.updateCounter = updateCounter; this.updateCounter = updateCounter;
this.updateAttemptCounter = updateAttemptCounter; this.updateAttemptCounter = updateAttemptCounter;
this.lockedForUpdate = lockedForUpdate; this.lockedForUpdate = false;
this.appendCounter = 0; this.appendCounter = 0;
} }
// This one is used for locking // This one is used for locking
RootReference(RootReference r) { RootReference(RootReference r, int attempt) {
this.root = r.root; this.root = r.root;
this.version = r.version; this.version = r.version;
this.previous = r.previous; this.previous = r.previous;
this.updateCounter = r.updateCounter; this.updateCounter = r.updateCounter + 1;
this.updateAttemptCounter = r.updateAttemptCounter; this.updateAttemptCounter = r.updateAttemptCounter + attempt;
this.lockedForUpdate = true; this.lockedForUpdate = true;
this.appendCounter = 0; this.appendCounter = r.appendCounter;
} }
// This one is used for unlocking // This one is used for unlocking
RootReference(RootReference r, Page root, int attempt) { RootReference(RootReference r, Page root, int appendCounter) {
this.root = root; this.root = root;
this.version = r.version; this.version = r.version;
this.previous = r.previous; this.previous = r.previous;
this.updateCounter = r.updateCounter + 1; this.updateCounter = r.updateCounter;
this.updateAttemptCounter = r.updateAttemptCounter + attempt; this.updateAttemptCounter = r.updateAttemptCounter;
this.lockedForUpdate = false; this.lockedForUpdate = false;
this.appendCounter = 0; this.appendCounter = (byte)appendCounter;
}
RootReference(RootReference r, Page root, int appendCounter, boolean lockedForUpdate) {
this.root = root;
this.version = r.version;
this.previous = r.previous;
this.updateCounter = r.updateCounter;
this.updateAttemptCounter = r.updateAttemptCounter;
this.lockedForUpdate = lockedForUpdate;
this.appendCounter = (byte)appendCounter;
} }
// This one is used for version change // This one is used for version change
...@@ -1421,13 +1510,13 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1421,13 +1510,13 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
// This one is used for append buffer maintenance // This one is used for append buffer maintenance
RootReference(RootReference r, int appendCounter, int attempt) { RootReference(RootReference r, int appendCounter, int attempt, boolean lockedForUpdate) {
this.root = r.root; this.root = r.root;
this.version = r.version; this.version = r.version;
this.previous = r.previous; this.previous = r.previous;
this.updateCounter = r.updateCounter + 1; this.updateCounter = r.updateCounter + 1;
this.updateAttemptCounter = r.updateAttemptCounter + attempt; this.updateAttemptCounter = r.updateAttemptCounter + attempt;
this.lockedForUpdate = r.lockedForUpdate; this.lockedForUpdate = lockedForUpdate;
this.appendCounter = (byte)appendCounter; this.appendCounter = (byte)appendCounter;
} }
...@@ -1703,34 +1792,43 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1703,34 +1792,43 @@ public class MVMap<K, V> extends AbstractMap<K, V>
public V operate(K key, V value, DecisionMaker<? super V> decisionMaker) { public V operate(K key, V value, DecisionMaker<? super V> decisionMaker) {
beforeWrite(); beforeWrite();
IntValueHolder unsavedMemoryHolder = new IntValueHolder();
int attempt = 0; int attempt = 0;
RootReference oldRootReference = null; // RootReference oldRootReference = null;
while(true) { while(true) {
RootReference rootReference = getRoot(); RootReference rootReference = getRoot();
int contention = 0; // int contention = 1;
if (oldRootReference != null) { // if (oldRootReference != null) {
long updateAttemptCounter = rootReference.updateAttemptCounter - // long updateAttemptCounter = rootReference.updateAttemptCounter -
oldRootReference.updateAttemptCounter; // oldRootReference.updateAttemptCounter;
assert updateAttemptCounter >= 0 : updateAttemptCounter; // assert updateAttemptCounter >= 0 : updateAttemptCounter;
long updateCounter = rootReference.updateCounter - oldRootReference.updateCounter; // long updateCounter = rootReference.updateCounter - oldRootReference.updateCounter;
assert updateCounter >= 0 : updateCounter; // assert updateCounter >= 0 : updateCounter;
assert updateAttemptCounter >= updateCounter : updateAttemptCounter + " >= " + updateCounter; // assert updateAttemptCounter >= updateCounter : updateAttemptCounter + " >= " + updateCounter;
contention = (int)((updateAttemptCounter+1) / (updateCounter+1)); // contention = (int)((updateAttemptCounter+1) / (updateCounter+1));
// }
// oldRootReference = rootReference;
RootReference lockedRootReference = null;
if ((++attempt > 3 || rootReference.lockedForUpdate)) {
lockedRootReference = lockRoot(rootReference, attempt);
rootReference = lockedRootReference;
} }
oldRootReference = rootReference; Page rootPage = rootReference.root;
++attempt; int appendCounter = rootReference.getAppendCounter();
CursorPos pos = traverseDown(rootReference.root, key); CursorPos tip;
Page p = pos.page; V result;
int index = pos.index; unsavedMemoryHolder.value = 0;
CursorPos tip = pos;
pos = pos.parent;
@SuppressWarnings("unchecked")
V result = index < 0 ? null : (V)p.getValue(index);
Decision decision = decisionMaker.decide(result, value);
int unsavedMemory = 0;
boolean needUnlock = false;
try { try {
CursorPos pos = traverseDown(rootPage, key);
Page p = pos.page;
int index = pos.index;
tip = pos;
pos = pos.parent;
//noinspection unchecked
result = index < 0 ? null : (V)p.getValue(index);
Decision decision = decisionMaker.decide(result, value);
switch (decision) { switch (decision) {
case REPEAT: case REPEAT:
decisionMaker.reset(); decisionMaker.reset();
...@@ -1749,10 +1847,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1749,10 +1847,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
return null; return null;
} }
if (attempt > 2 && !(needUnlock = lockRoot(decisionMaker, rootReference,
attempt, contention))) {
continue;
}
if (p.getTotalCount() == 1 && pos != null) { if (p.getTotalCount() == 1 && pos != null) {
p = pos.page; p = pos.page;
index = pos.index; index = pos.index;
...@@ -1769,10 +1864,6 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1769,10 +1864,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
break; break;
} }
case PUT: { case PUT: {
if (attempt > 2 && !(needUnlock = lockRoot(decisionMaker, rootReference,
attempt, contention))) {
continue;
}
value = decisionMaker.selectValue(result, value); value = decisionMaker.selectValue(result, value);
p = p.copy(); p = p.copy();
if (index < 0) { if (index < 0) {
...@@ -1785,11 +1876,10 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1785,11 +1876,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
int at = keyCount >> 1; int at = keyCount >> 1;
Object k = p.getKey(at); Object k = p.getKey(at);
Page split = p.split(at); Page split = p.split(at);
unsavedMemory += p.getMemory(); unsavedMemoryHolder.value += p.getMemory() + split.getMemory();
unsavedMemory += split.getMemory();
if (pos == null) { if (pos == null) {
Object keys[] = { k }; Object[] keys = { k };
Page.PageReference children[] = { Page.PageReference[] children = {
new Page.PageReference(p), new Page.PageReference(p),
new Page.PageReference(split) new Page.PageReference(split)
}; };
...@@ -1810,6 +1900,8 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1810,6 +1900,8 @@ public class MVMap<K, V> extends AbstractMap<K, V>
break; break;
} }
} }
p = replacePage(pos, p, unsavedMemoryHolder);
/*
unsavedMemory += p.getMemory(); unsavedMemory += p.getMemory();
while (pos != null) { while (pos != null) {
Page c = p; Page c = p;
...@@ -1819,61 +1911,99 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1819,61 +1911,99 @@ public class MVMap<K, V> extends AbstractMap<K, V>
unsavedMemory += p.getMemory(); unsavedMemory += p.getMemory();
pos = pos.parent; pos = pos.parent;
} }
if(needUnlock) { */
unlockRoot(p, attempt); rootPage = p;
needUnlock = false; if(lockedRootReference == null && !updateRoot(rootReference, p, attempt)) {
} else if(!updateRoot(rootReference, p, attempt)) {
decisionMaker.reset(); decisionMaker.reset();
continue; continue;
} }
while (tip != null) {
tip.page.removePage();
tip = tip.parent;
}
if (store.getFileStore() != null) {
store.registerUnsavedPage(unsavedMemory);
}
return result;
} finally { } finally {
if(needUnlock) { if(lockedRootReference != null) {
unlockRoot(rootReference.root, attempt); unlockRoot(rootPage, appendCounter);
} }
} }
while (tip != null) {
tip.page.removePage();
tip = tip.parent;
}
if (store.getFileStore() != null) {
store.registerUnsavedPage(unsavedMemoryHolder.value);
}
return result;
} }
} }
private boolean lockRoot(DecisionMaker<? super V> decisionMaker, RootReference rootReference, private RootReference lockRoot(RootReference rootReference, int attempt) {
int attempt, int contention) { while(true) {
boolean success = lockRoot(rootReference); RootReference lockedRootReference = tryLock(rootReference, attempt++);
if (!success) { if (lockedRootReference != null) {
decisionMaker.reset(); return lockedRootReference;
if(attempt > 4) { }
if (attempt <= 24) { rootReference = getRootInternal();
Thread.yield(); }
} else { }
private RootReference tryLock(RootReference rootReference, int attempt) {
if (!rootReference.lockedForUpdate) {
RootReference lockedRootReference = new RootReference(rootReference, attempt);
if (root.compareAndSet(rootReference, lockedRootReference)) {
return lockedRootReference;
}
}
RootReference oldRootReference = rootReference.previous;
int contention = 1;
if (oldRootReference != null) {
long updateAttemptCounter = rootReference.updateAttemptCounter -
oldRootReference.updateAttemptCounter;
assert updateAttemptCounter >= 0 : updateAttemptCounter;
long updateCounter = rootReference.updateCounter - oldRootReference.updateCounter;
assert updateCounter >= 0 : updateCounter;
assert updateAttemptCounter >= updateCounter : updateAttemptCounter + " >= " + updateCounter;
contention += (int)((updateAttemptCounter+1) / (updateCounter+1));
}
if(attempt > 4) {
if (attempt <= 12) {
Thread.yield();
} else if (attempt <= 64) {
try {
Thread.sleep(0, 10 * contention + 5);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
} else {
synchronized (lock) {
notificationRequested = true;
try { try {
Thread.sleep(0, 100 / contention + 50); lock.wait(0, 100);
} catch (InterruptedException ex) { } catch (InterruptedException ignore) {
throw new RuntimeException(ex);
} }
} }
} }
} }
return success; return null;
}
private boolean lockRoot(RootReference rootReference) {
return !rootReference.lockedForUpdate
&& root.compareAndSet(rootReference, new RootReference(rootReference));
} }
private void unlockRoot(Page newRoot, int attempt) { private RootReference unlockRoot(Page newRoot, int appendCounter) {
RootReference updatedRootReference;
boolean success; boolean success;
do { do {
RootReference rootReference = getRoot(); RootReference rootReference = getRootInternal();
RootReference updatedRootReference = new RootReference(rootReference, newRoot, attempt); // assert rootReference.lockedForUpdate;
if (!rootReference.lockedForUpdate) {
return rootReference;
}
updatedRootReference = new RootReference(rootReference, newRoot, appendCounter);
success = root.compareAndSet(rootReference, updatedRootReference); success = root.compareAndSet(rootReference, updatedRootReference);
} while(!success); } while(!success);
if (notificationRequested) {
synchronized (lock) {
notificationRequested = false;
lock.notifyAll();;
}
}
return updatedRootReference;
} }
private static CursorPos traverseDown(Page p, Object key) { private static CursorPos traverseDown(Page p, Object key) {
...@@ -1923,4 +2053,7 @@ public class MVMap<K, V> extends AbstractMap<K, V> ...@@ -1923,4 +2053,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
} }
} }
private static final class IntValueHolder {
int value;
}
} }
...@@ -493,6 +493,16 @@ public abstract class Page implements Cloneable ...@@ -493,6 +493,16 @@ public abstract class Page implements Cloneable
return bKeys; return bKeys;
} }
abstract void expand(int extraKeyCount, Object[] extraKeys, Object[] extraValues);
final void expandKeys(int extraKeyCount, Object[] extraKeys) {
int keyCount = getKeyCount();
Object[] newKeys = createKeyStorage(keyCount + extraKeyCount);
System.arraycopy(keys, 0, newKeys, 0, keyCount);
System.arraycopy(extraKeys, 0, newKeys, keyCount, extraKeyCount);
keys = newKeys;
}
/** /**
* Get the total number of key-value pairs, including child pages. * Get the total number of key-value pairs, including child pages.
* *
...@@ -1012,6 +1022,11 @@ public abstract class Page implements Cloneable ...@@ -1012,6 +1022,11 @@ public abstract class Page implements Cloneable
return newPage; return newPage;
} }
@Override
public void expand(int keyCount, Object[] extraKys, Object[] extraValues) {
throw new UnsupportedOperationException();
}
@Override @Override
public long getTotalCount() { public long getTotalCount() {
assert !isComplete() || totalCount == calculateTotalCount() : assert !isComplete() || totalCount == calculateTotalCount() :
...@@ -1324,6 +1339,21 @@ public abstract class Page implements Cloneable ...@@ -1324,6 +1339,21 @@ public abstract class Page implements Cloneable
return newPage; return newPage;
} }
@Override
public void expand(int extraKeyCount, Object[] extraKeys, Object[] extraValues) {
int keyCount = getKeyCount();
expandKeys(extraKeyCount, extraKeys);
if(values != null) {
Object[] newValues = createValueStorage(keyCount + extraKeyCount);
System.arraycopy(values, 0, newValues, 0, keyCount);
System.arraycopy(extraValues, 0, newValues, keyCount, extraKeyCount);
values = newValues;
}
if(isPersistent()) {
recalculateMemory();
}
}
@Override @Override
public long getTotalCount() { public long getTotalCount() {
return getKeyCount(); return getKeyCount();
......
...@@ -577,7 +577,7 @@ public class Transaction { ...@@ -577,7 +577,7 @@ public class Transaction {
} }
private static String stateToString(long state) { private static String stateToString(long state) {
return STATUS_NAMES[getStatus(state)] + (hasRollback(state) ? "" : "!") + " " + getLogId(state); return STATUS_NAMES[getStatus(state)] + (hasRollback(state) ? "<" : "") + " " + getLogId(state);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论