1 /* 2 * Copyright (c) 1995, 2011, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt.image; 27 28 import java.util.Vector; 29 import 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 */ 44 class 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 * If there is no available fetcher to process an ImageFetchable, then 65 * reports failure to caller. 66 */ 67 public static boolean add(ImageFetchable src) { 68 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 69 synchronized(info.waitList) { 70 if (!info.waitList.contains(src)) { 71 info.waitList.addElement(src); 72 if (info.numWaiting == 0 && 73 info.numFetchers < info.fetchers.length) { 74 createFetchers(info); 75 } 76 /* Creation of new fetcher may fail due to high vm load 77 * or some other reason. 78 * If there is already exist, but busy, fetcher, we leave 79 * the src in queue (it will be handled by existing 80 * fetcher later). 81 * Otherwise, we report failure: there is no fetcher 82 * to handle the src. 83 */ 84 if (info.numFetchers > 0) { 85 info.waitList.notify(); 86 } else { 87 info.waitList.removeElement(src); 88 return false; 89 } 90 } 91 } 92 return true; 93 } 94 95 /** 96 * Removes an ImageFetchable from the queue of items to fetch. 97 */ 98 public static void remove(ImageFetchable src) { 99 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 100 synchronized(info.waitList) { 101 if (info.waitList.contains(src)) { 102 info.waitList.removeElement(src); 103 } 104 } 105 } 106 107 /** 108 * Checks to see if the given thread is one of the ImageFetchers. 109 */ 110 public static boolean isFetcher(Thread t) { 111 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 112 synchronized(info.waitList) { 113 for (int i = 0; i < info.fetchers.length; i++) { 114 if (info.fetchers[i] == t) { 115 return true; 116 } 117 } 118 } 119 return false; 120 } 121 122 /** 123 * Checks to see if the current thread is one of the ImageFetchers. 124 */ 125 public static boolean amFetcher() { 126 return isFetcher(Thread.currentThread()); 127 } 128 129 /** 130 * Returns the next ImageFetchable to be processed. If TIMEOUT 131 * elapses in the mean time, or if the ImageFetcher is interrupted, 132 * null is returned. 133 */ 134 private static ImageFetchable nextImage() { 135 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 136 synchronized(info.waitList) { 137 ImageFetchable src = null; 138 long end = System.currentTimeMillis() + TIMEOUT; 139 while (src == null) { 140 while (info.waitList.size() == 0) { 141 long now = System.currentTimeMillis(); 142 if (now >= end) { 143 return null; 144 } 145 try { 146 info.numWaiting++; 147 info.waitList.wait(end - now); 148 } catch (InterruptedException e) { 149 // A normal occurrence as an AppContext is disposed 150 return null; 151 } finally { 152 info.numWaiting--; 153 } 154 } 155 src = (ImageFetchable) info.waitList.elementAt(0); 156 info.waitList.removeElement(src); 157 } 158 return src; 159 } 160 } 161 162 /** 163 * The main run() method of an ImageFetcher Thread. Calls fetchloop() 164 * to do the work, then removes itself from the array of ImageFetchers. 165 */ 166 public void run() { 167 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 168 try { 169 fetchloop(); 170 } catch (Exception e) { 171 e.printStackTrace(); 172 } finally { 173 synchronized(info.waitList) { 174 Thread me = Thread.currentThread(); 175 for (int i = 0; i < info.fetchers.length; i++) { 176 if (info.fetchers[i] == me) { 177 info.fetchers[i] = null; 178 info.numFetchers--; 179 } 180 } 181 } 182 } 183 } 184 185 /** 186 * The main ImageFetcher loop. Repeatedly calls nextImage(), and 187 * fetches the returned ImageFetchable objects until nextImage() 188 * returns null. 189 */ 190 private void fetchloop() { 191 Thread me = Thread.currentThread(); 192 while (isFetcher(me)) { 193 // we're ignoring the return value and just clearing 194 // the interrupted flag, instead of bailing out if 195 // the fetcher was interrupted, as we used to, 196 // because there may be other images waiting 197 // to be fetched (see 4789067) 198 me.interrupted(); 199 me.setPriority(HIGH_PRIORITY); 200 ImageFetchable src = nextImage(); 201 if (src == null) { 202 return; 203 } 204 try { 205 src.doFetch(); 206 } catch (Exception e) { 207 System.err.println("Uncaught error fetching image:"); 208 e.printStackTrace(); 209 } 210 stoppingAnimation(me); 211 } 212 } 213 214 215 /** 216 * Recycles this ImageFetcher thread as an image animator thread. 217 * Removes this ImageFetcher from the array of ImageFetchers, and 218 * resets the thread name to "ImageAnimator". 219 */ 220 static void startingAnimation() { 221 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 222 Thread me = Thread.currentThread(); 223 synchronized(info.waitList) { 224 for (int i = 0; i < info.fetchers.length; i++) { 225 if (info.fetchers[i] == me) { 226 info.fetchers[i] = null; 227 info.numFetchers--; 228 me.setName("Image Animator " + i); 229 if(info.waitList.size() > info.numWaiting) { 230 createFetchers(info); 231 } 232 return; 233 } 234 } 235 } 236 me.setPriority(ANIM_PRIORITY); 237 me.setName("Image Animator"); 238 } 239 240 /** 241 * Returns this image animator thread back to service as an ImageFetcher 242 * if possible. Puts it back into the array of ImageFetchers and sets 243 * the thread name back to "Image Fetcher". If there are already the 244 * maximum number of ImageFetchers, this method simply returns, and 245 * fetchloop() will drop out when it sees that this thread isn't one of 246 * the ImageFetchers, and this thread will die. 247 */ 248 private static void stoppingAnimation(Thread me) { 249 final FetcherInfo info = FetcherInfo.getFetcherInfo(); 250 synchronized(info.waitList) { 251 int index = -1; 252 for (int i = 0; i < info.fetchers.length; i++) { 253 if (info.fetchers[i] == me) { 254 return; 255 } 256 if (info.fetchers[i] == null) { 257 index = i; 258 } 259 } 260 if (index >= 0) { 261 info.fetchers[index] = me; 262 info.numFetchers++; 263 me.setName("Image Fetcher " + index); 264 return; 265 } 266 } 267 } 268 269 /** 270 * Create and start ImageFetcher threads in the appropriate ThreadGroup. 271 */ 272 private static void createFetchers(final FetcherInfo info) { 273 // We need to instantiate a new ImageFetcher thread. 274 // First, figure out which ThreadGroup we'll put the 275 // new ImageFetcher into 276 final AppContext appContext = AppContext.getAppContext(); 277 ThreadGroup threadGroup = appContext.getThreadGroup(); 278 ThreadGroup fetcherThreadGroup; 279 try { 280 if (threadGroup.getParent() != null) { 281 // threadGroup is not the root, so we proceed 282 fetcherThreadGroup = threadGroup; 283 } else { 284 // threadGroup is the root ("system") ThreadGroup. 285 // We instead want to use its child: the "main" 286 // ThreadGroup. Thus, we start with the current 287 // ThreadGroup, and go up the tree until 288 // threadGroup.getParent().getParent() == null. 289 threadGroup = Thread.currentThread().getThreadGroup(); 290 ThreadGroup parent = threadGroup.getParent(); 291 while ((parent != null) 292 && (parent.getParent() != null)) { 293 threadGroup = parent; 294 parent = threadGroup.getParent(); 295 } 296 fetcherThreadGroup = threadGroup; 297 } 298 } catch (SecurityException e) { 299 // Not allowed access to parent ThreadGroup -- just use 300 // the AppContext's ThreadGroup 301 fetcherThreadGroup = appContext.getThreadGroup(); 302 } 303 final ThreadGroup fetcherGroup = fetcherThreadGroup; 304 305 java.security.AccessController.doPrivileged( 306 new java.security.PrivilegedAction() { 307 public Object run() { 308 for (int i = 0; i < info.fetchers.length; i++) { 309 if (info.fetchers[i] == null) { 310 ImageFetcher f = new ImageFetcher( 311 fetcherGroup, i); 312 try { 313 f.start(); 314 info.fetchers[i] = f; 315 info.numFetchers++; 316 break; 317 } catch (Error e) { 318 } 319 } 320 } 321 return null; 322 } 323 }); 324 return; 325 } 326 327 } 328 329 /** 330 * The FetcherInfo class encapsulates the per-AppContext ImageFetcher 331 * information. This includes the array of ImageFetchers, as well as 332 * the queue of ImageFetchable objects. 333 */ 334 class FetcherInfo { 335 static final int MAX_NUM_FETCHERS_PER_APPCONTEXT = 4; 336 337 Thread[] fetchers; 338 int numFetchers; 339 int numWaiting; 340 Vector waitList; 341 342 private FetcherInfo() { 343 fetchers = new Thread[MAX_NUM_FETCHERS_PER_APPCONTEXT]; 344 numFetchers = 0; 345 numWaiting = 0; 346 waitList = new Vector(); 347 } 348 349 /* The key to put()/get() the FetcherInfo into/from the AppContext. */ 350 private static final Object FETCHER_INFO_KEY = 351 new StringBuffer("FetcherInfo"); 352 353 static FetcherInfo getFetcherInfo() { 354 AppContext appContext = AppContext.getAppContext(); 355 synchronized(appContext) { 356 FetcherInfo info = (FetcherInfo)appContext.get(FETCHER_INFO_KEY); 357 if (info == null) { 358 info = new FetcherInfo(); 359 appContext.put(FETCHER_INFO_KEY, info); 360 } 361 return info; 362 } 363 } 364 }