JAL-961, JAL-1115 - pending test to ensure only one worker waits around for another...
[jalview.git] / src / jalview / workers / AlignCalcManager.java
1 package jalview.workers;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashSet;
6 import java.util.Hashtable;
7 import java.util.List;
8 import java.util.Map;
9
10 import jalview.api.AlignCalcManagerI;
11 import jalview.api.AlignCalcWorkerI;
12 import jalview.datamodel.AlignmentAnnotation;
13
14 public class AlignCalcManager implements AlignCalcManagerI
15 {
16   private volatile List<AlignCalcWorkerI> restartable = Collections
17           .synchronizedList(new ArrayList<AlignCalcWorkerI>());
18
19   private volatile List<Class> blackList = Collections
20           .synchronizedList(new ArrayList<Class>());
21
22   /**
23    * global record of calculations in progress
24    */
25   private volatile Map<Class, AlignCalcWorkerI> inProgress = Collections
26           .synchronizedMap(new Hashtable<Class, AlignCalcWorkerI>());
27
28   /**
29    * record of calculations pending or in progress in the current context
30    */
31   private volatile Map<Class, List<AlignCalcWorkerI>> updating = Collections
32           .synchronizedMap(new Hashtable<Class, List<AlignCalcWorkerI>>());
33
34   @Override
35   public void notifyStart(AlignCalcWorkerI worker)
36   {
37     synchronized (updating)
38     {
39       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
40       if (upd == null)
41       {
42         updating.put(
43                 worker.getClass(),
44                 upd = Collections
45                         .synchronizedList(new ArrayList<AlignCalcWorkerI>()));
46       }
47       synchronized (upd)
48       {
49         upd.add(worker);
50       }
51     }
52   }
53
54   @Override
55   public boolean alreadyDoing(AlignCalcWorkerI worker)
56   {
57     synchronized (inProgress)
58     {
59       return inProgress.containsKey(worker.getClass());
60     }
61   }
62
63   /*
64    * (non-Javadoc)
65    *
66    * @see jalview.api.AlignCalcManagerI#isPending(jalview.api.AlignCalcWorkerI)
67    */
68   @Override
69   public boolean isPending(AlignCalcWorkerI workingClass)
70   {
71     List<AlignCalcWorkerI> upd;
72     synchronized (updating)
73     {
74       upd = updating.get(workingClass.getClass());
75       if (upd == null)
76       {
77         return false;
78       }
79       synchronized (upd)
80       {
81         if (upd.size() > 1)
82         {
83           return true;
84         }
85       }
86       return false;
87     }
88   }
89
90   // TODO make into api method if needed ?
91   public int numberLive(AlignCalcWorkerI worker)
92   {
93     synchronized (updating)
94     {
95       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
96       if (upd == null)
97       {
98         return 0;
99       }
100       ;
101       return upd.size();
102     }
103   }
104
105   @Override
106   public boolean notifyWorking(AlignCalcWorkerI worker)
107   {
108     synchronized (inProgress)
109     {
110       // TODO: decide if we should throw exceptions here if multiple workers
111       // start to work
112       if (inProgress.get(worker.getClass()) != null)
113       {
114         if (false)
115         {
116           System.err
117                   .println("Warning: Multiple workers are running of type "
118                           + worker.getClass());
119         }
120         return false;
121       }
122       inProgress.put(worker.getClass(), worker);
123     }
124     return true;
125   }
126
127   private final HashSet<AlignCalcWorkerI> canUpdate = new HashSet<AlignCalcWorkerI>();
128
129   @Override
130   public void workerComplete(AlignCalcWorkerI worker)
131   {
132     synchronized (inProgress)
133     {
134       inProgress.remove(worker.getClass());
135       List<AlignCalcWorkerI> upd = updating.get(worker.getClass());
136       if (upd != null)
137       {
138         synchronized (upd)
139         {
140           upd.remove(worker);
141         }
142         canUpdate.add(worker);
143       }
144     }
145   }
146
147   @Override
148   public void workerCannotRun(AlignCalcWorkerI worker)
149   {
150     synchronized (blackList)
151     {
152       blackList.add(worker.getClass());
153     }
154   }
155
156   public boolean isBlackListed(Class workerType)
157   {
158     synchronized (blackList)
159     {
160       return blackList.contains(workerType);
161     }
162   }
163
164   @Override
165   public void startWorker(AlignCalcWorkerI worker)
166   {
167     Thread tw = new Thread(worker);
168     tw.setName(worker.getClass().toString());
169     tw.start();
170   }
171
172   @Override
173   public boolean isWorking(AlignCalcWorkerI worker)
174   {
175     synchronized (inProgress)
176     {// System.err.println("isWorking : worker "+(worker!=null ?
177      // worker.getClass():"null")+ " "+hashCode());
178       return worker != null && inProgress.get(worker.getClass()) == worker;
179     }
180   }
181
182   @Override
183   public boolean isWorking()
184   {
185     synchronized (inProgress)
186     {
187       // System.err.println("isWorking "+hashCode());
188       return inProgress.size() > 0;
189     }
190   }
191
192   @Override
193   public void registerWorker(AlignCalcWorkerI worker)
194   {
195     synchronized (restartable)
196     {
197       if (!restartable.contains(worker))
198       {
199         restartable.add(worker);
200       }
201       startWorker(worker);
202     }
203   }
204
205   @Override
206   public void restartWorkers()
207   {
208     synchronized (restartable)
209     {
210       for (AlignCalcWorkerI worker : restartable)
211       {
212         startWorker(worker);
213       }
214     }
215   }
216
217   @Override
218   public boolean workingInvolvedWith(AlignmentAnnotation alignmentAnnotation)
219   {
220     synchronized (inProgress)
221     {
222       for (AlignCalcWorkerI worker : inProgress.values())
223       {
224         if (worker.involves(alignmentAnnotation))
225         {
226           return true;
227         }
228       }
229     }
230     synchronized (updating)
231     {
232       for (List<AlignCalcWorkerI> workers : updating.values())
233       {
234         for (AlignCalcWorkerI worker : workers)
235           if (worker.involves(alignmentAnnotation))
236           {
237             return true;
238           }
239       }
240     }
241     return false;
242   }
243
244   @Override
245   public void updateAnnotationFor(Class workerClass)
246   {
247
248     AlignCalcWorkerI[] workers;
249     synchronized (canUpdate)
250     {
251       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
252     }
253     for (AlignCalcWorkerI worker : workers)
254     {
255       if (workerClass.equals(worker.getClass()))
256       {
257         worker.updateAnnotation();
258       }
259     }
260   }
261
262   @Override
263   public List<AlignCalcWorkerI> getRegisteredWorkersOfClass(
264           Class workerClass)
265   {
266     List<AlignCalcWorkerI> workingClass = new ArrayList<AlignCalcWorkerI>();
267     AlignCalcWorkerI[] workers;
268     synchronized (canUpdate)
269     {
270       workers = canUpdate.toArray(new AlignCalcWorkerI[0]);
271     }
272     for (AlignCalcWorkerI worker : workers)
273     {
274       if (workerClass.equals(worker.getClass()))
275       {
276         workingClass.add(worker);
277       }
278     }
279     return (workingClass.size() == 0) ? null : workingClass;
280   }
281
282   @Override
283   public boolean startRegisteredWorkersOfClass(Class workerClass)
284   {
285     List<AlignCalcWorkerI> workers = getRegisteredWorkersOfClass(workerClass);
286     if (workers == null)
287     {
288       return false;
289     }
290     for (AlignCalcWorkerI worker : workers)
291     {
292       if (!isPending(worker))
293       {
294         startWorker(worker);
295       }
296       else
297       {
298         System.err.println("Pending exists for " + workerClass);
299       }
300     }
301     return true;
302   }
303
304   @Override
305   public void workerMayRun(AlignCalcWorkerI worker)
306   {
307     synchronized (blackList)
308     {
309       if (blackList.contains(worker.getClass()))
310       {
311         blackList.remove(worker.getClass());
312       }
313     }
314   }
315 }