/jEdit/tags/jedit-4-2-pre14/org/gjt/sp/util/WorkThreadPool.java
Java | 481 lines | 293 code | 61 blank | 127 comment | 71 complexity | c139e3cc4fe7d7cf989051a7749f483b MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
1/*
2 * WorkThreadPool.java - Background thread pool that does stuff
3 * :tabSize=8:indentSize=8:noTabs=false:
4 * :folding=explicit:collapseFolds=1:
5 *
6 * Copyright (C) 2000 Slava Pestov
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23package org.gjt.sp.util;
24
25//{{{ Imports
26import javax.swing.event.EventListenerList;
27import javax.swing.SwingUtilities;
28//}}}
29
30/**
31 * A pool of work threads.
32 * @author Slava Pestov
33 * @version $Id: WorkThreadPool.java 5053 2004-05-29 01:55:26Z spestov $
34 * @see org.gjt.sp.util.WorkThread
35 * @since jEdit 2.6pre1
36 */
37public class WorkThreadPool
38{
39 //{{{ WorkThreadPool constructor
40 /**
41 * Creates a new work thread pool with the specified number of
42 * work threads.
43 * @param name The thread name prefix
44 * @param count The number of work threads
45 */
46 public WorkThreadPool(String name, int count)
47 {
48 listenerList = new EventListenerList();
49
50 if(count != 0)
51 {
52 threadGroup = new ThreadGroup(name);
53 threads = new WorkThread[count];
54 for(int i = 0; i < threads.length; i++)
55 {
56 threads[i] = new WorkThread(this,threadGroup,name + " #" + (i+1));
57 }
58 }
59 else
60 Log.log(Log.WARNING,this,"Async I/O disabled");
61 } //}}}
62
63 //{{{ start() method
64 /**
65 * Starts all the threads in this thread pool.
66 */
67 public void start()
68 {
69 /* not really needed since threads don't start until after */
70 synchronized(lock)
71 {
72 started = true;
73
74 if(awtRequestCount != 0 && requestCount == 0)
75 queueAWTRunner();
76 }
77
78 if(threads != null)
79 {
80 for(int i = 0; i < threads.length; i++)
81 {
82 threads[i].start();
83 }
84 }
85 } //}}}
86
87 //{{{ addWorkRequest() method
88 /**
89 * Adds a work request to the queue.
90 * @param run The runnable
91 * @param inAWT If true, will be executed in AWT thread. Otherwise,
92 * will be executed in work thread
93 */
94 public void addWorkRequest(Runnable run, boolean inAWT)
95 {
96 if(threads == null)
97 {
98 run.run();
99 return;
100 }
101
102 synchronized(lock)
103 {
104 //{{{ if there are no requests, execute AWT requests immediately
105 if(started && inAWT && requestCount == 0 && awtRequestCount == 0)
106 {
107// Log.log(Log.DEBUG,this,"AWT immediate: " + run);
108
109 if(SwingUtilities.isEventDispatchThread())
110 run.run();
111 else
112 SwingUtilities.invokeLater(run);
113
114 return;
115 } //}}}
116
117 Request request = new Request(run);
118
119 //{{{ Add to AWT queue...
120 if(inAWT)
121 {
122 if(firstAWTRequest == null && lastAWTRequest == null)
123 firstAWTRequest = lastAWTRequest = request;
124 else
125 {
126 lastAWTRequest.next = request;
127 lastAWTRequest = request;
128 }
129
130 awtRequestCount++;
131
132 // if no requests are running, requestDone()
133 // will not be called, so we must queue the
134 // AWT runner ourselves.
135 if(started && requestCount == 0)
136 queueAWTRunner();
137 } //}}}
138 //{{{ Add to work thread queue...
139 else
140 {
141 if(firstRequest == null && lastRequest == null)
142 firstRequest = lastRequest = request;
143 else
144 {
145 lastRequest.next = request;
146 lastRequest = request;
147 }
148
149 requestCount++;
150 } //}}}
151
152 lock.notifyAll();
153 }
154 } //}}}
155
156 //{{{ waitForRequests() method
157 /**
158 * Waits until all requests are complete.
159 */
160 public void waitForRequests()
161 {
162 if(threads == null)
163 return;
164
165 synchronized(waitForAllLock)
166 {
167 while(requestCount != 0)
168 {
169 try
170 {
171 waitForAllLock.wait();
172 }
173 catch(InterruptedException ie)
174 {
175 Log.log(Log.ERROR,this,ie);
176 }
177 }
178 }
179
180 if(SwingUtilities.isEventDispatchThread())
181 {
182 // do any queued AWT runnables
183 doAWTRequests();
184 }
185 else
186 {
187 try
188 {
189 SwingUtilities.invokeAndWait(new RunRequestsInAWTThread());
190 }
191 catch(Exception e)
192 {
193 Log.log(Log.ERROR,this,e);
194 }
195 }
196 } //}}}
197
198 //{{{ getRequestCount() method
199 /**
200 * Returns the number of pending requests.
201 */
202 public int getRequestCount()
203 {
204 return requestCount;
205 } //}}}
206
207 //{{{ getThreadCount() method
208 /**
209 * Returns the number of threads in this pool.
210 */
211 public int getThreadCount()
212 {
213 if(threads == null)
214 return 0;
215 else
216 return threads.length;
217 } //}}}
218
219 //{{{ getThread() method
220 /**
221 * Returns the specified thread.
222 * @param index The index of the thread
223 */
224 public WorkThread getThread(int index)
225 {
226 return threads[index];
227 } //}}}
228
229 //{{{ addProgressListener() method
230 /**
231 * Adds a progress listener to this thread pool.
232 * @param listener The listener
233 */
234 public void addProgressListener(WorkThreadProgressListener listener)
235 {
236 listenerList.add(WorkThreadProgressListener.class,listener);
237 } //}}}
238
239 //{{{ removeProgressListener() method
240 /**
241 * Removes a progress listener from this thread pool.
242 * @param listener The listener
243 */
244 public void removeProgressListener(WorkThreadProgressListener listener)
245 {
246 listenerList.remove(WorkThreadProgressListener.class,listener);
247 } //}}}
248
249 //{{{ Package-private members
250 Object lock = new Object();
251 Object waitForAllLock = new Object();
252
253 //{{{ fireStatusChanged() method
254 void fireStatusChanged(WorkThread thread)
255 {
256 final Object[] listeners = listenerList.getListenerList();
257 if(listeners.length != 0)
258 {
259 int index = 0;
260 for(int i = 0; i < threads.length; i++)
261 {
262 if(threads[i] == thread)
263 {
264 index = i;
265 break;
266 }
267 }
268
269 for(int i = listeners.length - 2; i >= 0; i--)
270 {
271 if(listeners[i] == WorkThreadProgressListener.class)
272 {
273 ((WorkThreadProgressListener)listeners[i+1])
274 .statusUpdate(WorkThreadPool.this,index);
275 }
276 }
277 }
278 } //}}}
279
280 //{{{ fireProgressChanged() method
281 void fireProgressChanged(WorkThread thread)
282 {
283 final Object[] listeners = listenerList.getListenerList();
284 if(listeners.length != 0)
285 {
286 int index = 0;
287 for(int i = 0; i < threads.length; i++)
288 {
289 if(threads[i] == thread)
290 {
291 index = i;
292 break;
293 }
294 }
295
296 for(int i = listeners.length - 2; i >= 0; i--)
297 {
298 if(listeners[i] == WorkThreadProgressListener.class)
299 {
300 ((WorkThreadProgressListener)listeners[i+1])
301 .progressUpdate(WorkThreadPool.this,index);
302 }
303 }
304 }
305 } //}}}
306
307 //{{{ requestDone() method
308 void requestDone()
309 {
310 synchronized(lock)
311 {
312 requestCount--;
313
314 if(requestCount == 0 && firstAWTRequest != null)
315 queueAWTRunner();
316 }
317 } //}}}
318
319 //{{{ getNextRequest() method
320 Request getNextRequest()
321 {
322 synchronized(lock)
323 {
324 Request request = firstRequest;
325 if(request == null)
326 return null;
327
328 firstRequest = firstRequest.next;
329 if(firstRequest == null)
330 lastRequest = null;
331
332 if(request.alreadyRun)
333 throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
334 request.alreadyRun = true;
335
336 /* StringBuffer buf = new StringBuffer("request queue is now: ");
337 Request _request = request.next;
338 while(_request != null)
339 {
340 buf.append(_request.id);
341 if(_request.next != null)
342 buf.append(",");
343 _request = _request.next;
344 }
345 Log.log(Log.DEBUG,this,buf.toString()); */
346
347 return request;
348 }
349 } //}}}
350
351 //}}}
352
353 //{{{ Private members
354
355 //{{{ Instance variables
356 private boolean started;
357 private ThreadGroup threadGroup;
358 private WorkThread[] threads;
359
360 // Request queue
361 private Request firstRequest;
362 private Request lastRequest;
363 private int requestCount;
364
365 // AWT thread magic
366 private boolean awtRunnerQueued;
367 private Request firstAWTRequest;
368 private Request lastAWTRequest;
369 private int awtRequestCount;
370
371 private EventListenerList listenerList;
372 //}}}
373
374 //{{{ doAWTRequests() method
375 /** Must always be called with the lock held. */
376 private void doAWTRequests()
377 {
378 while(requestCount == 0 && firstAWTRequest != null)
379 {
380 doAWTRequest(getNextAWTRequest());
381 }
382 } //}}}
383
384 //{{{ doAWTRequest() method
385 /** Must always be called with the lock held. */
386 private void doAWTRequest(Request request)
387 {
388// Log.log(Log.DEBUG,this,"Running in AWT thread: " + request);
389
390 try
391 {
392 request.run.run();
393 }
394 catch(Throwable t)
395 {
396 Log.log(Log.ERROR,WorkThread.class,"Exception "
397 + "in AWT thread:");
398 Log.log(Log.ERROR,WorkThread.class,t);
399 }
400
401 awtRequestCount--;
402 } //}}}
403
404 //{{{ queueAWTRunner() method
405 /** Must always be called with the lock held. */
406 private void queueAWTRunner()
407 {
408 if(!awtRunnerQueued)
409 {
410 awtRunnerQueued = true;
411 SwingUtilities.invokeLater(new RunRequestsInAWTThread());
412// Log.log(Log.DEBUG,this,"AWT runner queued");
413 }
414 } //}}}
415
416 //{{{ getNextAWTRequest() method
417 private Request getNextAWTRequest()
418 {
419 Request request = firstAWTRequest;
420 firstAWTRequest = firstAWTRequest.next;
421 if(firstAWTRequest == null)
422 lastAWTRequest = null;
423
424 if(request.alreadyRun)
425 throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
426 request.alreadyRun = true;
427
428 /* StringBuffer buf = new StringBuffer("AWT request queue is now: ");
429 Request _request = request.next;
430 while(_request != null)
431 {
432 buf.append(_request.id);
433 if(_request.next != null)
434 buf.append(",");
435 _request = _request.next;
436 }
437 Log.log(Log.DEBUG,this,buf.toString()); */
438
439 return request;
440 } //}}}
441
442 //}}}
443
444 static int ID;
445
446 //{{{ Request class
447 static class Request
448 {
449 int id = ++ID;
450
451 Runnable run;
452
453 boolean alreadyRun;
454
455 Request next;
456
457 Request(Runnable run)
458 {
459 this.run = run;
460 }
461
462 public String toString()
463 {
464 return "[id=" + id + ",run=" + run + "]";
465 }
466 } //}}}
467
468 //{{{ RunRequestsInAWTThread class
469 class RunRequestsInAWTThread implements Runnable
470 {
471 public void run()
472 {
473 synchronized(lock)
474 {
475 awtRunnerQueued = false;
476 if(requestCount == 0)
477 doAWTRequests();
478 }
479 }
480 } //}}}
481}