Lucene.Net.Core.Search.ControlledRealTimeReopenThread: Changed implementation to be more .NETified, which fixes the TestCRTReopen() test (fix provided by Vincent Van Den Berghe). Updated documentation comments.
Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/dfb2e928 Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/dfb2e928 Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/dfb2e928 Branch: refs/heads/api-work Commit: dfb2e92839dd29cbdc47d8c37af9325ea351709b Parents: e30f52d Author: Shad Storhaug <[email protected]> Authored: Fri Mar 24 17:36:12 2017 +0700 Committer: Shad Storhaug <[email protected]> Committed: Fri Mar 24 17:36:12 2017 +0700 ---------------------------------------------------------------------- .../Search/ControlledRealTimeReopenThread.cs | 241 ++++++++----------- 1 file changed, 95 insertions(+), 146 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dfb2e928/src/Lucene.Net.Core/Search/ControlledRealTimeReopenThread.cs ---------------------------------------------------------------------- diff --git a/src/Lucene.Net.Core/Search/ControlledRealTimeReopenThread.cs b/src/Lucene.Net.Core/Search/ControlledRealTimeReopenThread.cs index 294c1eb..79fb237 100644 --- a/src/Lucene.Net.Core/Search/ControlledRealTimeReopenThread.cs +++ b/src/Lucene.Net.Core/Search/ControlledRealTimeReopenThread.cs @@ -5,42 +5,41 @@ using System.Threading; namespace Lucene.Net.Search { /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ using TrackingIndexWriter = Lucene.Net.Index.TrackingIndexWriter; /// <summary> - /// Utility class that runs a thread to manage periodicc - /// reopens of a <seealso cref="ReferenceManager"/>, with methods to wait for a specific - /// index changes to become visible. To use this class you - /// must first wrap your <seealso cref="Index.IndexWriter"/> with a {@link - /// TrackingIndexWriter} and always use it to make changes - /// to the index, saving the returned generation. Then, - /// when a given search request needs to see a specific - /// index change, call the {#waitForGeneration} to wait for - /// that change to be visible. Note that this will only - /// scale well if most searches do not need to wait for a - /// specific index generation. - /// + /// Utility class that runs a thread to manage periodic + /// reopens of a <see cref="ReferenceManager{T}"/>, with methods to wait for a specific + /// index changes to become visible. To use this class you + /// must first wrap your <see cref="Index.IndexWriter"/> with a + /// <see cref="TrackingIndexWriter"/> and always use it to make changes + /// to the index, saving the returned generation. Then, + /// when a given search request needs to see a specific + /// index change, call the <see cref="WaitForGeneration(long)"/> to wait for + /// that change to be visible. Note that this will only + /// scale well if most searches do not need to wait for a + /// specific index generation. + /// <para/> /// @lucene.experimental /// </summary> - public class ControlledRealTimeReopenThread<T> : ThreadClass, IDisposable - where T : class + where T : class { private readonly ReferenceManager<T> manager; private readonly long targetMaxStaleNS; @@ -51,14 +50,14 @@ namespace Lucene.Net.Search private long searchingGen; private long refreshStartGen; - private readonly ReentrantLock reopenLock = new ReentrantLock(); private EventWaitHandle reopenCond = new AutoResetEvent(false); + private EventWaitHandle available = new AutoResetEvent(false); private const long MILLISECONDS_PER_NANOSECOND = 1000000; /// <summary> - /// Create ControlledRealTimeReopenThread, to periodically - /// reopen the a <seealso cref="ReferenceManager"/>. + /// Create <see cref="ControlledRealTimeReopenThread{T}"/>, to periodically + /// reopen the a <see cref="ReferenceManager{T}"/>. /// </summary> /// <param name="targetMaxStaleSec"> Maximum time until a new /// reader must be opened; this sets the upper bound @@ -107,50 +106,32 @@ namespace Lucene.Net.Search { lock (this) { - // LUCENENET specific - reading searchingGen not thread safe, so use Interlocked.Exchange() - Interlocked.Exchange(ref searchingGen, refreshStartGen); - Monitor.PulseAll(this); + // if we're finishing, , make it out so that all waiting search threads will return + searchingGen = finish ? long.MaxValue : refreshStartGen; + available.Set(); } + reopenCond.Reset(); } public void Dispose() // LUCENENET TODO: Implement disposable pattern { - lock (this) - { - //System.out.println("NRT: set finish"); - - finish = true; - - // So thread wakes up and notices it should finish: - reopenLock.Lock(); - try - { - reopenCond.Set(); - } - finally - { - reopenLock.Unlock(); - } - + finish = true; + reopenCond.Set(); #if !NETSTANDARD - try - { + try + { #endif - Join(); + Join(); #if !NETSTANDARD - } - catch (ThreadInterruptedException ie) - { - throw new ThreadInterruptedException(ie.ToString(), ie); - } -#endif - // Max it out so any waiting search threads will return: - searchingGen = long.MaxValue; - Monitor.PulseAll(this); - - // LUCENENET specific: dispose reset event - reopenCond.Dispose(); } + catch (ThreadInterruptedException ie) + { + throw new ThreadInterruptedException(ie.ToString(), ie); + } +#endif + // LUCENENET specific: dispose reset event + reopenCond.Dispose(); + available.Dispose(); } /// <summary> @@ -159,7 +140,7 @@ namespace Lucene.Net.Search /// If the current searcher is older than the /// target generation, this method will block /// until the searcher is reopened, by another via - /// <seealso cref="ReferenceManager#maybeRefresh"/> or until the <seealso cref="ReferenceManager"/> is closed. + /// <see cref="ReferenceManager{T}.MaybeRefresh()"/> or until the <see cref="ReferenceManager{T}"/> is closed. /// </summary> /// <param name="targetGen"> the generation to wait for </param> public virtual void WaitForGeneration(long targetGen) @@ -173,74 +154,59 @@ namespace Lucene.Net.Search /// If the current searcher is older than the target /// generation, this method will block until the /// searcher has been reopened by another thread via - /// <seealso cref="ReferenceManager#maybeRefresh"/>, the given waiting time has elapsed, or until - /// the <seealso cref="ReferenceManager"/> is closed. - /// <p> + /// <see cref="ReferenceManager{T}.MaybeRefresh()"/>, the given waiting time has elapsed, or until + /// the <seealso cref="ReferenceManager{T}"/> is closed. + /// <para/> /// NOTE: if the waiting time elapses before the requested target generation is - /// available the current <seealso cref="SearcherManager"/> is returned instead. + /// available the current <see cref="SearcherManager"/> is returned instead. /// </summary> /// <param name="targetGen"> /// the generation to wait for </param> /// <param name="maxMS"> /// maximum milliseconds to wait, or -1 to wait indefinitely </param> - /// <returns> true if the targetGeneration is now available, - /// or false if maxMS wait time was exceeded </returns> + /// <returns> true if the <paramref name="targetGen"/> is now available, + /// or false if <paramref name="maxMS"/> wait time was exceeded </returns> public virtual bool WaitForGeneration(long targetGen, int maxMS) { - lock (this) + long curGen = writer.Generation; + if (targetGen > curGen) { - long curGen = writer.Generation; - if (targetGen > curGen) + throw new System.ArgumentException("targetGen=" + targetGen + " was never returned by the ReferenceManager instance (current gen=" + curGen + ")"); + } + lock (this) + if (targetGen <= searchingGen) + return true; + else { - throw new System.ArgumentException("targetGen=" + targetGen + " was never returned by the ReferenceManager instance (current gen=" + curGen + ")"); + waitingGen = Math.Max(waitingGen, targetGen); + reopenCond.Set(); + available.Reset(); } - // LUCENENET specific - reading searchingGen not thread safe, so use Interlocked.Read() - if (targetGen > Interlocked.Read(ref searchingGen)) - { - // Notify the reopen thread that the waitingGen has - // changed, so it may wake up and realize it should - // not sleep for much or any longer before reopening: - reopenLock.Lock(); - // Need to find waitingGen inside lock as its used to determine - // stale time - waitingGen = Math.Max(waitingGen, targetGen); + long startMS = Environment.TickCount;//System.nanoTime() / 1000000; - try - { - reopenCond.Set(); - } - finally + // LUCENENET specific - reading searchingGen not thread safe, so use Interlocked.Read() + while (targetGen > Interlocked.Read(ref searchingGen)) + { + if (maxMS < 0) + { + available.WaitOne(); + } + else + { + long msLeft = (startMS + maxMS) - Environment.TickCount;//(System.nanoTime()) / 1000000; + if (msLeft <= 0) { - reopenLock.Unlock(); + return false; } - - long startMS = Environment.TickCount;//System.nanoTime() / 1000000; - - // LUCENENET specific - reading searchingGen not thread safe, so use Interlocked.Read() - while (targetGen > Interlocked.Read(ref searchingGen)) + else { - if (maxMS < 0) - { - Monitor.Wait(this); - } - else - { - long msLeft = (startMS + maxMS) - Environment.TickCount;//(System.nanoTime()) / 1000000; - if (msLeft <= 0) - { - return false; - } - else - { - Monitor.Wait(this, TimeSpan.FromMilliseconds(msLeft)); - } - } + available.WaitOne(TimeSpan.FromMilliseconds(msLeft)); } } - - return true; } + + return true; } public override void Run() @@ -252,36 +218,22 @@ namespace Lucene.Net.Search //System.out.println("reopen: start"); while (!finish) { - // TODO: try to guestimate how long reopen might - // take based on past data? + bool hasWaiting; - // Loop until we've waiting long enough before the - // next reopen: - while (!finish) - { - // Need lock before finding out if has waiting + lock (this) + hasWaiting = waitingGen > searchingGen; - reopenLock.Lock(); + long nextReopenStartNS = lastReopenStartNS + (hasWaiting ? targetMinStaleNS : targetMaxStaleNS); + long sleepNS = nextReopenStartNS - (Environment.TickCount * MILLISECONDS_PER_NANOSECOND); + if (sleepNS > 0) +#if !NETSTANDARD try { - // True if we have someone waiting for reopened searcher: - bool hasWaiting = waitingGen > searchingGen; - long nextReopenStartNS = lastReopenStartNS + (hasWaiting ? targetMinStaleNS : targetMaxStaleNS); - - long sleepNS = nextReopenStartNS - (Environment.TickCount * MILLISECONDS_PER_NANOSECOND); - - if (sleepNS > 0) - { - reopenCond.WaitOne(TimeSpan.FromMilliseconds(sleepNS / MILLISECONDS_PER_NANOSECOND));//Convert NS to Ticks - } - else - { - break; - } - - } +#endif + reopenCond.WaitOne(TimeSpan.FromMilliseconds(sleepNS / MILLISECONDS_PER_NANOSECOND));//Convert NS to Ticks #if !NETSTANDARD + } #pragma warning disable 168 catch (ThreadInterruptedException ie) #pragma warning restore 168 @@ -290,11 +242,6 @@ namespace Lucene.Net.Search return; } #endif - finally - { - reopenLock.Unlock(); - } - } if (finish) { @@ -315,6 +262,8 @@ namespace Lucene.Net.Search throw new Exception(ioe.ToString(), ioe); } } + // this will set the searchingGen so that all waiting threads will exit + RefreshDone(); } } } \ No newline at end of file
