-
Notifications
You must be signed in to change notification settings - Fork 641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IndexSearcher fix for virtual calls from constructor #837
Changes from 6 commits
3d404c5
6a15e75
cf30971
71ff3a8
e92da05
daf510e
00843c3
8f93c53
ca71b4e
9113263
d9f4daa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,11 @@ | ||
using Lucene.Net.Diagnostics; | ||
using Lucene.Net.Support; | ||
using Lucene.Net.Support.Threading; | ||
using Lucene.Net.Util; | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Lucene.Net.Search | ||
|
@@ -118,7 +117,7 @@ public IndexSearcher(IndexReader r) | |
/// @lucene.experimental | ||
/// </summary> | ||
public IndexSearcher(IndexReader r, TaskScheduler executor) | ||
: this(r.Context, executor) | ||
: this(r?.Context, executor) | ||
{ | ||
} | ||
|
||
|
@@ -135,13 +134,44 @@ public IndexSearcher(IndexReader r, TaskScheduler executor) | |
/// <seealso cref="IndexReaderContext"/> | ||
/// <seealso cref="IndexReader.Context"/> | ||
public IndexSearcher(IndexReaderContext context, TaskScheduler executor) | ||
: this(context, executor, allocateLeafSlices: executor is not null) | ||
{ | ||
if (Debugging.AssertsEnabled) Debugging.Assert(context.IsTopLevel,"IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); | ||
} | ||
|
||
/// <summary> | ||
/// LUCENENET specific constructor that can be used by the subclasses to | ||
/// control whether the leaf slices are allocated in the base class or subclass. | ||
/// </summary> | ||
/// <remarks> | ||
/// If executor is non-<c>null</c> and you choose to skip allocating the leaf slices | ||
/// (i.e. <paramref name="allocateLeafSlices"/> == <c>false</c>), you must | ||
/// set the <see cref="m_leafSlices"/> field in your subclass constructor. | ||
/// This is commonly done by calling <see cref="GetSlices(IList{AtomicReaderContext})"/> | ||
/// and using the result to set <see cref="m_leafSlices"/>. You may wish to do this if you | ||
/// have state to pass into your constructor and need to set it prior to the call to | ||
/// <see cref="GetSlices(IList{AtomicReaderContext})"/> so it is available for use | ||
/// as a member field or property inside a custom override of | ||
/// <see cref="GetSlices(IList{AtomicReaderContext})"/>. | ||
/// </remarks> | ||
[SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] | ||
[SuppressMessage("CodeQuality", "S1699:Constructors should only call non-overridable methods", Justification = "Required for continuity with Lucene's design")] | ||
protected IndexSearcher(IndexReaderContext context, TaskScheduler executor, bool allocateLeafSlices) | ||
{ | ||
if (context is null) | ||
throw new ArgumentNullException(nameof(context)); | ||
|
||
if (Debugging.AssertsEnabled) | ||
NightOwl888 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Debugging.Assert(context.IsTopLevel,"IndexSearcher's ReaderContext must be topLevel for reader {0}", context.Reader); | ||
|
||
reader = context.Reader; | ||
this.executor = executor; | ||
this.m_readerContext = context; | ||
m_leafContexts = context.Leaves; | ||
this.m_leafSlices = executor is null ? null : GetSlicesInternal(m_leafContexts); | ||
|
||
if (allocateLeafSlices) | ||
{ | ||
this.m_leafSlices = GetSlices(m_leafContexts); | ||
} | ||
} | ||
|
||
/// <summary> | ||
|
@@ -160,18 +190,8 @@ public IndexSearcher(IndexReaderContext context) | |
/// Expert: Creates an array of leaf slices each holding a subset of the given leaves. | ||
/// Each <see cref="LeafSlice"/> is executed in a single thread. By default there | ||
/// will be one <see cref="LeafSlice"/> per leaf (<see cref="AtomicReaderContext"/>). | ||
/// | ||
/// NOTE: When overriding this method, be aware that the constructor of this class calls | ||
/// a private method and not this virtual method. So if you need to override | ||
/// the behavior during the initialization, call your own private method from the constructor | ||
/// with whatever custom behavior you need. | ||
/// </summary> | ||
// LUCENENET specific - renamed to GetSlices to better indicate the purpose of this method | ||
protected virtual LeafSlice[] GetSlices(IList<AtomicReaderContext> leaves) | ||
=> GetSlicesInternal(leaves); | ||
|
||
// LUCENENET specific - creating this so that we can call it from the constructor | ||
protected LeafSlice[] GetSlicesInternal(IList<AtomicReaderContext> leaves) | ||
{ | ||
LeafSlice[] slices = new LeafSlice[leaves.Count]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not a fan of these type of additions. This is not present in the original source, not even in the latest version. We could add a lot of extra typing/code (that's not even interesting, just copy paste of exception docs and guar checks). This is why I wasn't sure what you meant by guard clauses as it seemed excessive. |
||
for (int i = 0; i < slices.Length; i++) | ||
|
@@ -464,6 +484,9 @@ protected virtual TopDocs Search(Weight weight, ScoreDoc after, int nDocs) | |
ReentrantLock @lock = new ReentrantLock(); | ||
ExecutionHelper<TopDocs> runner = new ExecutionHelper<TopDocs>(executor); | ||
|
||
if (m_leafSlices is null) | ||
throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); | ||
|
||
for (int i = 0; i < m_leafSlices.Length; i++) // search each sub | ||
{ | ||
runner.Submit(new SearcherCallableNoSort(@lock, this, m_leafSlices[i], weight, after, nDocs, hq).Call); | ||
|
@@ -560,6 +583,9 @@ protected virtual TopFieldDocs Search(Weight weight, FieldDoc after, int nDocs, | |
ReentrantLock @lock = new ReentrantLock(); | ||
ExecutionHelper<TopFieldDocs> runner = new ExecutionHelper<TopFieldDocs>(executor); | ||
|
||
if (m_leafSlices is null) | ||
throw new InvalidOperationException($"When the constructor is passed a non-null {nameof(TaskScheduler)}, {nameof(m_leafSlices)} must also be set to a non-null value in the constructor."); | ||
|
||
for (int i = 0; i < m_leafSlices.Length; i++) // search each leaf slice | ||
{ | ||
runner.Submit(new SearcherCallableWithSort(@lock, this, m_leafSlices[i], weight, after, nDocs, topCollector, sort, doDocScores, doMaxScore).Call); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. I missed that the second word here should be
<paramref name="executor"/>
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!