Skip to content

Commit c75050a

Browse files
paulirwinclaude
andcommitted
Backport SearcherManager thread-safety from LUCENE-7564 (73f068e5033)
Force single-threaded access to the SearcherManager when performing an acquire() or reassigning, to prevent race conditions between Build(), EnsureOpen(), DoLookup(), GetSizeInBytes(), and Count. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ddec28d commit c75050a

1 file changed

Lines changed: 114 additions & 62 deletions

File tree

src/Lucene.Net.Suggest/Suggest/Analyzing/AnalyzingInfixSuggester.cs

Lines changed: 114 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ namespace Lucene.Net.Search.Suggest.Analyzing
6464
public class AnalyzingInfixSuggester : Lookup, IDisposable
6565
{
6666
private readonly object syncLock = new object(); //uses syncLock as substitute for Java's synchronized (method) keyword
67+
// LUCENENET specific - Support for LUCENE-7564.
68+
// Forces single-threaded access to the SearcherManager when performing an acquire() or reassigning.
69+
private readonly object searcherMgrLock = new object();
6770

6871
/// <summary>
6972
/// Field name used for the indexed text. </summary>
@@ -294,74 +297,82 @@ protected internal virtual Directory GetDirectory(DirectoryInfo path)
294297

295298
public override void Build(IInputEnumerator enumerator)
296299
{
297-
if (m_searcherMgr != null)
298-
{
299-
m_searcherMgr.Dispose();
300-
m_searcherMgr = null;
301-
}
302-
303-
if (writer != null)
304-
{
305-
writer.Dispose();
306-
writer = null;
307-
}
308-
309-
AtomicReader r = null;
310-
bool success = false;
300+
UninterruptableMonitor.Enter(searcherMgrLock);
311301
try
312302
{
313-
// First pass: build a temporary normal Lucene index,
314-
// just indexing the suggestions as they iterate:
315-
writer = new IndexWriter(dir, indexWriterConfigFactory.Get(matchVersion, GetGramAnalyzer(), OpenMode.CREATE));
316-
//long t0 = System.nanoTime();
317-
318-
// TODO: use threads?
319-
BytesRef text;
320-
while (enumerator.MoveNext())
303+
if (m_searcherMgr != null)
321304
{
322-
text = enumerator.Current;
323-
BytesRef payload;
324-
if (enumerator.HasPayloads)
325-
{
326-
payload = enumerator.Payload;
327-
}
328-
else
329-
{
330-
payload = null;
331-
}
332-
333-
Add(text, enumerator.Contexts, enumerator.Weight, payload);
305+
m_searcherMgr.Dispose();
306+
m_searcherMgr = null;
334307
}
335308

336-
//System.out.println("initial indexing time: " + ((System.nanoTime()-t0)/1000000) + " msec");
337-
if (commitOnBuild || closeIndexWriterOnBuild) // LUCENENET specific - Support for LUCENE-5889, LUCENE-7564.
309+
if (writer != null)
338310
{
339-
Commit();
311+
writer.Dispose();
312+
writer = null;
340313
}
341-
m_searcherMgr = new SearcherManager(writer, true, null);
342-
success = true;
343-
}
344-
finally
345-
{
346-
if (success)
314+
315+
AtomicReader r = null;
316+
bool success = false;
317+
try
347318
{
348-
if (closeIndexWriterOnBuild) // LUCENENET specific - Support for LUCENE-7564.
319+
// First pass: build a temporary normal Lucene index,
320+
// just indexing the suggestions as they iterate:
321+
writer = new IndexWriter(dir, indexWriterConfigFactory.Get(matchVersion, GetGramAnalyzer(), OpenMode.CREATE));
322+
//long t0 = System.nanoTime();
323+
324+
// TODO: use threads?
325+
BytesRef text;
326+
while (enumerator.MoveNext())
327+
{
328+
text = enumerator.Current;
329+
BytesRef payload;
330+
if (enumerator.HasPayloads)
331+
{
332+
payload = enumerator.Payload;
333+
}
334+
else
335+
{
336+
payload = null;
337+
}
338+
339+
Add(text, enumerator.Contexts, enumerator.Weight, payload);
340+
}
341+
342+
//System.out.println("initial indexing time: " + ((System.nanoTime()-t0)/1000000) + " msec");
343+
if (commitOnBuild || closeIndexWriterOnBuild) // LUCENENET specific - Support for LUCENE-5889, LUCENE-7564.
349344
{
350-
writer.Dispose();
351-
writer = null;
345+
Commit();
352346
}
353-
IOUtils.Dispose(r);
347+
m_searcherMgr = new SearcherManager(writer, true, null);
348+
success = true;
354349
}
355-
else
350+
finally
356351
{
357-
if (writer != null)
352+
if (success)
358353
{
359-
writer.Rollback();
360-
writer = null;
354+
if (closeIndexWriterOnBuild) // LUCENENET specific - Support for LUCENE-7564.
355+
{
356+
writer.Dispose();
357+
writer = null;
358+
}
359+
IOUtils.Dispose(r);
360+
}
361+
else
362+
{
363+
if (writer != null)
364+
{
365+
writer.Rollback();
366+
writer = null;
367+
}
368+
IOUtils.DisposeWhileHandlingException(r);
361369
}
362-
IOUtils.DisposeWhileHandlingException(r);
363370
}
364371
}
372+
finally
373+
{
374+
UninterruptableMonitor.Exit(searcherMgrLock);
375+
}
365376
}
366377

367378
// LUCENENET specific - Support for LUCENE-5889, LUCENE-7564.
@@ -437,11 +448,19 @@ private void EnsureOpen()
437448
{
438449
writer = new IndexWriter(dir, indexWriterConfigFactory.Get(matchVersion, GetGramAnalyzer(), OpenMode.CREATE));
439450
}
440-
SearcherManager oldSearcherMgr = m_searcherMgr;
441-
m_searcherMgr = new SearcherManager(writer, true, null);
442-
if (oldSearcherMgr != null)
451+
UninterruptableMonitor.Enter(searcherMgrLock);
452+
try
453+
{
454+
SearcherManager oldSearcherMgr = m_searcherMgr;
455+
m_searcherMgr = new SearcherManager(writer, true, null);
456+
if (oldSearcherMgr != null)
457+
{
458+
oldSearcherMgr.Dispose();
459+
}
460+
}
461+
finally
443462
{
444-
oldSearcherMgr.Dispose();
463+
UninterruptableMonitor.Exit(searcherMgrLock);
445464
}
446465
}
447466
}
@@ -691,8 +710,19 @@ public virtual IList<LookupResult> DoLookup(string key, IEnumerable<BytesRef> co
691710
// We sorted postings by weight during indexing, so we
692711
// only retrieve the first num hits now:
693712
ICollector c2 = new EarlyTerminatingSortingCollector(c, SORT, num);
694-
IndexSearcher searcher = m_searcherMgr.Acquire();
695713
IList<LookupResult> results = null;
714+
SearcherManager mgr; // LUCENENET specific - Support for LUCENE-7564: acquire & release on same SearcherManager, via local reference
715+
IndexSearcher searcher;
716+
UninterruptableMonitor.Enter(searcherMgrLock);
717+
try
718+
{
719+
mgr = m_searcherMgr;
720+
searcher = mgr.Acquire();
721+
}
722+
finally
723+
{
724+
UninterruptableMonitor.Exit(searcherMgrLock);
725+
}
696726
try
697727
{
698728
//System.out.println("got searcher=" + searcher);
@@ -706,7 +736,7 @@ public virtual IList<LookupResult> DoLookup(string key, IEnumerable<BytesRef> co
706736
}
707737
finally
708738
{
709-
m_searcherMgr.Release(searcher);
739+
mgr.Release(searcher);
710740
}
711741

712742
//System.out.println(((J2N.Time.NanoTime() / J2N.Time.MillisecondsPerNanosecond) - t0) + " msec for infix suggest"); // LUCENENET: Use NanoTime() rather than CurrentTimeMilliseconds() for more accurate/reliable results
@@ -951,7 +981,18 @@ public override long GetSizeInBytes()
951981
{
952982
if (m_searcherMgr != null)
953983
{
954-
IndexSearcher searcher = m_searcherMgr.Acquire();
984+
SearcherManager mgr; // LUCENENET specific - Support for LUCENE-7564: acquire & release on same SearcherManager, via local reference
985+
IndexSearcher searcher;
986+
UninterruptableMonitor.Enter(searcherMgrLock);
987+
try
988+
{
989+
mgr = m_searcherMgr;
990+
searcher = mgr.Acquire();
991+
}
992+
finally
993+
{
994+
UninterruptableMonitor.Exit(searcherMgrLock);
995+
}
955996
try
956997
{
957998
foreach (AtomicReaderContext context in searcher.IndexReader.Leaves)
@@ -965,7 +1006,7 @@ public override long GetSizeInBytes()
9651006
}
9661007
finally
9671008
{
968-
m_searcherMgr.Release(searcher);
1009+
mgr.Release(searcher);
9691010
}
9701011
}
9711012
return mem;
@@ -984,14 +1025,25 @@ public override long Count
9841025
{
9851026
return 0;
9861027
}
987-
IndexSearcher searcher = m_searcherMgr.Acquire();
1028+
SearcherManager mgr; // LUCENENET specific - Support for LUCENE-7564: acquire & release on same SearcherManager, via local reference
1029+
IndexSearcher searcher;
1030+
UninterruptableMonitor.Enter(searcherMgrLock);
1031+
try
1032+
{
1033+
mgr = m_searcherMgr;
1034+
searcher = mgr.Acquire();
1035+
}
1036+
finally
1037+
{
1038+
UninterruptableMonitor.Exit(searcherMgrLock);
1039+
}
9881040
try
9891041
{
9901042
return searcher.IndexReader.NumDocs;
9911043
}
9921044
finally
9931045
{
994-
m_searcherMgr.Release(searcher);
1046+
mgr.Release(searcher);
9951047
}
9961048
}
9971049
}

0 commit comments

Comments
 (0)