KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > jobs > WorkerPool


1 /*******************************************************************************
2  * Copyright (c) 2003, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM - Initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.jobs;
12
13 import org.eclipse.core.runtime.Assert;
14 import org.eclipse.core.runtime.IStatus;
15 import org.eclipse.core.runtime.jobs.Job;
16
17 /**
18  * Maintains a pool of worker threads. Threads are constructed lazily as
19  * required, and are eventually discarded if not in use for awhile. This class
20  * maintains the thread creation/destruction policies for the job manager.
21  *
22  * Implementation note: all the data structures of this class are protected
23  * by the instance's object monitor. To avoid deadlock with third party code,
24  * this lock is never held when calling methods outside this class that may in
25  * turn use locks.
26  */

27 class WorkerPool {
28     /**
29      * Threads not used by their best before timestamp are destroyed.
30      */

31     private static final int BEST_BEFORE = 60000;
32     /**
33      * There will always be at least MIN_THREADS workers in the pool.
34      */

35     private static final int MIN_THREADS = 1;
36     /**
37      * Use the busy thread count to avoid starting new threads when a living
38      * thread is just doing house cleaning (notifying listeners, etc).
39      */

40     private int busyThreads = 0;
41
42     /**
43      * The default context class loader to use when creating worker threads.
44      */

45     protected final ClassLoader JavaDoc defaultContextLoader;
46
47     /**
48      * Records whether new worker threads should be daemon threads.
49      */

50     private boolean isDaemon = false;
51
52     private JobManager manager;
53     /**
54      * The number of workers in the threads array
55      */

56     private int numThreads = 0;
57     /**
58      * The number of threads that are currently sleeping
59      */

60     private int sleepingThreads = 0;
61     /**
62      * The living set of workers in this pool.
63      */

64     private Worker[] threads = new Worker[10];
65
66     protected WorkerPool(JobManager manager) {
67         this.manager = manager;
68         this.defaultContextLoader = Thread.currentThread().getContextClassLoader();
69     }
70
71     /**
72      * Adds a worker to the list of workers.
73      */

74     private synchronized void add(Worker worker) {
75         int size = threads.length;
76         if (numThreads + 1 > size) {
77             Worker[] newThreads = new Worker[2 * size];
78             System.arraycopy(threads, 0, newThreads, 0, size);
79             threads = newThreads;
80         }
81         threads[numThreads++] = worker;
82     }
83
84     private synchronized void decrementBusyThreads() {
85         //impossible to have less than zero busy threads
86
if (--busyThreads < 0) {
87             if (JobManager.DEBUG)
88                 Assert.isTrue(false, Integer.toString(busyThreads));
89             busyThreads = 0;
90         }
91     }
92
93     /**
94      * Signals the end of a job. Note that this method can be called under
95      * OutOfMemoryError conditions and thus must be paranoid about allocating objects.
96      */

97     protected void endJob(InternalJob job, IStatus result) {
98         decrementBusyThreads();
99         //need to end rule in graph before ending job so that 2 threads
100
//do not become the owners of the same rule in the graph
101
if ((job.getRule() != null) && !(job instanceof ThreadJob)) {
102             //remove any locks this thread may be owning on that rule
103
manager.getLockManager().removeLockCompletely(Thread.currentThread(), job.getRule());
104         }
105         manager.endJob(job, result, true);
106         //ensure this thread no longer owns any scheduling rules
107
manager.implicitJobs.endJob(job);
108     }
109
110     /**
111      * Signals the death of a worker thread. Note that this method can be called under
112      * OutOfMemoryError conditions and thus must be paranoid about allocating objects.
113      */

114     protected synchronized void endWorker(Worker worker) {
115         if (remove(worker) && JobManager.DEBUG)
116             JobManager.debug("worker removed from pool: " + worker); //$NON-NLS-1$
117
}
118
119     private synchronized void incrementBusyThreads() {
120         //impossible to have more busy threads than there are threads
121
if (++busyThreads > numThreads) {
122             if (JobManager.DEBUG)
123                 Assert.isTrue(false, Integer.toString(busyThreads) + ',' + numThreads);
124             busyThreads = numThreads;
125         }
126     }
127
128     /**
129      * Notification that a job has been added to the queue. Wake a worker,
130      * creating a new worker if necessary. The provided job may be null.
131      */

132     protected synchronized void jobQueued() {
133         //if there is a sleeping thread, wake it up
134
if (sleepingThreads > 0) {
135             notify();
136             return;
137         }
138         //create a thread if all threads are busy
139
if (busyThreads >= numThreads) {
140             Worker worker = new Worker(this);
141             worker.setDaemon(isDaemon);
142             add(worker);
143             if (JobManager.DEBUG)
144                 JobManager.debug("worker added to pool: " + worker); //$NON-NLS-1$
145
worker.start();
146             return;
147         }
148     }
149
150     /**
151      * Remove a worker thread from our list.
152      * @return true if a worker was removed, and false otherwise.
153      */

154     private synchronized boolean remove(Worker worker) {
155         for (int i = 0; i < threads.length; i++) {
156             if (threads[i] == worker) {
157                 System.arraycopy(threads, i + 1, threads, i, numThreads - i - 1);
158                 threads[--numThreads] = null;
159                 return true;
160             }
161         }
162         return false;
163     }
164
165     /**
166      * Sets whether threads created in the worker pool should be daemon threads.
167      */

168     void setDaemon(boolean value) {
169         this.isDaemon = value;
170     }
171
172     protected synchronized void shutdown() {
173         notifyAll();
174     }
175
176     /**
177      * Sleep for the given duration or until woken.
178      */

179     private synchronized void sleep(long duration) {
180         sleepingThreads++;
181         busyThreads--;
182         if (JobManager.DEBUG)
183             JobManager.debug("worker sleeping for: " + duration + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
184
try {
185             wait(duration);
186         } catch (InterruptedException JavaDoc e) {
187             if (JobManager.DEBUG)
188                 JobManager.debug("worker interrupted while waiting... :-|"); //$NON-NLS-1$
189
} finally {
190             sleepingThreads--;
191             busyThreads++;
192         }
193     }
194
195     /**
196      * Returns a new job to run. Returns null if the thread should die.
197      */

198     protected InternalJob startJob(Worker worker) {
199         //if we're above capacity, kill the thread
200
synchronized (this) {
201             if (!manager.isActive()) {
202                 //must remove the worker immediately to prevent all threads from expiring
203
endWorker(worker);
204                 return null;
205             }
206             //set the thread to be busy now in case of reentrant scheduling
207
incrementBusyThreads();
208         }
209         Job job = null;
210         try {
211             job = manager.startJob();
212             //spin until a job is found or until we have been idle for too long
213
long idleStart = System.currentTimeMillis();
214             while (manager.isActive() && job == null) {
215                 long hint = manager.sleepHint();
216                 if (hint > 0)
217                     sleep(Math.min(hint, BEST_BEFORE));
218                 job = manager.startJob();
219                 //if we were already idle, and there are still no new jobs, then
220
// the thread can expire
221
synchronized (this) {
222                     if (job == null && (System.currentTimeMillis() - idleStart > BEST_BEFORE) && (numThreads - busyThreads) > MIN_THREADS) {
223                         //must remove the worker immediately to prevent all threads from expiring
224
endWorker(worker);
225                         return null;
226                     }
227                 }
228             }
229             if (job != null) {
230                 //if this job has a rule, then we are essentially acquiring a lock
231
if ((job.getRule() != null) && !(job instanceof ThreadJob)) {
232                     //don't need to re-aquire locks because it was not recorded in the graph
233
//that this thread waited to get this rule
234
manager.getLockManager().addLockThread(Thread.currentThread(), job.getRule());
235                 }
236                 //see if we need to wake another worker
237
if (manager.sleepHint() <= 0)
238                     jobQueued();
239             }
240         } finally {
241             //decrement busy thread count if we're not running a job
242
if (job == null)
243                 decrementBusyThreads();
244         }
245         return job;
246     }
247 }
248
Popular Tags