Skip to content

Commit 157b46b

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 e4b4d0b commit 157b46b

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
@@ -65,6 +65,9 @@ namespace Lucene.Net.Search.Suggest.Analyzing
6565
public class AnalyzingInfixSuggester : Lookup, IDisposable
6666
{
6767
private readonly object syncLock = new object(); //uses syncLock as substitute for Java's synchronized (method) keyword
68+
// LUCENENET specific - Support for LUCENE-7564.
69+
// Forces single-threaded access to the SearcherManager when performing an acquire() or reassigning.
70+
private readonly object searcherMgrLock = new object();
6871

6972
/// <summary>
7073
/// Field name used for the indexed text. </summary>
@@ -295,74 +298,82 @@ protected internal virtual Directory GetDirectory(DirectoryInfo path)
295298

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

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

368379
// LUCENENET specific - Support for LUCENE-5889, LUCENE-7564.
@@ -438,11 +449,19 @@ private void EnsureOpen()
438449
{
439450
writer = new IndexWriter(dir, indexWriterConfigFactory.Get(matchVersion, GetGramAnalyzer(), OpenMode.CREATE));
440451
}
441-
SearcherManager oldSearcherMgr = m_searcherMgr;
442-
m_searcherMgr = new SearcherManager(writer, true, null);
443-
if (oldSearcherMgr != null)
452+
UninterruptableMonitor.Enter(searcherMgrLock);
453+
try
454+
{
455+
SearcherManager oldSearcherMgr = m_searcherMgr;
456+
m_searcherMgr = new SearcherManager(writer, true, null);
457+
if (oldSearcherMgr != null)
458+
{
459+
oldSearcherMgr.Dispose();
460+
}
461+
}
462+
finally
444463
{
445-
oldSearcherMgr.Dispose();
464+
UninterruptableMonitor.Exit(searcherMgrLock);
446465
}
447466
}
448467
}
@@ -704,8 +723,19 @@ public virtual IList<LookupResult> DoLookup(string key,
704723
// We sorted postings by weight during indexing, so we
705724
// only retrieve the first num hits now:
706725
ICollector c2 = new EarlyTerminatingSortingCollector(c, SORT, num);
707-
IndexSearcher searcher = m_searcherMgr.Acquire();
708726
IList<LookupResult> results = null;
727+
SearcherManager mgr; // LUCENENET specific - Support for LUCENE-7564: acquire & release on same SearcherManager, via local reference
728+
IndexSearcher searcher;
729+
UninterruptableMonitor.Enter(searcherMgrLock);
730+
try
731+
{
732+
mgr = m_searcherMgr;
733+
searcher = mgr.Acquire();
734+
}
735+
finally
736+
{
737+
UninterruptableMonitor.Exit(searcherMgrLock);
738+
}
709739
try
710740
{
711741
//System.out.println("got searcher=" + searcher);
@@ -719,7 +749,7 @@ public virtual IList<LookupResult> DoLookup(string key,
719749
}
720750
finally
721751
{
722-
m_searcherMgr.Release(searcher);
752+
mgr.Release(searcher);
723753
}
724754

725755
//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
@@ -964,7 +994,18 @@ public override long GetSizeInBytes()
964994
{
965995
if (m_searcherMgr != null)
966996
{
967-
IndexSearcher searcher = m_searcherMgr.Acquire();
997+
SearcherManager mgr; // LUCENENET specific - Support for LUCENE-7564: acquire & release on same SearcherManager, via local reference
998+
IndexSearcher searcher;
999+
UninterruptableMonitor.Enter(searcherMgrLock);
1000+
try
1001+
{
1002+
mgr = m_searcherMgr;
1003+
searcher = mgr.Acquire();
1004+
}
1005+
finally
1006+
{
1007+
UninterruptableMonitor.Exit(searcherMgrLock);
1008+
}
9681009
try
9691010
{
9701011
foreach (AtomicReaderContext context in searcher.IndexReader.Leaves)
@@ -978,7 +1019,7 @@ public override long GetSizeInBytes()
9781019
}
9791020
finally
9801021
{
981-
m_searcherMgr.Release(searcher);
1022+
mgr.Release(searcher);
9821023
}
9831024
}
9841025
return mem;
@@ -997,14 +1038,25 @@ public override long Count
9971038
{
9981039
return 0;
9991040
}
1000-
IndexSearcher searcher = m_searcherMgr.Acquire();
1041+
SearcherManager mgr; // LUCENENET specific - Support for LUCENE-7564: acquire & release on same SearcherManager, via local reference
1042+
IndexSearcher searcher;
1043+
UninterruptableMonitor.Enter(searcherMgrLock);
1044+
try
1045+
{
1046+
mgr = m_searcherMgr;
1047+
searcher = mgr.Acquire();
1048+
}
1049+
finally
1050+
{
1051+
UninterruptableMonitor.Exit(searcherMgrLock);
1052+
}
10011053
try
10021054
{
10031055
return searcher.IndexReader.NumDocs;
10041056
}
10051057
finally
10061058
{
1007-
m_searcherMgr.Release(searcher);
1059+
mgr.Release(searcher);
10081060
}
10091061
}
10101062
}

0 commit comments

Comments
 (0)