Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
TimeLimitingCollector.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using Lucene.Net.Support;
20 using IndexReader = Lucene.Net.Index.IndexReader;
21 
22 namespace Lucene.Net.Search
23 {
24 
25  /// <summary> The <see cref="TimeLimitingCollector" /> is used to timeout search requests that
26  /// take longer than the maximum allowed search time limit. After this time is
27  /// exceeded, the search thread is stopped by throwing a
28  /// <see cref="TimeExceededException" />.
29  /// </summary>
31  {
32  private void InitBlock()
33  {
34  greedy = DEFAULT_GREEDY;
35  }
36 
37  /// <summary> Default timer resolution.</summary>
38  /// <seealso cref="Resolution">
39  /// </seealso>
40  public const int DEFAULT_RESOLUTION = 20;
41 
42  /// <summary> Default for <see cref="IsGreedy()" />.</summary>
43  /// <seealso cref="IsGreedy()">
44  /// </seealso>
45  public bool DEFAULT_GREEDY = false;
46 
47  private static uint resolution = DEFAULT_RESOLUTION;
48 
49  private bool greedy;
50 
51  private sealed class TimerThread:ThreadClass
52  {
53 
54  // NOTE: we can avoid explicit synchronization here for several reasons:
55  // * updates to volatile long variables are atomic
56  // * only single thread modifies this value
57  // * use of volatile keyword ensures that it does not reside in
58  // a register, but in main memory (so that changes are visible to
59  // other threads).
60  // * visibility of changes does not need to be instantanous, we can
61  // afford losing a tick or two.
62  //
63  // See section 17 of the Java Language Specification for details.
64  private volatile uint time = 0;
65 
66  /// <summary> TimerThread provides a pseudo-clock service to all searching
67  /// threads, so that they can count elapsed time with less overhead
68  /// than repeatedly calling System.currentTimeMillis. A single
69  /// thread should be created to be used for all searches.
70  /// </summary>
71  internal TimerThread():base("TimeLimitedCollector timer thread")
72  {
73  this.IsBackground = true;
74  }
75 
76  override public void Run()
77  {
78  while (true)
79  {
80  // TODO: Use System.nanoTime() when Lucene moves to Java SE 5.
81  time += Lucene.Net.Search.TimeLimitingCollector.resolution;
82  System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * Lucene.Net.Search.TimeLimitingCollector.resolution));
83 
84  }
85  }
86 
87  /// <summary> Get the timer value in milliseconds.</summary>
88  public long Milliseconds
89  {
90  get { return time; }
91  }
92  }
93 
94  /// <summary>Thrown when elapsed search time exceeds allowed search time. </summary>
95  [Serializable]
96  public class TimeExceededException:System.SystemException
97  {
98  private long timeAllowed;
99  private long timeElapsed;
100  private int lastDocCollected;
101  internal TimeExceededException(long timeAllowed, long timeElapsed, int lastDocCollected):base("Elapsed time: " + timeElapsed + "Exceeded allowed search time: " + timeAllowed + " ms.")
102  {
103  this.timeAllowed = timeAllowed;
104  this.timeElapsed = timeElapsed;
105  this.lastDocCollected = lastDocCollected;
106  }
107 
108  /// <summary>Returns allowed time (milliseconds). </summary>
109  public virtual long TimeAllowed
110  {
111  get { return timeAllowed; }
112  }
113 
114  /// <summary>Returns elapsed time (milliseconds). </summary>
115  public virtual long TimeElapsed
116  {
117  get { return timeElapsed; }
118  }
119 
120  /// <summary>Returns last doc(absolute doc id) that was collected when the search time exceeded. </summary>
121  public virtual int LastDocCollected
122  {
123  get { return lastDocCollected; }
124  }
125  }
126 
127  // Declare and initialize a single static timer thread to be used by
128  // all TimeLimitedCollector instances. The JVM assures that
129  // this only happens once.
130  private static readonly TimerThread TIMER_THREAD = new TimerThread();
131 
132  private long t0;
133  private long timeout;
134  private Collector collector;
135 
136  private int docBase;
137 
138  /// <summary> Create a TimeLimitedCollector wrapper over another <see cref="Collector" /> with a specified timeout.</summary>
139  /// <param name="collector">the wrapped <see cref="Collector" />
140  /// </param>
141  /// <param name="timeAllowed">max time allowed for collecting hits after which <see cref="TimeExceededException" /> is thrown
142  /// </param>
143  public TimeLimitingCollector(Collector collector, long timeAllowed)
144  {
145  InitBlock();
146  this.collector = collector;
147  t0 = TIMER_THREAD.Milliseconds;
148  this.timeout = t0 + timeAllowed;
149  }
150 
151  /// <summary>
152  /// Gets or sets the timer resolution.
153  /// The default timer resolution is 20 milliseconds.
154  /// This means that a search required to take no longer than
155  /// 800 milliseconds may be stopped after 780 to 820 milliseconds.
156  /// <br/>Note that:
157  /// <list type="bullet">
158  /// <item>Finer (smaller) resolution is more accurate but less efficient.</item>
159  /// <item>Setting resolution to less than 5 milliseconds will be silently modified to 5 milliseconds.</item>
160  /// <item>Setting resolution smaller than current resolution might take effect only after current
161  /// resolution. (Assume current resolution of 20 milliseconds is modified to 5 milliseconds,
162  /// then it can take up to 20 milliseconds for the change to have effect.</item>
163  /// </list>
164  /// </summary>
165  public static long Resolution
166  {
167  get { return resolution; }
168  set
169  {
170  // 5 milliseconds is about the minimum reasonable time for a Object.wait(long) call.
171  resolution = (uint)System.Math.Max(value, 5);
172  }
173  }
174 
175  /// <summary> Checks if this time limited collector is greedy in collecting the last hit.
176  /// A non greedy collector, upon a timeout, would throw a <see cref="TimeExceededException" />
177  /// without allowing the wrapped collector to collect current doc. A greedy one would
178  /// first allow the wrapped hit collector to collect current doc and only then
179  /// throw a <see cref="TimeExceededException" />.
180  /// </summary>
181  public virtual bool IsGreedy
182  {
183  get { return greedy; }
184  set { this.greedy = value; }
185  }
186 
187  /// <summary> Calls <see cref="Collector.Collect(int)" /> on the decorated <see cref="Collector" />
188  /// unless the allowed time has passed, in which case it throws an exception.
189  ///
190  /// </summary>
191  /// <throws> TimeExceededException </throws>
192  /// <summary> if the time allowed has exceeded.
193  /// </summary>
194  public override void Collect(int doc)
195  {
196  long time = TIMER_THREAD.Milliseconds;
197  if (timeout < time)
198  {
199  if (greedy)
200  {
201  //System.out.println(this+" greedy: before failing, collecting doc: "+doc+" "+(time-t0));
202  collector.Collect(doc);
203  }
204  //System.out.println(this+" failing on: "+doc+" "+(time-t0));
205  throw new TimeExceededException(timeout - t0, time - t0, docBase + doc);
206  }
207  //System.out.println(this+" collecting: "+doc+" "+(time-t0));
208  collector.Collect(doc);
209  }
210 
211  public override void SetNextReader(IndexReader reader, int base_Renamed)
212  {
213  collector.SetNextReader(reader, base_Renamed);
214  this.docBase = base_Renamed;
215  }
216 
217  public override void SetScorer(Scorer scorer)
218  {
219  collector.SetScorer(scorer);
220  }
221 
222  public override bool AcceptsDocsOutOfOrder
223  {
224  get { return collector.AcceptsDocsOutOfOrder; }
225  }
226 
227  static TimeLimitingCollector()
228  {
229  {
230  TIMER_THREAD.Start();
231  }
232  }
233  }
234 }