4 // Copyright (C) 2004 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
29 using System
.Collections
;
30 using System
.Diagnostics
;
31 using System
.Threading
;
34 namespace Beagle
.Daemon
{
36 public class QueryResult
: IQueryResult
, IDisposable
{
38 public delegate void StartedHandler (QueryResult source
);
39 public event StartedHandler StartedEvent
;
41 public delegate void HitsAddedHandler (QueryResult source
, ICollection someHits
, int total_results
);
42 public event HitsAddedHandler HitsAddedEvent
;
44 public delegate void HitsSubtractedHandler (QueryResult source
, ICollection someUris
);
45 public event HitsSubtractedHandler HitsSubtractedEvent
;
47 public delegate void FinishedHandler (QueryResult source
);
48 public event FinishedHandler FinishedEvent
;
50 //////////////////////////////////
53 bool cancelled
= false;
54 Hashtable uri_hash
= UriFu
.NewHashtable ();
55 DateTime started_time
;
56 DateTime finished_time
;
57 Hashtable per_worker_started_time
= new Hashtable ();
58 bool is_index_listener
= false;
66 //////////////////////////////////
68 public void Dispose ()
77 //////////////////////////////////
80 get { return workers > 0 && ! cancelled; }
83 public bool Cancelled
{
84 get { return cancelled; }
87 public bool IsIndexListener
{
88 get { return is_index_listener; }
89 set { is_index_listener = value; }
101 public void Add (ICollection some_hits
)
106 // Note: some_hits is allowed to contain null.
107 // They are silently ignored.
108 public void Add (ICollection some_hits
, int total_results
)
114 Debug
.Assert (workers
> 0, "Adding Hits to idle QueryResult");
116 if (some_hits
.Count
== 0)
119 if (IsIndexListener
) {
120 if (HitsAddedEvent
!= null)
121 HitsAddedEvent (this, some_hits
, total_results
);
125 // Be careful not to report the same hit twice.
126 ArrayList hits_to_report
;
127 hits_to_report
= new ArrayList ();
128 foreach (Hit hit
in some_hits
) {
129 if (hit
!= null && ! uri_hash
.Contains (hit
.Uri
)) {
130 uri_hash
[hit
.Uri
] = hit
;
131 hits_to_report
.Add (hit
);
135 if (HitsAddedEvent
!= null && hits_to_report
.Count
> 0)
136 HitsAddedEvent (this, hits_to_report
, total_results
);
140 // Note: some_uris is allowed to contain null.
141 // They are silently ignored.
142 public void Subtract (ICollection some_uris
)
148 Debug
.Assert (workers
> 0, "Subtracting Hits from idle QueryResult");
150 if (some_uris
.Count
== 0)
153 if (IsIndexListener
) {
154 if (HitsSubtractedEvent
!= null)
155 HitsSubtractedEvent (this, some_uris
);
159 ArrayList filtered_uris
= new ArrayList ();
161 // We only get to subtract a URI if it was previously added.
162 foreach (Uri uri
in some_uris
) {
163 if (uri
!= null && uri_hash
.Contains (uri
)) {
164 filtered_uris
.Add (uri
);
165 uri_hash
.Remove (uri
);
169 if (HitsSubtractedEvent
!= null && filtered_uris
.Count
> 0)
170 HitsSubtractedEvent (this, filtered_uris
);
174 //////////////////////////////////
176 // Given the Uri of a Hit contained in the QueryResult, return that Hit.
177 public Hit
GetHitFromUri (Uri uri
)
179 return uri_hash
[uri
] as Hit
;
182 public ICollection HitUris
{
183 get { return uri_hash.Keys; }
186 //////////////////////////////////////////////////////////////////////////////////////
188 class QueryWorkerClosure
{
192 public QueryWorkerClosure (IQueryWorker _worker
, QueryResult _result
)
202 } catch (Exception e
) {
203 Logger
.Log
.Error (e
, "QueryWorker '{0}' threw an exception", worker
);
206 result
.WorkerFinished (worker
);
207 } catch (Exception e
) {
208 Logger
.Log
.Error ("QueryResult threw an exception while calling WorkerFinished for '{0}'",
214 public void AttachWorker (IQueryWorker worker
)
220 QueryWorkerClosure qwc
;
221 qwc
= new QueryWorkerClosure (worker
, this);
223 // QueryDriver has an enclosing WorkerStart,
224 // so if we call WorkerStart in this thread,
225 // all the workers will have a chance
226 // to start before Finished is called
228 if (!WorkerStartNoLock (worker
))
231 ExceptionHandlingThread
.Start (new ThreadStart (qwc
.Start
));
235 private bool WorkerStartNoLock (object o
)
237 if (!Shutdown
.WorkerStart (o
))
240 DateTime now
= DateTime
.Now
;
241 per_worker_started_time
[o
] = now
;
245 if (StartedEvent
!= null)
254 internal bool WorkerStart (object o
)
257 return WorkerStartNoLock (o
);
261 internal void WorkerFinished (object o
)
264 Debug
.Assert (workers
> 0, "Too many calls to WorkerFinished");
268 DateTime now
= DateTime
.Now
;
270 //DateTime then = (DateTime) per_worker_started_time [o];
271 //Logger.Log.Debug ("{0} finished in {1:0.00}s", o, (now - then).TotalSeconds);
275 //Logger.Log.Debug ("Last worker finished {0:0.00}s after start",
276 //(finished_time - started_time).TotalSeconds);
277 if (FinishedEvent
!= null)
278 FinishedEvent (this);
279 Monitor
.Pulse (this);
282 Shutdown
.WorkerFinished (o
);
289 if (cancelled
|| workers
== 0)