PageRenderTime 111ms CodeModel.GetById 82ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 476 lines | 292 code | 61 blank | 123 comment | 67 complexity | 61d418dcbcf817ef01c04139e7c36322 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 3925 2001-11-30 11:40:16Z 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		//{{{ if there are no requests, execute AWT requests immediately
102		if(started && inAWT && requestCount == 0 && awtRequestCount == 0)
103		{
104// 			Log.log(Log.DEBUG,this,"AWT immediate: " + run);
105
106			if(SwingUtilities.isEventDispatchThread())
107				run.run();
108			else
109				SwingUtilities.invokeLater(run);
110
111			return;
112		} //}}}
113
114		Request request = new Request(run);
115
116		synchronized(lock)
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		synchronized(lock)
416		{
417			Request request = firstAWTRequest;
418			firstAWTRequest = firstAWTRequest.next;
419			if(firstAWTRequest == null)
420				lastAWTRequest = null;
421
422			if(request.alreadyRun)
423				throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
424			request.alreadyRun = true;
425
426			/* StringBuffer buf = new StringBuffer("AWT request queue is now: ");
427			Request _request = request.next;
428			while(_request != null)
429			{
430				buf.append(_request.id);
431				if(_request.next != null)
432					buf.append(",");
433				_request = _request.next;
434			}
435			Log.log(Log.DEBUG,this,buf.toString()); */
436
437			return request;
438		}
439	} //}}}
440
441	//}}}
442
443	static int ID;
444
445	//{{{ Request class
446	static class Request
447	{
448		int id = ++ID;
449
450		Runnable run;
451
452		boolean alreadyRun;
453
454		Request next;
455
456		Request(Runnable run)
457		{
458			this.run = run;
459		}
460
461		public String toString()
462		{
463			return "[id=" + id + ",run=" + run + "]";
464		}
465	} //}}}
466
467	//{{{ RunRequestsInAWTThread class
468	class RunRequestsInAWTThread implements Runnable
469	{
470		public void run()
471		{
472			awtRunnerQueued = false;
473			doAWTRequests();
474		}
475	} //}}}
476}