PageRenderTime 40ms CodeModel.GetById 15ms 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
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. * 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. package org.gjt.sp.util;
  23. //{{{ Imports
  24. import javax.swing.event.EventListenerList;
  25. import javax.swing.SwingUtilities;
  26. //}}}
  27. /**
  28. * A pool of work threads.
  29. * @author Slava Pestov
  30. * @version $Id: WorkThreadPool.java 3925 2001-11-30 11:40:16Z spestov $
  31. * @see org.gjt.sp.util.WorkThread
  32. * @since jEdit 2.6pre1
  33. */
  34. public class WorkThreadPool
  35. {
  36. //{{{ WorkThreadPool constructor
  37. /**
  38. * Creates a new work thread pool with the specified number of
  39. * work threads.
  40. * @param name The thread name prefix
  41. * @param count The number of work threads
  42. */
  43. public WorkThreadPool(String name, int count)
  44. {
  45. listenerList = new EventListenerList();
  46. if(count != 0)
  47. {
  48. threadGroup = new ThreadGroup(name);
  49. threads = new WorkThread[count];
  50. for(int i = 0; i < threads.length; i++)
  51. {
  52. threads[i] = new WorkThread(this,threadGroup,name + " #" + (i+1));
  53. }
  54. }
  55. else
  56. Log.log(Log.WARNING,this,"Async I/O disabled");
  57. } //}}}
  58. //{{{ start() method
  59. /**
  60. * Starts all the threads in this thread pool.
  61. */
  62. public void start()
  63. {
  64. synchronized(lock)
  65. {
  66. started = true;
  67. if(awtRequestCount != 0 && requestCount == 0)
  68. queueAWTRunner();
  69. }
  70. if(threads != null)
  71. {
  72. for(int i = 0; i < threads.length; i++)
  73. {
  74. threads[i].start();
  75. }
  76. }
  77. } //}}}
  78. //{{{ addWorkRequest() method
  79. /**
  80. * Adds a work request to the queue.
  81. * @param run The runnable
  82. * @param inAWT If true, will be executed in AWT thread. Otherwise,
  83. * will be executed in work thread
  84. */
  85. public void addWorkRequest(Runnable run, boolean inAWT)
  86. {
  87. if(threads == null)
  88. {
  89. run.run();
  90. return;
  91. }
  92. //{{{ if there are no requests, execute AWT requests immediately
  93. if(started && inAWT && requestCount == 0 && awtRequestCount == 0)
  94. {
  95. // Log.log(Log.DEBUG,this,"AWT immediate: " + run);
  96. if(SwingUtilities.isEventDispatchThread())
  97. run.run();
  98. else
  99. SwingUtilities.invokeLater(run);
  100. return;
  101. } //}}}
  102. Request request = new Request(run);
  103. synchronized(lock)
  104. {
  105. //{{{ Add to AWT queue...
  106. if(inAWT)
  107. {
  108. if(firstAWTRequest == null && lastAWTRequest == null)
  109. firstAWTRequest = lastAWTRequest = request;
  110. else
  111. {
  112. lastAWTRequest.next = request;
  113. lastAWTRequest = request;
  114. }
  115. awtRequestCount++;
  116. // if no requests are running, requestDone()
  117. // will not be called, so we must queue the
  118. // AWT runner ourselves.
  119. if(started && requestCount == 0)
  120. queueAWTRunner();
  121. } //}}}
  122. //{{{ Add to work thread queue...
  123. else
  124. {
  125. if(firstRequest == null && lastRequest == null)
  126. firstRequest = lastRequest = request;
  127. else
  128. {
  129. lastRequest.next = request;
  130. lastRequest = request;
  131. }
  132. requestCount++;
  133. } //}}}
  134. lock.notify();
  135. }
  136. } //}}}
  137. //{{{ waitForRequests() method
  138. /**
  139. * Waits until all requests are complete.
  140. */
  141. public void waitForRequests()
  142. {
  143. if(threads == null)
  144. return;
  145. synchronized(waitForAllLock)
  146. {
  147. while(requestCount != 0)
  148. {
  149. try
  150. {
  151. waitForAllLock.wait();
  152. }
  153. catch(InterruptedException ie)
  154. {
  155. Log.log(Log.ERROR,this,ie);
  156. }
  157. }
  158. }
  159. if(SwingUtilities.isEventDispatchThread())
  160. {
  161. // do any queued AWT runnables
  162. doAWTRequests();
  163. }
  164. else
  165. {
  166. try
  167. {
  168. SwingUtilities.invokeAndWait(new RunRequestsInAWTThread());
  169. }
  170. catch(Exception e)
  171. {
  172. Log.log(Log.ERROR,this,e);
  173. }
  174. }
  175. } //}}}
  176. //{{{ getRequestCount() method
  177. /**
  178. * Returns the number of pending requests.
  179. */
  180. public int getRequestCount()
  181. {
  182. return requestCount;
  183. } //}}}
  184. //{{{ getThreadCount() method
  185. /**
  186. * Returns the number of threads in this pool.
  187. */
  188. public int getThreadCount()
  189. {
  190. if(threads == null)
  191. return 0;
  192. else
  193. return threads.length;
  194. } //}}}
  195. //{{{ getThread() method
  196. /**
  197. * Returns the specified thread.
  198. * @param index The index of the thread
  199. */
  200. public WorkThread getThread(int index)
  201. {
  202. return threads[index];
  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. {
  211. listenerList.add(WorkThreadProgressListener.class,listener);
  212. } //}}}
  213. //{{{ removeProgressListener() method
  214. /**
  215. * Removes a progress listener from this thread pool.
  216. * @param listener The listener
  217. */
  218. public void removeProgressListener(WorkThreadProgressListener listener)
  219. {
  220. listenerList.remove(WorkThreadProgressListener.class,listener);
  221. } //}}}
  222. //{{{ Package-private members
  223. Object lock = new String("Work thread pool request queue lock");
  224. Object waitForAllLock = new String("Work thread pool waitForAll() notifier");
  225. //{{{ fireStatusChanged() method
  226. void fireStatusChanged(WorkThread thread)
  227. {
  228. final Object[] listeners = listenerList.getListenerList();
  229. if(listeners.length != 0)
  230. {
  231. int index = 0;
  232. for(int i = 0; i < threads.length; i++)
  233. {
  234. if(threads[i] == thread)
  235. {
  236. index = i;
  237. break;
  238. }
  239. }
  240. for(int i = listeners.length - 2; i >= 0; i--)
  241. {
  242. if(listeners[i] == WorkThreadProgressListener.class)
  243. {
  244. ((WorkThreadProgressListener)listeners[i+1])
  245. .statusUpdate(WorkThreadPool.this,index);
  246. }
  247. }
  248. }
  249. } //}}}
  250. //{{{ fireProgressChanged() method
  251. void fireProgressChanged(WorkThread thread)
  252. {
  253. final Object[] listeners = listenerList.getListenerList();
  254. if(listeners.length != 0)
  255. {
  256. int index = 0;
  257. for(int i = 0; i < threads.length; i++)
  258. {
  259. if(threads[i] == thread)
  260. {
  261. index = i;
  262. break;
  263. }
  264. }
  265. for(int i = listeners.length - 2; i >= 0; i--)
  266. {
  267. if(listeners[i] == WorkThreadProgressListener.class)
  268. {
  269. ((WorkThreadProgressListener)listeners[i+1])
  270. .progressUpdate(WorkThreadPool.this,index);
  271. }
  272. }
  273. }
  274. } //}}}
  275. //{{{ requestDone() method
  276. void requestDone()
  277. {
  278. synchronized(lock)
  279. {
  280. requestCount--;
  281. if(requestCount == 0 && firstAWTRequest != null)
  282. queueAWTRunner();
  283. }
  284. } //}}}
  285. //{{{ getNextRequest() method
  286. Request getNextRequest()
  287. {
  288. synchronized(lock)
  289. {
  290. Request request = firstRequest;
  291. if(request == null)
  292. return null;
  293. firstRequest = firstRequest.next;
  294. if(firstRequest == null)
  295. lastRequest = null;
  296. if(request.alreadyRun)
  297. throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
  298. request.alreadyRun = true;
  299. /* StringBuffer buf = new StringBuffer("request queue is now: ");
  300. Request _request = request.next;
  301. while(_request != null)
  302. {
  303. buf.append(_request.id);
  304. if(_request.next != null)
  305. buf.append(",");
  306. _request = _request.next;
  307. }
  308. Log.log(Log.DEBUG,this,buf.toString()); */
  309. return request;
  310. }
  311. } //}}}
  312. //}}}
  313. //{{{ Private members
  314. //{{{ Instance variables
  315. private boolean started;
  316. private ThreadGroup threadGroup;
  317. private WorkThread[] threads;
  318. // Request queue
  319. private Request firstRequest;
  320. private Request lastRequest;
  321. private int requestCount;
  322. // AWT thread magic
  323. private boolean awtRunnerQueued;
  324. private Request firstAWTRequest;
  325. private Request lastAWTRequest;
  326. private int awtRequestCount;
  327. private EventListenerList listenerList;
  328. //}}}
  329. //{{{ doAWTRequests() method
  330. private void doAWTRequests()
  331. {
  332. while(firstAWTRequest != null)
  333. {
  334. doAWTRequest(getNextAWTRequest());
  335. }
  336. } //}}}
  337. //{{{ doAWTRequest() method
  338. private void doAWTRequest(Request request)
  339. {
  340. // Log.log(Log.DEBUG,this,"Running in AWT thread: " + request);
  341. try
  342. {
  343. request.run.run();
  344. }
  345. catch(Throwable t)
  346. {
  347. Log.log(Log.ERROR,WorkThread.class,"Exception "
  348. + "in AWT thread:");
  349. Log.log(Log.ERROR,WorkThread.class,t);
  350. }
  351. awtRequestCount--;
  352. } //}}}
  353. //{{{ queueAWTRunner() method
  354. private void queueAWTRunner()
  355. {
  356. if(!awtRunnerQueued)
  357. {
  358. awtRunnerQueued = true;
  359. SwingUtilities.invokeLater(new RunRequestsInAWTThread());
  360. //Log.log(Log.DEBUG,this,"AWT runner queued");
  361. }
  362. } //}}}
  363. //{{{ getNextAWTRequest() method
  364. private Request getNextAWTRequest()
  365. {
  366. synchronized(lock)
  367. {
  368. Request request = firstAWTRequest;
  369. firstAWTRequest = firstAWTRequest.next;
  370. if(firstAWTRequest == null)
  371. lastAWTRequest = null;
  372. if(request.alreadyRun)
  373. throw new InternalError("AIEE!!! Request run twice!!! " + request.run);
  374. request.alreadyRun = true;
  375. /* StringBuffer buf = new StringBuffer("AWT request queue is now: ");
  376. Request _request = request.next;
  377. while(_request != null)
  378. {
  379. buf.append(_request.id);
  380. if(_request.next != null)
  381. buf.append(",");
  382. _request = _request.next;
  383. }
  384. Log.log(Log.DEBUG,this,buf.toString()); */
  385. return request;
  386. }
  387. } //}}}
  388. //}}}
  389. static int ID;
  390. //{{{ Request class
  391. static class Request
  392. {
  393. int id = ++ID;
  394. Runnable run;
  395. boolean alreadyRun;
  396. Request next;
  397. Request(Runnable run)
  398. {
  399. this.run = run;
  400. }
  401. public String toString()
  402. {
  403. return "[id=" + id + ",run=" + run + "]";
  404. }
  405. } //}}}
  406. //{{{ RunRequestsInAWTThread class
  407. class RunRequestsInAWTThread implements Runnable
  408. {
  409. public void run()
  410. {
  411. awtRunnerQueued = false;
  412. doAWTRequests();
  413. }
  414. } //}}}
  415. }