fix ThreadPool bug
[org.ibex.util.git] / src / org / ibex / util / ThreadPool.java
1 // Copyright 2000-2009 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.util;
6
7 import java.io.*;
8
9 /**
10  *   A thread pool.
11  * 
12  *   - Attempting to add a task will block until the task is assigned to
13  *     a thread.
14  *   - If a task has not been assigned after MINIMUM_DISPATCH_DELAY, a new
15  *     thread (above and beyond the ThreadPool size) will be created to
16  *     handle it.
17  *   - Whenever at least minIdleThreads are idle and more than minThreads
18  *     threads exist overall, any threads which complete their task will
19  *     be allowed to die.
20  *
21  *   Locking order: never attempt to acquire a lock on a PooledThread
22  *   while holding the ThreadPool lock.
23  */
24 public final class ThreadPool {
25
26     private static final int MINIMUM_DISPATCH_DELAY = 1000;
27     private static final int minIdleThreads         = 3;
28
29     private final int            minThreads;
30     private       int            numThreads;
31     private final PooledThread[] idleThreads;
32     private       int            numIdleThreads;
33
34     public ThreadPool(int minThreads) {
35         this.minThreads     = minThreads;
36         this.numThreads     = minThreads;
37         this.numIdleThreads = 0;
38         this.idleThreads    = new PooledThread[minThreads];
39         for(int i=0; i<this.idleThreads.length; i++)
40             new PooledThread();
41     }
42
43     public void start(Runnable r) {
44         synchronized(ThreadPool.this) {
45             /* if no idle threads, wait for MINIMUM_DISPATCH_DELAY or until notified */
46             if (numIdleThreads == 0) try {
47                 this.wait(MINIMUM_DISPATCH_DELAY);
48             } catch (Exception e) { Log.error(this, e); }
49             
50             /* if there are idle threads, use one */
51             if (numIdleThreads > 0) {
52                 numIdleThreads--;
53                 idleThreads[numIdleThreads].start(r);
54                 idleThreads[numIdleThreads] = null;
55                 return;
56             }
57             /* otherwise, create a new thread */
58             numThreads++;
59         }
60         new PooledThread().start(r);
61     }
62
63     private class PooledThread extends Thread {
64         private Runnable runnable = null;
65         public PooledThread() { start(); }
66         void start(Runnable runnable) {
67             synchronized(this) {
68                 if (this.runnable != null)
69                     throw new Error("start() on a thread that is alread busy!");
70                 this.runnable = runnable;
71                 notify();
72             }
73         }
74         public void run() {
75             try {
76                 while(true) {
77                     synchronized(this) {
78                         if (runnable==null) {
79                             synchronized(ThreadPool.this) {
80                                 /* if the idle array is full, just let ourselves die */
81                                 if (numIdleThreads > minIdleThreads && numThreads > minThreads) return;
82                                 /* otherwise put ourselves back in the pool and release anybody who is waiting */
83                                 idleThreads[numIdleThreads++] = this;
84                                 ThreadPool.this.notifyAll();
85                             }
86                         }
87                     }
88                     try {
89                         synchronized(this) { while (runnable==null) wait(); }
90                         runnable.run();
91                         synchronized(this) { runnable = null; }
92                     } catch (Exception e) { Log.error(this, e); }
93                 }
94             } finally {
95                 synchronized(ThreadPool.this) {
96                     numThreads--;
97                 }
98             }
99         }
100     }
101
102 }
103