PageRenderTime 65ms CodeModel.GetById 50ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 477 lines | 293 code | 61 blank | 123 comment | 69 complexity | dda322694ba58ec79f2e5e3755b35525 MD5 | raw file
  1/*
  2 * WorkThread.java - Background thread 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 4132 2002-04-08 13:13:56Z 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		synchronized(lock)
 70		{
 71			started = true;
 72
 73			if(awtRequestCount != 0 && requestCount == 0)
 74				queueAWTRunner();
 75		}
 76
 77		if(threads != null)
 78		{
 79			for(int i = 0; i < threads.length; i++)
 80			{
 81				threads[i].start();
 82			}
 83		}
 84	} //}}}
 85
 86	//{{{ addWorkRequest() method
 87	/**
 88	 * Adds a work request to the queue.
 89	 * @param run The runnable
 90	 * @param inAWT If true, will be executed in AWT thread. Otherwise,
 91	 * will be executed in work thread
 92	 */
 93	public void addWorkRequest(Runnable run, boolean inAWT)
 94	{
 95		if(threads == null)
 96		{
 97			run.run();
 98			return;
 99		}
100
101		synchronized(lock)
102		{
103			//{{{ if there are no requests, execute AWT requests immediately
104			if(started && inAWT && requestCount == 0 && awtRequestCount == 0)
105			{
106//				Log.log(Log.DEBUG,this,"AWT immediate: " + run);
107
108				if(SwingUtilities.isEventDispatchThread())
109					run.run();
110				else
111					SwingUtilities.invokeLater(run);
112
113				return;
114			} //}}}
115
116			Request request = new Request(run);
117
118			//{{{ Add to AWT queue...
119			if(inAWT)
120			{
121				if(firstAWTRequest == null && lastAWTRequest == null)
122					firstAWTRequest = lastAWTRequest = request;
123				else
124				{
125					lastAWTRequest.next = request;
126					lastAWTRequest = request;
127				}
128
129				awtRequestCount++;
130
131				// if no requests are running, requestDone()
132				// will not be called, so we must queue the
133				// AWT runner ourselves.
134				if(started && requestCount == 0)
135					queueAWTRunner();
136			} //}}}
137			//{{{ Add to work thread queue...
138			else
139			{
140				if(firstRequest == null && lastRequest == null)
141					firstRequest = lastRequest = request;
142				else
143				{
144					lastRequest.next = request;
145					lastRequest = request;
146				}
147
148				requestCount++;
149			} //}}}
150
151			lock.notify();
152		}
153	} //}}}
154
155	//{{{ waitForRequests() method
156	/**
157	 * Waits until all requests are complete.
158	 */
159	public void waitForRequests()
160	{
161		if(threads == null)
162			return;
163
164		synchronized(waitForAllLock)
165		{
166			while(requestCount != 0)
167			{
168				try
169				{
170					waitForAllLock.wait();
171				}
172				catch(InterruptedException ie)
173				{
174					Log.log(Log.ERROR,this,ie);
175				}
176			}
177		}
178
179		if(SwingUtilities.isEventDispatchThread())
180		{
181			// do any queued AWT runnables
182			doAWTRequests();
183		}
184		else
185		{
186			try
187			{
188				SwingUtilities.invokeAndWait(new RunRequestsInAWTThread());
189			}
190			catch(Exception e)
191			{
192				Log.log(Log.ERROR,this,e);
193			}
194		}
195	} //}}}
196
197	//{{{ getRequestCount() method
198	/**
199	 * Returns the number of pending requests.
200	 */
201	public int getRequestCount()
202	{
203		return requestCount;
204	} //}}}
205
206	//{{{ getThreadCount() method
207	/**
208	 * Returns the number of threads in this pool.
209	 */
210	public int getThreadCount()
211	{
212		if(threads == null)
213			return 0;
214		else
215			return threads.length;
216	} //}}}
217
218	//{{{ getThread() method
219	/**
220	 * Returns the specified thread.
221	 * @param index The index of the thread
222	 */
223	public WorkThread getThread(int index)
224	{
225		return threads[index];
226	} //}}}
227
228	//{{{ addProgressListener() method
229	/**
230	 * Adds a progress listener to this thread pool.
231	 * @param listener The listener
232	 */
233	public void addProgressListener(WorkThreadProgressListener listener)
234	{
235		listenerList.add(WorkThreadProgressListener.class,listener);
236	} //}}}
237
238	//{{{ removeProgressListener() method
239	/**
240	 * Removes a progress listener from this thread pool.
241	 * @param listener The listener
242	 */
243	public void removeProgressListener(WorkThreadProgressListener listener)
244	{
245		listenerList.remove(WorkThreadProgressListener.class,listener);
246	} //}}}
247
248	//{{{ Package-private members
249	Object lock = new String("Work thread pool request queue lock");
250	Object waitForAllLock = new String("Work thread pool waitForAll() notifier");
251
252	//{{{ fireStatusChanged() method
253	void fireStatusChanged(WorkThread thread)
254	{
255		final Object[] listeners = listenerList.getListenerList();
256		if(listeners.length != 0)
257		{
258			int index = 0;
259			for(int i = 0; i < threads.length; i++)
260			{
261				if(threads[i] == thread)
262				{
263					index = i;
264					break;
265				}
266			}
267
268			for(int i = listeners.length - 2; i >= 0; i--)
269			{
270				if(listeners[i] == WorkThreadProgressListener.class)
271				{
272					((WorkThreadProgressListener)listeners[i+1])
273						.statusUpdate(WorkThreadPool.this,index);
274				}
275			}
276		}
277	} //}}}
278
279	//{{{ fireProgressChanged() method
280	void fireProgressChanged(WorkThread thread)
281	{
282		final Object[] listeners = listenerList.getListenerList();
283		if(listeners.length != 0)
284		{
285			int index = 0;
286			for(int i = 0; i < threads.length; i++)
287			{
288				if(threads[i] == thread)
289				{
290					index = i;
291					break;
292				}
293			}
294
295			for(int i = listeners.length - 2; i >= 0; i--)
296			{
297				if(listeners[i] == WorkThreadProgressListener.class)
298				{
299					((WorkThreadProgressListener)listeners[i+1])
300						.progressUpdate(WorkThreadPool.this,index);
301				}
302			}
303		}
304	} //}}}
305
306	//{{{ requestDone() method
307	void requestDone()
308	{
309		synchronized(lock)
310		{
311			requestCount--;
312
313			if(requestCount == 0 && firstAWTRequest != null)
314				queueAWTRunner();
315		}
316	} //}}}
317
318	//{{{ getNextRequest() method
319	Request getNextRequest()
320	{
321		synchronized(lock)
322		{
323			Request request = firstRequest;
324			if(request == null)
325				return null;
326
327			firstRequest = firstRequest.next;
328			if(firstRequest == null)
329				lastRequest = null;
330
331			if(request.alreadyRun)
332				throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
333			request.alreadyRun = true;
334
335			/* StringBuffer buf = new StringBuffer("request queue is now: ");
336			Request _request = request.next;
337			while(_request != null)
338			{
339				buf.append(_request.id);
340				if(_request.next != null)
341					buf.append(",");
342				_request = _request.next;
343			}
344			Log.log(Log.DEBUG,this,buf.toString()); */
345
346			return request;
347		}
348	} //}}}
349
350	//}}}
351
352	//{{{ Private members
353
354	//{{{ Instance variables
355	private boolean started;
356	private ThreadGroup threadGroup;
357	private WorkThread[] threads;
358
359	// Request queue
360	private Request firstRequest;
361	private Request lastRequest;
362	private int requestCount;
363
364	// AWT thread magic
365	private boolean awtRunnerQueued;
366	private Request firstAWTRequest;
367	private Request lastAWTRequest;
368	private int awtRequestCount;
369
370	private EventListenerList listenerList;
371	//}}}
372
373	//{{{ doAWTRequests() method
374	private void doAWTRequests()
375	{
376		while(firstAWTRequest != null)
377		{
378			doAWTRequest(getNextAWTRequest());
379		}
380	} //}}}
381
382	//{{{ doAWTRequest() method
383	private void doAWTRequest(Request request)
384	{
385//		Log.log(Log.DEBUG,this,"Running in AWT thread: " + request);
386
387		try
388		{
389			request.run.run();
390		}
391		catch(Throwable t)
392		{
393			Log.log(Log.ERROR,WorkThread.class,"Exception "
394				+ "in AWT thread:");
395			Log.log(Log.ERROR,WorkThread.class,t);
396		}
397
398		awtRequestCount--;
399	} //}}}
400
401	//{{{ queueAWTRunner() method
402	private void queueAWTRunner()
403	{
404		if(!awtRunnerQueued)
405		{
406			awtRunnerQueued = true;
407			SwingUtilities.invokeLater(new RunRequestsInAWTThread());
408//			Log.log(Log.DEBUG,this,"AWT runner queued");
409		}
410	} //}}}
411
412	//{{{ getNextAWTRequest() method
413	private Request getNextAWTRequest()
414	{
415		Request request = firstAWTRequest;
416		firstAWTRequest = firstAWTRequest.next;
417		if(firstAWTRequest == null)
418			lastAWTRequest = null;
419
420		if(request.alreadyRun)
421			throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
422		request.alreadyRun = true;
423
424		/* StringBuffer buf = new StringBuffer("AWT request queue is now: ");
425		Request _request = request.next;
426		while(_request != null)
427		{
428			buf.append(_request.id);
429			if(_request.next != null)
430				buf.append(",");
431			_request = _request.next;
432		}
433		Log.log(Log.DEBUG,this,buf.toString()); */
434
435		return request;
436	} //}}}
437
438	//}}}
439
440	static int ID;
441
442	//{{{ Request class
443	static class Request
444	{
445		int id = ++ID;
446
447		Runnable run;
448
449		boolean alreadyRun;
450
451		Request next;
452
453		Request(Runnable run)
454		{
455			this.run = run;
456		}
457
458		public String toString()
459		{
460			return "[id=" + id + ",run=" + run + "]";
461		}
462	} //}}}
463
464	//{{{ RunRequestsInAWTThread class
465	class RunRequestsInAWTThread implements Runnable
466	{
467		public void run()
468		{
469			synchronized(lock)
470			{
471				awtRunnerQueued = false;
472				if(requestCount == 0)
473					doAWTRequests();
474			}
475		}
476	} //}}}
477}