0N/A/*
3909N/A * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.image;
0N/A
0N/Aimport java.util.Vector;
0N/Aimport sun.awt.AppContext;
0N/A
0N/A/**
0N/A * An ImageFetcher is a thread used to fetch ImageFetchable objects.
0N/A * Once an ImageFetchable object has been fetched, the ImageFetcher
0N/A * thread may also be used to animate it if necessary, via the
0N/A * startingAnimation() / stoppingAnimation() methods.
0N/A *
0N/A * There can be up to FetcherInfo.MAX_NUM_FETCHERS_PER_APPCONTEXT
0N/A * ImageFetcher threads for each AppContext. A per-AppContext queue
0N/A * of ImageFetchables is used to track objects to fetch.
0N/A *
0N/A * @author Jim Graham
0N/A * @author Fred Ecks
0N/A */
0N/Aclass ImageFetcher extends Thread {
0N/A static final int HIGH_PRIORITY = 8;
0N/A static final int LOW_PRIORITY = 3;
0N/A static final int ANIM_PRIORITY = 2;
0N/A
0N/A static final int TIMEOUT = 5000; // Time in milliseconds to wait for an
0N/A // ImageFetchable to be added to the
0N/A // queue before an ImageFetcher dies
0N/A
0N/A /**
0N/A * Constructor for ImageFetcher -- only called by add() below.
0N/A */
0N/A private ImageFetcher(ThreadGroup threadGroup, int index) {
0N/A super(threadGroup, "Image Fetcher " + index);
0N/A setDaemon(true);
0N/A }
0N/A
0N/A /**
0N/A * Adds an ImageFetchable to the queue of items to fetch. Instantiates
0N/A * a new ImageFetcher if it's reasonable to do so.
3536N/A * If there is no available fetcher to process an ImageFetchable, then
3536N/A * reports failure to caller.
0N/A */
3536N/A public static boolean add(ImageFetchable src) {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A synchronized(info.waitList) {
0N/A if (!info.waitList.contains(src)) {
0N/A info.waitList.addElement(src);
0N/A if (info.numWaiting == 0 &&
0N/A info.numFetchers < info.fetchers.length) {
0N/A createFetchers(info);
0N/A }
3536N/A /* Creation of new fetcher may fail due to high vm load
3536N/A * or some other reason.
3536N/A * If there is already exist, but busy, fetcher, we leave
3536N/A * the src in queue (it will be handled by existing
3536N/A * fetcher later).
3536N/A * Otherwise, we report failure: there is no fetcher
3536N/A * to handle the src.
3536N/A */
3536N/A if (info.numFetchers > 0) {
3536N/A info.waitList.notify();
3536N/A } else {
3536N/A info.waitList.removeElement(src);
3536N/A return false;
3536N/A }
0N/A }
0N/A }
3536N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Removes an ImageFetchable from the queue of items to fetch.
0N/A */
0N/A public static void remove(ImageFetchable src) {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A synchronized(info.waitList) {
0N/A if (info.waitList.contains(src)) {
0N/A info.waitList.removeElement(src);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Checks to see if the given thread is one of the ImageFetchers.
0N/A */
0N/A public static boolean isFetcher(Thread t) {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A synchronized(info.waitList) {
0N/A for (int i = 0; i < info.fetchers.length; i++) {
0N/A if (info.fetchers[i] == t) {
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Checks to see if the current thread is one of the ImageFetchers.
0N/A */
0N/A public static boolean amFetcher() {
0N/A return isFetcher(Thread.currentThread());
0N/A }
0N/A
0N/A /**
0N/A * Returns the next ImageFetchable to be processed. If TIMEOUT
0N/A * elapses in the mean time, or if the ImageFetcher is interrupted,
0N/A * null is returned.
0N/A */
0N/A private static ImageFetchable nextImage() {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A synchronized(info.waitList) {
0N/A ImageFetchable src = null;
0N/A long end = System.currentTimeMillis() + TIMEOUT;
0N/A while (src == null) {
0N/A while (info.waitList.size() == 0) {
0N/A long now = System.currentTimeMillis();
0N/A if (now >= end) {
0N/A return null;
0N/A }
0N/A try {
0N/A info.numWaiting++;
0N/A info.waitList.wait(end - now);
0N/A } catch (InterruptedException e) {
0N/A // A normal occurrence as an AppContext is disposed
0N/A return null;
0N/A } finally {
0N/A info.numWaiting--;
0N/A }
0N/A }
0N/A src = (ImageFetchable) info.waitList.elementAt(0);
0N/A info.waitList.removeElement(src);
0N/A }
0N/A return src;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * The main run() method of an ImageFetcher Thread. Calls fetchloop()
0N/A * to do the work, then removes itself from the array of ImageFetchers.
0N/A */
0N/A public void run() {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A try {
0N/A fetchloop();
0N/A } catch (Exception e) {
0N/A e.printStackTrace();
0N/A } finally {
0N/A synchronized(info.waitList) {
0N/A Thread me = Thread.currentThread();
0N/A for (int i = 0; i < info.fetchers.length; i++) {
0N/A if (info.fetchers[i] == me) {
0N/A info.fetchers[i] = null;
0N/A info.numFetchers--;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * The main ImageFetcher loop. Repeatedly calls nextImage(), and
0N/A * fetches the returned ImageFetchable objects until nextImage()
0N/A * returns null.
0N/A */
0N/A private void fetchloop() {
0N/A Thread me = Thread.currentThread();
0N/A while (isFetcher(me)) {
0N/A // we're ignoring the return value and just clearing
0N/A // the interrupted flag, instead of bailing out if
0N/A // the fetcher was interrupted, as we used to,
0N/A // because there may be other images waiting
0N/A // to be fetched (see 4789067)
0N/A me.interrupted();
0N/A me.setPriority(HIGH_PRIORITY);
0N/A ImageFetchable src = nextImage();
0N/A if (src == null) {
0N/A return;
0N/A }
0N/A try {
0N/A src.doFetch();
0N/A } catch (Exception e) {
0N/A System.err.println("Uncaught error fetching image:");
0N/A e.printStackTrace();
0N/A }
0N/A stoppingAnimation(me);
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Recycles this ImageFetcher thread as an image animator thread.
0N/A * Removes this ImageFetcher from the array of ImageFetchers, and
0N/A * resets the thread name to "ImageAnimator".
0N/A */
0N/A static void startingAnimation() {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A Thread me = Thread.currentThread();
0N/A synchronized(info.waitList) {
0N/A for (int i = 0; i < info.fetchers.length; i++) {
0N/A if (info.fetchers[i] == me) {
0N/A info.fetchers[i] = null;
0N/A info.numFetchers--;
0N/A me.setName("Image Animator " + i);
0N/A if(info.waitList.size() > info.numWaiting) {
0N/A createFetchers(info);
0N/A }
0N/A return;
0N/A }
0N/A }
0N/A }
0N/A me.setPriority(ANIM_PRIORITY);
0N/A me.setName("Image Animator");
0N/A }
0N/A
0N/A /**
0N/A * Returns this image animator thread back to service as an ImageFetcher
0N/A * if possible. Puts it back into the array of ImageFetchers and sets
0N/A * the thread name back to "Image Fetcher". If there are already the
0N/A * maximum number of ImageFetchers, this method simply returns, and
0N/A * fetchloop() will drop out when it sees that this thread isn't one of
0N/A * the ImageFetchers, and this thread will die.
0N/A */
0N/A private static void stoppingAnimation(Thread me) {
0N/A final FetcherInfo info = FetcherInfo.getFetcherInfo();
0N/A synchronized(info.waitList) {
0N/A int index = -1;
0N/A for (int i = 0; i < info.fetchers.length; i++) {
0N/A if (info.fetchers[i] == me) {
0N/A return;
0N/A }
0N/A if (info.fetchers[i] == null) {
0N/A index = i;
0N/A }
0N/A }
0N/A if (index >= 0) {
0N/A info.fetchers[index] = me;
0N/A info.numFetchers++;
0N/A me.setName("Image Fetcher " + index);
0N/A return;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Create and start ImageFetcher threads in the appropriate ThreadGroup.
0N/A */
0N/A private static void createFetchers(final FetcherInfo info) {
0N/A // We need to instantiate a new ImageFetcher thread.
0N/A // First, figure out which ThreadGroup we'll put the
0N/A // new ImageFetcher into
0N/A final AppContext appContext = AppContext.getAppContext();
0N/A ThreadGroup threadGroup = appContext.getThreadGroup();
0N/A ThreadGroup fetcherThreadGroup;
0N/A try {
0N/A if (threadGroup.getParent() != null) {
0N/A // threadGroup is not the root, so we proceed
0N/A fetcherThreadGroup = threadGroup;
0N/A } else {
0N/A // threadGroup is the root ("system") ThreadGroup.
0N/A // We instead want to use its child: the "main"
0N/A // ThreadGroup. Thus, we start with the current
0N/A // ThreadGroup, and go up the tree until
0N/A // threadGroup.getParent().getParent() == null.
0N/A threadGroup = Thread.currentThread().getThreadGroup();
0N/A ThreadGroup parent = threadGroup.getParent();
0N/A while ((parent != null)
0N/A && (parent.getParent() != null)) {
0N/A threadGroup = parent;
0N/A parent = threadGroup.getParent();
0N/A }
0N/A fetcherThreadGroup = threadGroup;
0N/A }
0N/A } catch (SecurityException e) {
0N/A // Not allowed access to parent ThreadGroup -- just use
0N/A // the AppContext's ThreadGroup
0N/A fetcherThreadGroup = appContext.getThreadGroup();
0N/A }
0N/A final ThreadGroup fetcherGroup = fetcherThreadGroup;
0N/A
0N/A java.security.AccessController.doPrivileged(
0N/A new java.security.PrivilegedAction() {
0N/A public Object run() {
0N/A for (int i = 0; i < info.fetchers.length; i++) {
0N/A if (info.fetchers[i] == null) {
3536N/A ImageFetcher f = new ImageFetcher(
0N/A fetcherGroup, i);
3536N/A try {
3536N/A f.start();
3536N/A info.fetchers[i] = f;
3536N/A info.numFetchers++;
3536N/A break;
3536N/A } catch (Error e) {
3536N/A }
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A return;
0N/A }
0N/A
0N/A}
0N/A
0N/A/**
0N/A * The FetcherInfo class encapsulates the per-AppContext ImageFetcher
0N/A * information. This includes the array of ImageFetchers, as well as
0N/A * the queue of ImageFetchable objects.
0N/A */
0N/Aclass FetcherInfo {
0N/A static final int MAX_NUM_FETCHERS_PER_APPCONTEXT = 4;
0N/A
0N/A Thread[] fetchers;
0N/A int numFetchers;
0N/A int numWaiting;
0N/A Vector waitList;
0N/A
0N/A private FetcherInfo() {
0N/A fetchers = new Thread[MAX_NUM_FETCHERS_PER_APPCONTEXT];
0N/A numFetchers = 0;
0N/A numWaiting = 0;
0N/A waitList = new Vector();
0N/A }
0N/A
0N/A /* The key to put()/get() the FetcherInfo into/from the AppContext. */
0N/A private static final Object FETCHER_INFO_KEY =
0N/A new StringBuffer("FetcherInfo");
0N/A
0N/A static FetcherInfo getFetcherInfo() {
0N/A AppContext appContext = AppContext.getAppContext();
0N/A synchronized(appContext) {
0N/A FetcherInfo info = (FetcherInfo)appContext.get(FETCHER_INFO_KEY);
0N/A if (info == null) {
0N/A info = new FetcherInfo();
0N/A appContext.put(FETCHER_INFO_KEY, info);
0N/A }
0N/A return info;
0N/A }
0N/A }
0N/A}