001: /*
002: * WorkThreadPool.java - Background thread pool that does stuff
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2000 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.util;
024:
025: //{{{ Imports
026: import javax.swing.event.EventListenerList;
027: import javax.swing.SwingUtilities;
028:
029: //}}}
030:
031: /**
032: * A pool of work threads.
033: * @author Slava Pestov
034: * @version $Id: WorkThreadPool.java 10796 2007-10-04 08:46:18Z kpouer $
035: * @see org.gjt.sp.util.WorkThread
036: * @since jEdit 2.6pre1
037: */
038: public class WorkThreadPool {
039: //{{{ WorkThreadPool constructor
040: /**
041: * Creates a new work thread pool with the specified number of
042: * work threads.
043: * @param name The thread name prefix
044: * @param count The number of work threads
045: */
046: public WorkThreadPool(String name, int count) {
047: listenerList = new EventListenerList();
048:
049: if (count != 0) {
050: threadGroup = new ThreadGroup(name);
051: threads = new WorkThread[count];
052: for (int i = 0; i < threads.length; i++) {
053: threads[i] = new WorkThread(this , threadGroup, name
054: + " #" + (i + 1));
055: }
056: } else
057: Log.log(Log.WARNING, this , "Async I/O disabled");
058: } //}}}
059:
060: //{{{ start() method
061: /**
062: * Starts all the threads in this thread pool.
063: */
064: public void start() {
065: /* not really needed since threads don't start until after */
066: synchronized (lock) {
067: started = true;
068:
069: if (awtRequestCount != 0 && requestCount == 0)
070: queueAWTRunner();
071: }
072:
073: if (threads != null) {
074: for (int i = 0; i < threads.length; i++) {
075: threads[i].start();
076: }
077: }
078: } //}}}
079:
080: //{{{ addWorkRequest() method
081: /**
082: * Adds a work request to the queue.
083: * @param run The runnable
084: * @param inAWT If true, will be executed in AWT thread. Otherwise,
085: * will be executed in work thread
086: */
087: public void addWorkRequest(Runnable run, boolean inAWT) {
088: if (threads == null) {
089: run.run();
090: return;
091: }
092:
093: synchronized (lock) {
094: //{{{ if there are no requests, execute AWT requests immediately
095: if (started && inAWT && requestCount == 0
096: && awtRequestCount == 0) {
097: // Log.log(Log.DEBUG,this,"AWT immediate: " + run);
098:
099: if (SwingUtilities.isEventDispatchThread())
100: run.run();
101: else
102: SwingUtilities.invokeLater(run);
103:
104: return;
105: } //}}}
106:
107: Request request = new Request(run);
108:
109: //{{{ Add to AWT queue...
110: if (inAWT) {
111: if (firstAWTRequest == null && lastAWTRequest == null)
112: firstAWTRequest = lastAWTRequest = request;
113: else {
114: lastAWTRequest.next = request;
115: lastAWTRequest = request;
116: }
117:
118: awtRequestCount++;
119:
120: // if no requests are running, requestDone()
121: // will not be called, so we must queue the
122: // AWT runner ourselves.
123: if (started && requestCount == 0)
124: queueAWTRunner();
125: } //}}}
126: //{{{ Add to work thread queue...
127: else {
128: if (firstRequest == null && lastRequest == null)
129: firstRequest = lastRequest = request;
130: else {
131: lastRequest.next = request;
132: lastRequest = request;
133: }
134:
135: requestCount++;
136: } //}}}
137:
138: lock.notifyAll();
139: }
140: } //}}}
141:
142: //{{{ waitForRequests() method
143: /**
144: * Waits until all requests are complete.
145: */
146: public void waitForRequests() {
147: if (threads == null)
148: return;
149:
150: synchronized (waitForAllLock) {
151: while (requestCount != 0) {
152: try {
153: waitForAllLock.wait();
154: } catch (InterruptedException ie) {
155: Log.log(Log.ERROR, this , ie);
156: }
157: }
158: }
159:
160: if (SwingUtilities.isEventDispatchThread()) {
161: // do any queued AWT runnables
162: doAWTRequests();
163: } else {
164: try {
165: SwingUtilities
166: .invokeAndWait(new RunRequestsInAWTThread());
167: } catch (Exception e) {
168: Log.log(Log.ERROR, this , e);
169: }
170: }
171: } //}}}
172:
173: //{{{ getRequestCount() method
174: /**
175: * Returns the number of pending requests.
176: * @return the pending request count
177: */
178: public int getRequestCount() {
179: return requestCount;
180: } //}}}
181:
182: //{{{ getThreadCount() method
183: /**
184: * Returns the number of threads in this pool.
185: * @return the thread count
186: */
187: public int getThreadCount() {
188: if (threads == null)
189: return 0;
190: else
191: return threads.length;
192: } //}}}
193:
194: //{{{ getThread() method
195: /**
196: * Returns the specified thread.
197: * @param index The index of the thread
198: * @return a WorkThread
199: */
200: public WorkThread getThread(int index) {
201: return threads[index];
202: } //}}}
203:
204: //{{{ addProgressListener() method
205: /**
206: * Adds a progress listener to this thread pool.
207: * @param listener The listener
208: */
209: public void addProgressListener(WorkThreadProgressListener listener) {
210: listenerList.add(WorkThreadProgressListener.class, listener);
211: } //}}}
212:
213: //{{{ removeProgressListener() method
214: /**
215: * Removes a progress listener from this thread pool.
216: * @param listener The listener
217: */
218: public void removeProgressListener(
219: WorkThreadProgressListener listener) {
220: listenerList.remove(WorkThreadProgressListener.class, listener);
221: } //}}}
222:
223: //{{{ Package-private members
224: final Object lock = new Object();
225: final Object waitForAllLock = new Object();
226:
227: //{{{ fireStatusChanged() method
228: void fireStatusChanged(WorkThread thread) {
229: final Object[] listeners = listenerList.getListenerList();
230: if (listeners.length != 0) {
231: int index = 0;
232: for (int i = 0; i < threads.length; i++) {
233: if (threads[i] == thread) {
234: index = i;
235: break;
236: }
237: }
238:
239: for (int i = listeners.length - 2; i >= 0; i--) {
240: if (listeners[i] == WorkThreadProgressListener.class) {
241: ((WorkThreadProgressListener) listeners[i + 1])
242: .statusUpdate(WorkThreadPool.this , index);
243: }
244: }
245: }
246: } //}}}
247:
248: //{{{ fireProgressChanged() method
249: void fireProgressChanged(WorkThread thread) {
250: final Object[] listeners = listenerList.getListenerList();
251: if (listeners.length != 0) {
252: int index = 0;
253: for (int i = 0; i < threads.length; i++) {
254: if (threads[i] == thread) {
255: index = i;
256: break;
257: }
258: }
259:
260: for (int i = listeners.length - 2; i >= 0; i--) {
261: if (listeners[i] == WorkThreadProgressListener.class) {
262: ((WorkThreadProgressListener) listeners[i + 1])
263: .progressUpdate(WorkThreadPool.this , index);
264: }
265: }
266: }
267: } //}}}
268:
269: //{{{ requestDone() method
270: void requestDone() {
271: synchronized (lock) {
272: requestCount--;
273:
274: if (requestCount == 0 && firstAWTRequest != null)
275: queueAWTRunner();
276: }
277: } //}}}
278:
279: //{{{ getNextRequest() method
280: Request getNextRequest() {
281: synchronized (lock) {
282: Request request = firstRequest;
283: if (request == null)
284: return null;
285:
286: firstRequest = firstRequest.next;
287: if (firstRequest == null)
288: lastRequest = null;
289:
290: if (request.alreadyRun)
291: throw new InternalError("AIEE!!! Request run twice!!! "
292: + request.run);
293: request.alreadyRun = true;
294:
295: /* StringBuffer buf = new StringBuffer("request queue is now: ");
296: Request _request = request.next;
297: while(_request != null)
298: {
299: buf.append(_request.id);
300: if(_request.next != null)
301: buf.append(",");
302: _request = _request.next;
303: }
304: Log.log(Log.DEBUG,this,buf.toString()); */
305:
306: return request;
307: }
308: } //}}}
309:
310: //}}}
311:
312: //{{{ Private members
313:
314: //{{{ Instance variables
315: private boolean started;
316: private ThreadGroup threadGroup;
317: private WorkThread[] threads;
318:
319: // Request queue
320: private Request firstRequest;
321: private Request lastRequest;
322: private int requestCount;
323:
324: // AWT thread magic
325: private boolean awtRunnerQueued;
326: private Request firstAWTRequest;
327: private Request lastAWTRequest;
328: private int awtRequestCount;
329:
330: private EventListenerList listenerList;
331:
332: //}}}
333:
334: //{{{ doAWTRequests() method
335: /** Must always be called with the lock held. */
336: private void doAWTRequests() {
337: while (requestCount == 0 && firstAWTRequest != null) {
338: doAWTRequest(getNextAWTRequest());
339: }
340: } //}}}
341:
342: //{{{ doAWTRequest() method
343: /**
344: * Must always be called with the lock held.
345: * @param request the request to run
346: */
347: private void doAWTRequest(Request request) {
348: // Log.log(Log.DEBUG,this,"Running in AWT thread: " + request);
349:
350: try {
351: request.run.run();
352: } catch (Throwable t) {
353: Log.log(Log.ERROR, WorkThread.class, "Exception "
354: + "in AWT thread:");
355: Log.log(Log.ERROR, WorkThread.class, t);
356: }
357:
358: awtRequestCount--;
359: } //}}}
360:
361: //{{{ queueAWTRunner() method
362: /** Must always be called with the lock held. */
363: private void queueAWTRunner() {
364: if (!awtRunnerQueued) {
365: awtRunnerQueued = true;
366: SwingUtilities.invokeLater(new RunRequestsInAWTThread());
367: // Log.log(Log.DEBUG,this,"AWT runner queued");
368: }
369: } //}}}
370:
371: //{{{ getNextAWTRequest() method
372: private Request getNextAWTRequest() {
373: Request request = firstAWTRequest;
374: firstAWTRequest = firstAWTRequest.next;
375: if (firstAWTRequest == null)
376: lastAWTRequest = null;
377:
378: if (request.alreadyRun)
379: throw new InternalError("AIEE!!! Request run twice!!! "
380: + request.run);
381: request.alreadyRun = true;
382:
383: /* StringBuffer buf = new StringBuffer("AWT request queue is now: ");
384: Request _request = request.next;
385: while(_request != null)
386: {
387: buf.append(_request.id);
388: if(_request.next != null)
389: buf.append(",");
390: _request = _request.next;
391: }
392: Log.log(Log.DEBUG,this,buf.toString()); */
393:
394: return request;
395: } //}}}
396:
397: //}}}
398:
399: static int ID;
400:
401: //{{{ Request class
402: static class Request {
403: int id = ++ID;
404:
405: Runnable run;
406:
407: boolean alreadyRun;
408:
409: Request next;
410:
411: Request(Runnable run) {
412: this .run = run;
413: }
414:
415: public String toString() {
416: return "[id=" + id + ",run=" + run + "]";
417: }
418: } //}}}
419:
420: //{{{ RunRequestsInAWTThread class
421: class RunRequestsInAWTThread implements Runnable {
422: public void run() {
423: synchronized (lock) {
424: awtRunnerQueued = false;
425: if (requestCount == 0)
426: doAWTRequests();
427: }
428: }
429: } //}}}
430: }
|