2005-07-05 Gabor Kelemen <kelemeng@gnome.hu>
[beagle.git] / beagled / QueryResult.cs
blobb3c3675f60e9a59f58314568804ac680ecb93efa
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 : 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 hit_regulators = new Hashtable ();
58 Hashtable uri_hash = UriFu.NewHashtable ();
59 DateTime started_time;
60 DateTime finished_time;
61 Hashtable per_worker_started_time = new Hashtable ();
64 public QueryResult ()
69 //////////////////////////////////
71 public void Dispose ()
73 lock (this) {
74 if (cancelled)
75 return;
76 cancelled = true;
80 //////////////////////////////////
82 public bool Active {
83 get { return workers > 0 && ! cancelled; }
86 public bool Cancelled {
87 get { return cancelled; }
90 public void Cancel ()
92 lock (this) {
93 if (cancelled)
94 return;
95 cancelled = true;
97 if (CancelledEvent != null)
98 CancelledEvent (this);
102 public void Add (ICollection some_hits)
104 lock (this) {
105 if (cancelled)
106 return;
108 Debug.Assert (workers > 0, "Adding Hits to idle QueryResult");
110 if (some_hits.Count == 0)
111 return;
113 foreach (Hit hit in some_hits)
114 uri_hash [hit.Uri] = hit;
116 if (HitsAddedEvent != null)
117 HitsAddedEvent (this, some_hits);
121 public void Subtract (ICollection some_uris)
123 lock (this) {
124 if (cancelled)
125 return;
127 Debug.Assert (workers > 0, "Subtracting Hits from idle QueryResult");
129 if (some_uris.Count == 0)
130 return;
132 ArrayList filtered_uris = new ArrayList ();
134 // We only get to subtract a URI if it was previously added.
135 foreach (Uri uri in some_uris) {
136 if (uri_hash.Contains (uri)) {
137 filtered_uris.Add (uri);
138 uri_hash.Remove (uri);
142 if (HitsSubtractedEvent != null)
143 HitsSubtractedEvent (this, filtered_uris);
147 //////////////////////////////////
149 public HitRegulator GetHitRegulator (Queryable queryable)
151 lock (hit_regulators) {
152 HitRegulator hr = hit_regulators [queryable] as HitRegulator;
153 if (hr == null) {
154 hr = new HitRegulator (queryable);
155 hit_regulators [queryable] = hr;
157 return hr;
161 // Given the Uri of a Hit contained in the QueryResult, return that Hit.
162 public Hit GetHitFromUri (Uri uri)
164 return uri_hash [uri] as Hit;
167 public ICollection HitUris {
168 get { return uri_hash.Keys; }
171 //////////////////////////////////////////////////////////////////////////////////////
173 class QueryWorkerClosure {
174 IQueryWorker worker;
175 QueryResult result;
177 public QueryWorkerClosure (IQueryWorker _worker, QueryResult _result)
179 worker = _worker;
180 result = _result;
183 public void Start ()
185 try {
186 worker.DoWork ();
187 } catch (Exception e) {
188 Logger.Log.Error ("QueryWorker '{0}' threw an exception", worker);
189 Logger.Log.Error (e);
191 try {
192 result.WorkerFinished (worker);
193 } catch (Exception e) {
194 Logger.Log.Error ("QueryResult threw an exception while calling WorkerFinished for '{0}'",
195 worker);
200 public void AttachWorker (IQueryWorker worker)
202 lock (this) {
203 if (cancelled)
204 return;
206 QueryWorkerClosure qwc;
207 qwc = new QueryWorkerClosure (worker, this);
209 // QueryDriver has an enclosing WorkerStart,
210 // so if we call WorkerStart in this thread,
211 // all the workers will have a chance
212 // to start before Finished is called
214 if (!WorkerStartNoLock (worker))
215 return;
217 // We don't need to use an ExceptionHandlingThread
218 // here, because qwc.Start is careful about catching
219 // any exceptions.
220 Thread th;
221 th = new Thread (new ThreadStart (qwc.Start));
222 th.Start ();
226 private bool WorkerStartNoLock (object o)
228 if (!Shutdown.WorkerStart (o))
229 return false;
231 DateTime now = DateTime.Now;
232 per_worker_started_time [o] = now;
233 ++workers;
234 if (workers == 1) {
235 started_time = now;
236 if (StartedEvent != null)
237 StartedEvent (this);
241 return true;
245 internal bool WorkerStart (object o)
247 lock (this) {
248 return WorkerStartNoLock (o);
252 internal void WorkerFinished (object o)
254 lock (this) {
255 Debug.Assert (workers > 0, "Too many calls to WorkerFinished");
256 --workers;
259 DateTime now = DateTime.Now;
261 //DateTime then = (DateTime) per_worker_started_time [o];
262 //Logger.Log.Debug ("{0} finished in {1:0.00}s", o, (now - then).TotalSeconds);
264 if (workers == 0) {
265 finished_time = now;
266 Logger.Log.Debug ("Last worker finished {0:0.00}s after start",
267 (finished_time - started_time).TotalSeconds);
268 if (FinishedEvent != null)
269 FinishedEvent (this);
270 Monitor.Pulse (this);
273 Shutdown.WorkerFinished (o);
276 public void Wait ()
278 lock (this) {
279 while (true) {
280 if (cancelled || workers == 0)
281 return;
282 Monitor.Wait (this);