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