Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.BackgroundProcessing/3.2/WorkerMonitor.cs @ 3070

Last change on this file since 3070 was 2410, checked in by epitzer, 15 years ago

make CancelAll asynchronous, add support for several worker monitors. (#769)

File size: 4.8 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.ComponentModel;
6using System.Threading;
7using System.Collections.ObjectModel;
8using System.Collections.Specialized;
9using System.Collections;
10
11namespace HeuristicLab.BackgroundProcessing {
12  /// <summary>
13  /// Provides a list of all currently running or pending ObservableBackgroundWorkers.
14  /// </summary>
15  public class WorkerMonitor : IObservableEnumerable<ObservableBackgroundWorker> {
16
17    public static WorkerMonitor Default = new WorkerMonitor();
18
19    /// <summary>
20    /// Report all unhandled exceptions during background worker execution.
21    /// These exceptions are the same as reported by the RunWorkerCompleted but wrapped
22    /// in an additional exception containing the worker name as message and the original
23    /// exception as inner exception.
24    /// </summary>
25    public event ThreadExceptionEventHandler BackgroundWorkerException;
26    public event NotifyCollectionChangedEventHandler CollectionChanged;
27
28    private List<ObservableBackgroundWorker> BackgroundWorkers;
29    private ReaderWriterLockSlim workerLock;
30
31    public WorkerMonitor() {
32      BackgroundWorkers = new List<ObservableBackgroundWorker>();
33      workerLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
34    }
35
36    internal void RegisterWorker(ObservableBackgroundWorker worker) {
37      worker.RunWorkerCompleted += worker_RunWorkerCompleted;
38      worker.WorkerStopped += worker_WorkerStopped;
39      try {
40        workerLock.EnterUpgradeableReadLock();
41        try {
42          workerLock.EnterWriteLock();
43          BackgroundWorkers.Add(worker);
44        } finally {
45          workerLock.ExitWriteLock();
46        }
47        int index = BackgroundWorkers.Count - 1;
48        OnCollectionChanged(new NotifyCollectionChangedEventArgs(
49          NotifyCollectionChangedAction.Add, worker, index));
50      } finally {
51        workerLock.ExitUpgradeableReadLock();
52      }
53    }
54
55    void worker_WorkerStopped(object sender, EventArgs e) {
56      ObservableBackgroundWorker worker = sender as ObservableBackgroundWorker;
57      try {
58        workerLock.EnterUpgradeableReadLock();
59        int index = BackgroundWorkers.IndexOf(worker);
60        try {
61          workerLock.EnterWriteLock();
62          BackgroundWorkers.RemoveAt(index);
63        } finally {
64          workerLock.ExitWriteLock();
65        }
66        OnCollectionChanged(new NotifyCollectionChangedEventArgs(
67          NotifyCollectionChangedAction.Remove, worker, index));
68      } finally {
69        workerLock.ExitUpgradeableReadLock();
70      }
71    }
72
73    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
74      ObservableBackgroundWorker worker = sender as ObservableBackgroundWorker;
75      if (e.Error != null) {
76        OnThreadException(new Exception(worker.Name, e.Error));
77      }
78    }
79
80    protected void OnThreadException(Exception x) {
81      if (BackgroundWorkerException != null)
82        BackgroundWorkerException(this, new ThreadExceptionEventArgs(x));
83    }
84
85    public IEnumerator<ObservableBackgroundWorker> GetEnumerator() {
86      try {
87        workerLock.EnterReadLock();
88        IList<ObservableBackgroundWorker> copy = BackgroundWorkers.ToList();
89        return copy.GetEnumerator();
90      } finally {
91        workerLock.ExitReadLock();
92      }
93    }
94
95    IEnumerator IEnumerable.GetEnumerator() {
96      return GetEnumerator();
97    }
98
99    protected void OnCollectionChanged(NotifyCollectionChangedEventArgs args) {
100      if (CollectionChanged != null)
101        CollectionChanged(this, args);
102    }
103
104    public void CancelAllAsync() {
105      CancelAllAsync(null, null);
106    }
107
108    public void CancelAllAsync(WaitCallback callback, object state) {
109      ThreadPool.QueueUserWorkItem(_ => {
110        List<ObservableBackgroundWorker> cancelableWorkers = GetCancelableWorkers();
111        lock (cancelableWorkers) {
112          foreach (var worker in cancelableWorkers.ToList()) {
113            worker.WorkerStopped += (sender, args) => {
114              lock (cancelableWorkers) {
115                cancelableWorkers.Remove((ObservableBackgroundWorker)sender);
116                Monitor.Pulse(cancelableWorkers);
117              }
118            };
119            worker.CancelAsync();
120            if (!worker.IsRunning)
121              cancelableWorkers.Remove(worker);
122          }
123          while (cancelableWorkers.Count > 0)
124            Monitor.Wait(cancelableWorkers);
125        }
126        if (callback != null)
127          callback(state);
128      });
129    }
130
131    private List<ObservableBackgroundWorker> GetCancelableWorkers() {
132      try {
133        workerLock.EnterReadLock();
134        return BackgroundWorkers.Where(w => w.WorkerSupportsCancellation).ToList();
135      } finally {
136        workerLock.ExitReadLock();
137      }
138    }
139  }
140}
Note: See TracBrowser for help on using the repository browser.