0fb6d0761637932f1d4010a832f9d79b2734dd53
[org.ibex.util.git] / src / org / ibex / util / ThreadPool.java
1 // Copyright 2000-2005 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  */
22 public final class ThreadPool {
23
24     private static final int MINIMUM_DISPATCH_DELAY = 1000;
25     private static final int minIdleThreads         = 3;
26
27     private final int            minThreads;
28     private       int            numThreads;
29     private final PooledThread[] idleThreads;
30     private       int            numIdleThreads;
31
32     public ThreadPool(int minThreads) {
33         this.minThreads     = minThreads;
34         this.numThreads     = 0;
35         this.numIdleThreads = 0;
36         this.idleThreads    = new PooledThread[minThreads];
37         for(int i=0; i<this.idleThreads.length; i++)
38             new PooledThread();
39     }
40
41     public synchronized void start(Runnable r) {
42         /* if no idle threads, wait for MINIMUM_DISPATCH_DELAY or until notified */
43         if (numIdleThreads == 0) try {
44             this.wait(MINIMUM_DISPATCH_DELAY);
45         } catch (Exception e) { Log.error(this, e); }
46
47         /* if there are idle threads, use one */
48         if (numIdleThreads > 0) {
49             numIdleThreads--;
50             idleThreads[numIdleThreads].start(r);
51             idleThreads[numIdleThreads] = null;
52             return;
53         }
54
55         /* otherwise, create a new thread */
56         new PooledThread().start(r);
57     }
58
59     private class PooledThread extends Thread {
60         private Runnable runnable = null;
61         public PooledThread() {
62             synchronized(ThreadPool.this) { numThreads++; }
63             start();
64         }
65         public synchronized void start(Runnable runnable) {
66             if (this.runnable != null)
67                 throw new Error("start() on a thread that is alread busy!");
68             this.runnable = runnable;
69             notify();
70         }
71         public void run() {
72             try {
73                 while(true) {
74                     synchronized(this) {
75                         if (runnable==null)
76                             synchronized(ThreadPool.this) {
77                                 /* if the idle array is full, just let ourselves die */
78                                 if (numIdleThreads > minIdleThreads && numThreads > minThreads) return;
79                                 /* otherwise put ourselves back in the pool and release anybody who is waiting */
80                                 idleThreads[numIdleThreads++] = this;
81                                 ThreadPool.this.notifyAll();
82                             }
83                     }
84                     try {
85                         synchronized(this) { while (runnable==null) wait(); }
86                         runnable.run();
87                     } catch (Exception e) { Log.error(this, e); }
88                     runnable = null;
89                 }
90             } finally {
91                 synchronized(ThreadPool.this) {
92                     numThreads--;
93                 }
94             }
95         }
96     }
97
98 }
99