public void shutdown() {
    synchronized (cache) {
      cache.clear();
      curSet.clear();
      urgentImageIds.clear();

      // wake any sleeping threads
      cache.notifyAll();
    }

    loaderPool.shutdown();
  }
  /**
   * Get the GLImage associated with the specified image-id. If the image is cached in memory this
   * call will be very fast, otherwise it will be loaded from disk and saved to cache.
   *
   * @return null if the specified image is not in the current working set
   */
  public GLImage getGLImage(String imageId) {
    synchronized (cache) {
      GLImage img = cache.get(imageId);
      if (img == null && curSet.contains(imageId)) {
        // elevate the urgency of this image
        if (DEBUG) System.out.printf("RamTextureCache: requesting immediate load of %s\n", imageId);
        urgentImageIds.add(imageId);
        loaderPool.makeTopPriority(imageId);

        long startTime = System.currentTimeMillis();

        // wait for image to arrive
        while (curSet.contains(imageId) && !cache.contains(imageId)) {
          try {
            cache.wait(10000);

            img = cache.get(imageId);

            // timed out?
            if (img == null && (System.currentTimeMillis() - startTime) >= 10000) {
              System.err.println("RamTextureCache: timed out waiting for " + imageId);
              break;
            }
          } catch (InterruptedException e) {
            // ignore
          }
        }

        urgentImageIds.remove(imageId);

        // use fallback image
        if (img == null) img = GLImage.createNullImage();
      } else if (DEBUG && img == null)
        System.out.printf("RamTextureCache: %s not in working set\n", imageId);

      return img;
    }
  }
  public boolean shouldLoad(String filePath, int estimatedImageSize) {
    synchronized (cache) // because of race with changeWorkingSet() and getGLImage
    {
      // if somebody is waiting for it we have no choice
      if (urgentImageIds.contains(filePath) && curSet.contains(filePath)) return true;
      else {
        int priority = curSet.getPriority(filePath);

        // lower priority by one - ImageLoaderPool should not bother loading an image
        // if it's just going to replace one of equal priority - there's no point
        if (priority > Integer.MIN_VALUE) priority--;

        return cache.canStore(estimatedImageSize, priority);
      }
    }
  }