Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / beagled / QueryResult.cs
blobb70c5f37dff1734101087e866cd32aed9d3746b0
1 //
2 // QueryResult.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
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.
28 using System;
29 using System.Collections;
30 using System.Diagnostics;
31 using System.Threading;
32 using Beagle.Util;
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);
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 public delegate void CancelledHandler (QueryResult source);
51 public event CancelledHandler CancelledEvent;
53 //////////////////////////////////
55 int workers = 0;
56 bool cancelled = false;
57 Hashtable uri_hash = UriFu.NewHashtable ();
58 DateTime started_time;
59 DateTime finished_time;
60 Hashtable per_worker_started_time = new Hashtable ();
63 public QueryResult ()
68 //////////////////////////////////
70 public void Dispose ()
72 lock (this) {
73 if (cancelled)
74 return;
75 cancelled = true;
79 //////////////////////////////////
81 public bool Active {
82 get { return workers > 0 && ! cancelled; }
85 public bool Cancelled {
86 get { return cancelled; }
89 public void Cancel ()
91 lock (this) {
92 if (cancelled)
93 return;
94 cancelled = true;
96 if (CancelledEvent != null)
97 CancelledEvent (this);
101 // Note: some_hits is allowed to contain null.
102 // They are silently ignored.
103 public void Add (ICollection some_hits)
105 lock (this) {
106 if (cancelled)
107 return;
109 Debug.Assert (workers > 0, "Adding Hits to idle QueryResult");
111 if (some_hits.Count == 0)
112 return;
114 // Be careful not to report the same hit twice.
115 ArrayList hits_to_report;
116 hits_to_report = new ArrayList ();
117 foreach (Hit hit in some_hits) {
118 if (hit != null && ! uri_hash.Contains (hit.Uri)) {
119 uri_hash [hit.Uri] = hit;
120 hits_to_report.Add (hit);
124 if (HitsAddedEvent != null && hits_to_report.Count > 0)
125 HitsAddedEvent (this, hits_to_report);
129 // Note: some_uris is allowed to contain null.
130 // They are silently ignored.
131 public void Subtract (ICollection some_uris)
133 lock (this) {
134 if (cancelled)
135 return;
137 Debug.Assert (workers > 0, "Subtracting Hits from idle QueryResult");
139 if (some_uris.Count == 0)
140 return;
142 ArrayList filtered_uris = new ArrayList ();
144 // We only get to subtract a URI if it was previously added.
145 foreach (Uri uri in some_uris) {
146 if (uri != null && uri_hash.Contains (uri)) {
147 filtered_uris.Add (uri);
148 uri_hash.Remove (uri);
152 if (HitsSubtractedEvent != null && filtered_uris.Count > 0)
153 HitsSubtractedEvent (this, filtered_uris);
157 //////////////////////////////////
159 // Given the Uri of a Hit contained in the QueryResult, return that Hit.
160 public Hit GetHitFromUri (Uri uri)
162 return uri_hash [uri] as Hit;
165 public ICollection HitUris {
166 get { return uri_hash.Keys; }
169 //////////////////////////////////////////////////////////////////////////////////////
171 class QueryWorkerClosure {
172 IQueryWorker worker;
173 QueryResult result;
175 public QueryWorkerClosure (IQueryWorker _worker, QueryResult _result)
177 worker = _worker;
178 result = _result;
181 public void Start ()
183 try {
184 worker.DoWork ();
185 } catch (Exception e) {
186 Logger.Log.Error ("QueryWorker '{0}' threw an exception", worker);
187 Logger.Log.Error (e);
189 try {
190 result.WorkerFinished (worker);
191 } catch (Exception e) {
192 Logger.Log.Error ("QueryResult threw an exception while calling WorkerFinished for '{0}'",
193 worker);
198 public void AttachWorker (IQueryWorker worker)
200 lock (this) {
201 if (cancelled)
202 return;
204 QueryWorkerClosure qwc;
205 qwc = new QueryWorkerClosure (worker, this);
207 // QueryDriver has an enclosing WorkerStart,
208 // so if we call WorkerStart in this thread,
209 // all the workers will have a chance
210 // to start before Finished is called
212 if (!WorkerStartNoLock (worker))
213 return;
215 // We don't need to use an ExceptionHandlingThread
216 // here, because qwc.Start is careful about catching
217 // any exceptions.
218 Thread th;
219 th = new Thread (new ThreadStart (qwc.Start));
220 th.Start ();
224 private bool WorkerStartNoLock (object o)
226 if (!Shutdown.WorkerStart (o))
227 return false;
229 DateTime now = DateTime.Now;
230 per_worker_started_time [o] = now;
231 ++workers;
232 if (workers == 1) {
233 started_time = now;
234 if (StartedEvent != null)
235 StartedEvent (this);
239 return true;
243 internal bool WorkerStart (object o)
245 lock (this) {
246 return WorkerStartNoLock (o);
250 internal void WorkerFinished (object o)
252 lock (this) {
253 Debug.Assert (workers > 0, "Too many calls to WorkerFinished");
254 --workers;
257 DateTime now = DateTime.Now;
259 //DateTime then = (DateTime) per_worker_started_time [o];
260 //Logger.Log.Debug ("{0} finished in {1:0.00}s", o, (now - then).TotalSeconds);
262 if (workers == 0) {
263 finished_time = now;
264 //Logger.Log.Debug ("Last worker finished {0:0.00}s after start",
265 //(finished_time - started_time).TotalSeconds);
266 if (FinishedEvent != null)
267 FinishedEvent (this);
268 Monitor.Pulse (this);
271 Shutdown.WorkerFinished (o);
274 public void Wait ()
276 lock (this) {
277 while (true) {
278 if (cancelled || workers == 0)
279 return;
280 Monitor.Wait (this);