LUCENE-2137: switch to AtomicInteger for some ref counts

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@888621 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2009-12-08 23:05:29 +00:00
parent 4e88c6cb93
commit 3df3ab8e9f
5 changed files with 45 additions and 65 deletions

View File

@ -78,6 +78,9 @@ Optimizations
This avoid the overhead of using a PQ unecessarily. (Mike This avoid the overhead of using a PQ unecessarily. (Mike
McCandless) McCandless)
* LUCENE-2137: Switch to AtomicInteger for some ref counting (Earwin
Burrfoot via Mike McCandless)
Build Build
Test Cases Test Cases

View File

@ -42,7 +42,7 @@
<property name="Name" value="Lucene"/> <property name="Name" value="Lucene"/>
<property name="dev.version" value="3.1-dev"/> <property name="dev.version" value="3.1-dev"/>
<property name="version" value="${dev.version}"/> <property name="version" value="${dev.version}"/>
<property name="compatibility.tag" value="lucene_3_0_back_compat_tests_20091207"/> <property name="compatibility.tag" value="lucene_3_0_back_compat_tests_20091208"/>
<property name="spec.version" value="${version}"/> <property name="spec.version" value="${version}"/>
<property name="year" value="2000-${current.year}"/> <property name="year" value="2000-${current.year}"/>
<property name="final.name" value="lucene-${name}-${version}"/> <property name="final.name" value="lucene-${name}-${version}"/>

View File

@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector; import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.search.Similarity; import org.apache.lucene.search.Similarity;
@ -53,7 +54,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
CloseableThreadLocal<TermVectorsReader> termVectorsLocal = new CloseableThreadLocal<TermVectorsReader>(); CloseableThreadLocal<TermVectorsReader> termVectorsLocal = new CloseableThreadLocal<TermVectorsReader>();
BitVector deletedDocs = null; BitVector deletedDocs = null;
Ref deletedDocsRef = null; AtomicInteger deletedDocsRef = null;
private boolean deletedDocsDirty = false; private boolean deletedDocsDirty = false;
private boolean normsDirty = false; private boolean normsDirty = false;
private int pendingDeleteCount; private int pendingDeleteCount;
@ -65,7 +66,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
// optionally used for the .nrm file shared by multiple norms // optionally used for the .nrm file shared by multiple norms
private IndexInput singleNormStream; private IndexInput singleNormStream;
private Ref singleNormRef; private AtomicInteger singleNormRef;
CoreReaders core; CoreReaders core;
@ -79,7 +80,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
// closed. A given instance of SegmentReader may be // closed. A given instance of SegmentReader may be
// closed, even those it shares core objects with other // closed, even those it shares core objects with other
// SegmentReaders: // SegmentReaders:
private final Ref ref = new Ref(); private final AtomicInteger ref = new AtomicInteger(1);
final String segment; final String segment;
final FieldInfos fieldInfos; final FieldInfos fieldInfos;
@ -150,7 +151,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
} }
synchronized void incRef() { synchronized void incRef() {
ref.incRef(); ref.incrementAndGet();
} }
synchronized Directory getCFSReader() { synchronized Directory getCFSReader() {
@ -196,7 +197,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
synchronized void decRef() throws IOException { synchronized void decRef() throws IOException {
if (ref.decRef() == 0) { if (ref.decrementAndGet() == 0) {
// close everything, nothing is shared anymore with other readers // close everything, nothing is shared anymore with other readers
if (tis != null) { if (tis != null) {
@ -299,31 +300,6 @@ public class SegmentReader extends IndexReader implements Cloneable {
} }
} }
static class Ref {
private int refCount = 1;
@Override
public String toString() {
return "refcount: "+refCount;
}
public synchronized int refCount() {
return refCount;
}
public synchronized int incRef() {
assert refCount > 0;
refCount++;
return refCount;
}
public synchronized int decRef() {
assert refCount > 0;
refCount--;
return refCount;
}
}
/** /**
* Byte[] referencing is used because a new norm object needs * Byte[] referencing is used because a new norm object needs
* to be created for each clone, and the byte array is all * to be created for each clone, and the byte array is all
@ -344,7 +320,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
private long normSeek; private long normSeek;
// null until bytes is set // null until bytes is set
private Ref bytesRef; private AtomicInteger bytesRef;
private byte[] bytes; private byte[] bytes;
private boolean dirty; private boolean dirty;
private int number; private int number;
@ -369,7 +345,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
} else { } else {
// We are sharing this with others -- decRef and // We are sharing this with others -- decRef and
// maybe close the shared norm stream // maybe close the shared norm stream
if (singleNormRef.decRef() == 0) { if (singleNormRef.decrementAndGet() == 0) {
singleNormStream.close(); singleNormStream.close();
singleNormStream = null; singleNormStream = null;
} }
@ -392,7 +368,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
if (bytes != null) { if (bytes != null) {
assert bytesRef != null; assert bytesRef != null;
bytesRef.decRef(); bytesRef.decrementAndGet();
bytes = null; bytes = null;
bytesRef = null; bytesRef = null;
} else { } else {
@ -435,7 +411,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
// byte[] // byte[]
bytes = origNorm.bytes(); bytes = origNorm.bytes();
bytesRef = origNorm.bytesRef; bytesRef = origNorm.bytesRef;
bytesRef.incRef(); bytesRef.incrementAndGet();
// Once we've loaded the bytes we no longer need // Once we've loaded the bytes we no longer need
// origNorm: // origNorm:
@ -457,7 +433,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
in.readBytes(bytes, 0, count, false); in.readBytes(bytes, 0, count, false);
} }
bytesRef = new Ref(); bytesRef = new AtomicInteger(1);
closeInput(); closeInput();
} }
} }
@ -466,7 +442,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
} }
// Only for testing // Only for testing
Ref bytesRef() { AtomicInteger bytesRef() {
return bytesRef; return bytesRef;
} }
@ -477,15 +453,15 @@ public class SegmentReader extends IndexReader implements Cloneable {
bytes(); bytes();
assert bytes != null; assert bytes != null;
assert bytesRef != null; assert bytesRef != null;
if (bytesRef.refCount() > 1) { if (bytesRef.get() > 1) {
// I cannot be the origNorm for another norm // I cannot be the origNorm for another norm
// instance if I'm being changed. Ie, only the // instance if I'm being changed. Ie, only the
// "head Norm" can be changed: // "head Norm" can be changed:
assert refCount == 1; assert refCount == 1;
final Ref oldRef = bytesRef; final AtomicInteger oldRef = bytesRef;
bytes = cloneNormBytes(bytes); bytes = cloneNormBytes(bytes);
bytesRef = new Ref(); bytesRef = new AtomicInteger(1);
oldRef.decRef(); oldRef.decrementAndGet();
} }
dirty = true; dirty = true;
return bytes; return bytes;
@ -511,7 +487,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
assert origNorm == null; assert origNorm == null;
// Clone holds a reference to my bytes: // Clone holds a reference to my bytes:
clone.bytesRef.incRef(); clone.bytesRef.incrementAndGet();
} else { } else {
assert bytesRef == null; assert bytesRef == null;
if (origNorm == null) { if (origNorm == null) {
@ -602,7 +578,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
// NOTE: the bitvector is stored using the regular directory, not cfs // NOTE: the bitvector is stored using the regular directory, not cfs
if (hasDeletions(si)) { if (hasDeletions(si)) {
deletedDocs = new BitVector(directory(), si.getDelFileName()); deletedDocs = new BitVector(directory(), si.getDelFileName());
deletedDocsRef = new Ref(); deletedDocsRef = new AtomicInteger(1);
assert si.getDelCount() == deletedDocs.count() : assert si.getDelCount() == deletedDocs.count() :
"delete count mismatch: info=" + si.getDelCount() + " vs BitVector=" + deletedDocs.count(); "delete count mismatch: info=" + si.getDelCount() + " vs BitVector=" + deletedDocs.count();
@ -696,7 +672,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
if (doClone) { if (doClone) {
if (deletedDocs != null) { if (deletedDocs != null) {
deletedDocsRef.incRef(); deletedDocsRef.incrementAndGet();
clone.deletedDocs = deletedDocs; clone.deletedDocs = deletedDocs;
clone.deletedDocsRef = deletedDocsRef; clone.deletedDocsRef = deletedDocsRef;
} }
@ -706,7 +682,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
assert clone.deletedDocs == null; assert clone.deletedDocs == null;
clone.loadDeletedDocs(); clone.loadDeletedDocs();
} else if (deletedDocs != null) { } else if (deletedDocs != null) {
deletedDocsRef.incRef(); deletedDocsRef.incrementAndGet();
clone.deletedDocs = deletedDocs; clone.deletedDocs = deletedDocs;
clone.deletedDocsRef = deletedDocsRef; clone.deletedDocsRef = deletedDocsRef;
} }
@ -784,7 +760,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
fieldsReaderLocal.close(); fieldsReaderLocal.close();
if (deletedDocs != null) { if (deletedDocs != null) {
deletedDocsRef.decRef(); deletedDocsRef.decrementAndGet();
// null so if an app hangs on to us we still free most ram // null so if an app hangs on to us we still free most ram
deletedDocs = null; deletedDocs = null;
} }
@ -820,16 +796,16 @@ public class SegmentReader extends IndexReader implements Cloneable {
protected void doDelete(int docNum) { protected void doDelete(int docNum) {
if (deletedDocs == null) { if (deletedDocs == null) {
deletedDocs = new BitVector(maxDoc()); deletedDocs = new BitVector(maxDoc());
deletedDocsRef = new Ref(); deletedDocsRef = new AtomicInteger(1);
} }
// there is more than 1 SegmentReader with a reference to this // there is more than 1 SegmentReader with a reference to this
// deletedDocs BitVector so decRef the current deletedDocsRef, // deletedDocs BitVector so decRef the current deletedDocsRef,
// clone the BitVector, create a new deletedDocsRef // clone the BitVector, create a new deletedDocsRef
if (deletedDocsRef.refCount() > 1) { if (deletedDocsRef.get() > 1) {
Ref oldRef = deletedDocsRef; AtomicInteger oldRef = deletedDocsRef;
deletedDocs = cloneDeletedDocs(deletedDocs); deletedDocs = cloneDeletedDocs(deletedDocs);
deletedDocsRef = new Ref(); deletedDocsRef = new AtomicInteger(1);
oldRef.decRef(); oldRef.decrementAndGet();
} }
deletedDocsDirty = true; deletedDocsDirty = true;
if (!deletedDocs.getAndSet(docNum)) if (!deletedDocs.getAndSet(docNum))
@ -841,7 +817,7 @@ public class SegmentReader extends IndexReader implements Cloneable {
deletedDocsDirty = false; deletedDocsDirty = false;
if (deletedDocs != null) { if (deletedDocs != null) {
assert deletedDocsRef != null; assert deletedDocsRef != null;
deletedDocsRef.decRef(); deletedDocsRef.decrementAndGet();
deletedDocs = null; deletedDocs = null;
deletedDocsRef = null; deletedDocsRef = null;
pendingDeleteCount = 0; pendingDeleteCount = 0;
@ -1056,9 +1032,9 @@ public class SegmentReader extends IndexReader implements Cloneable {
normSeek = nextNormSeek; normSeek = nextNormSeek;
if (singleNormStream == null) { if (singleNormStream == null) {
singleNormStream = d.openInput(fileName, readBufferSize); singleNormStream = d.openInput(fileName, readBufferSize);
singleNormRef = new Ref(); singleNormRef = new AtomicInteger(1);
} else { } else {
singleNormRef.incRef(); singleNormRef.incrementAndGet();
} }
// All norms in the .nrm file can share a single IndexInput since // All norms in the .nrm file can share a single IndexInput since
// they are only used in a synchronized context. // they are only used in a synchronized context.

View File

@ -331,7 +331,7 @@ public class TestIndexReaderClone extends LuceneTestCase {
assertDelDocsRefCountEquals(1, origSegmentReader); assertDelDocsRefCountEquals(1, origSegmentReader);
// check the norm refs // check the norm refs
Norm norm = clonedSegmentReader.norms.get("field1"); Norm norm = clonedSegmentReader.norms.get("field1");
assertEquals(1, norm.bytesRef().refCount()); assertEquals(1, norm.bytesRef().get());
clonedSegmentReader.close(); clonedSegmentReader.close();
dir1.close(); dir1.close();
} }
@ -442,7 +442,7 @@ public class TestIndexReaderClone extends LuceneTestCase {
} }
private void assertDelDocsRefCountEquals(int refCount, SegmentReader reader) { private void assertDelDocsRefCountEquals(int refCount, SegmentReader reader) {
assertEquals(refCount, reader.deletedDocsRef.refCount()); assertEquals(refCount, reader.deletedDocsRef.get());
} }
public void testCloneSubreaders() throws Exception { public void testCloneSubreaders() throws Exception {

View File

@ -21,6 +21,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document; import org.apache.lucene.document.Document;
@ -173,11 +174,11 @@ public class TestIndexReaderCloneNorms extends LuceneTestCase {
SegmentReader reader1 = SegmentReader.getOnlySegmentReader(dir1); SegmentReader reader1 = SegmentReader.getOnlySegmentReader(dir1);
reader1.norms("field1"); reader1.norms("field1");
Norm r1norm = reader1.norms.get("field1"); Norm r1norm = reader1.norms.get("field1");
SegmentReader.Ref r1BytesRef = r1norm.bytesRef(); AtomicInteger r1BytesRef = r1norm.bytesRef();
SegmentReader reader2 = (SegmentReader)reader1.clone(); SegmentReader reader2 = (SegmentReader)reader1.clone();
assertEquals(2, r1norm.bytesRef().refCount()); assertEquals(2, r1norm.bytesRef().get());
reader1.close(); reader1.close();
assertEquals(1, r1BytesRef.refCount()); assertEquals(1, r1BytesRef.get());
reader2.norms("field1"); reader2.norms("field1");
reader2.close(); reader2.close();
dir1.close(); dir1.close();
@ -192,19 +193,19 @@ public class TestIndexReaderCloneNorms extends LuceneTestCase {
SegmentReader segmentReader2C = SegmentReader.getOnlySegmentReader(reader2C); SegmentReader segmentReader2C = SegmentReader.getOnlySegmentReader(reader2C);
segmentReader2C.norms("field1"); // load the norms for the field segmentReader2C.norms("field1"); // load the norms for the field
Norm reader2CNorm = segmentReader2C.norms.get("field1"); Norm reader2CNorm = segmentReader2C.norms.get("field1");
assertTrue("reader2CNorm.bytesRef()=" + reader2CNorm.bytesRef(), reader2CNorm.bytesRef().refCount() == 2); assertTrue("reader2CNorm.bytesRef()=" + reader2CNorm.bytesRef(), reader2CNorm.bytesRef().get() == 2);
IndexReader reader3C = (IndexReader) reader2C.clone(); IndexReader reader3C = (IndexReader) reader2C.clone();
SegmentReader segmentReader3C = SegmentReader.getOnlySegmentReader(reader3C); SegmentReader segmentReader3C = SegmentReader.getOnlySegmentReader(reader3C);
Norm reader3CCNorm = segmentReader3C.norms.get("field1"); Norm reader3CCNorm = segmentReader3C.norms.get("field1");
assertEquals(3, reader3CCNorm.bytesRef().refCount()); assertEquals(3, reader3CCNorm.bytesRef().get());
// edit a norm and the refcount should be 1 // edit a norm and the refcount should be 1
IndexReader reader4C = (IndexReader) reader3C.clone(); IndexReader reader4C = (IndexReader) reader3C.clone();
SegmentReader segmentReader4C = SegmentReader.getOnlySegmentReader(reader4C); SegmentReader segmentReader4C = SegmentReader.getOnlySegmentReader(reader4C);
assertEquals(4, reader3CCNorm.bytesRef().refCount()); assertEquals(4, reader3CCNorm.bytesRef().get());
reader4C.setNorm(5, "field1", 0.33f); reader4C.setNorm(5, "field1", 0.33f);
// generate a cannot update exception in reader1 // generate a cannot update exception in reader1
@ -219,14 +220,14 @@ public class TestIndexReaderCloneNorms extends LuceneTestCase {
assertTrue(Similarity.getDefault().decodeNormValue(segmentReader3C.norms("field1")[5]) assertTrue(Similarity.getDefault().decodeNormValue(segmentReader3C.norms("field1")[5])
!= Similarity.getDefault().decodeNormValue(segmentReader4C.norms("field1")[5])); != Similarity.getDefault().decodeNormValue(segmentReader4C.norms("field1")[5]));
Norm reader4CCNorm = segmentReader4C.norms.get("field1"); Norm reader4CCNorm = segmentReader4C.norms.get("field1");
assertEquals(3, reader3CCNorm.bytesRef().refCount()); assertEquals(3, reader3CCNorm.bytesRef().get());
assertEquals(1, reader4CCNorm.bytesRef().refCount()); assertEquals(1, reader4CCNorm.bytesRef().get());
IndexReader reader5C = (IndexReader) reader4C.clone(); IndexReader reader5C = (IndexReader) reader4C.clone();
SegmentReader segmentReader5C = SegmentReader.getOnlySegmentReader(reader5C); SegmentReader segmentReader5C = SegmentReader.getOnlySegmentReader(reader5C);
Norm reader5CCNorm = segmentReader5C.norms.get("field1"); Norm reader5CCNorm = segmentReader5C.norms.get("field1");
reader5C.setNorm(5, "field1", 0.7f); reader5C.setNorm(5, "field1", 0.7f);
assertEquals(1, reader5CCNorm.bytesRef().refCount()); assertEquals(1, reader5CCNorm.bytesRef().get());
reader5C.close(); reader5C.close();
reader4C.close(); reader4C.close();