PageRenderTime 85ms CodeModel.GetById 70ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/util/WorkThreadPool.java

#
Java | 481 lines | 293 code | 61 blank | 127 comment | 71 complexity | c139e3cc4fe7d7cf989051a7749f483b MD5 | raw file
  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}