blob: f5210945860508a88056e67393dfbf9dd47363fe [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1995-2003 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.image;
27
28import java.util.Vector;
29import sun.awt.AppContext;
30
31/**
32 * An ImageFetcher is a thread used to fetch ImageFetchable objects.
33 * Once an ImageFetchable object has been fetched, the ImageFetcher
34 * thread may also be used to animate it if necessary, via the
35 * startingAnimation() / stoppingAnimation() methods.
36 *
37 * There can be up to FetcherInfo.MAX_NUM_FETCHERS_PER_APPCONTEXT
38 * ImageFetcher threads for each AppContext. A per-AppContext queue
39 * of ImageFetchables is used to track objects to fetch.
40 *
41 * @author Jim Graham
42 * @author Fred Ecks
43 */
44class ImageFetcher extends Thread {
45 static final int HIGH_PRIORITY = 8;
46 static final int LOW_PRIORITY = 3;
47 static final int ANIM_PRIORITY = 2;
48
49 static final int TIMEOUT = 5000; // Time in milliseconds to wait for an
50 // ImageFetchable to be added to the
51 // queue before an ImageFetcher dies
52
53 /**
54 * Constructor for ImageFetcher -- only called by add() below.
55 */
56 private ImageFetcher(ThreadGroup threadGroup, int index) {
57 super(threadGroup, "Image Fetcher " + index);
58 setDaemon(true);
59 }
60
61 /**
62 * Adds an ImageFetchable to the queue of items to fetch. Instantiates
63 * a new ImageFetcher if it's reasonable to do so.
64 */
65 public static void add(ImageFetchable src) {
66 final FetcherInfo info = FetcherInfo.getFetcherInfo();
67 synchronized(info.waitList) {
68 if (!info.waitList.contains(src)) {
69 info.waitList.addElement(src);
70 if (info.numWaiting == 0 &&
71 info.numFetchers < info.fetchers.length) {
72 createFetchers(info);
73 }
74 info.waitList.notify();
75 }
76 }
77 }
78
79 /**
80 * Removes an ImageFetchable from the queue of items to fetch.
81 */
82 public static void remove(ImageFetchable src) {
83 final FetcherInfo info = FetcherInfo.getFetcherInfo();
84 synchronized(info.waitList) {
85 if (info.waitList.contains(src)) {
86 info.waitList.removeElement(src);
87 }
88 }
89 }
90
91 /**
92 * Checks to see if the given thread is one of the ImageFetchers.
93 */
94 public static boolean isFetcher(Thread t) {
95 final FetcherInfo info = FetcherInfo.getFetcherInfo();
96 synchronized(info.waitList) {
97 for (int i = 0; i < info.fetchers.length; i++) {
98 if (info.fetchers[i] == t) {
99 return true;
100 }
101 }
102 }
103 return false;
104 }
105
106 /**
107 * Checks to see if the current thread is one of the ImageFetchers.
108 */
109 public static boolean amFetcher() {
110 return isFetcher(Thread.currentThread());
111 }
112
113 /**
114 * Returns the next ImageFetchable to be processed. If TIMEOUT
115 * elapses in the mean time, or if the ImageFetcher is interrupted,
116 * null is returned.
117 */
118 private static ImageFetchable nextImage() {
119 final FetcherInfo info = FetcherInfo.getFetcherInfo();
120 synchronized(info.waitList) {
121 ImageFetchable src = null;
122 long end = System.currentTimeMillis() + TIMEOUT;
123 while (src == null) {
124 while (info.waitList.size() == 0) {
125 long now = System.currentTimeMillis();
126 if (now >= end) {
127 return null;
128 }
129 try {
130 info.numWaiting++;
131 info.waitList.wait(end - now);
132 } catch (InterruptedException e) {
133 // A normal occurrence as an AppContext is disposed
134 return null;
135 } finally {
136 info.numWaiting--;
137 }
138 }
139 src = (ImageFetchable) info.waitList.elementAt(0);
140 info.waitList.removeElement(src);
141 }
142 return src;
143 }
144 }
145
146 /**
147 * The main run() method of an ImageFetcher Thread. Calls fetchloop()
148 * to do the work, then removes itself from the array of ImageFetchers.
149 */
150 public void run() {
151 final FetcherInfo info = FetcherInfo.getFetcherInfo();
152 try {
153 fetchloop();
154 } catch (Exception e) {
155 e.printStackTrace();
156 } finally {
157 synchronized(info.waitList) {
158 Thread me = Thread.currentThread();
159 for (int i = 0; i < info.fetchers.length; i++) {
160 if (info.fetchers[i] == me) {
161 info.fetchers[i] = null;
162 info.numFetchers--;
163 }
164 }
165 }
166 }
167 }
168
169 /**
170 * The main ImageFetcher loop. Repeatedly calls nextImage(), and
171 * fetches the returned ImageFetchable objects until nextImage()
172 * returns null.
173 */
174 private void fetchloop() {
175 Thread me = Thread.currentThread();
176 while (isFetcher(me)) {
177 // we're ignoring the return value and just clearing
178 // the interrupted flag, instead of bailing out if
179 // the fetcher was interrupted, as we used to,
180 // because there may be other images waiting
181 // to be fetched (see 4789067)
182 me.interrupted();
183 me.setPriority(HIGH_PRIORITY);
184 ImageFetchable src = nextImage();
185 if (src == null) {
186 return;
187 }
188 try {
189 src.doFetch();
190 } catch (Exception e) {
191 System.err.println("Uncaught error fetching image:");
192 e.printStackTrace();
193 }
194 stoppingAnimation(me);
195 }
196 }
197
198
199 /**
200 * Recycles this ImageFetcher thread as an image animator thread.
201 * Removes this ImageFetcher from the array of ImageFetchers, and
202 * resets the thread name to "ImageAnimator".
203 */
204 static void startingAnimation() {
205 final FetcherInfo info = FetcherInfo.getFetcherInfo();
206 Thread me = Thread.currentThread();
207 synchronized(info.waitList) {
208 for (int i = 0; i < info.fetchers.length; i++) {
209 if (info.fetchers[i] == me) {
210 info.fetchers[i] = null;
211 info.numFetchers--;
212 me.setName("Image Animator " + i);
213 if(info.waitList.size() > info.numWaiting) {
214 createFetchers(info);
215 }
216 return;
217 }
218 }
219 }
220 me.setPriority(ANIM_PRIORITY);
221 me.setName("Image Animator");
222 }
223
224 /**
225 * Returns this image animator thread back to service as an ImageFetcher
226 * if possible. Puts it back into the array of ImageFetchers and sets
227 * the thread name back to "Image Fetcher". If there are already the
228 * maximum number of ImageFetchers, this method simply returns, and
229 * fetchloop() will drop out when it sees that this thread isn't one of
230 * the ImageFetchers, and this thread will die.
231 */
232 private static void stoppingAnimation(Thread me) {
233 final FetcherInfo info = FetcherInfo.getFetcherInfo();
234 synchronized(info.waitList) {
235 int index = -1;
236 for (int i = 0; i < info.fetchers.length; i++) {
237 if (info.fetchers[i] == me) {
238 return;
239 }
240 if (info.fetchers[i] == null) {
241 index = i;
242 }
243 }
244 if (index >= 0) {
245 info.fetchers[index] = me;
246 info.numFetchers++;
247 me.setName("Image Fetcher " + index);
248 return;
249 }
250 }
251 }
252
253 /**
254 * Create and start ImageFetcher threads in the appropriate ThreadGroup.
255 */
256 private static void createFetchers(final FetcherInfo info) {
257 // We need to instantiate a new ImageFetcher thread.
258 // First, figure out which ThreadGroup we'll put the
259 // new ImageFetcher into
260 final AppContext appContext = AppContext.getAppContext();
261 ThreadGroup threadGroup = appContext.getThreadGroup();
262 ThreadGroup fetcherThreadGroup;
263 try {
264 if (threadGroup.getParent() != null) {
265 // threadGroup is not the root, so we proceed
266 fetcherThreadGroup = threadGroup;
267 } else {
268 // threadGroup is the root ("system") ThreadGroup.
269 // We instead want to use its child: the "main"
270 // ThreadGroup. Thus, we start with the current
271 // ThreadGroup, and go up the tree until
272 // threadGroup.getParent().getParent() == null.
273 threadGroup = Thread.currentThread().getThreadGroup();
274 ThreadGroup parent = threadGroup.getParent();
275 while ((parent != null)
276 && (parent.getParent() != null)) {
277 threadGroup = parent;
278 parent = threadGroup.getParent();
279 }
280 fetcherThreadGroup = threadGroup;
281 }
282 } catch (SecurityException e) {
283 // Not allowed access to parent ThreadGroup -- just use
284 // the AppContext's ThreadGroup
285 fetcherThreadGroup = appContext.getThreadGroup();
286 }
287 final ThreadGroup fetcherGroup = fetcherThreadGroup;
288
289 java.security.AccessController.doPrivileged(
290 new java.security.PrivilegedAction() {
291 public Object run() {
292 for (int i = 0; i < info.fetchers.length; i++) {
293 if (info.fetchers[i] == null) {
294 info.fetchers[i] = new ImageFetcher(
295 fetcherGroup, i);
296 info.fetchers[i].start();
297 info.numFetchers++;
298 break;
299 }
300 }
301 return null;
302 }
303 });
304 return;
305 }
306
307}
308
309/**
310 * The FetcherInfo class encapsulates the per-AppContext ImageFetcher
311 * information. This includes the array of ImageFetchers, as well as
312 * the queue of ImageFetchable objects.
313 */
314class FetcherInfo {
315 static final int MAX_NUM_FETCHERS_PER_APPCONTEXT = 4;
316
317 Thread[] fetchers;
318 int numFetchers;
319 int numWaiting;
320 Vector waitList;
321
322 private FetcherInfo() {
323 fetchers = new Thread[MAX_NUM_FETCHERS_PER_APPCONTEXT];
324 numFetchers = 0;
325 numWaiting = 0;
326 waitList = new Vector();
327 }
328
329 /* The key to put()/get() the FetcherInfo into/from the AppContext. */
330 private static final Object FETCHER_INFO_KEY =
331 new StringBuffer("FetcherInfo");
332
333 static FetcherInfo getFetcherInfo() {
334 AppContext appContext = AppContext.getAppContext();
335 synchronized(appContext) {
336 FetcherInfo info = (FetcherInfo)appContext.get(FETCHER_INFO_KEY);
337 if (info == null) {
338 info = new FetcherInfo();
339 appContext.put(FETCHER_INFO_KEY, info);
340 }
341 return info;
342 }
343 }
344}